From 2fd25d77f031f48f501344b5d77aeea62b42da88 Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Mon, 15 Sep 2025 18:34:51 +0300 Subject: [PATCH 01/43] MDEV-37541 Race of rolling back and committing transaction to binlog Two transactions could binlog their completions in opposite to how it is done in Engine. That is is rare situations ROLLBACK in Engine of the dependency parent transaction could be scheduled by the transaction before its binlogging. That give a follower dependency child one get binlogged ahead of the parent. For fixing this bug its necessary to ensure the binlogging phase is always first one in the internal one-phase rollback protocol. The commit makes sure the binlog handlerton always rollbacks as first handlerton in no-2pc cases. An added test demonstrates how the child could otherwise reach binlog before its parent. --- .../suite/binlog/r/binlog_mdev37541.result | 41 +++++++++++++++ .../suite/binlog/t/binlog_mdev37541.test | 50 +++++++++++++++++++ sql/handler.cc | 11 +++- sql/log.cc | 1 + 4 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 mysql-test/suite/binlog/r/binlog_mdev37541.result create mode 100644 mysql-test/suite/binlog/t/binlog_mdev37541.test diff --git a/mysql-test/suite/binlog/r/binlog_mdev37541.result b/mysql-test/suite/binlog/r/binlog_mdev37541.result new file mode 100644 index 00000000000..0c9f162a8f4 --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_mdev37541.result @@ -0,0 +1,41 @@ +create table t1 (a int primary key, b text) engine=innodb; +connect trx1_rollback,localhost,root,,; +CREATE TABLE t_x (a int) engine=MEMORY; +SET binlog_format=row; +CREATE TEMPORARY TABLE tt_tmp ( id INT ) ENGINE = Memory; +BEGIN; +insert into t_x values (1); +drop temporary table tt_tmp; +insert into t1 values (99, "gotta log first"); +SET DEBUG_SYNC= 'reset'; +SET DEBUG_SYNC= "before_group_commit_queue SIGNAL trx1_at_log WAIT_FOR trx1_go"; +ROLLBACK; +connect trx2_commit,localhost,root,,; +insert into t1 values (99, "second best in binlog"); +connection default; +SET DEBUG_SYNC= "now WAIT_FOR trx1_at_log"; +SET DEBUG_SYNC= "now SIGNAL trx1_go"; +connection trx2_commit; +select * from t1; +a b +99 second best in binlog +# Prove the logging order is Trx1, Trx2 +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Annotate_rows # # insert into t_x values (1) +master-bin.000001 # Table_map # # table_id: # (test.t_x) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # COMMIT +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Annotate_rows # # insert into t1 values (99, "gotta log first") +master-bin.000001 # Table_map # # table_id: # (test.t1) +master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F +master-bin.000001 # Query # # ROLLBACK +master-bin.000001 # Gtid # # BEGIN GTID #-#-# +master-bin.000001 # Query # # use `test`; insert into t1 values (99, "second best in binlog") +master-bin.000001 # Xid # # COMMIT /* XID */ +drop table t_x, t1; +disconnect trx1_rollback; +disconnect trx2_commit; +# end of the tests diff --git a/mysql-test/suite/binlog/t/binlog_mdev37541.test b/mysql-test/suite/binlog/t/binlog_mdev37541.test new file mode 100644 index 00000000000..de94f5a5ebd --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_mdev37541.test @@ -0,0 +1,50 @@ +--source include/have_debug_sync.inc +--source include/have_innodb.inc +--source include/have_binlog_format_mixed.inc + +create table t1 (a int primary key, b text) engine=innodb; + +connect(trx1_rollback,localhost,root,,); +CREATE TABLE t_x (a int) engine=MEMORY; + +--let $master_file= query_get_value(SHOW MASTER STATUS, File, 1) +--let $binlog_start=query_get_value(SHOW MASTER STATUS, Position, 1) +SET binlog_format=row; +CREATE TEMPORARY TABLE tt_tmp ( id INT ) ENGINE = Memory; +BEGIN; + insert into t_x values (1); + drop temporary table tt_tmp; + insert into t1 values (99, "gotta log first"); + +SET DEBUG_SYNC= 'reset'; +SET DEBUG_SYNC= "before_group_commit_queue SIGNAL trx1_at_log WAIT_FOR trx1_go"; + +--send ROLLBACK + +connect(trx2_commit,localhost,root,,); +--send insert into t1 values (99, "second best in binlog") + +connection default; + +# Make sure ROLLBACK is in the binlogging phase .. +SET DEBUG_SYNC= "now WAIT_FOR trx1_at_log"; +# .. and the contender trx2 in the locking phase .. +let $wait_condition= + select count(*) = 1 from information_schema.innodb_trx + where trx_state = "LOCK WAIT" and trx_query like "%insert into t1 values%"; +source include/wait_condition.inc; +# .. when both provided unfreeze the trx:s .. +SET DEBUG_SYNC= "now SIGNAL trx1_go"; + +connection trx2_commit; +reap; +select * from t1; + +# .. to observe proper binlogging. +--echo # Prove the logging order is Trx1, Trx2 +--source include/show_binlog_events.inc + +drop table t_x, t1; +disconnect trx1_rollback; +disconnect trx2_commit; +--echo # end of the tests diff --git a/sql/handler.cc b/sql/handler.cc index 6219569b429..67ccb31e4f9 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2308,11 +2308,18 @@ int ha_rollback_trans(THD *thd, bool all) if (is_real_trans) /* not a statement commit */ thd->stmt_map.close_transient_cursors(); + int err; + if (has_binlog_hton(ha_info) && + (err= binlog_hton->rollback(binlog_hton, thd, all))) + { + my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); + error= 1; + } for (; ha_info; ha_info= ha_info_next) { - int err; handlerton *ht= ha_info->ht(); - if ((err= ht->rollback(ht, thd, all))) + + if (ht != binlog_hton && (err= ht->rollback(ht, thd, all))) { // cannot happen my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), err); diff --git a/sql/log.cc b/sql/log.cc index c41889cb108..962557ad769 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -8235,6 +8235,7 @@ end: bool MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry) { + DEBUG_SYNC(entry->thd, "before_group_commit_queue"); int is_leader= queue_for_group_commit(entry); #ifdef WITH_WSREP /* commit order was released in queue_for_group_commit() call, From 85a801cb241b321dba83a9a2b9ec6cf79537a299 Mon Sep 17 00:00:00 2001 From: Mohammad Tafzeel Shams Date: Thu, 27 Nov 2025 09:58:22 +0530 Subject: [PATCH 02/43] MDEV-24558: Assertion !index->is_spatial() failed Issue: Adding a SPATIAL INDEX triggers a debug assertion failure when the mysql.innodb_table_stats schema is missing. Fix: dict_stats_update_transient_for_index() now exits early with empty statistics for any index that is not a regular B-tree. --- .../innodb_gis/r/create_spatial_index.result | 17 ++++++---------- .../innodb_gis/t/create_spatial_index.test | 20 ++++++++----------- storage/innobase/dict/dict0stats.cc | 1 + 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/mysql-test/suite/innodb_gis/r/create_spatial_index.result b/mysql-test/suite/innodb_gis/r/create_spatial_index.result index d3c69294c10..d9194713685 100644 --- a/mysql-test/suite/innodb_gis/r/create_spatial_index.result +++ b/mysql-test/suite/innodb_gis/r/create_spatial_index.result @@ -1281,18 +1281,13 @@ geometrycollection(point(1,-1)),b ); 1 drop table t1; -CREATE TABLE t1(c1 POINT NOT NULL); -DROP TABLE mysql.innodb_table_stats; +# +# MDEV-24558 : Assertion !index->is_spatial() failed +# +CREATE TABLE t1(c1 POINT NOT NULL) ENGINE=InnoDB; +RENAME TABLE mysql.innodb_table_stats TO mysql.innodb_table_stats_; CALL mtr.add_suppression("InnoDB: Table `mysql`.`innodb_table_stats` not found."); CALL mtr.add_suppression("InnoDB: Fetch of persistent statistics requested for table `test`.`t1` but the required system tables mysql.innodb_table_stats and mysql.innodb_index_stats are not present or have unexpected structure. Using transient stats instead."); CREATE SPATIAL INDEX idx2 ON t1(c1); DROP TABLE t1; -CREATE TABLE mysql.innodb_table_stats ( -database_name varchar(64) COLLATE utf8_bin NOT NULL, -table_name varchar(199) COLLATE utf8_bin NOT NULL, -last_update timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -n_rows bigint(20) unsigned NOT NULL, -clustered_index_size bigint(20) unsigned NOT NULL, -sum_of_other_index_sizes bigint(20) unsigned NOT NULL, -PRIMARY KEY (`database_name`,`table_name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0; +RENAME TABLE mysql.innodb_table_stats_ TO mysql.innodb_table_stats; diff --git a/mysql-test/suite/innodb_gis/t/create_spatial_index.test b/mysql-test/suite/innodb_gis/t/create_spatial_index.test index 5278292b56c..9adb04ce405 100644 --- a/mysql-test/suite/innodb_gis/t/create_spatial_index.test +++ b/mysql-test/suite/innodb_gis/t/create_spatial_index.test @@ -10,6 +10,7 @@ # Test spatial index with table have no primary key column # Test spatial index with table have no auto_increment # Test spatial index with check constraint +# Test spatial index with missing mysql.innodb_table_stats # ********************************************************** --source include/have_innodb.inc --source include/have_innodb_16k.inc @@ -1164,20 +1165,15 @@ select 1 from t1 where st_intersects( ); drop table t1; -# Reproduce case for updating statistic after droping stats info table. -CREATE TABLE t1(c1 POINT NOT NULL); -DROP TABLE mysql.innodb_table_stats; +# Test spatial index with missing mysql.innodb_table_stats +--echo # +--echo # MDEV-24558 : Assertion !index->is_spatial() failed +--echo # +CREATE TABLE t1(c1 POINT NOT NULL) ENGINE=InnoDB; +RENAME TABLE mysql.innodb_table_stats TO mysql.innodb_table_stats_; CALL mtr.add_suppression("InnoDB: Table `mysql`.`innodb_table_stats` not found."); CALL mtr.add_suppression("InnoDB: Fetch of persistent statistics requested for table `test`.`t1` but the required system tables mysql.innodb_table_stats and mysql.innodb_index_stats are not present or have unexpected structure. Using transient stats instead."); CREATE SPATIAL INDEX idx2 ON t1(c1); DROP TABLE t1; -CREATE TABLE mysql.innodb_table_stats ( - database_name varchar(64) COLLATE utf8_bin NOT NULL, - table_name varchar(199) COLLATE utf8_bin NOT NULL, - last_update timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - n_rows bigint(20) unsigned NOT NULL, - clustered_index_size bigint(20) unsigned NOT NULL, - sum_of_other_index_sizes bigint(20) unsigned NOT NULL, - PRIMARY KEY (`database_name`,`table_name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin STATS_PERSISTENT=0; +RENAME TABLE mysql.innodb_table_stats_ TO mysql.innodb_table_stats; diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index 49a604ef9f5..b98a975f7ad 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -1152,6 +1152,7 @@ dummy_empty: goto dummy_empty; #endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */ } else if (dict_index_is_online_ddl(index) || !index->is_committed() + || !index->is_btree() || !index->table->space) { goto dummy_empty; } else { From 11dc83e06fe59e18c93d50d3d5de4c48347e6ca7 Mon Sep 17 00:00:00 2001 From: Monty Date: Mon, 1 Dec 2025 10:40:25 +0200 Subject: [PATCH 03/43] Remove 'auto' from sql_class.cc This is to make the code more readable. --- sql/sql_class.cc | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 62d1cb068c3..e9de780adbc 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -90,7 +90,7 @@ char empty_c_string[1]= {0}; /* used for not defined db */ extern "C" const uchar *get_var_key(const void *entry_, size_t *length, my_bool) { - auto entry= static_cast(entry_); + const user_var_entry *entry= static_cast(entry_); *length= entry->name.length; return reinterpret_cast(entry->name.str); } @@ -109,7 +109,7 @@ extern "C" void free_user_var(void *entry_) extern "C" const uchar *get_sequence_last_key(const void *entry_, size_t *length, my_bool) { - auto *entry= static_cast(entry_); + const SEQUENCE_LAST_VALUE *entry= static_cast(entry_); *length= entry->length; return entry->key; } @@ -3889,7 +3889,7 @@ bool select_max_min_finder_subselect::cmp_time() { Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); THD *thd= current_thd; - auto val1= cache->val_time_packed(thd), val2= maxmin->val_time_packed(thd); + longlong val1= cache->val_time_packed(thd), val2= maxmin->val_time_packed(thd); /* Ignore NULLs for ANY and keep them for ALL subqueries */ if (cache->null_value) @@ -4163,7 +4163,7 @@ C_MODE_START static const uchar *get_statement_id_as_hash_key(const void *record, size_t *key_length, my_bool) { - auto statement= static_cast(record); + const Statement *statement= static_cast(record); *key_length= sizeof(statement->id); return reinterpret_cast(&(statement)->id); } @@ -4176,7 +4176,7 @@ static void delete_statement_as_hash_key(void *key) static const uchar *get_stmt_name_hash_key(const void *entry_, size_t *length, my_bool) { - auto entry= static_cast(entry_); + const Statement *entry= static_cast(entry_); *length= entry->name.length; return reinterpret_cast(entry->name.str); } @@ -5111,10 +5111,10 @@ void destroy_thd(MYSQL_THD thd) extern "C" pthread_key(struct st_my_thread_var *, THR_KEY_mysys); MYSQL_THD create_background_thd() { - auto save_thd = current_thd; + THD *save_thd= current_thd; set_current_thd(nullptr); - auto save_mysysvar= pthread_getspecific(THR_KEY_mysys); + void *save_mysysvar= pthread_getspecific(THR_KEY_mysys); /* Allocate new mysys_var specifically new THD, @@ -5122,8 +5122,8 @@ MYSQL_THD create_background_thd() */ pthread_setspecific(THR_KEY_mysys, 0); my_thread_init(); - auto thd_mysysvar= pthread_getspecific(THR_KEY_mysys); - auto thd= new THD(0); + void *thd_mysysvar= pthread_getspecific(THR_KEY_mysys); + THD *thd= new THD(0); pthread_setspecific(THR_KEY_mysys, save_mysysvar); thd->set_psi(nullptr); set_current_thd(save_thd); @@ -5160,7 +5160,7 @@ void *thd_attach_thd(MYSQL_THD thd) DBUG_ASSERT(!current_thd); DBUG_ASSERT(thd && thd->mysys_var); - auto save_mysysvar= pthread_getspecific(THR_KEY_mysys); + void *save_mysysvar= pthread_getspecific(THR_KEY_mysys); pthread_setspecific(THR_KEY_mysys, thd->mysys_var); thd->store_globals(); return save_mysysvar; @@ -5185,8 +5185,8 @@ void thd_detach_thd(void *mysysvar) void destroy_background_thd(MYSQL_THD thd) { DBUG_ASSERT(!current_thd); - auto thd_mysys_var= thd->mysys_var; - auto save_mysys_var= thd_attach_thd(thd); + struct st_my_thread_var *thd_mysys_var= thd->mysys_var; + void *save_mysys_var= thd_attach_thd(thd); DBUG_ASSERT(thd_mysys_var != save_mysys_var); /* Workaround the adverse effect decrementing thread_count on THD() @@ -5205,7 +5205,7 @@ void destroy_background_thd(MYSQL_THD thd) would kill it, if we're not careful. */ #ifdef HAVE_PSI_THREAD_INTERFACE - auto save_psi_thread= PSI_CALL_get_thread(); + PSI_thread *save_psi_thread= PSI_CALL_get_thread(); #endif PSI_CALL_set_thread(0); pthread_setspecific(THR_KEY_mysys, thd_mysys_var); @@ -6364,7 +6364,7 @@ start_new_trans::start_new_trans(THD *thd) mdl_savepoint= thd->mdl_context.mdl_savepoint(); memcpy(old_ha_data, thd->ha_data, sizeof(old_ha_data)); thd->reset_n_backup_open_tables_state(&open_tables_state_backup); - for (auto &data : thd->ha_data) + for (Ha_data &data : thd->ha_data) data.reset(); old_transaction= thd->transaction; thd->transaction= &new_transaction; From eedee458857aaf2889461fdac398883a11c2f380 Mon Sep 17 00:00:00 2001 From: Monty Date: Mon, 1 Dec 2025 11:19:17 +0200 Subject: [PATCH 04/43] MDEV-23132 Assertion `trn->locked_tables == 0 && trn->used_instances == 0' failed in trnman_end_trn The reason for the crash was that two tables where updating Aria's TRN->used_instances at the same time. This could happen when a thread started a sub transaction with Aria tables, like reading a stored procedure from the proc table, at the same time another table was clearing the table list after committing a transaction involving Aria tables. The timing window for this to happen is very small, which is why we did not notice this issue for 5 years. The fix was to change reset_thd_trn() to clear the table links directly, instead of calling _ma_reset_trn_for_table() which removed the table from the linked list, which included updating TRN->used_instances. This bug could happen when maria_commit or maria_rollback() where called but not in ha_maria::implicit_commit() which had already a fix for this problem. Other things: - Removed duplicate call to thd_set_ha_data(thd, maria_hton, trn) in ha_maria::implicit_commit() and maria_commit() when TRN is null. --- storage/maria/ha_maria.cc | 25 ++++++++++++++++++++----- storage/maria/ma_trnman.h | 12 ++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index 28e5fd970db..5035b714cae 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -3028,17 +3028,23 @@ int ha_maria::start_stmt(THD *thd, thr_lock_type lock_type) Reset THD_TRN and all file->trn related to the transaction This is needed as some calls, like extra() or external_lock() may access it before next transaction is started + Note that trn for the table is already freed and may be reused by another + thread. */ static void reset_thd_trn(THD *thd, MARIA_HA *first_table) { + MARIA_HA *next; + TRN *trn __attribute__((unused)) = first_table ? first_table->trn : 0; DBUG_ENTER("reset_thd_trn"); thd_set_ha_data(thd, maria_hton, 0); - MARIA_HA *next; for (MARIA_HA *table= first_table; table ; table= next) { + DBUG_ASSERT(table->trn == trn); next= table->trn_next; - _ma_reset_trn_for_table(table); + table->trn_prev= 0; + table->trn_next= 0; + table->trn= 0; /* If table has changed by this statement, invalidate it from the query @@ -3121,13 +3127,15 @@ int ha_maria::implicit_commit(THD *thd, bool new_trn) statement assuming they have a trn (see ha_maria::start_stmt()). */ trn= trnman_new_trn(& thd->transaction->wt); - thd_set_ha_data(thd, maria_hton, trn); if (unlikely(trn == NULL)) { - reset_thd_trn(thd, used_tables); + reset_thd_trn(thd, used_tables); // Calls thd_set_ha_data() error= HA_ERR_OUT_OF_MEM; goto end; } + else + thd_set_ha_data(thd, maria_hton, trn); + /* Move all locked tables to the new transaction We must do it here as otherwise file->thd and file->state may be @@ -3142,6 +3150,14 @@ int ha_maria::implicit_commit(THD *thd, bool new_trn) trn_next= handler->trn_next; DBUG_ASSERT(handler->s->base.born_transactional); + /* + We reset the link to the old trn to avoid the asserts + in _ma_set_trn_for_table() + */ + handler->trn_next= 0; + handler->trn_prev= 0; + handler->trn= 0; + /* If handler uses versioning */ if (handler->s->lock_key_trees) { @@ -3569,7 +3585,6 @@ static int maria_commit(handlerton *hton __attribute__ ((unused)), if (ma_commit(trn)) res= HA_ERR_COMMIT_ERROR; reset_thd_trn(thd, used_instances); - thd_set_ha_data(thd, maria_hton, 0); DBUG_RETURN(res); } diff --git a/storage/maria/ma_trnman.h b/storage/maria/ma_trnman.h index c863eac5bdc..4a73dcda680 100644 --- a/storage/maria/ma_trnman.h +++ b/storage/maria/ma_trnman.h @@ -31,6 +31,10 @@ static inline void _ma_set_trn_for_table(MARIA_HA *tbl, TRN *newtrn) /* check that we are not calling this twice in a row */ DBUG_ASSERT(newtrn->used_instances != (void*) tbl); + DBUG_ASSERT(newtrn != &dummy_transaction_object); + DBUG_ASSERT(tbl->trn == 0); + DBUG_ASSERT(tbl->trn_next == 0); + DBUG_ASSERT(tbl->trn_prev == 0); tbl->trn= newtrn; /* Link into used list */ @@ -63,17 +67,25 @@ static inline void _ma_set_tmp_trn_for_table(MARIA_HA *tbl, TRN *newtrn) static inline void _ma_reset_trn_for_table(MARIA_HA *tbl) { + TRN *trn __attribute__((unused))= tbl->trn; DBUG_PRINT("info",("table: %p trn: %p -> NULL", tbl, tbl->trn)); /* The following is only false if tbl->trn == &dummy_transaction_object */ if (tbl->trn_prev) { if (tbl->trn_next) + { + DBUG_ASSERT(tbl->trn_next->trn == trn); tbl->trn_next->trn_prev= tbl->trn_prev; + } *tbl->trn_prev= tbl->trn_next; tbl->trn_prev= 0; tbl->trn_next= 0; } + else + { + DBUG_ASSERT(tbl->trn_next == 0); + } tbl->trn= 0; } From 080d92a6217a757300b629360cacf625f3c7ea60 Mon Sep 17 00:00:00 2001 From: Pekka Lampio Date: Thu, 27 Nov 2025 16:38:11 +0200 Subject: [PATCH 05/43] MDEV-31517 Wrong variable name in the configuration leads Galera to think SST/IST failed, at next restart will request a full SST This patch fixes an unwanted behavior of a Galera cluster node when Server startup fails because of an error in configuration file: after the failure full SST is requested at the next Server startup even though full SST is not needed (MDEV-31517). If Server startup fails because of a configuration error, this patch ensures that Galera state of the failing node remains unchanged. This avoids full SST at the next Server restart. This fix consists of three patches for the following components: 1) Server, 2) WSREP library, 3) Galera. --- .../suite/galera/r/galera_mdev_31517.result | 24 ++++ .../suite/galera/t/galera_mdev_31517.test | 132 ++++++++++++++++++ sql/mysqld.cc | 8 +- wsrep-lib | 2 +- 4 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 mysql-test/suite/galera/r/galera_mdev_31517.result create mode 100644 mysql-test/suite/galera/t/galera_mdev_31517.test diff --git a/mysql-test/suite/galera/r/galera_mdev_31517.result b/mysql-test/suite/galera/r/galera_mdev_31517.result new file mode 100644 index 00000000000..efb3f985622 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_mdev_31517.result @@ -0,0 +1,24 @@ +connection node_2; +connection node_1; +connection node_2; +CALL mtr.add_suppression("unknown variable 'non_existing_variable=ON'"); +CALL mtr.add_suppression("Aborting"); +CALL mtr.add_suppression("sst_received failed: State wait was interrupted"); +CALL mtr.add_suppression("State transfer interrupted, shutting down gracefully"); +connection node_1; +CREATE TABLE t(i INT NOT NULL PRIMARY KEY) ENGINE INNODB; +INSERT INTO t VALUES(1); +connection node_2; +connection node_1; +connection node_2; +connection node_1; +connection node_2; +connection node_1; +connection node_2; +Starting server ... +Starting server ... +SET GLOBAL wsrep_mode = DEFAULT; +connection node_1; +DROP TABLE t; +disconnect node_2; +disconnect node_1; diff --git a/mysql-test/suite/galera/t/galera_mdev_31517.test b/mysql-test/suite/galera/t/galera_mdev_31517.test new file mode 100644 index 00000000000..39ceb2db5a1 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_mdev_31517.test @@ -0,0 +1,132 @@ +# +# Test for MDEV-31517: Wrong variable name in the configuration leads +# Galera to think SST/IST failed, at next restart will request a full +# SST. +# +# To reproduce: +# +# 1. Start Galera cluster +# 2. Stop a Node +# 3. Start the node +# 4. Stop a Node +# 5. Add non_existing_variable=ON in the config +# 6. Start the node, this will fail +# 7 Remove non_existing_variable=ON from the config file +# 8 Restart the server +# 9. Observe a full SST happening +# +# This test checks that an IST takes place at Step 9 instead of a full SST. + +# Step 1: Start Galera cluster +--source include/galera_cluster.inc +--source include/have_mariabackup.inc +--echo # Make sure that the test is operating on the right version of galera library. +--let $galera_version=26.4.25 +source ../wsrep/include/check_galera_version.inc; + +# Suppress expected errors and warnings: +--connection node_2 +CALL mtr.add_suppression("unknown variable 'non_existing_variable=ON'"); +CALL mtr.add_suppression("Aborting"); +CALL mtr.add_suppression("sst_received failed: State wait was interrupted"); +CALL mtr.add_suppression("State transfer interrupted, shutting down gracefully"); + +# Count the number of "SST completed" messages in the log file before +# and after testing. To do this we need to save original log file +# before testing: +# +--let TEST_LOG=$MYSQLTEST_VARDIR/log/mysqld.2.err +--perl + use strict; + my $test_log=$ENV{'TEST_LOG'} or die "TEST_LOG not set"; + my $test_log_copy=$test_log . '.copy'; + if (-e $test_log_copy) { + unlink $test_log_copy; + } +EOF +--copy_file $TEST_LOG $TEST_LOG.copy + +--connection node_1 +CREATE TABLE t(i INT NOT NULL PRIMARY KEY) ENGINE INNODB; +INSERT INTO t VALUES(1); + +# Step 2: Stop node 2 +--connection node_2 +--source include/shutdown_mysqld.inc + +--connection node_1 +--let $wait_condition = SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size' +--source include/wait_condition.inc + +# Step 3: Start node 2 +--connection node_2 +--source include/start_mysqld.inc + +--connection node_1 +--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size' +--source include/wait_condition.inc + +# Step 4: Stop node 2 +--connection node_2 +let $MYSQLD_DATADIR= `SELECT @@datadir`; +--source include/shutdown_mysqld.inc + +--connection node_1 +--let $wait_condition = SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size' +--source include/wait_condition.inc + +# Step 5: Add non_existing_variable=ON in the config +--exec cp $MYSQLTEST_VARDIR/my.cnf $MYSQLTEST_VARDIR/my.cnf-orig +--exec echo '[mysqld.2]' >> $MYSQLTEST_VARDIR/my.cnf +--exec echo 'non_existing_variable=ON' >> $MYSQLTEST_VARDIR/my.cnf + +# Step 6: start the stopped node, this will fail +--connection node_2 +--exec cp -p $MYSQLD_DATADIR/grastate.dat $MYSQLD_DATADIR/grastate.dat_before +--echo Starting server ... +--error 1 +--exec $MYSQLD --defaults-group-suffix=.2 --defaults-file=$MYSQLTEST_VARDIR/my.cnf | grep 'non_existing_variable' +--exec cp -p $MYSQLD_DATADIR/grastate.dat $MYSQLD_DATADIR/grastate.dat_after + +# Step 7: remove the wrong variable in the config file +--exec cp $MYSQLTEST_VARDIR/my.cnf-orig $MYSQLTEST_VARDIR/my.cnf + +# Step 8: Start the node +--echo Starting server ... +let $restart_noprint=2; +--source include/start_mysqld.inc + +--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; +--source include/wait_condition.inc + +--let $wait_condition = SELECT VARIABLE_VALUE = 'ON' FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_ready'; +--source include/wait_condition.inc + +# cleanup +SET GLOBAL wsrep_mode = DEFAULT; + +--connection node_1 +DROP TABLE t; + +# Count the number of "SST completed" messages in the log file during +# test phase - to print the error message if the number of such +# messages in log file increased at the end of the test: +# +--perl + use strict; + my $test_log=$ENV{'TEST_LOG'} or die "TEST_LOG not set"; + my $test_log_copy=$test_log . '.copy'; + open(FILE, $test_log_copy) or die("Unable to open $test_log_copy: $!\n"); + my $initial=grep(/SST completed/gi,); + close(FILE); + open(FILE, $test_log) or die("Unable to open $test_log: $!\n"); + my $final=grep(/SST completed/gi,); + close(FILE); + if ($final != $initial) { + my $diff=$final-$initial; + print("Full WSREP SST performed $diff times.\n"); + } +EOF +--remove_file $TEST_LOG.copy + +--source include/galera_end.inc diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f7e0100d6bf..0d8008d2df5 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5361,8 +5361,14 @@ static int init_server_components() #endif if ((ho_error= handle_options(&remaining_argc, &remaining_argv, removed_opts, - mysqld_get_one_option))) + mysqld_get_one_option))) { +#ifdef WITH_WSREP + Wsrep_server_state::instance().disable_node_reset(); +#endif + unireg_abort(ho_error); + } + /* Add back the program name handle_options removes */ remaining_argc++; remaining_argv--; diff --git a/wsrep-lib b/wsrep-lib index 14ce8cab76e..7010f0ab584 160000 --- a/wsrep-lib +++ b/wsrep-lib @@ -1 +1 @@ -Subproject commit 14ce8cab76e9388f7266b91b163a62c654b1d329 +Subproject commit 7010f0ab584ab9cdebb285272a0fb0ff0a5a791d From 4cff562f3f89d4df03e09233d835d0451bc37cc4 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 21 Nov 2025 13:17:19 +0200 Subject: [PATCH 06/43] MDEV-38164: Fix the estimates reported by TABLE::key_storage_length() They were based on the maximum possible key tuple length, which can be much larger than the real data size. The return value is used by handler::keyread_time(), which is used to estimate the cost of range access. This could cause range access not to be picked, even if it uses the clustered PK and reads about 8% of the table. The fix is to add KEY::stat_storage_length (next to KEY::rec_per_key) and have the storage engine fill it in handler::info(HA_STATUS_CONST). Currently, only InnoDB fills this based on its internal statistics: index->stat_index_size and ib_table->stat_n_rows. Also changed: - In handler::calculate_costs(), use ha_keyread_clustered_time() when computing clustered PK read cost, not ha_keyread_time(). The fix is OFF by default and enabled by setting FIX_INDEX_LOOKUP_COST flag in @@new_mode. --- mysql-test/main/mysqld--help.result | 2 +- mysql-test/main/optimizer_costs_innodb.result | 42 ++++++++++++++++++ mysql-test/main/optimizer_costs_innodb.test | 44 +++++++++++++++++++ mysql-test/suite/sys_vars/r/new_mode.result | 2 +- .../sys_vars/r/sysvars_server_embedded.result | 2 +- .../r/sysvars_server_notembedded.result | 2 +- sql/multi_range_read.cc | 22 +++++++--- sql/sql_class.h | 3 +- sql/sql_select.cc | 3 ++ sql/structs.h | 6 +++ sql/sys_vars.cc | 5 ++- sql/table.cc | 15 +++++++ sql/table.h | 15 ++++++- storage/innobase/handler/ha_innodb.cc | 22 ++++++++++ 14 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 mysql-test/main/optimizer_costs_innodb.result create mode 100644 mysql-test/main/optimizer_costs_innodb.test diff --git a/mysql-test/main/mysqld--help.result b/mysql-test/main/mysqld--help.result index f9ed13e1e17..7d8e9c08e28 100644 --- a/mysql-test/main/mysqld--help.result +++ b/mysql-test/main/mysqld--help.result @@ -744,7 +744,7 @@ The following specify which files/extra groups are read (specified before remain connection before aborting the write --new-mode=name Used to introduce new behavior to existing MariaDB versions. Any combination of: - FIX_INDEX_STATS_FOR_ALL_NULLS + FIX_INDEX_STATS_FOR_ALL_NULLS, FIX_INDEX_LOOKUP_COST Use 'ALL' to set all combinations. --note-verbosity=name Verbosity level for note-warnings given to the user. See diff --git a/mysql-test/main/optimizer_costs_innodb.result b/mysql-test/main/optimizer_costs_innodb.result new file mode 100644 index 00000000000..0e79f9996b1 --- /dev/null +++ b/mysql-test/main/optimizer_costs_innodb.result @@ -0,0 +1,42 @@ +# +# MDEV-38164: Poor cost calculations for index access cause bad query plans for big VARCHARs in 11.x +# +create table t1 ( +pk1 int not null, +pk2 int not null, +filler1 varchar(500), +filler2 varchar(500), +filler3 varchar(500), +filler4 varchar(500), +filler5 varchar(500), +filler6 varchar(500), +filler7 varchar(500), +filler8 varchar(500), +filler9 varchar(500), +fillerA varchar(500), +fillerB varchar(500), +fillerC varchar(500), +fillerD varchar(500), +fillerE varchar(500), +fillerF varchar(500), +primary key(pk1, pk2) +) collate utf8mb4_general_ci engine=innodb; +insert into t1 (pk1, pk2) select +mod(seq, 8), seq +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 +# Before the fix: this will use full table scan: +explain select * from t1 where pk1=1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL PRIMARY NULL NULL # 10000 Using where +set @save_new_mode=@@new_mode; +set new_mode=concat(@@new_mode,'FIX_INDEX_LOOKUP_COST'); +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 # 1250 +set new_mode=@save_new_mode; +drop table t1; diff --git a/mysql-test/main/optimizer_costs_innodb.test b/mysql-test/main/optimizer_costs_innodb.test new file mode 100644 index 00000000000..330f1157f65 --- /dev/null +++ b/mysql-test/main/optimizer_costs_innodb.test @@ -0,0 +1,44 @@ +--source include/have_innodb.inc +--source include/have_sequence.inc + +--echo # +--echo # MDEV-38164: Poor cost calculations for index access cause bad query plans for big VARCHARs in 11.x +--echo # +create table t1 ( + pk1 int not null, + pk2 int not null, + filler1 varchar(500), + filler2 varchar(500), + filler3 varchar(500), + filler4 varchar(500), + filler5 varchar(500), + filler6 varchar(500), + filler7 varchar(500), + filler8 varchar(500), + filler9 varchar(500), + fillerA varchar(500), + fillerB varchar(500), + fillerC varchar(500), + fillerD varchar(500), + fillerE varchar(500), + fillerF varchar(500), + primary key(pk1, pk2) +) collate utf8mb4_general_ci engine=innodb; + +insert into t1 (pk1, pk2) select + mod(seq, 8), seq +from + seq_1_to_10000; +analyze table t1; + +--echo # Before the fix: this will use full table scan: +--replace_column 8 # +explain select * from t1 where pk1=1; + +set @save_new_mode=@@new_mode; +set new_mode=concat(@@new_mode,'FIX_INDEX_LOOKUP_COST'); +--replace_column 8 # +explain select * from t1 where pk1=1; + +set new_mode=@save_new_mode; +drop table t1; diff --git a/mysql-test/suite/sys_vars/r/new_mode.result b/mysql-test/suite/sys_vars/r/new_mode.result index d078537a1e1..eeec6544e53 100644 --- a/mysql-test/suite/sys_vars/r/new_mode.result +++ b/mysql-test/suite/sys_vars/r/new_mode.result @@ -25,7 +25,7 @@ ERROR 42000: Variable 'new_mode' can't be set to the value of 'TEST_WARNING3' SET @@session.new_mode = "ALL"; select @@session.new_mode; @@session.new_mode -FIX_INDEX_STATS_FOR_ALL_NULLS +FIX_INDEX_STATS_FOR_ALL_NULLS,FIX_INDEX_LOOKUP_COST SET @@global.new_mode = NULL; ERROR 42000: Variable 'new_mode' can't be set to the value of 'NULL' SET @@global.new_mode = ''; 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 34c01434f40..cf27b7294b4 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -2319,7 +2319,7 @@ VARIABLE_COMMENT Used to introduce new behavior to existing MariaDB versions NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST FIX_INDEX_STATS_FOR_ALL_NULLS +ENUM_VALUE_LIST FIX_INDEX_STATS_FOR_ALL_NULLS,FIX_INDEX_LOOKUP_COST READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME NOTE_VERBOSITY 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 c898835003e..3a3ea304d71 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -2529,7 +2529,7 @@ VARIABLE_COMMENT Used to introduce new behavior to existing MariaDB versions NUMERIC_MIN_VALUE NULL NUMERIC_MAX_VALUE NULL NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST FIX_INDEX_STATS_FOR_ALL_NULLS +ENUM_VALUE_LIST FIX_INDEX_STATS_FOR_ALL_NULLS,FIX_INDEX_LOOKUP_COST READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED VARIABLE_NAME NOTE_VERBOSITY diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc index 71143965162..d0f85e572a1 100644 --- a/sql/multi_range_read.cc +++ b/sql/multi_range_read.cc @@ -75,9 +75,19 @@ void handler::calculate_costs(Cost_estimate *cost, uint keyno, { /* Clustered index */ io_blocks= unassigned_single_point_ranges; - cost->index_cost= ha_keyread_time(keyno, n_ranges, - total_rows + multi_row_ranges, - io_blocks); + if (TEST_NEW_MODE_FLAG(table->in_use, NEW_MODE_FIX_INDEX_LOOKUP_COST)) + { + cost->index_cost= + ha_keyread_clustered_time(keyno, n_ranges, + total_rows + multi_row_ranges, + io_blocks); + } + else + { + cost->index_cost= ha_keyread_time(keyno, n_ranges, + total_rows + multi_row_ranges, + io_blocks); + } cost->copy_cost= rows2double(total_rows) * ROW_COPY_COST; } /* Adjust io cost to data size */ @@ -178,11 +188,9 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq, */ ulonglong unassigned_single_point_ranges= 0; - uint len= table->key_info[keyno].key_length + table->file->ref_length; - if (table->file->is_clustering_key(keyno)) - len= table->s->stored_rec_length; + size_t len= table->key_storage_length(keyno); /* Assume block is 75 % full */ - uint avg_block_records= ((uint) (stats.block_size*3/4))/len + 1; + size_t avg_block_records= ((uint) (stats.block_size*3/4))/len + 1; uint limit= thd->variables.eq_range_index_dive_limit; bool use_statistics_for_eq_range= eq_ranges_exceeds_limit(seq, seq_init_param, diff --git a/sql/sql_class.h b/sql/sql_class.h index a255f952885..066064d2ef5 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -220,7 +220,8 @@ void old_mode_deprecated_warnings(THD *thd, ulonglong v); */ #define NEW_MODE_FIX_INDEX_STATS_FOR_ALL_NULLS (1ULL << 0) -#define NEW_MODE_MAX 1 +#define NEW_MODE_FIX_INDEX_LOOKUP_COST (1ULL << 1) +#define NEW_MODE_MAX 2 /* Definitions above that have transitioned from new behaviour to default */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c97241d2b75..8e693ea1734 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13486,6 +13486,7 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab, keyinfo->flags= HA_GENERATED_KEY; keyinfo->is_statistics_from_stat_tables= FALSE; keyinfo->all_nulls_key_parts= 0; + keyinfo->stat_storage_length= 0; keyinfo->name.str= "$hj"; keyinfo->name.length= 3; keyinfo->rec_per_key= (ulong*) thd->calloc(sizeof(ulong)*key_parts); @@ -22482,6 +22483,7 @@ bool Create_tmp_table::finalize(THD *thd, keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->is_statistics_from_stat_tables= FALSE; keyinfo->all_nulls_key_parts= 0; + keyinfo->stat_storage_length= 0; keyinfo->name= group_key; keyinfo->comment.str= 0; ORDER *cur_group= m_group; @@ -22604,6 +22606,7 @@ bool Create_tmp_table::finalize(THD *thd, keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->is_statistics_from_stat_tables= FALSE; keyinfo->all_nulls_key_parts= 0; + keyinfo->stat_storage_length= 0; keyinfo->read_stats= NULL; keyinfo->collected_stats= NULL; diff --git a/sql/structs.h b/sql/structs.h index d81666e256e..cad3763b89b 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -154,6 +154,12 @@ typedef struct st_key { */ ulong *rec_per_key; + /* + Average space index tuple takes on disk, according to the engine's + statistics. 0 if statistics is not available. + */ + size_t stat_storage_length; + /* This structure is used for statistical data on the index that has been read from the statistical table index_stat diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 3d565b01000..96621793567 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -4056,6 +4056,7 @@ static Sys_var_set Sys_old_behavior( static const char *new_mode_all_names[]= { "FIX_INDEX_STATS_FOR_ALL_NULLS", + "FIX_INDEX_LOOKUP_COST", "TEST_WARNING1", // Default from here, See NEW_MODE_MAX "TEST_WARNING2", 0 @@ -4063,8 +4064,8 @@ static const char *new_mode_all_names[]= static int new_mode_hidden_names[] = { - 1, // TEST_WARNING1 - 2, // TEST_WARNING2 + 2, // TEST_WARNING1 + 3, // TEST_WARNING2 -1 // End of list }; diff --git a/sql/table.cc b/sql/table.cc index ff311a87572..3c0a6108bcd 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -8643,6 +8643,7 @@ bool TABLE::add_tmp_key(uint key, uint key_parts, keyinfo->read_stats= NULL; keyinfo->collected_stats= NULL; keyinfo->all_nulls_key_parts= 0; + keyinfo->stat_storage_length= 0; for (i= 0; i < key_parts; i++) { @@ -10932,3 +10933,17 @@ void TABLE::mark_table_for_reopen() DBUG_ASSERT(thd); thd->locked_tables_list.mark_table_for_reopen(this); } + +/* + @brief + Estimate how much space is required to store one index tuple +*/ +size_t TABLE::key_storage_length(uint index) +{ + /* Use storage engine's statistics if it is available */ + if (TEST_NEW_MODE_FLAG(in_use, NEW_MODE_FIX_INDEX_LOOKUP_COST) && + key_info[index].stat_storage_length) + return key_info[index].stat_storage_length; + + return key_storage_length_from_ddl(index); +} diff --git a/sql/table.h b/sql/table.h index 84749b5a1f8..81278b1dcce 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1756,7 +1756,20 @@ public: uint actual_n_key_parts(KEY *keyinfo); ulong actual_key_flags(KEY *keyinfo); int update_virtual_field(Field *vf, bool ignore_warnings); - inline size_t key_storage_length(uint index) + + size_t key_storage_length(uint index); + /* + @brief + Estimate how index tuple takes in storage, based solely on table's DDL + + @detail + This is a conservative number that assumes the value is stored in + KeyTupleFormat (or table->record format for clustered PK), without + endspace compression, etc. + On the other hand, it doesn't account that the storage engine may need + to store transactionIds, etc. + */ + inline size_t key_storage_length_from_ddl(uint index) { if (is_clustering_key(index)) return s->stored_rec_length; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 5a875595479..6f11349d878 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -15003,6 +15003,28 @@ stats_fetch: } KEY* key = &table->key_info[i]; + /* + Provide statistics about how many bytes an index record takes on disk, + on average. + */ + if (ib_table->stat_n_rows && !(key->flags & (HA_FULLTEXT | HA_SPATIAL))) { + /* + Start with total space used by the index divided by number of rows + */ + key->stat_storage_length = (size_t) ((index->stat_index_size * srv_page_size) / + ib_table->stat_n_rows); + + /* + The above can be too large + A) in case of tables with very few rows + B) in case of indexes with partially full pages. + So, clip the above estimate by a conservative estimate we've used + before: + */ + size_t conservative_estimate = table->key_storage_length_from_ddl(i); + if (key->stat_storage_length > conservative_estimate) + key->stat_storage_length = conservative_estimate; + } for (j = 0; j < key->ext_key_parts; j++) { From d613f03c201915667dafb80152338ac4d012b59e Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Tue, 2 Dec 2025 17:14:45 +0200 Subject: [PATCH 07/43] MDEV-37541 Race of rolling back and committing transaction to binlog Two transactions could binlog their completions in opposite to how it is done in Engine. That is is rare situations ROLLBACK in Engine of the dependency parent transaction could be scheduled by the transaction before its binlogging. That give a follower dependency child one get binlogged ahead of the parent. For fixing this bug its necessary to ensure the binlogging phase is always first one in the internal one-phase rollback protocol. The commit combines 1. a code polishing piece over a part of MDEV-21117 that made binlog handlerton always commit first in no-2pc cases and 2. the same rule now applies to the rollback. An added test demonstrates how the child could otherwise reach binlog before its parent. --- .../suite/binlog/t/binlog_mdev37541.test | 9 +++ sql/handler.cc | 70 +++++++++++++------ sql/log.cc | 3 +- sql/log.h | 1 + 4 files changed, 58 insertions(+), 25 deletions(-) diff --git a/mysql-test/suite/binlog/t/binlog_mdev37541.test b/mysql-test/suite/binlog/t/binlog_mdev37541.test index de94f5a5ebd..c6902c9571e 100644 --- a/mysql-test/suite/binlog/t/binlog_mdev37541.test +++ b/mysql-test/suite/binlog/t/binlog_mdev37541.test @@ -2,6 +2,15 @@ --source include/have_innodb.inc --source include/have_binlog_format_mixed.inc +# MDEV-37541 Race of rolling back and committing transaction to binlog +# The tests reproduce a scenario of two conflicting transactions +# of which the dependency decides to rollback. In order to reproduce the bug +# the parent features side effects (produced by DROP TEMPORARY TABLE) +# and multiple engines. +# The tests prove that the child transaction can not win race to binlogging +# with its parent as it gets stuck at engine lock acquiring all time +# while the parent conducts binlogging on its own. + create table t1 (a int primary key, b text) engine=innodb; connect(trx1_rollback,localhost,root,,); diff --git a/sql/handler.cc b/sql/handler.cc index 19c0b31c1fe..de34df3f9ff 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2152,11 +2152,34 @@ static bool is_ro_1pc_trans(THD *thd, Ha_trx_info *ha_info, bool all, return !rw_trans; } -static bool has_binlog_hton(Ha_trx_info *ha_info) +inline Ha_trx_info* get_binlog_hton(Ha_trx_info *ha_info) { - bool rc; - for (rc= false; ha_info && !rc; ha_info= ha_info->next()) - rc= ha_info->ht() == binlog_hton; + for (; ha_info; ha_info= ha_info->next()) + if (ha_info->ht() == binlog_hton) + return ha_info; + + return ha_info; +} + +static int run_binlog_first(THD *thd, bool all, THD_TRANS *trans, + bool is_real_trans, bool is_commit) +{ + int rc= 0; + Ha_trx_info *ha_info= trans->ha_list; + + if ((ha_info= get_binlog_hton(ha_info))) + { + int err; + if ((err= is_commit ? binlog_commit(thd, all, + is_ro_1pc_trans(thd, ha_info, all, + is_real_trans)) + : binlog_rollback(ha_info->ht(), thd, all))) + { + my_error(is_commit ? ER_ERROR_DURING_COMMIT : ER_ERROR_DURING_ROLLBACK, + MYF(0), err); + rc= 1; + } + } return rc; } @@ -2174,18 +2197,19 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans) if (ha_info) { int err= 0; - - if (has_binlog_hton(ha_info) && - (err= binlog_commit(thd, all, - is_ro_1pc_trans(thd, ha_info, all, is_real_trans)))) + /* + Binlog hton must be called first regardless of its position + in trans->ha_list at least to prevent from commiting any engine + branches when afterward a duplicate GTID error out of binlog_commit() + is generated. + */ + for (int binlog_err= error= + run_binlog_first(thd, all, trans, is_real_trans, true); + ha_info; ha_info= ha_info_next) { - my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); - error= 1; + if (binlog_err) + goto err; - goto err; - } - for (; ha_info; ha_info= ha_info_next) - { handlerton *ht= ha_info->ht(); if ((err= ht->commit(ht, thd, all))) { @@ -2311,17 +2335,17 @@ int ha_rollback_trans(THD *thd, bool all) if (is_real_trans) /* not a statement commit */ thd->stmt_map.close_transient_cursors(); - int err; - if (has_binlog_hton(ha_info) && - (err= binlog_hton->rollback(binlog_hton, thd, all))) - { - my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); - error= 1; - } - for (; ha_info; ha_info= ha_info_next) + /* + Binlog hton must be called first regardless of its position + in trans->ha_list in order to avoid race to binlog between the current + rollbacker and any transaction that depends on it. This guarantees + the execution time dependency identifies binlog ordering. + */ + for (error= run_binlog_first(thd, all, trans, is_real_trans, false); + ha_info; ha_info= ha_info_next) { + int err; handlerton *ht= ha_info->ht(); - if (ht != binlog_hton && (err= ht->rollback(ht, thd, all))) { // cannot happen diff --git a/sql/log.cc b/sql/log.cc index 9e35cc9530a..13fd5846294 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -97,7 +97,6 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv); static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv); static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton, THD *thd); -static int binlog_rollback(handlerton *hton, THD *thd, bool all); static int binlog_prepare(handlerton *hton, THD *thd, bool all); static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd); static int binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr, @@ -2489,7 +2488,7 @@ int binlog_commit(THD *thd, bool all, bool ro_1pc) @see handlerton::rollback */ -static int binlog_rollback(handlerton *hton, THD *thd, bool all) +int binlog_rollback(handlerton *hton, THD *thd, bool all) { DBUG_ENTER("binlog_rollback"); diff --git a/sql/log.h b/sql/log.h index e779271c7f5..d8af4916804 100644 --- a/sql/log.h +++ b/sql/log.h @@ -1277,6 +1277,7 @@ const char * get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list); int binlog_commit(THD *thd, bool all, bool is_ro_1pc= false); +int binlog_rollback(handlerton *hton, THD *thd, bool all); int binlog_commit_by_xid(handlerton *hton, XID *xid); int binlog_rollback_by_xid(handlerton *hton, XID *xid); bool write_bin_log_start_alter(THD *thd, bool& partial_alter, From d0ae38c0ad48701dacfb517bac4eb90c55d953b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Wed, 3 Dec 2025 08:48:24 +0200 Subject: [PATCH 08/43] MDEV-37688: Relax a too strict assertion page_zip_decompress_low(): Skip the 8 bytes after FIL_PAGE_TYPE to be consistent with the rest of ROW_FORMAT=COMPRESSED page handling. The 8 bytes after FIL_PAGE_TYPE may differ between the compressed and uncompressed copy of a page when SET GLOBAL innodb_encrypt_tables is being executed while data pages are being evicted and reloaded into the buffer pool. --- storage/innobase/page/page0zip.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/storage/innobase/page/page0zip.cc b/storage/innobase/page/page0zip.cc index 0448f24a42c..551371b93b4 100644 --- a/storage/innobase/page/page0zip.cc +++ b/storage/innobase/page/page0zip.cc @@ -3013,8 +3013,9 @@ page_zip_decompress_low( /* Check that the bytes that we skip are identical. */ #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG ut_a(!memcmp(FIL_PAGE_TYPE + page, - FIL_PAGE_TYPE + page_zip->data, - PAGE_HEADER - FIL_PAGE_TYPE)); + FIL_PAGE_TYPE + page_zip->data, 2)); + ut_a(!memcmp(FIL_PAGE_SPACE_ID + page, + FIL_PAGE_SPACE_ID + page_zip->data, 4)); ut_a(!memcmp(PAGE_HEADER + PAGE_LEVEL + page, PAGE_HEADER + PAGE_LEVEL + page_zip->data, PAGE_DATA - (PAGE_HEADER + PAGE_LEVEL))); @@ -3028,7 +3029,8 @@ page_zip_decompress_low( #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG /* Check that the page headers match after copying. */ - ut_a(!memcmp(page, page_zip->data, PAGE_DATA)); + ut_a(!memcmp(page_zip->data + FIL_PAGE_DATA, + page + FIL_PAGE_DATA, PAGE_DATA - FIL_PAGE_DATA)); #endif /* UNIV_DEBUG || UNIV_ZIP_DEBUG */ } From f3a5d38fa7bd51cfff41892cc3c12fceab794964 Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Thu, 4 Dec 2025 19:26:40 +0200 Subject: [PATCH 09/43] Revert "MDEV-37686 rpl.create_or_replace_mix2 fails in MDEV-35915 branch" This reverts commit 3241798214b066d62ba3274ba5dc29549349ca65. Due to MDEV-38212. --- sql/sql_class.cc | 3 --- sql/sql_class.h | 5 ----- 2 files changed, 8 deletions(-) diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 5c4b3d8cc0a..1769fe9912b 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -6364,8 +6364,6 @@ start_new_trans::start_new_trans(THD *thd) thd->server_status&= ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY); thd->server_status|= SERVER_STATUS_AUTOCOMMIT; - org_rgi_slave= thd->rgi_slave; - thd->rgi_slave= NULL; } @@ -6382,7 +6380,6 @@ void start_new_trans::restore_old_transaction() MYSQL_COMMIT_TRANSACTION(org_thd->m_transaction_psi); org_thd->m_transaction_psi= m_transaction_psi; org_thd->variables.wsrep_on= wsrep_on; - org_thd->rgi_slave= org_rgi_slave; org_thd= 0; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 7b649d2db35..119a036fd98 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -5885,11 +5885,6 @@ class start_new_trans uint in_sub_stmt; uint server_status; my_bool wsrep_on; - /* - THD:rgi_slave may hold a part of the replicated "old" transaction's - execution context. Therefore it has to be reset/restored too. - */ - rpl_group_info* org_rgi_slave; public: start_new_trans(THD *thd); From 88efded2d205cf7f07d592ca0e87075ead640f65 Mon Sep 17 00:00:00 2001 From: Mohammad El-Shennawy Date: Mon, 24 Nov 2025 09:15:13 +0200 Subject: [PATCH 10/43] MDEV-36269: improve error handling for source command - refactor batch_readline_init to mysql.cc for proper error handling - add unit test and move it to the end of main/mysql_client_test.test to resolve embedded test failures - remove unnecessary code and duplicates to clean up implementation - redirect error message from stderr to stdout in .test file - use labels to avoid code duplication - handle windows check for block device - ensure file failing to open in windows because being a directory is different from any other reason for clear error message --- client/my_readline.h | 3 +- client/mysql.cc | 94 ++++++++++++++++++++---- client/readline.cc | 30 +------- mysql-test/main/mysql_client_test.result | 6 ++ mysql-test/main/mysql_client_test.test | 10 +++ 5 files changed, 98 insertions(+), 45 deletions(-) diff --git a/client/my_readline.h b/client/my_readline.h index ec43d81f2c0..2ff5edcf31c 100644 --- a/client/my_readline.h +++ b/client/my_readline.h @@ -34,9 +34,10 @@ typedef struct st_line_buffer bool truncated; } LINE_BUFFER; -extern LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file); extern LINE_BUFFER *batch_readline_command(LINE_BUFFER *buffer, char * str); extern char *batch_readline(LINE_BUFFER *buffer, bool binary_mode); extern void batch_readline_end(LINE_BUFFER *buffer); +extern bool init_line_buffer(LINE_BUFFER *buffer, File file, ulong size, + ulong max_size); #endif /* CLIENT_MY_READLINE_INCLUDED */ diff --git a/client/mysql.cc b/client/mysql.cc index bce4a4e5fa2..ea8e350956c 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1207,6 +1207,81 @@ inline int get_command_index(char cmd_char) return -1; } +static LINE_BUFFER *batch_readline_init(ulong max_size, const char *path) +{ + LINE_BUFFER *line_buff; + File file; + MY_STAT input_file_stat; + char buff[FN_REFLEN + 512]; + + if (path) + { + if ((file= my_open(path, O_RDONLY | O_BINARY, MYF(0))) < 0) + { +#ifdef _WIN32 + if (my_errno == EACCES && my_stat(path, &input_file_stat, MYF(0)) && + MY_S_ISDIR(input_file_stat.st_mode)) + my_snprintf(buff, sizeof(buff), "Can't read from a directory '%.*s'", + FN_REFLEN, path); + else +#endif + my_snprintf(buff, sizeof(buff), "Failed to open file '%.*s', error: %d", + FN_REFLEN, path, my_errno); + put_info(buff, INFO_ERROR, 0); + return 0; + } + } + else + { + file= my_fileno(stdin); + } + + if (my_fstat(file, &input_file_stat, MYF(0))) + { + my_snprintf(buff, sizeof(buff), "Failed to stat file '%.*s', error: %d", + FN_REFLEN, path ? path : "stdin", my_errno); + goto err1; + } + + if (MY_S_ISDIR(input_file_stat.st_mode)) + { + my_snprintf(buff, sizeof(buff), "Can't read from a directory '%.*s'", + FN_REFLEN, path ? path : "stdin"); + goto err1; + } + +#ifndef _WIN32 + if (MY_S_ISBLK(input_file_stat.st_mode)) + { + my_snprintf(buff, sizeof(buff), "Can't read from a block device '%.*s'", + FN_REFLEN, path ? path : "stdin"); + goto err1; + } +#endif + + if (!(line_buff= (LINE_BUFFER*) my_malloc(PSI_NOT_INSTRUMENTED, + sizeof(*line_buff), + MYF(MY_WME | MY_ZEROFILL)))) + { + goto err; + } + + if (init_line_buffer(line_buff, file, IO_SIZE, max_size)) + { + my_free(line_buff); + goto err; + } + + return line_buff; + +err1: + put_info(buff, INFO_ERROR, 0); +err: + if (path) + my_close(file, MYF(0)); + return 0; +} + static int delimiter_index= -1; static int charset_index= -1; static int sandbox_index= -1; @@ -1281,10 +1356,8 @@ int main(int argc,char *argv[]) } if (status.batch && !status.line_buff && - !(status.line_buff= batch_readline_init(MAX_BATCH_BUFFER_SIZE, stdin))) + !(status.line_buff= batch_readline_init(MAX_BATCH_BUFFER_SIZE, NULL))) { - put_info("Can't initialize batch_readline - may be the input source is " - "a directory or a block device.", INFO_ERROR, 0); free_defaults(defaults_argv); my_end(0); exit(1); @@ -4682,7 +4755,6 @@ static int com_source(String *, char *line) LINE_BUFFER *line_buff; int error; STATUS old_status; - FILE *sql_file; my_bool save_ignore_errors; if (status.sandbox) @@ -4702,18 +4774,10 @@ static int com_source(String *, char *line) end--; end[0]=0; unpack_filename(source_name,source_name); - /* open file name */ - if (!(sql_file = my_fopen(source_name, O_RDONLY | O_BINARY,MYF(0)))) - { - char buff[FN_REFLEN+60]; - sprintf(buff,"Failed to open file '%s', error: %d", source_name,errno); - return put_info(buff, INFO_ERROR, 0); - } - if (!(line_buff= batch_readline_init(MAX_BATCH_BUFFER_SIZE, sql_file))) + if (!(line_buff= batch_readline_init(MAX_BATCH_BUFFER_SIZE, source_name))) { - my_fclose(sql_file,MYF(0)); - return put_info("Can't initialize batch_readline", INFO_ERROR, 0); + return ignore_errors ? -1 : 1; } /* Save old status */ @@ -4732,7 +4796,7 @@ static int com_source(String *, char *line) ignore_errors= save_ignore_errors; status=old_status; // Continue as before in_com_source= aborted= 0; - my_fclose(sql_file,MYF(0)); + my_close(line_buff->file, MYF(0)); batch_readline_end(line_buff); /* If we got an error during source operation, don't abort the client diff --git a/client/readline.cc b/client/readline.cc index e4014658c08..f88a942f012 100644 --- a/client/readline.cc +++ b/client/readline.cc @@ -23,38 +23,11 @@ #include #include "my_readline.h" -static bool init_line_buffer(LINE_BUFFER *buffer,File file,ulong size, - ulong max_size); static bool init_line_buffer_from_string(LINE_BUFFER *buffer,char * str); static size_t fill_buffer(LINE_BUFFER *buffer); static char *intern_read_line(LINE_BUFFER *buffer, ulong *out_length); -LINE_BUFFER *batch_readline_init(ulong max_size,FILE *file) -{ - LINE_BUFFER *line_buff; - -#ifndef _WIN32 - MY_STAT input_file_stat; - if (my_fstat(fileno(file), &input_file_stat, MYF(MY_WME)) || - MY_S_ISDIR(input_file_stat.st_mode) || - MY_S_ISBLK(input_file_stat.st_mode)) - return 0; -#endif - - if (!(line_buff=(LINE_BUFFER*) - my_malloc(PSI_NOT_INSTRUMENTED, sizeof(*line_buff), - MYF(MY_WME | MY_ZEROFILL)))) - return 0; - if (init_line_buffer(line_buff,my_fileno(file),IO_SIZE,max_size)) - { - my_free(line_buff); - return 0; - } - return line_buff; -} - - char *batch_readline(LINE_BUFFER *line_buff, bool binary_mode) { char *pos; @@ -105,8 +78,7 @@ LINE_BUFFER *batch_readline_command(LINE_BUFFER *line_buff, char * str) Functions to handle buffered readings of lines from a stream ******************************************************************************/ -static bool -init_line_buffer(LINE_BUFFER *buffer,File file,ulong size,ulong max_buffer) +bool init_line_buffer(LINE_BUFFER *buffer,File file,ulong size,ulong max_buffer) { buffer->file=file; buffer->bufread=size; diff --git a/mysql-test/main/mysql_client_test.result b/mysql-test/main/mysql_client_test.result index dbd5aaeaae5..4c7b20314c0 100644 --- a/mysql-test/main/mysql_client_test.result +++ b/mysql-test/main/mysql_client_test.result @@ -262,3 +262,9 @@ SET @@global.collation_server= @save_collation_server; SET @@global.character_set_client= @save_character_set_client; SET @@global.collation_connection= @save_collation_connection; FOUND 1 /Aborted connection.*'u' host: '192.0.2.1' real ip: '(localhost|::1)'/ in mysqld.1.err +# +# MDEV-36269: Improve error handling for SOURCE command +# Test that SOURCE on an existent directory returns a clear error +# +ERROR at line 1: Can't read from a directory '.' +End of 10.11 tests diff --git a/mysql-test/main/mysql_client_test.test b/mysql-test/main/mysql_client_test.test index d620f4c7078..f99aecace80 100644 --- a/mysql-test/main/mysql_client_test.test +++ b/mysql-test/main/mysql_client_test.test @@ -63,3 +63,13 @@ SET @@global.collation_connection= @save_collation_connection; let SEARCH_FILE=$MYSQLTEST_VARDIR/log/mysqld.1.err; let SEARCH_PATTERN= Aborted connection.*'u' host: '192.0.2.1' real ip: '(localhost|::1)'; source include/search_pattern_in_file.inc; + +--echo # +--echo # MDEV-36269: Improve error handling for SOURCE command +--echo # Test that SOURCE on an existent directory returns a clear error +--echo # + +--error 1 +--exec $MYSQL -e "source ." 2>&1 + +--echo End of 10.11 tests From 4ee491f1b2eb356e9dc40f388f79b62cfeb13609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 11 Dec 2025 08:22:41 +0200 Subject: [PATCH 11/43] MDEV-38289: innodb.log_corruption_recovery sporadically fails When the test is starting up the server with innodb_force_recovery=1, there will be messages about the LSN being in the future. The current LSN is expected to be 12338 plus any number of FILE_CHECKPOINT records (16 bytes each). We have observed anything up to 12402=12338+16*4 in our CI systems. To be on the safe side, let us allow up to ten records. --- mysql-test/suite/innodb/r/log_corruption_recovery.result | 2 +- mysql-test/suite/innodb/t/log_corruption_recovery.test | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/innodb/r/log_corruption_recovery.result b/mysql-test/suite/innodb/r/log_corruption_recovery.result index 8d44df0b2ea..54bbe092f90 100644 --- a/mysql-test/suite/innodb/r/log_corruption_recovery.result +++ b/mysql-test/suite/innodb/r/log_corruption_recovery.result @@ -5,7 +5,7 @@ call mtr.add_suppression("InnoDB: OPT_PAGE_CHECKSUM mismatch on \\[page id: spac call mtr.add_suppression("InnoDB: Set innodb_force_recovery=1"); call mtr.add_suppression("InnoDB: Cannot apply log to \\[page id: space=127, page number=0\\] of corrupted file '.*test/t\\.ibd"); call mtr.add_suppression("(InnoDB: Plugin|Plugin 'InnoDB')"); -call mtr.add_suppression("InnoDB: Page .* Current system log sequence number 123(38|54)\\."); +call mtr.add_suppression("InnoDB: Page .* Current system log sequence number 12(3(38|54|70|86)|4(02|18|34|50|66|82|98))"); SET GLOBAL innodb_fast_shutdown=0; # restart SELECT * FROM INFORMATION_SCHEMA.ENGINES diff --git a/mysql-test/suite/innodb/t/log_corruption_recovery.test b/mysql-test/suite/innodb/t/log_corruption_recovery.test index 06069679e91..489010f9b77 100644 --- a/mysql-test/suite/innodb/t/log_corruption_recovery.test +++ b/mysql-test/suite/innodb/t/log_corruption_recovery.test @@ -14,8 +14,8 @@ call mtr.add_suppression("InnoDB: OPT_PAGE_CHECKSUM mismatch on \\[page id: spac call mtr.add_suppression("InnoDB: Set innodb_force_recovery=1"); call mtr.add_suppression("InnoDB: Cannot apply log to \\[page id: space=127, page number=0\\] of corrupted file '.*test/t\\.ibd"); call mtr.add_suppression("(InnoDB: Plugin|Plugin 'InnoDB')"); -call mtr.add_suppression("InnoDB: Page .* Current system log sequence number 123(38|54)\\."); - +# Allow innodb_force_recovery=1 to write up to 10 FILE_CHECKPOINT records +call mtr.add_suppression("InnoDB: Page .* Current system log sequence number 12(3(38|54|70|86)|4(02|18|34|50|66|82|98))"); SET GLOBAL innodb_fast_shutdown=0; --source include/shutdown_mysqld.inc --move_file $DATADIR/ib_logfile0 $DATADIR/ib_logfile0.old From 478802a8edb45886b0f105280ed613414f6a580c Mon Sep 17 00:00:00 2001 From: Akshat Nehra Date: Tue, 22 Apr 2025 17:06:00 +0000 Subject: [PATCH 12/43] MDEV-28823 Secure mariadb-secure-installation output file with chmod Fix security issue where temporary output files containing SQL commands and password hashes were created with default permissions (world-readable). Modified prepare() function to create and umask the $output $config and $command files. It is more secure than chmod as files can be opened, before the chmod is applied, and then read when its its later populated. All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc. --- scripts/mysql_secure_installation.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) mode change 100644 => 100755 scripts/mysql_secure_installation.sh diff --git a/scripts/mysql_secure_installation.sh b/scripts/mysql_secure_installation.sh old mode 100644 new mode 100755 index 27911774be6..a4fffc24836 --- a/scripts/mysql_secure_installation.sh +++ b/scripts/mysql_secure_installation.sh @@ -1,16 +1,16 @@ #!/bin/sh # Copyright (c) 2002, 2016, 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-1335 USA @@ -212,8 +212,8 @@ validate_reply () { } prepare() { - touch $config $command - chmod 600 $config $command + umask 0077 + touch $config $command $output } do_query() { From bb32acebf3ded9b5ac77ff9d0db30e7712d72aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Thu, 27 Nov 2025 10:11:38 +0200 Subject: [PATCH 13/43] MDEV-37354 : SIGSEGV in Wsrep_server_service::release_high_priority_service | discard_streaming_applier This issue seems to be already fixed. However, to avoid future problems: Wsrep_server_service::release_storage_service Add assertion that storage service is not nullptr and contains thd. In production binaries add guard to not use nullptr. Wsrep_server_service::release_high_priority_service Add assertion that high_priority service is not nullptr and contains thd. In production binaries add guard to not use nullptr. wsrep_is_BF_lock_timeout Remove printing of record lock because its page might not be latched leading to assertion in multi-master testing. --- .../suite/galera/r/galera_mdev_31517.result | 4 ++ .../suite/galera/t/galera_mdev_31517.test | 6 +++ sql/wsrep_server_service.cc | 44 ++++++++++++++----- storage/innobase/lock/lock0lock.cc | 22 ++-------- 4 files changed, 46 insertions(+), 30 deletions(-) diff --git a/mysql-test/suite/galera/r/galera_mdev_31517.result b/mysql-test/suite/galera/r/galera_mdev_31517.result index efb3f985622..600df72a43b 100644 --- a/mysql-test/suite/galera/r/galera_mdev_31517.result +++ b/mysql-test/suite/galera/r/galera_mdev_31517.result @@ -1,5 +1,9 @@ connection node_2; connection node_1; +# Make sure that the test is operating on the right version of galera library. +# Correct Galera library found +connection node_1; +connection node_2; connection node_2; CALL mtr.add_suppression("unknown variable 'non_existing_variable=ON'"); CALL mtr.add_suppression("Aborting"); diff --git a/mysql-test/suite/galera/t/galera_mdev_31517.test b/mysql-test/suite/galera/t/galera_mdev_31517.test index 39ceb2db5a1..853746cebac 100644 --- a/mysql-test/suite/galera/t/galera_mdev_31517.test +++ b/mysql-test/suite/galera/t/galera_mdev_31517.test @@ -24,6 +24,11 @@ --let $galera_version=26.4.25 source ../wsrep/include/check_galera_version.inc; +# Save original auto_increment_offset values. +--let $node_1=node_1 +--let $node_2=node_2 +--source include/auto_increment_offset_save.inc + # Suppress expected errors and warnings: --connection node_2 CALL mtr.add_suppression("unknown variable 'non_existing_variable=ON'"); @@ -129,4 +134,5 @@ DROP TABLE t; EOF --remove_file $TEST_LOG.copy +--source include/auto_increment_offset_restore.inc --source include/galera_end.inc diff --git a/sql/wsrep_server_service.cc b/sql/wsrep_server_service.cc index ca25a41d132..0ede84e4cf9 100644 --- a/sql/wsrep_server_service.cc +++ b/sql/wsrep_server_service.cc @@ -80,11 +80,22 @@ void Wsrep_server_service::release_storage_service( { Wsrep_storage_service* ss= static_cast(storage_service); - THD* thd= ss->m_thd; - wsrep_reset_threadvars(thd); - server_threads.erase(thd); - delete ss; - delete thd; + DBUG_ASSERT(ss && ss->m_thd); + + // Do not crash server on production + if (ss) + { + THD* thd= ss->m_thd; + if (thd) + { + wsrep_reset_threadvars(thd); + server_threads.erase(thd); + delete ss; + delete thd; + } + else + delete ss; + } } Wsrep_applier_service* @@ -141,12 +152,23 @@ void Wsrep_server_service::release_high_priority_service(wsrep::high_priority_se { Wsrep_high_priority_service* hps= static_cast(high_priority_service); - THD* thd= hps->m_thd; - delete hps; - wsrep_store_threadvars(thd); - server_threads.erase(thd); - delete thd; - wsrep_delete_threadvars(); + DBUG_ASSERT(hps && hps->m_thd); + + // Do not crash server on production + if (hps) + { + THD* thd= hps->m_thd; + if (thd) + { + delete hps; + wsrep_store_threadvars(thd); + server_threads.erase(thd); + delete thd; + wsrep_delete_threadvars(); + } + else + delete hps; + } } void Wsrep_server_service::background_rollback( diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index 220cd895761..572a7591a77 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -670,25 +670,9 @@ bool wsrep_is_BF_lock_timeout(const trx_t &trx) << " error: " << trx.error_state << " query: " << wsrep_thd_query(trx.mysql_thd); - if (const lock_t*wait_lock = trx.lock.wait_lock) - { - const my_hrtime_t now= my_hrtime_coarse(); - const my_hrtime_t suspend_time= trx.lock.suspend_time; - fprintf(stderr, - "------- TRX HAS BEEN WAITING %llu us" - " FOR THIS LOCK TO BE GRANTED:\n", - now.val - suspend_time.val); - - if (!wait_lock->is_table()) { - mtr_t mtr; - lock_rec_print(stderr, wait_lock, mtr); - } else { - lock_table_print(stderr, wait_lock); - } - - fprintf(stderr, "------------------\n"); - } - + // TODO: Can't use lock_rec_print from here because + // record lock page might not be latched and we are + // actually only interested lock information. return true; } From db26f446fd50a0040aba1656ec9472c3bc516783 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Mon, 15 Dec 2025 17:09:21 +1100 Subject: [PATCH 14/43] MDEV-35562 Do not emit warning when deleting .par file in quick_rm_table ...just like the when deleting the .frm file in the same function. This improves consistency: internal duplicate deletion of .par file and .frm file both fail silently. --- mysql-test/main/partition_alter.result | 25 +++++++++++++++++++++++++ mysql-test/main/partition_alter.test | 26 ++++++++++++++++++++++++++ sql/ha_partition.cc | 12 ++++++++---- sql/ha_partition.h | 3 ++- sql/handler.cc | 6 ++++-- sql/handler.h | 6 ++++-- sql/sql_table.cc | 3 ++- 7 files changed, 71 insertions(+), 10 deletions(-) diff --git a/mysql-test/main/partition_alter.result b/mysql-test/main/partition_alter.result index 29e239aa087..c0a59588a3a 100644 --- a/mysql-test/main/partition_alter.result +++ b/mysql-test/main/partition_alter.result @@ -391,3 +391,28 @@ partitio values in (2, 3, 4), pn values in (52, 53, 54)); drop table t1; # End of 10.7 tests +# +# MDEV-35562 On partitionen tables algorithm is not supported +# throw "no such file or directory" error +# +set @old_alter_algorithm=@@alter_algorithm; +set alter_algorithm='NOCOPY'; +CREATE TABLE t1par ( +f1 datetime , +f2 VARCHAR(2) , +f3 VARCHAR(200) NOT NULL , +f4 VARCHAR(100) charset utf8 +) +PARTITION BY RANGE COLUMNS(f2) +( +PARTITION p_01 VALUES LESS THAN ('02') ENGINE = InnoDB, +PARTITION p_31 VALUES LESS THAN (MAXVALUE) ENGINE = InnoDB +); +ALTER online TABLE t1par MODIFY COLUMN f3 VARCHAR(201) NULL , LOCK=NONE; +ERROR 0A000: ALGORITHM=NOCOPY is not supported for this operation. Try ALGORITHM=INPLACE +show warnings; +Level Code Message +Error 1845 ALGORITHM=NOCOPY is not supported for this operation. Try ALGORITHM=INPLACE +drop table t1par; +set alter_algorithm=@old_alter_algorithm; +# End of 10.11 tests diff --git a/mysql-test/main/partition_alter.test b/mysql-test/main/partition_alter.test index 827527695fa..b5e9defb4b1 100644 --- a/mysql-test/main/partition_alter.test +++ b/mysql-test/main/partition_alter.test @@ -323,3 +323,29 @@ partition by list(x) ( drop table t1; --echo # End of 10.7 tests + +--echo # +--echo # MDEV-35562 On partitionen tables algorithm is not supported +--echo # throw "no such file or directory" error +--echo # + +set @old_alter_algorithm=@@alter_algorithm; +set alter_algorithm='NOCOPY'; +CREATE TABLE t1par ( +f1 datetime , +f2 VARCHAR(2) , +f3 VARCHAR(200) NOT NULL , +f4 VARCHAR(100) charset utf8 +) +PARTITION BY RANGE COLUMNS(f2) +( + PARTITION p_01 VALUES LESS THAN ('02') ENGINE = InnoDB, + PARTITION p_31 VALUES LESS THAN (MAXVALUE) ENGINE = InnoDB +); +--error ER_ALTER_OPERATION_NOT_SUPPORTED +ALTER online TABLE t1par MODIFY COLUMN f3 VARCHAR(201) NULL , LOCK=NONE; +show warnings; +drop table t1par; +set alter_algorithm=@old_alter_algorithm; + +--echo # End of 10.11 tests diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index e40fe12b429..f3b2bf194c6 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -670,8 +670,10 @@ int ha_partition::rename_table(const char *from, const char *to) SYNOPSIS create_partitioning_metadata() path Path to the new frm file (without ext) - old_p Path to the old frm file (without ext) - create_info Create info generated for CREATE TABLE + old_path Path to the old frm file (without ext) + action_flag Action to take + ignore_delete_error Whether to ignore error in call to + mysql_file_delete RETURN VALUE >0 Error @@ -687,7 +689,8 @@ int ha_partition::rename_table(const char *from, const char *to) int ha_partition::create_partitioning_metadata(const char *path, const char *old_path, - chf_create_flags action_flag) + chf_create_flags action_flag, + bool ignore_delete_error) { partition_element *part; DBUG_ENTER("ha_partition::create_partitioning_metadata"); @@ -706,7 +709,8 @@ int ha_partition::create_partitioning_metadata(const char *path, strxmov(name, path, ha_par_ext, NullS); strxmov(old_name, old_path, ha_par_ext, NullS); if ((action_flag == CHF_DELETE_FLAG && - mysql_file_delete(key_file_ha_partition_par, name, MYF(MY_WME))) || + mysql_file_delete(key_file_ha_partition_par, name, + ignore_delete_error ? MYF(0) : MYF(MY_WME))) || (action_flag == CHF_RENAME_FLAG && mysql_file_rename(key_file_ha_partition_par, old_name, name, MYF(MY_WME)))) diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 09b4802624d..05b67f542d7 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -552,7 +552,8 @@ public: HA_CREATE_INFO *create_info) override; int create_partitioning_metadata(const char *name, const char *old_name, - chf_create_flags action_flag) + chf_create_flags action_flag, + bool ignore_delete_error) override; bool check_if_updates_are_ignored(const char *op) const override; void update_create_info(HA_CREATE_INFO *create_info) override; diff --git a/sql/handler.cc b/sql/handler.cc index de34df3f9ff..09e1072b443 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -5696,7 +5696,8 @@ handler::ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info_arg) int handler::ha_create_partitioning_metadata(const char *name, const char *old_name, - chf_create_flags action_flag) + chf_create_flags action_flag, + bool ignore_delete_error) { /* Normally this is done when unlocked, but in fast_alter_partition_table, @@ -5706,7 +5707,8 @@ handler::ha_create_partitioning_metadata(const char *name, DBUG_ASSERT(m_lock_type == F_UNLCK || (!old_name && strcmp(name, table_share->path.str))); - return create_partitioning_metadata(name, old_name, action_flag); + return create_partitioning_metadata(name, old_name, action_flag, + ignore_delete_error); } diff --git a/sql/handler.h b/sql/handler.h index b919aaeea55..df750d4ee22 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -3529,7 +3529,8 @@ public: int ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info); int ha_create_partitioning_metadata(const char *name, const char *old_name, - chf_create_flags action_flag); + chf_create_flags action_flag, + bool ignore_delete_error= false); int ha_change_partitions(HA_CREATE_INFO *create_info, const char *path, @@ -5071,7 +5072,8 @@ public: virtual int create_partitioning_metadata(const char *name, const char *old_name, - chf_create_flags action_flag) + chf_create_flags action_flag, + bool ignore_delete_error) { return FALSE; } virtual int change_partitions(HA_CREATE_INFO *create_info, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 5e790bcaf45..b9e24cb6a36 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2102,7 +2102,8 @@ bool quick_rm_table(THD *thd, handlerton *base, const LEX_CSTRING *db, handler *file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base); if (!file) DBUG_RETURN(true); - (void) file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG); + (void) file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG, + true); delete file; } if (!(flags & (FRM_ONLY|NO_HA_TABLE))) From ba6e0cbd1ff9606a86da5d8e8c5a4d4c62b6f961 Mon Sep 17 00:00:00 2001 From: Denis Protivensky Date: Wed, 5 Nov 2025 13:57:27 +0300 Subject: [PATCH 15/43] MDEV-37229: Set proper trx isolation level for Wsrep system threads Every Wsrep system thread should run with READ_COMMITTED transaction isolation level to prevent issues caused by InnoDB gap locks. The exception is statement-based replication for appliers, where REPEATABLE_READ is required by the server code. To account for that, set the isolation level before every applied event. It won't affect an already running transaction, but allows to handle both statement- and row-based replications accordingly. However, the problem might arise with the mixed replication format. Apart from that, there was a separate issue with applier thread vars: wsrep_plugins_post_init() would overwrite thd->variables for every applier thread and forget to restore proper default isolation level. Then, upon every server transaction termination, trans_reset_one_shot_statistics() would set thread's isolation level to the one stored in thd->variables, thus spoiling the isolation level value for appliers. --- mysql-test/suite/galera/r/MDEV-37229.result | 53 +++++++++++++++++++ .../r/galera_binlog_stmt_autoinc.result | 26 +++++++++ mysql-test/suite/galera/t/MDEV-37229.test | 49 +++++++++++++++++ .../galera/t/galera_binlog_stmt_autoinc.cnf | 1 + .../galera/t/galera_binlog_stmt_autoinc.test | 11 +++- sql/sql_plugin.cc | 2 + sql/wsrep_applier.cc | 24 +++++++++ sql/wsrep_schema.cc | 1 + sql/wsrep_storage_service.cc | 1 + 9 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/galera/r/MDEV-37229.result create mode 100644 mysql-test/suite/galera/t/MDEV-37229.test diff --git a/mysql-test/suite/galera/r/MDEV-37229.result b/mysql-test/suite/galera/r/MDEV-37229.result new file mode 100644 index 00000000000..59d908c451e --- /dev/null +++ b/mysql-test/suite/galera/r/MDEV-37229.result @@ -0,0 +1,53 @@ +connection node_2; +connection node_1; +connection node_2; +connection node_1; +CREATE TABLE t1 (i INT NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB; +SET GLOBAL wsrep_forced_binlog_format='STATEMENT'; +START TRANSACTION; +INSERT INTO t1(i) VALUES(NULL); +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave +INSERT INTO t1(i) VALUES(NULL); +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave +SET GLOBAL wsrep_forced_binlog_format='ROW'; +INSERT INTO t1(i) VALUES(NULL); +INSERT INTO t1(i) VALUES(NULL); +COMMIT; +SET GLOBAL wsrep_forced_binlog_format='ROW'; +START TRANSACTION; +INSERT INTO t1(i) VALUES(NULL); +INSERT INTO t1(i) VALUES(NULL); +SET GLOBAL wsrep_forced_binlog_format='STATEMENT'; +INSERT INTO t1(i) VALUES(NULL); +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave +INSERT INTO t1(i) VALUES(NULL); +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave +COMMIT; +SELECT * FROM t1 ORDER BY i; +i +1 +3 +5 +7 +9 +11 +13 +15 +connection node_2; +SELECT * FROM t1 ORDER BY i; +i +1 +3 +5 +7 +9 +11 +13 +15 +connection node_1; +SET GLOBAL wsrep_forced_binlog_format='NONE'; +DROP TABLE t1; diff --git a/mysql-test/suite/galera/r/galera_binlog_stmt_autoinc.result b/mysql-test/suite/galera/r/galera_binlog_stmt_autoinc.result index 10b94fdbc01..d4b025c3eb9 100644 --- a/mysql-test/suite/galera/r/galera_binlog_stmt_autoinc.result +++ b/mysql-test/suite/galera/r/galera_binlog_stmt_autoinc.result @@ -33,6 +33,28 @@ i c 3 dummy_text 5 dummy_text 7 dummy_text +insert into t1(i) values(null); +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave +insert into t1(i) values(null); +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave +insert into t1(i) values(null); +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave +insert into t1(i) values(null); +Warnings: +Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement is unsafe because it uses a system variable that may have a different value on the slave +select * from t1 order by i; +i c +1 dummy_text +3 dummy_text +5 dummy_text +7 dummy_text +9 dummy_text +11 dummy_text +13 dummy_text +15 dummy_text connection node_2; show variables like 'auto_increment%'; Variable_name Value @@ -44,6 +66,10 @@ i c 3 dummy_text 5 dummy_text 7 dummy_text +9 dummy_text +11 dummy_text +13 dummy_text +15 dummy_text SET GLOBAL wsrep_forced_binlog_format='none'; connection node_1; SET GLOBAL wsrep_forced_binlog_format='none'; diff --git a/mysql-test/suite/galera/t/MDEV-37229.test b/mysql-test/suite/galera/t/MDEV-37229.test new file mode 100644 index 00000000000..8d1fc600330 --- /dev/null +++ b/mysql-test/suite/galera/t/MDEV-37229.test @@ -0,0 +1,49 @@ +## +## Test that mixed binlog replication works at the applier side. +## + +--source include/galera_cluster.inc +--source include/have_innodb.inc + +--connection node_2 +--disable_query_log +call mtr.add_suppression("Unsafe statement written to the binary log"); +--enable_query_log + +--connection node_1 +--disable_query_log +call mtr.add_suppression("Unsafe statement written to the binary log"); +--enable_query_log + +CREATE TABLE t1 (i INT NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB; + +# Try mixed replication: statement then row. +SET GLOBAL wsrep_forced_binlog_format='STATEMENT'; +START TRANSACTION; +INSERT INTO t1(i) VALUES(NULL); +INSERT INTO t1(i) VALUES(NULL); +SET GLOBAL wsrep_forced_binlog_format='ROW'; +INSERT INTO t1(i) VALUES(NULL); +INSERT INTO t1(i) VALUES(NULL); +COMMIT; + +# Try mixed replication: row then statement. +SET GLOBAL wsrep_forced_binlog_format='ROW'; +START TRANSACTION; +INSERT INTO t1(i) VALUES(NULL); +INSERT INTO t1(i) VALUES(NULL); +SET GLOBAL wsrep_forced_binlog_format='STATEMENT'; +INSERT INTO t1(i) VALUES(NULL); +INSERT INTO t1(i) VALUES(NULL); +COMMIT; + +SELECT * FROM t1 ORDER BY i; + +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 8 FROM t1; +--source include/wait_condition.inc +SELECT * FROM t1 ORDER BY i; + +--connection node_1 +SET GLOBAL wsrep_forced_binlog_format='NONE'; +DROP TABLE t1; diff --git a/mysql-test/suite/galera/t/galera_binlog_stmt_autoinc.cnf b/mysql-test/suite/galera/t/galera_binlog_stmt_autoinc.cnf index 00e5017455f..d176cdd845d 100644 --- a/mysql-test/suite/galera/t/galera_binlog_stmt_autoinc.cnf +++ b/mysql-test/suite/galera/t/galera_binlog_stmt_autoinc.cnf @@ -5,5 +5,6 @@ auto_increment_offset=1 auto_increment_increment=2 [mysqld.2] +wsrep_slave_threads=2 auto_increment_offset=2 auto_increment_increment=2 diff --git a/mysql-test/suite/galera/t/galera_binlog_stmt_autoinc.test b/mysql-test/suite/galera/t/galera_binlog_stmt_autoinc.test index 81bf6738086..3998da5cca9 100644 --- a/mysql-test/suite/galera/t/galera_binlog_stmt_autoinc.test +++ b/mysql-test/suite/galera/t/galera_binlog_stmt_autoinc.test @@ -48,8 +48,17 @@ insert into t1(i) values(null), (null), (null); select * from t1 order by i; +# Ensure both appliers have some work to perform in parallel. +# This way we can see they both have proper isolation level set. +insert into t1(i) values(null); +insert into t1(i) values(null); +insert into t1(i) values(null); +insert into t1(i) values(null); + +select * from t1 order by i; + --connection node_2 ---let $wait_condition = SELECT COUNT(*) = 4 FROM t1; +--let $wait_condition = SELECT COUNT(*) = 8 FROM t1; --source include/wait_condition.inc show variables like 'auto_increment%'; select * from t1 order by i; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index c7fad3a9a59..03f89c3de9f 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -4547,6 +4547,8 @@ my_bool post_init_callback(THD *thd, void *) thd->variables.tx_read_only= false; // Restore option_bits thd->variables.option_bits= option_bits_saved; + // Restore proper default isolation level for appliers + thd->variables.tx_isolation= ISO_READ_COMMITTED; } set_current_thd(0); return 0; diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc index 4eaef4fb489..dfd20708561 100644 --- a/sql/wsrep_applier.cc +++ b/sql/wsrep_applier.cc @@ -279,6 +279,30 @@ int wsrep_apply_events(THD* thd, } } + /* Statement-based replication requires InnoDB repeatable read + transaction isolation level or higher. + The isolation level will be reset to the default upon + transaction termination. + Although the isolation level can only be set on a newly + started server transaction, it cannot be determined until + we see the first applied binlog event. + + Even though the isolation level is changed for every next + applied event, it won't affect the overall setting for a + running transaction. + This should create an issue with the mixed replication mode + as applying query and row events should be performed with + different isolation levels, but now it doesn't surface. + */ + if (LOG_EVENT_IS_QUERY(typ)) + { + thd->tx_isolation= ISO_REPEATABLE_READ; + } + else + { + thd->tx_isolation= ISO_READ_COMMITTED; + } + if (LOG_EVENT_IS_WRITE_ROW(typ) || LOG_EVENT_IS_UPDATE_ROW(typ) || LOG_EVENT_IS_DELETE_ROW(typ)) diff --git a/sql/wsrep_schema.cc b/sql/wsrep_schema.cc index dcce91e71f0..b1b651928f7 100644 --- a/sql/wsrep_schema.cc +++ b/sql/wsrep_schema.cc @@ -730,6 +730,7 @@ static void wsrep_init_thd_variables(THD *thd) /* No general log */ thd->variables.option_bits|= OPTION_LOG_OFF; /* Read committed isolation to avoid gap locking */ + thd->tx_isolation= ISO_READ_COMMITTED; thd->variables.tx_isolation= ISO_READ_COMMITTED; } diff --git a/sql/wsrep_storage_service.cc b/sql/wsrep_storage_service.cc index 7e9229c65e0..72ad3949a3a 100644 --- a/sql/wsrep_storage_service.cc +++ b/sql/wsrep_storage_service.cc @@ -63,6 +63,7 @@ Wsrep_storage_service::Wsrep_storage_service(THD* thd) thd->variables.option_bits |= OPTION_LOG_OFF; /* Read committed isolation to avoid gap locking */ + thd->tx_isolation = ISO_READ_COMMITTED; thd->variables.tx_isolation = ISO_READ_COMMITTED; /* Keep wsrep on to enter commit ordering hooks */ From 5cbe01730d244376a54f30f7be7d1400ae664a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Otto=20Kek=C3=A4l=C3=A4inen?= Date: Fri, 12 Dec 2025 11:19:12 -0800 Subject: [PATCH 16/43] MDEV-38332 Detect Hurd based on CMAKE_SYSTEM_NAME being "GNU" In 22afd13 Hurd detection was introduced using the processor type, as incidentally only the hurd-i386 build on Debian was using i686. This method however does not detect a hurd-amd64 system. Fix this by using the actual system name following CMake conventions. Hurd is a kernel project by the GNU project, thus system is called "GNU". --- cmake/build_configurations/mysql_release.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/build_configurations/mysql_release.cmake b/cmake/build_configurations/mysql_release.cmake index c52ff46843e..e2adb59027c 100644 --- a/cmake/build_configurations/mysql_release.cmake +++ b/cmake/build_configurations/mysql_release.cmake @@ -121,8 +121,8 @@ ELSEIF(DEB) SET(WITH_ZLIB system CACHE STRING "") SET(WITH_LIBWRAP ON) SET(HAVE_EMBEDDED_PRIVILEGE_CONTROL ON) - # No hurd implementation - IF(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "i686-AT386") + # On Hurd systems (GNU) there is no auth socket implementation + IF(NOT CMAKE_SYSTEM_NAME STREQUAL "GNU") SET(PLUGIN_AUTH_SOCKET YES CACHE STRING "") ENDIF() SET(WITH_EMBEDDED_SERVER ON CACHE BOOL "") From a9006bdf292ed432398d6618fd67420898e0ff71 Mon Sep 17 00:00:00 2001 From: ethanxhzhao Date: Fri, 4 Jul 2025 15:06:28 +0800 Subject: [PATCH 17/43] MDEV-37157 When using rownum in having clause without group by or in subquery, the result may be wrong Fix rownum in HAVING clause or subquery Description ========== When rownum used in HAVING without GROUP BY, accepted_rows should be incremented even if the having cond's value is 0. When rownum used in subquery 1. Forbid subquery transformed to semi join. 2. New conditions must be added into having clause when doing in2exists. --- mysql-test/main/rownum.result | 40 +++++++++++++++++++++++++++++++++++ mysql-test/main/rownum.test | 28 ++++++++++++++++++++++++ sql/item_subselect.cc | 9 ++++---- sql/opt_subselect.cc | 4 +++- sql/sql_select.cc | 7 ++++++ 5 files changed, 83 insertions(+), 5 deletions(-) diff --git a/mysql-test/main/rownum.result b/mysql-test/main/rownum.result index b61269b1b47..387b6183c05 100644 --- a/mysql-test/main/rownum.result +++ b/mysql-test/main/rownum.result @@ -1015,5 +1015,45 @@ ERROR HY000: The target table v of the UPDATE is not updatable DROP VIEW v; DROP TABLE t; # +# rownum in having without group by +# +create table t1 (a int primary key, b int); +insert into t1 values (1, 1), (2, 1), (3, 1), (4, 2), (5, 2), (6, 2); +select * from (select * from t1 order by a, b) dt having rownum() = 4; +a b +4 2 +drop table t1; +# +# rownum in subquery +# +create table t1 (a int primary key, b int); +insert into t1 values (1, 1), (2, 1), (3, 1), (4, 2), (5, 2), (6, 2); +explain select * from t1 where a in (select rownum() from t1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL PRIMARY NULL NULL NULL 6 +1 PRIMARY eq_ref distinct_key distinct_key 8 test.t1.a 1 Using where +2 MATERIALIZED t1 index NULL PRIMARY 4 NULL 6 Using index +select * from t1 where a in (select rownum() from t1); +a b +1 1 +2 1 +3 1 +4 2 +5 2 +6 2 +set optimizer_switch='materialization=off'; +explain select * from t1 where a + 1 in (select rownum() from t1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 6 Using where +2 DEPENDENT SUBQUERY t1 index NULL PRIMARY 4 NULL 6 Using index +select * from t1 where a + 1 in (select rownum() from t1); +a b +1 1 +2 1 +3 1 +4 2 +5 2 +drop table t1; +# # End of 10.6 tests # diff --git a/mysql-test/main/rownum.test b/mysql-test/main/rownum.test index a68a432b606..24fadf768af 100644 --- a/mysql-test/main/rownum.test +++ b/mysql-test/main/rownum.test @@ -617,6 +617,34 @@ UPDATE v SET f = 10 WHERE e > 42 LIMIT 1; DROP VIEW v; DROP TABLE t; +--echo # +--echo # rownum in having without group by +--echo # + +create table t1 (a int primary key, b int); +insert into t1 values (1, 1), (2, 1), (3, 1), (4, 2), (5, 2), (6, 2); +select * from (select * from t1 order by a, b) dt having rownum() = 4; + +drop table t1; + +--echo # +--echo # rownum in subquery +--echo # + +create table t1 (a int primary key, b int); +insert into t1 values (1, 1), (2, 1), (3, 1), (4, 2), (5, 2), (6, 2); + +# When rownum used in subquery, the subquery shouldn't be transformed to semijoin +explain select * from t1 where a in (select rownum() from t1); +select * from t1 where a in (select rownum() from t1); + +set optimizer_switch='materialization=off'; +# When rownum used in IN-subquery and the subquery is transformed to EXISTS, the new condition should be added to HAVING clause. +explain select * from t1 where a + 1 in (select rownum() from t1); +select * from t1 where a + 1 in (select rownum() from t1); + +drop table t1; + --echo # --echo # End of 10.6 tests --echo # diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 9c9e97fed2d..0378aad8537 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -2329,7 +2329,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, *having_item= NULL; if (join_having || select_lex->with_sum_func || - select_lex->group_list.elements) + select_lex->group_list.elements || select_lex->with_rownum) { LEX_CSTRING field_name= this->full_name_cstring(); Item *item= func->create(thd, expr, @@ -2576,9 +2576,10 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, during JOIN::optimize: this->tmp_having= this->having; this->having= 0; */ Item* join_having= join->having ? join->having : join->tmp_having; - bool is_having_used= (join_having || select_lex->with_sum_func || - select_lex->group_list.first || - !select_lex->table_list.elements); + bool is_having_used= + (join_having || select_lex->with_sum_func || + select_lex->group_list.first || !select_lex->table_list.elements || + select_lex->with_rownum); LEX_CSTRING list_ref= { STRING_WITH_LEN("")}; DBUG_ENTER("Item_in_subselect::create_row_in_to_exists_cond"); DBUG_ASSERT(thd == join->thd); diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 3d8a6c08d2f..63309acbf89 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -707,6 +707,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) 12. All tables supports comparable rowids. This is needed for DuplicateWeedout strategy to work (which is the catch-all semi-join strategy so it must be applicable). + 13. Subquery does not have ROWNUM */ if (optimizer_flag(thd, OPTIMIZER_SWITCH_SEMIJOIN) && in_subs && // 1 @@ -722,7 +723,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join) select_lex->outer_select()->join->select_options) // 10 & SELECT_STRAIGHT_JOIN) && // 10 select_lex->first_cond_optimization && // 11 - join->not_usable_rowid_map == 0) // 12 + join->not_usable_rowid_map == 0 && // 12 + !select_lex->with_rownum) // 13 { DBUG_PRINT("info", ("Subquery is semi-join conversion candidate")); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5bc1842c4d4..71715f743b6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -24331,7 +24331,14 @@ end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) copy_fields(&join->tmp_table_param); } if (join->having && join->having->val_bool() == 0) + { + /* + If we have HAVING clause and it is not satisfied, we don't send + the row to the client, but rownum should be incremented. + */ + join->accepted_rows++; DBUG_RETURN(NESTED_LOOP_OK); // Didn't match having + } if (join->procedure) { if (join->procedure->send_row(join->procedure_fields_list)) From a04ccfe9245a41aada50aa3918f57098ab50e84f Mon Sep 17 00:00:00 2001 From: Tony Chen Date: Fri, 20 Sep 2024 16:30:03 +0000 Subject: [PATCH 18/43] Add MTR to verify TLS with chain of trust Add tests to verify that TLS configurations with certificate chain of trust are supported. The contents of the commit are inspired from the MySQL project: https://github.com/mysql/mysql-server/commit/969afef933f1872c5f38ea93047ef05c4509c335 Credits to salman.s.khan@oracle.com All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc. --- mysql-test/lib/generate-ssl-certs.sh | 153 ++++++++++++++++++ mysql-test/main/chained_ssl_certificates.opt | 3 + .../main/chained_ssl_certificates.result | 12 ++ mysql-test/main/chained_ssl_certificates.test | 74 +++++++++ mysql-test/std_data/cachain/client.cachain | 62 +++++++ mysql-test/std_data/cachain/client.key | 52 ++++++ mysql-test/std_data/cachain/root.crt | 31 ++++ .../cachain/root_intermediate_ca1.crt | 62 +++++++ mysql-test/std_data/cachain/server.cachain | 62 +++++++ mysql-test/std_data/cachain/server.key | 52 ++++++ .../std_data/cachain/unrelated_root.crt | 31 ++++ mysql-test/suite.pm | 4 + 12 files changed, 598 insertions(+) create mode 100644 mysql-test/main/chained_ssl_certificates.opt create mode 100644 mysql-test/main/chained_ssl_certificates.result create mode 100644 mysql-test/main/chained_ssl_certificates.test create mode 100644 mysql-test/std_data/cachain/client.cachain create mode 100644 mysql-test/std_data/cachain/client.key create mode 100644 mysql-test/std_data/cachain/root.crt create mode 100644 mysql-test/std_data/cachain/root_intermediate_ca1.crt create mode 100644 mysql-test/std_data/cachain/server.cachain create mode 100644 mysql-test/std_data/cachain/server.key create mode 100644 mysql-test/std_data/cachain/unrelated_root.crt diff --git a/mysql-test/lib/generate-ssl-certs.sh b/mysql-test/lib/generate-ssl-certs.sh index 57caf3b7a4e..1ced695bf7f 100755 --- a/mysql-test/lib/generate-ssl-certs.sh +++ b/mysql-test/lib/generate-ssl-certs.sh @@ -64,3 +64,156 @@ rm -fv crldir/* cp -v client-cert.crl crldir/`openssl x509 -in client-cert.pem -noout -issuer_hash`.r0 rm -rf demoCA + +# --- Certificate Chain --- +# These tests are inspired from the following commit from MySQL Server +# https://github.com/mysql/mysql-server/commit/969afef933f1872c5f38ea93047ef05c4509c335 +# +# Credits to salman.s.khan@oracle.com +# +# ------------------------------------------------------------------------------------- +# +# STEPS TO GENERATE THE FOLLOWING CHAINED CERTIFICATES WHICH IS USED IN THE TEST CASE : +# +# +---------+ +# | Root CA | +# +---------+ +# | +# /------------+-----------\ +# | | +# +------------------+ +------------------+ +# | Intermediate CA1 | | Intermediate CA2 | +# +------------------+ +------------------+ +# | | +# +-------------+ +-------------+ +# | Server | | Client | +# | certificate | | certificate | +# +-------------+ +-------------+ + +cd cachain + +mkdir ca +mkdir server +mkdir clients + +mkdir ca/root.certs +touch ca/root.index.txt +touch ca/root.index.txt.attr +echo '01' > ca/root.serial + +cat > ca/root.cfg << EOF +[ ca ] +default_ca = CA_default +[ CA_default ] +dir = $PWD/ca +certs = \$dir/certs +database = \$dir/root.index.txt +serial = \$dir/root.serial +policy= policy_match +[ policy_match ] +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional +[ v3_ca ] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer +basicConstraints = critical,CA:TRUE +keyUsage = critical,keyCertSign,cRLSign +[ v3_ca_intermediate ] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer +basicConstraints = critical,CA:TRUE,pathlen:0 +keyUsage = critical,keyCertSign,cRLSign +EOF + +# Generate Root CA key and cert +openssl genrsa -out ca/root.key 4096 +openssl req -new -x509 '-sha256' -key ca/root.key -out ca/root.crt -days 7200 -subj "/O=MariaDB/OU=MariaDB/CN=Root CA" -config ca/root.cfg -extensions v3_ca + +# Generate Intermediate CA1 key and cert +openssl genrsa -out ca/intermediate_ca1.key 4096 +openssl req -new '-sha256' -key ca/intermediate_ca1.key -out ca/intermediate_ca1.csr -subj "/O=MariaDB/OU=MariaDB/CN=Intermediate CA1" + +openssl ca -batch -days 7200 -notext -md sha256 -in ca/intermediate_ca1.csr -out ca/intermediate_ca1.crt -keyfile ca/root.key -cert ca/root.crt -outdir ca/root.certs/ -config ca/root.cfg -extensions v3_ca_intermediate + +mkdir ca/intermediate_ca1.certs +touch ca/intermediate_ca1.index.txt +touch ca/intermediate_ca1.index.txt.attr +echo '01' > ca/intermediate_ca1.serial + +cat > ca/intermediate_ca1.cfg << EOF +[ ca ] +default_ca = CA_default +[ CA_default ] +dir = $PWD/ca +certs = \$dir/intermediate_ca1.certs +database = \$dir/intermediate_ca1.index.txt +serial = \$dir/intermediate_ca1.serial +policy= policy_match +[ policy_match ] +commonName = supplied +[ alt_names ] +DNS.1 = localhost +IP.1 = 127.0.0.1 +[ server_cert ] +basicConstraints = CA:FALSE +keyUsage = critical,digitalSignature,keyEncipherment +extendedKeyUsage = serverAuth +subjectAltName = @alt_names +EOF + +# Generate Server key and cert +openssl genrsa -out server/server.key 4096 +openssl req -new '-sha256' -key server/server.key -out server/server.csr -subj "/CN=localhost" + +openssl ca -batch -days 7200 -notext -md sha256 -in server/server.csr -out server/server.crt -keyfile ca/intermediate_ca1.key -cert ca/intermediate_ca1.crt -outdir ca/intermediate_ca1.certs/ -config ca/intermediate_ca1.cfg -extensions server_cert + +# Generate Intermediate CA2 key and cert +openssl genrsa -out ca/intermediate_ca2.key 4096 + +openssl req -new '-sha256' -key ca/intermediate_ca2.key -out ca/intermediate_ca2.csr -subj "/O=MariaDB/OU=MariaDB/CN=Intermediate CA2" +openssl ca -batch -days 7200 -notext -md sha256 -in ca/intermediate_ca2.csr -out ca/intermediate_ca2.crt -keyfile ca/root.key -cert ca/root.crt -outdir ca/root.certs/ -config ca/root.cfg -extensions v3_ca_intermediate + +mkdir ca/intermediate_ca2.certs +touch ca/intermediate_ca2.index.txt +touch ca/intermediate_ca2.index.txt.attr +echo '01' > ca/intermediate_ca2.serial + +cat > ca/intermediate_ca2.cfg << EOF +[ ca ] +default_ca = CA_default +[ CA_default ] +dir = $PWD/ca +certs = \$dir/intermediate_ca2.certs +database = \$dir/intermediate_ca2.index.txt +serial = \$dir/intermediate_ca2.serial +policy= policy_match +[ policy_match ] +commonName = supplied +[ client_cert ] +basicConstraints = CA:FALSE +keyUsage = critical,digitalSignature,keyEncipherment +extendedKeyUsage = clientAuth +EOF + +# Generate Client key and cert +openssl genrsa -out clients/client.key 4096 +openssl req -new '-sha256' -key clients/client.key -out clients/client.csr -subj "/CN=client" + +openssl ca -batch -days 7200 -notext -md sha256 -in clients/client.csr -out clients/client.crt -keyfile ca/intermediate_ca2.key -cert ca/intermediate_ca2.crt -outdir ca/intermediate_ca2.certs/ -config ca/intermediate_ca2.cfg -extensions client_cert + +cat server/server.crt ca/intermediate_ca1.crt > server/server.cachain + +cat clients/client.crt ca/intermediate_ca2.crt > clients/client.cachain + +cat ca/root.crt ca/intermediate_ca1.crt > ca/root_intermediate_ca1.crt + +# Generate Unrelated Root CA key and cert +openssl genrsa -out ca/unrelated_root.key 4096 +openssl req -new -x509 '-sha256' -key ca/unrelated_root.key -out ca/unrelated_root.crt -days 7200 -subj "/O=MariaDB/OU=MariaDB/CN=Root CA" -config ca/root.cfg -extensions v3_ca + +cp -v ca/root.crt ca/root_intermediate_ca1.crt ca/unrelated_root.crt server/server.key server/server.cachain clients/client.key clients/client.cachain ./ +rm -rf ca server clients + +cd .. diff --git a/mysql-test/main/chained_ssl_certificates.opt b/mysql-test/main/chained_ssl_certificates.opt new file mode 100644 index 00000000000..bb2f8e1d883 --- /dev/null +++ b/mysql-test/main/chained_ssl_certificates.opt @@ -0,0 +1,3 @@ +--ssl-ca=$MYSQL_TEST_DIR/std_data/cachain/root.crt +--ssl-key=$MYSQL_TEST_DIR/std_data/cachain/server.key +--ssl-cert=$MYSQL_TEST_DIR/std_data/cachain/server.cachain diff --git a/mysql-test/main/chained_ssl_certificates.result b/mysql-test/main/chained_ssl_certificates.result new file mode 100644 index 00000000000..08911b0c936 --- /dev/null +++ b/mysql-test/main/chained_ssl_certificates.result @@ -0,0 +1,12 @@ +call mtr.add_suppression("Server SSL certificate doesn't verify"); +CREATE USER 'user1'@'%' REQUIRE SSL; +Variable_name Value +Ssl_version TLS + +Restart server and provide ssl-ca comprising intermediate_ca1 in addition to the root ca. +Variable_name Value +Ssl_version TLS + +Restart server and provide unrelated ssl-ca at server startup +ERROR 2026 (HY000): TLS/SSL error: +DROP USER 'user1'; diff --git a/mysql-test/main/chained_ssl_certificates.test b/mysql-test/main/chained_ssl_certificates.test new file mode 100644 index 00000000000..9d315e94807 --- /dev/null +++ b/mysql-test/main/chained_ssl_certificates.test @@ -0,0 +1,74 @@ +# These tests are inspired from the following commit from MySQL Server +# https://github.com/mysql/mysql-server/commit/969afef933f1872c5f38ea93047ef05c4509c335 + +# Credits to salman.s.khan@oracle.com + +##################################################################################### +# This test verifies MariaDB can handle chained ssl certificate +# This test uses chained ssl certificates which is depicted as +# below:- +# +# +---------+ +# | Root CA | +# +---------+ +# | +# /------------+-----------\ +# | | +# +------------------+ +------------------+ +# | Intermediate CA1 | | Intermediate CA2 | +# +------------------+ +------------------+ +# | | +# +-------------+ +-------------+ +# | Server | | Client | +# | certificate | | certificate | +# +-------------+ +-------------+ +# +# certificates that may be helpful for chain construction. +# In order to validate server cert correctly, we need to +# provide the trusted root certificate and the untrusted +# intermediate certificates as part of ssl-ca. Hence +# root_intermediate_ca1.crt (trusted root certificate + +# untrusted intermediate ca1 certificate) is passed as with +# --ssl-ca option + +--source include/not_embedded.inc +--source include/have_ssl_communication.inc + +#Suppress warning by the server certificate verification check: unrelated CA +call mtr.add_suppression("Server SSL certificate doesn't verify"); + +CREATE USER 'user1'@'%' REQUIRE SSL; + +--replace_result TLSv1.3 TLS TLSv1.2 TLS +--exec $MYSQL --host=localhost -P $MASTER_MYPORT --user=user1 --ssl-verify-server-cert --ssl-ca=$MYSQL_TEST_DIR/std_data/cachain/root.crt --ssl-cert=$MYSQL_TEST_DIR/std_data/cachain/client.cachain --ssl-key=$MYSQL_TEST_DIR/std_data/cachain/client.key -e "SHOW STATUS LIKE 'ssl_version'" + +--echo +--echo Restart server and provide ssl-ca comprising intermediate_ca1 in addition to the root ca. +--write_line wait $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--shutdown_server +--source include/wait_until_disconnected.inc + +--write_line "restart:--ssl-ca=$MYSQL_TEST_DIR/std_data/cachain/root_intermediate_ca1.crt --ssl-key=$MYSQL_TEST_DIR/std_data/cachain/server.key --ssl-cert=$MYSQL_TEST_DIR/std_data/cachain/server.cachain" $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--enable_reconnect +--source include/wait_until_connected_again.inc + +--replace_result TLSv1.3 TLS TLSv1.2 TLS +--exec $MYSQL --host=localhost -P $MASTER_MYPORT --user=user1 --ssl-verify-server-cert --ssl-ca=$MYSQL_TEST_DIR/std_data/cachain/root.crt --ssl-cert=$MYSQL_TEST_DIR/std_data/cachain/client.cachain --ssl-key=$MYSQL_TEST_DIR/std_data/cachain/client.key -e "SHOW STATUS LIKE 'ssl_version'" + +--echo +--echo Restart server and provide unrelated ssl-ca at server startup +--write_line wait $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--shutdown_server +--source include/wait_until_disconnected.inc + +--write_line "restart:--ssl-ca=$MYSQL_TEST_DIR/std_data/cachain/unrelated_root.crt --ssl-key=$MYSQL_TEST_DIR/std_data/cachain/server.key --ssl-cert=$MYSQL_TEST_DIR/std_data/cachain/server.cachain" $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--enable_reconnect +--source include/wait_until_connected_again.inc + +--replace_regex /(ERROR 2026 \(HY000\): TLS\/SSL error:).*/\1/ +--error 1 +--exec $MYSQL --host=localhost -P $MASTER_MYPORT --user=user1 --ssl-verify-server-cert --ssl-ca=$MYSQL_TEST_DIR/std_data/cachain/unrelated_root.crt --ssl-cert=$MYSQL_TEST_DIR/std_data/cachain/client.cachain --ssl-key=$MYSQL_TEST_DIR/std_data/cachain/client.key -e "SHOW STATUS LIKE 'ssl_version'" 2>&1 +--echo + +# Cleanup +DROP USER 'user1'; diff --git a/mysql-test/std_data/cachain/client.cachain b/mysql-test/std_data/cachain/client.cachain new file mode 100644 index 00000000000..3a2aa6a8dba --- /dev/null +++ b/mysql-test/std_data/cachain/client.cachain @@ -0,0 +1,62 @@ +-----BEGIN CERTIFICATE----- +MIIFPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MRAwDgYDVQQKDAdNYXJp +YURCMRAwDgYDVQQLDAdNYXJpYURCMRkwFwYDVQQDDBBJbnRlcm1lZGlhdGUgQ0Ey +MB4XDTI1MTIxMjE2NTY0NFoXDTQ1MDgyOTE2NTY0NFowETEPMA0GA1UEAwwGY2xp +ZW50MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtP9k21MRWxut47OV +CjK9jn+nJHnZ0agpAJbgdvsIFKrkV4v0VHb7m5dURaqV1lF1WhrSmc8k7gcSB32O +l+6Fo0OToe80KxH3NeYN5YscfnVOtrtbpwEkoYt4CFSn95TpjFrEaF0nZUwg5mJb ++ZZsXzYk60GBA1It9uLMbqWAioRHbLprV6bIn0yDqKlf5JTQRk4JORxw3J66ppWC +y+4OI8UmuOF4jJx3nWHTmPmtZEA7FgNisW9ReQC46+A8mdJCUqupIq6M7foG682+ +eKarvX1kjlOhL60PeuzazCmaGYTnkYI1wFC6Js6nE9xRTzS0UWR2UhBfgf9/FB86 +dqtD4qnwleW3VbnOim7XnXFrVq7yJ22sBO0KGRLRmBa2RCb8qvAogZ8zzYj/Jt4x +QBHoqjFjGreaXZod0/P1yxTqgZBAz526WtH5EP4sO7xiFPvQONmrDsWAkG7PBRbw +QZkpnYyH+IiGWQuL4Xsv8uSK6aKDvnXyvY97xKNQ3R4Nm/U5lsif9Uuw/dJHsIZl +FkqG5m9p++7Kj5lAfliFtUmOOIHp3Vb3pMWds3muiJ5BH5g/1Ba2pe69GArWPx6P +e63nhgUJ6DbUcS0xGX4lCeQg9/NPEimIk1CyqIcqSM6WN8//UUSiJuPArHxV1t5C +Cv1BFIn27BPrTWGfrSEf4YThpDcCAwEAAaNyMHAwCQYDVR0TBAIwADAOBgNVHQ8B +Af8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHQYDVR0OBBYEFDE0JF4hyOMP +qskQdLbeJ/HEDQglMB8GA1UdIwQYMBaAFBvttDY6KgFqqyQy/GPUCB7wjMIjMA0G +CSqGSIb3DQEBCwUAA4ICAQBgWR9PtXBDD6FPeZPkm5THyopYdueTHQ2MY4zH01mf +tAgFJUOZGqaLPgnvhN1wd1JgU5K4kRR1MShz9JWo9B72t5PBL3Pr41xrhcoFja5C +P+A4VOQ8yzQwA+V96Na11iXa+DiS6K3Vv7dp/bhAtWEk+6O/FTSdGdL5GAjbDXtd +DKadvMx4tSrFZj4MW6V6xe3FzlCbg5ciEruhWUXPoW9XUOXqPScauPumXPJmYcsL +OSz0br/Wh46vc6c7nYFTQd8UbyXU63/IdbTqB1AbY/uAZ47bzj9vGketKDWtYgJy +uLFRybDZ5JX+S/p4kNYisfNeBwXr+Jc0ayTzWYnd2JjbIzeKBwcEsgCieT/rYJwt +tUWcUb2I9936eeb7cBpqAcxaXSSABfqRdP/tIoVZrfEaufIeou/Gvglc1/PlIdlt +nwURZBrwI6O2wWT6bZIlcM/kvIYpSVRV028p9SpZ9FGgn6aTYGVxcc50waWz6lSF +WAaFkhiTeSQZugIPzk7SgDVsLQzL0YudAEb6afNTlF/k0qXrjjLsw1PbFJeVKkON +guHQtkPAQaNgSRSPJtcG8s9+Qtcg8nGLTs33S0bY/BRF2M5v+aGVHTzlF9FNwVPx +sLwekeDo75H/IMiB2CWQLsQbkwoCNIVsYaDrcfWGvaWt4OUkNKRjSTQ8OgJn4+OD +hA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIBAjANBgkqhkiG9w0BAQsFADA2MRAwDgYDVQQKDAdNYXJp +YURCMRAwDgYDVQQLDAdNYXJpYURCMRAwDgYDVQQDDAdSb290IENBMB4XDTI1MTIx +MjE2NTY0NFoXDTQ1MDgyOTE2NTY0NFowPzEQMA4GA1UECgwHTWFyaWFEQjEQMA4G +A1UECwwHTWFyaWFEQjEZMBcGA1UEAwwQSW50ZXJtZWRpYXRlIENBMjCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL+xpdkcY7Xg/B+7ct45WHJtvArOwQdm +vo6itdVRrXEc+MprySGjbLYvm+MB4QhI/e0Xhwvhh/6WAacSF9nu+GiWw02fZ5hR +KNiepxAua+uxulPUcDapFRUt+w1H0HfB85qIChvJ1f56t+gtvLZpkNmS+p1U5wnp +pFdIyBVDF3t6lr0Aoa5LRo1mbcB9MT4lJ7uMzcoTHFFQq4ROeyoRnleFGN0tIbqa +njMBvN6lRpQToPfstQdD4B8LLwwYmJvPYInxJx76QV39PzPOsmq581WCMi49z3B4 +gBLxNP5/QfrXC6DVCFs1aq9SyuclcIcquMvgvrvQ0hI6l5xbvj6DLySUfJI1unCw +GUdvg2U1DCQN3TPU3j8tHXHQJgp7y9NNCbaLKi60X+vWB3uhWnAzqV+QMvDxbA3w +4PZobeBOeygst7f3020UVAcRZLpb4l/MJTzdjTVjmPz0I3wenKPj6m19BH+iVabz +p4jc4wlBZuyBmhQ4UpYuCYzuIh+ki2daZc43UmdDx8sEKBooE1LBnn85pSQNwBuM +Iu8FdqN/FpVlfnrbjM1sFKvDfm91knGRRQnd4R1WKm8PB0kmmQ6rq5eMc37Gn7oG +cCWXomCtaVDExZ7MVscPAWVtfTpHKNkHpKh0WjEtO2+qY1Ts3h1EeJMTjp602rTo +6vlmm8egTvQ9AgMBAAGjZjBkMB0GA1UdDgQWBBQb7bQ2OioBaqskMvxj1Age8IzC +IzAfBgNVHSMEGDAWgBQEiu6rzWpHMQmM8hx+uT1eOZrSrDASBgNVHRMBAf8ECDAG +AQH/AgEAMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAFRDs8s5g +OZu+0XoDGOka5rVEcuKukOd8hM1kx5YXuFOsKISShJywCGtpBbs2acjqEjKMiiME +uZSAapm/9YwQv1wM3XNqjzypxAl4ibE/5noJR7vDS0ixMgO884pDVdvHYwfw1aEJ +lxMfDQkY3foMVDLNgkscHtJWH3rQ9icf6ATzvJh03Oxsk/5ku056Fs2hcNzrK0un +omrycfAEIBikkqlS/OCWAHNp5nBkxcl/ZN1dO5empcbaYIuDSd9d4oPAFkbW3qoa +cqOYrwWbKjlbPQ1vXmst0KmWnErWurm7e3aSkXzUTLRor12p0pfyydpQ79vNm/md +uDXzhMc4/xdMQCSHMcVfbD2Oduz/Xh7G5n+NbGHNj1FzChraHj7Mr+/ls6sY8uRA +oE/xB+Y+BJcJgsghUhulVFnF/tDPWDwHkSiDG8yXxaOr98/hdtdVlk2SZRmhmu5J +8yVhAyPj/v7dxInQ4aE4AEkEtlSV9qHo1armzyORa9kqKbWUR9h3yba/U65citRQ +sT7eDkoYueNew6mYcWrh7mCY6HflhM9Kskyem+Ev5BOPjNHyRU/yuSytfOQefa4R +Zwh4oTAWlPImUlcImnDHaXTlCxu8carg87tk4RNlQX9VhsqJtZEFA9dvhfyCW1k0 +t9zG0FnuvV5vtSrkT/DUEnTHpSbzgO5dpU0= +-----END CERTIFICATE----- diff --git a/mysql-test/std_data/cachain/client.key b/mysql-test/std_data/cachain/client.key new file mode 100644 index 00000000000..d39b205bfdb --- /dev/null +++ b/mysql-test/std_data/cachain/client.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC0/2TbUxFbG63j +s5UKMr2Of6ckednRqCkAluB2+wgUquRXi/RUdvubl1RFqpXWUXVaGtKZzyTuBxIH +fY6X7oWjQ5Oh7zQrEfc15g3lixx+dU62u1unASShi3gIVKf3lOmMWsRoXSdlTCDm +Ylv5lmxfNiTrQYEDUi324sxupYCKhEdsumtXpsifTIOoqV/klNBGTgk5HHDcnrqm +lYLL7g4jxSa44XiMnHedYdOY+a1kQDsWA2Kxb1F5ALjr4DyZ0kJSq6kirozt+gbr +zb54pqu9fWSOU6EvrQ967NrMKZoZhOeRgjXAULomzqcT3FFPNLRRZHZSEF+B/38U +Hzp2q0PiqfCV5bdVuc6KbtedcWtWrvInbawE7QoZEtGYFrZEJvyq8CiBnzPNiP8m +3jFAEeiqMWMat5pdmh3T8/XLFOqBkEDPnbpa0fkQ/iw7vGIU+9A42asOxYCQbs8F +FvBBmSmdjIf4iIZZC4vhey/y5IrpooO+dfK9j3vEo1DdHg2b9TmWyJ/1S7D90kew +hmUWSobmb2n77sqPmUB+WIW1SY44gendVvekxZ2zea6InkEfmD/UFral7r0YCtY/ +Ho97reeGBQnoNtRxLTEZfiUJ5CD3808SKYiTULKohypIzpY3z/9RRKIm48CsfFXW +3kIK/UEUifbsE+tNYZ+tIR/hhOGkNwIDAQABAoICAAQY2eQbE8P/suZ6HLAP6PmO +Sy7IsVUNHOx9/dPDp/Gr0qJmsR+uV9C0UryG+YBR9LmW7Nh9d2UJ9O8FeRJYXF8P +OwrJ2uD0tadbL0pYZUs5hZAeWptDq9waR5vMqsawcL70GQkRvsAMwpvkcxeJv6dC +FwDaSTzVgePaS21fwrKf/uvnpmFkDZuJhFtvhhOrCLHmt0+7JJTh5PC7ZqwOFfTa +VB+QPwXTzuNZoQwP7sxbpdrANd41Wl9GVGlGeKi0uTqc1yXJVLhYVgrFa//YKgAO +lBWir/3fyX9rWCKR+IKGXyWMkIHrceNJ/z80WATWeRR0lTcTVs4fBfXjJRQlmVJA +7PkhK3wuhK7AR9B3ErT8H0eGe4pvouTvWD0gw4xjGfsFPyZuMAoHTFHss9SfDM4Z +5yAxCX2TYw8eE7ZZ8A7DmDAyfn6PEaI8UZQ1zCtcxTwW3mrj8zwKfEiw4Xt9igCq +EQaXFUtU2W8J2RaZC414THiD05LtYCH3rUlBAbZbH2nlin6s67BovU8P6EVDPb4F +HIY0xa4KeckHtdFp+EH3eado5KvRr9xk/Hlju+aAV6xu18/T91RhSxktEcG7KGXB +469RA47uuGRw//WYfH1B0avOLKe4UgCedUK/xU5jwQR7KifJHhc5Lh3xBSrUsGnx +zhv9ibTWsJkx5AyEZMopAoIBAQDfhXxGLqRm7cNZF6zxjbozb7SCqOQ9XNuKYC0B +2LjBbN4mKQrqNpetwkrbOlTwEOzDM5+jc5KgwHGJk43k6F1QqTo/2OR0QVNGZmgW +5xat/KY6Cpeld2i1QPJ++r8ICDWHMhkwN2Sp36GsAoz3MQJQ5H+a3zBRvDImyxev +rPzvJRsPKtnK/9rpNg7Twnzn2mLSM6tnmSy2dQWLRK+8A3/qW65/87KbQ4cGiLPy +f4dGCS9XLMzBH3OuZIDuqQqcOFdZezIrOtNJZH+S1I+Xr7tNlqpFAg38S2uXg3xc +iRaKuPBAvsZy5X+e0ylWm+sRuidiUx6zCoXcPUaADTjk8zBpAoIBAQDPTB17MHOL +gLLmtux2sKXqrpmdFngPTpOD7Sbq/OzeuaA6ZPzRYZsYAbeuy4yAP5kR0vg/FrmQ +8JTeCBYTYNhLKYXkBE+zXOtelrYovKBwaLFok9LHqTXsZ9GuRrlaQY35IwZpf6H0 +/1vNbgh10vSLs0uTA+QM0NsVMt2gQSbcW+v8WFaI02t8fOo5WdVvnYrNAUhHtW4/ +lBPaIuM/xZOfePozEJfEE5See/ZI/8OX0C3DCKdueRW3C2rPMGuPzpqPWmNwUDMc +QhcMHmrdCXvAvTdXohApkZz5rlsQoP2KDtvZlhOEHlSYQNLHvO3QkJEj1YlGs1Rw +K3V4vKamb5ufAoIBAC8Lx4Z2FsBbVvyPkSUecYyV/GZr6e5IdyT+7TowRWLg6Tka +9mMprm8yL9NIycvlpA/J89XRY5FWMA+G8Ry3E70AgXQi0KLvhPmSYGJBrXJddRdO +V3DKW02bJ+82E7M7FxknoEmXvyZKHZs4xjbZJ+Wi0oDkbSUm+V9U691cCbjEFJfy +qvihNZBUgnZrvyKFwrr/iRo7EUAuDUas5FgsXGYVefsQbg1j/y2O5GEsGmrRUBeH +JGmZlx2jpceV6NfMvAXnej3DGemer4NWHgsNP90q0xfHQO5TaYfwcuJ/MDFuj5Lx +FI/mYiApzc+e7bFDfzqgyBtjIZ2jgA0/uzPlQ9ECggEBAMhgZdrvMbE2PTggt5/f +DvIzxc7/qhv5s0QB1+2rgHembMglV/A47gcdmeT/YpeRYi/FgyrQl50PwIaCwHwI +z+qPf5mxxSy2c9lhy0DDXD5sJJjW0xcmbDZRAHD6ci5ZUZfyED+oHPBKaf3AD7N8 +r9/d9v5r82Oj+zGZkntWnu7Ad/LbnQHzg2G5CVtYh6S1Xzbct3aBXMleJHZl5M5+ ++DxdX1Q/BMSGvNEZOC7F89XqaGGvGtEBgEkIYz1VK5O9fM926snk7BKzm72eJWP1 +x8a94CH4SLE1zMvYirQfOhTaWg8QL1s1SkZF2rkx2ZsSMwm1XlWWANfQWmPTzy7w +kTcCggEBAIi2uhi60gBDrJ3xIPcXQC6YHtDHA/7p6XNFnp9Nx/9He7tPIULgH7AS +MbLza+etC45w2mc8Ypqy+w2+a44B8FnWpFZ3ff3fi9IlV6CHM9D0OrnL12GAnmOG +gUXEBPW4Kk5k8kVRyhw9If38qrh9IW7oSifUiYNdiDgoViv9ZTgw/ZWYiU8krqF9 +sPQlnzdhxhNu7TIEBu8iY0t0dh53f6PPlVWwKAHawyo+UXypM7MGNTE1AP0E+x+a +mUXVjbI5oCrs+mSFpjTA6BrP2NHHrIA+qPI8dchy7LIfXjOnW+2g0m85F7U/Egd0 +0txeBq/S0KIBpAsLK5G0aqD+qASd630= +-----END PRIVATE KEY----- diff --git a/mysql-test/std_data/cachain/root.crt b/mysql-test/std_data/cachain/root.crt new file mode 100644 index 00000000000..55f6f4d51fb --- /dev/null +++ b/mysql-test/std_data/cachain/root.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFXTCCA0WgAwIBAgIUMS/WV2Y7lwQy/yLQDAFU66GhILkwDQYJKoZIhvcNAQEL +BQAwNjEQMA4GA1UECgwHTWFyaWFEQjEQMA4GA1UECwwHTWFyaWFEQjEQMA4GA1UE +AwwHUm9vdCBDQTAeFw0yNTEyMTIxNjU2NDJaFw00NTA4MjkxNjU2NDJaMDYxEDAO +BgNVBAoMB01hcmlhREIxEDAOBgNVBAsMB01hcmlhREIxEDAOBgNVBAMMB1Jvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC74Ifa/BBjtlkdANjj +8IWn8+RL9/CODm5xURmWfJSzW4vebZZPGwkd3J5grTW3Zr+XGpXfbFPwC63pPDAu +8CzVXCDNSK+Fa2usoLsnYW2CmxeMqrUeBVZk4YCZ0ztjwvvZkwrbN6L/uk0OMpy7 +T6c0lxysMAuTXYPGY8NNDW0tpKh2LbxzGsAkREbM17vAHveHyiJ8YmWHmUeujqir +IlEcClTVEqSR8sWDwu01W/aD9u9wY1CJZyTNF3lJPZ8XqNBVZ89Ydl0y7W1oXOno +b/6CTZYUxMQ/xUL00L1i+VcIKjV8jZFrFyrjJsnGlvqLlzUQ3dwFKJISIMcF9Jb2 +PnZTGNjhzROil3zrItjKTbScgLiWuqAh8YNmRRisDIlkMr8kFU2FjznrMkFP3LK8 +BPBKKwNaYzcbeR6xwMgj3rJvT5PDk0ffBn/hjyhgpVJfp10hvkFtV2kSE/ttmU0O ++SQXgjXxsPs9223oTgjxt5FfZei+mdOJ69G6/boupF9eYpKe0cNLYnF0V+rFoxiK +LFUQhNMJVDBca/cp791N9Nb1bhKSHnR+zQNQjBH4BAmWKzuzKLrt3L6QKK59xyy9 +HdbM/ohbEEKialgm/Lwrr+olcpyPMwujM5B0r9B+Z4b/oPEIsncNpCl553r7YQZ5 +P9PYiJ/kOBxCwnYeYlwk+KygNQIDAQABo2MwYTAdBgNVHQ4EFgQUBIruq81qRzEJ +jPIcfrk9Xjma0qwwHwYDVR0jBBgwFoAUBIruq81qRzEJjPIcfrk9Xjma0qwwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIB +AD47H7tlk1aWFEf1UK9Xnh2hELTSXX3/uE5Sxx32DINTL2wcqiLOgxTMEaNr1EeF +dCkuADFclEsJfh+mWmhRsD7KKKlq4grMX9jTobfj0sd84M+d+YgkqAlP9f9b6QrX +e9pv2FVCL467JARWSZRuLJ7SZOHK88ajNnAG36LFdtXBDOAcogtddRfAil/5PMP4 +HIgZQOG96h0yMDoIJB1RIicf8f56xsC5m1Jbz4B3V8rPcCGK9CKWLrgd0FiXZV45 +GMuXLsOfEaUEtMz/gRIdQlMHgTWk6Inprm9wynL51J0gE3BZIm26G1gpae3GcG5Z +q8BQQbQfn6Tg/H8a+56H1CDclt9qQjRMRDH81r5w7hK1LvjKrEKjdmzmxaPAqq71 +Ne5gXJxYjgSwpFB5MYX4K0Do2zoqE7HhuGc/0oWhm0IrAy616tOZtmopoNiYoJ88 +xjHXRCKs6w6vPsNew75u5N7mU7IlCXjxVSF6cSyRd4fnBY3kRxZPJWlOe8jsIuED +PZYfAI+10nOGNY7c3scvxgZCnNU0THryem0PNIM0XaM/fR+SH+Kl+V9x14sDouIS +B6gCYEk2oZW7EdfLN3FXFufoIPNsR1KkDsh7RKYamMVlSsGEEFPt3h9I0c3kAaOT +FHZ29DjuAuSGaEvs4NiOsYmaQP51BM1sFs/gkz678KZy +-----END CERTIFICATE----- diff --git a/mysql-test/std_data/cachain/root_intermediate_ca1.crt b/mysql-test/std_data/cachain/root_intermediate_ca1.crt new file mode 100644 index 00000000000..bfe207adf48 --- /dev/null +++ b/mysql-test/std_data/cachain/root_intermediate_ca1.crt @@ -0,0 +1,62 @@ +-----BEGIN CERTIFICATE----- +MIIFXTCCA0WgAwIBAgIUMS/WV2Y7lwQy/yLQDAFU66GhILkwDQYJKoZIhvcNAQEL +BQAwNjEQMA4GA1UECgwHTWFyaWFEQjEQMA4GA1UECwwHTWFyaWFEQjEQMA4GA1UE +AwwHUm9vdCBDQTAeFw0yNTEyMTIxNjU2NDJaFw00NTA4MjkxNjU2NDJaMDYxEDAO +BgNVBAoMB01hcmlhREIxEDAOBgNVBAsMB01hcmlhREIxEDAOBgNVBAMMB1Jvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC74Ifa/BBjtlkdANjj +8IWn8+RL9/CODm5xURmWfJSzW4vebZZPGwkd3J5grTW3Zr+XGpXfbFPwC63pPDAu +8CzVXCDNSK+Fa2usoLsnYW2CmxeMqrUeBVZk4YCZ0ztjwvvZkwrbN6L/uk0OMpy7 +T6c0lxysMAuTXYPGY8NNDW0tpKh2LbxzGsAkREbM17vAHveHyiJ8YmWHmUeujqir +IlEcClTVEqSR8sWDwu01W/aD9u9wY1CJZyTNF3lJPZ8XqNBVZ89Ydl0y7W1oXOno +b/6CTZYUxMQ/xUL00L1i+VcIKjV8jZFrFyrjJsnGlvqLlzUQ3dwFKJISIMcF9Jb2 +PnZTGNjhzROil3zrItjKTbScgLiWuqAh8YNmRRisDIlkMr8kFU2FjznrMkFP3LK8 +BPBKKwNaYzcbeR6xwMgj3rJvT5PDk0ffBn/hjyhgpVJfp10hvkFtV2kSE/ttmU0O ++SQXgjXxsPs9223oTgjxt5FfZei+mdOJ69G6/boupF9eYpKe0cNLYnF0V+rFoxiK +LFUQhNMJVDBca/cp791N9Nb1bhKSHnR+zQNQjBH4BAmWKzuzKLrt3L6QKK59xyy9 +HdbM/ohbEEKialgm/Lwrr+olcpyPMwujM5B0r9B+Z4b/oPEIsncNpCl553r7YQZ5 +P9PYiJ/kOBxCwnYeYlwk+KygNQIDAQABo2MwYTAdBgNVHQ4EFgQUBIruq81qRzEJ +jPIcfrk9Xjma0qwwHwYDVR0jBBgwFoAUBIruq81qRzEJjPIcfrk9Xjma0qwwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIB +AD47H7tlk1aWFEf1UK9Xnh2hELTSXX3/uE5Sxx32DINTL2wcqiLOgxTMEaNr1EeF +dCkuADFclEsJfh+mWmhRsD7KKKlq4grMX9jTobfj0sd84M+d+YgkqAlP9f9b6QrX +e9pv2FVCL467JARWSZRuLJ7SZOHK88ajNnAG36LFdtXBDOAcogtddRfAil/5PMP4 +HIgZQOG96h0yMDoIJB1RIicf8f56xsC5m1Jbz4B3V8rPcCGK9CKWLrgd0FiXZV45 +GMuXLsOfEaUEtMz/gRIdQlMHgTWk6Inprm9wynL51J0gE3BZIm26G1gpae3GcG5Z +q8BQQbQfn6Tg/H8a+56H1CDclt9qQjRMRDH81r5w7hK1LvjKrEKjdmzmxaPAqq71 +Ne5gXJxYjgSwpFB5MYX4K0Do2zoqE7HhuGc/0oWhm0IrAy616tOZtmopoNiYoJ88 +xjHXRCKs6w6vPsNew75u5N7mU7IlCXjxVSF6cSyRd4fnBY3kRxZPJWlOe8jsIuED +PZYfAI+10nOGNY7c3scvxgZCnNU0THryem0PNIM0XaM/fR+SH+Kl+V9x14sDouIS +B6gCYEk2oZW7EdfLN3FXFufoIPNsR1KkDsh7RKYamMVlSsGEEFPt3h9I0c3kAaOT +FHZ29DjuAuSGaEvs4NiOsYmaQP51BM1sFs/gkz678KZy +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIBATANBgkqhkiG9w0BAQsFADA2MRAwDgYDVQQKDAdNYXJp +YURCMRAwDgYDVQQLDAdNYXJpYURCMRAwDgYDVQQDDAdSb290IENBMB4XDTI1MTIx +MjE2NTY0MloXDTQ1MDgyOTE2NTY0MlowPzEQMA4GA1UECgwHTWFyaWFEQjEQMA4G +A1UECwwHTWFyaWFEQjEZMBcGA1UEAwwQSW50ZXJtZWRpYXRlIENBMTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBALkW2aNhcIYexlXlPUth4nxSID4Oa1H2 +lMxfonge5Ww9nlFmr+DQFgIWeY47ibY5zOxNy6P0Q7iXVNnX9TWPcz57Lfg4Bnrj +/BzoJ2/dWYo1tdkYx6JLqFV7fT+mMivXAfzYfun6FF9dVJfqPAsu0TwGVJpt83ji +02P0yCagweDAnfVHdBFEKcLimcNMJh2Kf2gz1dUZoa3sEI3q/9GZYEoBxv7/jVMZ +s1WnSGXfJpLkZNxXY/btsiCl40K1T/tj3pz8oMDQ5Ew2UzOYZECayXnF7fCmdmis +ffSk7OW+6OV3I4e/jwT25h+aI8GL/uipOTS8EiilGJkl+0a9VqWs0SuzjwfHbuOJ +XP1KflVTOej6qex8MeQP7w8dw5boCRUkEEftT5fF0h/keidsLzHkwZ3LZVTPEGwc +Rw4fTJniy7eAb5LSsfFBXhH8bPSmw/xFErKytxrY9CD7oK6tPnT/dXNjQRM0e4zG +cRtRpl6oCbIzWdhivuJlR6FVwg1OcU6PnuSAtV7u6YZ25vPlH7VK9Dc++84k/ytA +7u5OOIHLg6MRiNMwNeCo4d5PpixFM2fD5C+iOz4kQedRzej8Q2/i06H+zP3qIt9V +lee8xi8MLfZt0zsjfYMRDWgnQG+g3ISRhjCGduoLiVCJDGOdNQT6TKVW7hvc0TkQ +bzj2+BDcyzYFAgMBAAGjZjBkMB0GA1UdDgQWBBS5EWzI/eI7TuAsZDv5XXgSz+xU +jzAfBgNVHSMEGDAWgBQEiu6rzWpHMQmM8hx+uT1eOZrSrDASBgNVHRMBAf8ECDAG +AQH/AgEAMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEApKU4JBjL +8/HxxKv6VxdZdxgzisr48wopo2or9DzJFzJrTDHgKbt6QHnEAZKRMaTn4+lIwAbM +XGlOjId1LXnLoMO+ya6Pu4ESjNSRyXVRUU+JhNL3kAWZSx5m0JnoVXSTIDBWcPq6 +1LFsoRBxY1Bc9S2LCpsvErNeKEPa570oDeE3pPkZkVXA1vntPkg6JzrQZkmfJiAI +5+iqvcRzYi0lAU+CSUS0J674mT9IFFYeer5qe1T+LYGiWRlLfTvGolk3+2mQA77m +x8R+liNKhrwO49arVkCwH+glut+Bizd0dkomb/8Vj/ZtDCpu9c3+WqiP90Xkd0uY +AY5vBT91drPV7hmccVBKZvYTO5zA9okrjnjwpG842CtieQ8OivsTDQ4knDvMA7X2 +29CYtKKx/4OmbAWJfB/1gfoQTUS1JsI9JwFV1ArfVCC3nVV/bxlAV+ThvlhYA+Ru +RAh9DpHDZ0s5tbHjuYGgvQaD+11qKf3LUxwl44hJfEk64AmxA6uTzE4l36SnCZW+ +a68TnhdlH0JooGNFK/6PRfFzONwIn29PlWN+ApZwSDmCexxo49ygGgRNE/ry/Ec0 +V9vmoieOq8sUlRtYuZPdPGVS5q7ghsghkkk95VkdJ2DAROTJhlfgXEOH8mUDvScy +wzJ8juBHAKg9M4albgBgoNUeUgw7glBXH8g= +-----END CERTIFICATE----- diff --git a/mysql-test/std_data/cachain/server.cachain b/mysql-test/std_data/cachain/server.cachain new file mode 100644 index 00000000000..fb3688d263b --- /dev/null +++ b/mysql-test/std_data/cachain/server.cachain @@ -0,0 +1,62 @@ +-----BEGIN CERTIFICATE----- +MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MRAwDgYDVQQKDAdNYXJp +YURCMRAwDgYDVQQLDAdNYXJpYURCMRkwFwYDVQQDDBBJbnRlcm1lZGlhdGUgQ0Ex +MB4XDTI1MTIxMjE2NTY0M1oXDTQ1MDgyOTE2NTY0M1owFDESMBAGA1UEAwwJbG9j +YWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoiFgd9lQ/0nc +EigR34t7kuOfLbk4mV4Fi3xTyEossd3YbXsU+3ghxztpsebXMz9SsshoCBRNcH5V +qrUA4KGZ+CpyxgUaJionNxakvYT+Wmrp/A59ue3t4FNQFdncZi6RfVCqSgiLPTDK +ZM85/w2Fw0gorYSCAp7fo+5nJOaX0KdcvLCL3v9ji0wYPn2T8mF+0iJlyIAf+zpB +lJa9jb5jEVloaN7nXZgb4MRsEyp6d3XhofWDzUhFMSArDSuQ87XDtN/WT3b+/Acd +SCFwSWWHxkWyyTxtxSg8yeuQyOZo3lWvAnOgD8k6nb2+anMD3BDBksJoBDrLIOm0 +353j0rUNvvamC+c5dowxSCyGDARNHPbZ++ZTa0uwlcS92KrX2b2Y3HCZ+5F6bQ95 +XA+qtR+zWy9rfsalkHiAR7FDYj/gn8g73BuCAgtXib+4f6Qjya+XJBI734sV17Kr +zezFckiUTW7fmOWj6zR1bNobk6c73ye+umwXLCIAxlFs7LOgbOd/Wp6jgyNFDp7l +MeHfRjjWBEKvbCz7CJEMRJYcxuN2UjoxIVqiU0+BDC/Gyhui9+XgBnRnQWjUXIW6 +/RjBBe3dZmi2vfEUy532qg8EbXAHOBbJEyPYQ1Z5zC5juHJLe4aeo8NvLh4Xadp7 +8fChxB7dNduLe5yFZ+6a4sPySL1206ECAwEAAaOBjzCBjDAJBgNVHRMEAjAAMA4G +A1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAaBgNVHREEEzARggls +b2NhbGhvc3SHBH8AAAEwHQYDVR0OBBYEFKSBD7mJScqWEJMqEmuMWZFeQZ8aMB8G +A1UdIwQYMBaAFLkRbMj94jtO4CxkO/ldeBLP7FSPMA0GCSqGSIb3DQEBCwUAA4IC +AQCctxHJgQH1ybfuRA7bygjtKg75sjSLSalfO14QcxQw0O4Wcz2S+r/hZD42Ukn5 +cLqh5PBeGwALzSa88BDlqBVSKBamjTdw2xa14RmAEpa/U0WMcFMRRmHmi+do26se +vSSz4Zqg2YOTdzuBYz+L7DVDvEqQrT55Gotbh4NBWaGfM7qiCeEUdPmT1BoUpnlG +CpG18hcnAbRR2DdBICs6SMBqPGF5nef+wikEXELZ0tDdqtdN/rewtJc7Rm+x2j0g +FosrFQkXUt4aQomXVv8IJ1gN4dhxFx/Y8LFbPz1w+j4NiKDmK1EJFHX5m86YNz+w +99KdIghwo/ChbtFL0+nyQbYD1z4a1qUfnoeuw+PAiRKMRJiHcHFQhkKd6kD46Kuz +qXKd+b7SrOROj2/ws7Bq94JhQ9xwfKr9VxxvyCd8m8/Ev6YjFEexZRaXvteiTCT9 +/0vob7yRWgMllr/zeYV4hUrYk5xnIUamVSBJmerpf34HCB3+YNdM7QlC5OoP15uu +cBkSVKCrs8GQgFFz5yZnBsV7Z0ZUL2+15+/Kz+W+6Rs3OzDdUnvby5MiRpi+m9kI +gDjdfDkL6WjN145i9fo5X5SZp17fbhlNgwtguB051g2noxje4XGQarVGzUVvlsNT +gD/tFYB4JmVuDPdmdwAy5aZCbpcoApzN7tGWm6fuTcVGCA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIBATANBgkqhkiG9w0BAQsFADA2MRAwDgYDVQQKDAdNYXJp +YURCMRAwDgYDVQQLDAdNYXJpYURCMRAwDgYDVQQDDAdSb290IENBMB4XDTI1MTIx +MjE2NTY0MloXDTQ1MDgyOTE2NTY0MlowPzEQMA4GA1UECgwHTWFyaWFEQjEQMA4G +A1UECwwHTWFyaWFEQjEZMBcGA1UEAwwQSW50ZXJtZWRpYXRlIENBMTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBALkW2aNhcIYexlXlPUth4nxSID4Oa1H2 +lMxfonge5Ww9nlFmr+DQFgIWeY47ibY5zOxNy6P0Q7iXVNnX9TWPcz57Lfg4Bnrj +/BzoJ2/dWYo1tdkYx6JLqFV7fT+mMivXAfzYfun6FF9dVJfqPAsu0TwGVJpt83ji +02P0yCagweDAnfVHdBFEKcLimcNMJh2Kf2gz1dUZoa3sEI3q/9GZYEoBxv7/jVMZ +s1WnSGXfJpLkZNxXY/btsiCl40K1T/tj3pz8oMDQ5Ew2UzOYZECayXnF7fCmdmis +ffSk7OW+6OV3I4e/jwT25h+aI8GL/uipOTS8EiilGJkl+0a9VqWs0SuzjwfHbuOJ +XP1KflVTOej6qex8MeQP7w8dw5boCRUkEEftT5fF0h/keidsLzHkwZ3LZVTPEGwc +Rw4fTJniy7eAb5LSsfFBXhH8bPSmw/xFErKytxrY9CD7oK6tPnT/dXNjQRM0e4zG +cRtRpl6oCbIzWdhivuJlR6FVwg1OcU6PnuSAtV7u6YZ25vPlH7VK9Dc++84k/ytA +7u5OOIHLg6MRiNMwNeCo4d5PpixFM2fD5C+iOz4kQedRzej8Q2/i06H+zP3qIt9V +lee8xi8MLfZt0zsjfYMRDWgnQG+g3ISRhjCGduoLiVCJDGOdNQT6TKVW7hvc0TkQ +bzj2+BDcyzYFAgMBAAGjZjBkMB0GA1UdDgQWBBS5EWzI/eI7TuAsZDv5XXgSz+xU +jzAfBgNVHSMEGDAWgBQEiu6rzWpHMQmM8hx+uT1eOZrSrDASBgNVHRMBAf8ECDAG +AQH/AgEAMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEApKU4JBjL +8/HxxKv6VxdZdxgzisr48wopo2or9DzJFzJrTDHgKbt6QHnEAZKRMaTn4+lIwAbM +XGlOjId1LXnLoMO+ya6Pu4ESjNSRyXVRUU+JhNL3kAWZSx5m0JnoVXSTIDBWcPq6 +1LFsoRBxY1Bc9S2LCpsvErNeKEPa570oDeE3pPkZkVXA1vntPkg6JzrQZkmfJiAI +5+iqvcRzYi0lAU+CSUS0J674mT9IFFYeer5qe1T+LYGiWRlLfTvGolk3+2mQA77m +x8R+liNKhrwO49arVkCwH+glut+Bizd0dkomb/8Vj/ZtDCpu9c3+WqiP90Xkd0uY +AY5vBT91drPV7hmccVBKZvYTO5zA9okrjnjwpG842CtieQ8OivsTDQ4knDvMA7X2 +29CYtKKx/4OmbAWJfB/1gfoQTUS1JsI9JwFV1ArfVCC3nVV/bxlAV+ThvlhYA+Ru +RAh9DpHDZ0s5tbHjuYGgvQaD+11qKf3LUxwl44hJfEk64AmxA6uTzE4l36SnCZW+ +a68TnhdlH0JooGNFK/6PRfFzONwIn29PlWN+ApZwSDmCexxo49ygGgRNE/ry/Ec0 +V9vmoieOq8sUlRtYuZPdPGVS5q7ghsghkkk95VkdJ2DAROTJhlfgXEOH8mUDvScy +wzJ8juBHAKg9M4albgBgoNUeUgw7glBXH8g= +-----END CERTIFICATE----- diff --git a/mysql-test/std_data/cachain/server.key b/mysql-test/std_data/cachain/server.key new file mode 100644 index 00000000000..019006f3bfa --- /dev/null +++ b/mysql-test/std_data/cachain/server.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCiIWB32VD/SdwS +KBHfi3uS458tuTiZXgWLfFPISiyx3dhtexT7eCHHO2mx5tczP1KyyGgIFE1wflWq +tQDgoZn4KnLGBRomKic3FqS9hP5aaun8Dn257e3gU1AV2dxmLpF9UKpKCIs9MMpk +zzn/DYXDSCithIICnt+j7mck5pfQp1y8sIve/2OLTBg+fZPyYX7SImXIgB/7OkGU +lr2NvmMRWWho3uddmBvgxGwTKnp3deGh9YPNSEUxICsNK5DztcO039ZPdv78Bx1I +IXBJZYfGRbLJPG3FKDzJ65DI5mjeVa8Cc6APyTqdvb5qcwPcEMGSwmgEOssg6bTf +nePStQ2+9qYL5zl2jDFILIYMBE0c9tn75lNrS7CVxL3YqtfZvZjccJn7kXptD3lc +D6q1H7NbL2t+xqWQeIBHsUNiP+CfyDvcG4ICC1eJv7h/pCPJr5ckEjvfixXXsqvN +7MVySJRNbt+Y5aPrNHVs2huTpzvfJ766bBcsIgDGUWzss6Bs539anqODI0UOnuUx +4d9GONYEQq9sLPsIkQxElhzG43ZSOjEhWqJTT4EML8bKG6L35eAGdGdBaNRchbr9 +GMEF7d1maLa98RTLnfaqDwRtcAc4FskTI9hDVnnMLmO4ckt7hp6jw28uHhdp2nvx +8KHEHt0124t7nIVn7priw/JIvXbToQIDAQABAoICABwyzur5Zp+IMpkL1BP72AnT +wh87PE9cbX11qcLHt99VtOtRwXP9UG6s9DSncB1vMYx707A4C8H5jx/KLHVlVfi2 +NVEgmQwzP6/SpeN6ipyiRMD8qjxY2AswoliylCVHizFvArcupj0Fy+Xdrvof2Xh+ +B+kqB0y6Yi5KbDEcVCJLwZLl5+XoqGEVYpAX2lAJSfv1VJubQTS6TKax0xUgnG1r +jf/TaQ07w/mDYEKzHCrD23MGVx6wp6wkKqi3P5ZLsla2qbkXesDN4fdmLXfuiz9e +ZvqeIn9sgKoj6QIwJOQqpe44uwmT17iQxFoSWAUB+xDx9BJsQWEflMBh3zqKF/Is +Le+QguZEWbgZ4Y4MpM/VpyJ/LOf2OtOI77LuAhsl8D77ZSWHfzAP4eN42v0ob9d4 +55vfXPkR1KehJWLY8hWnRu4NAXV/VOX+IZq+Ggld59kl/gc3csh1ivAPkDgeJRJ7 +KgVNZvHwYsFOxHv4P8PXOZt0f2OCj42zU5dFUlv+e2mBxYv8T4BlRRu43CVCVdx7 +kwG68WuRYGNMuJbDs+NTROuwxcp/bQtdw7kpfD+G7vHRb46dB4fQhD8gRz52iAPR +4646ELPhEHPra+3dDP4X/MrzkvwVLDoi+/a2dhOLlrfRNX0PZLq0iMLTwkdLhJf2 +B5N3XAPKHv/5AWdBqy95AoIBAQDczsWnj2EmDqviT0PxB9652XoHuHhnX4ZfnQRb +Mv7j5aVd+Q2HnvyYg1N6OVlcO9h2G7olHFpKledS2NcIFm/8W1y/crpQ4ZtKnlEb +2kJxNCuhsolUaR/Fk4XMHC3B9Xy6oDW/HP1/Iadtc9MxuFK4fFRUzQW1KVk3ilDD +sseHbHM6dQ6SR3D6zcvCMpKp8rDTECdlRlPgjoRFlVhIzEpykiqZ2pW1D8I041cY +RdB1rilYiRqeaupd36Y/tdm6/RlKLA0k1Dtwrz1bBBGERzKNiZe6wyrOmitJMWST +PRMoLU84qt94UnEJIxCBzG6pb+zOqv/tqBxJNmSHnpKdeNmNAoIBAQC7+H9GBWhB +lVUo4ZWjj+Lrn0Rs/KSNLhubXJiTlPkIz3WP4U9/HHrCt3ob6AJyBNkHVSnMcpMK +eFR+D4oqh3sglz1jMLMCSYXGYSqkbvUDifgvVXC6ILXlw6dPKA5yiaUMriUkhcxF +QIIXcFzCA/e40GdS6Svq5EoUL22jxx8eTfL3AbBG7p6lQloke9yGIzFEFd5KBXrO +QCF4XNx0zlLbZAJ47DsIT71hEGn9uonn9WMOvkUNZ3Y4QCTJM6CWOXeaUzbLZnyT +R9/JTqgDRJsL+UPcY4q7UgRW5Lz+J9esKA8/BEPB7TdnxIqYKru7Ezav7ditKHC2 +NTnQW5D0G7tlAoIBAAjf3p3Pa24mJM5htS9Yy6DYlsj8pwowkqwBGY24zPnH3w+Z +1OtgfnJ6xL4i0+6bESYeEysuqopY4AU6ZdoTXMjduyqOp+JcTut+1W+zI/x8xkwn +atGTsA020qfTchtbjHnLup1FwwJ7DcmeYByddeWoSZNm8hXH5agPzRMwekh9GVx+ +G+nRKT9LSicx9ScTc/vnqE2vt+tMjxBk4jl6zHsyWLf6Sw4N5hZOFNAK7kfsigje +6cz4Kxc+x+XDn06eoYeToaubRBBRWrvvu+x/GA8MOqbs9Sl8lMTKUWqBh+WTfJ2g +cH7bgWOGn0E4QDJbJz9fLA4XNZvTrdKtkuL1WnkCggEBAKZxOMmY2iNQhvLHJYiK +TSXUhnN3FoIItNtSF6x+ujFJ+lBn8sucPTuT2aVCQIATuNUcXQGy5m7b8/0X54EX +LDjEks/0/F1nGvR5b5fgxEXY8pF/N20or8h/AfQ+hOR461QaE7uOWj1sDIWSsh/+ +AWl96C/rVj8M3GeZQ51FIVHlNeHwddWpT4MICOfLYY7JNMOUFZw30/Ezqi95EqC/ +XXD00peenfsTGJ8NOL06YwipKYiRhHWFCpJugeRDOcwroLNfQeLDokWL3CXV50HV +ivEETEmnPt31lvAWZR8rynafljh3n0PluCzDRPHOzSKutbr8geN6Lqu437OWkPkG +YV0CggEBAKePJ2GJeq5XzrKJYsCKdbUfutGmLdesoYziRAZ+hfLIi2SfjH8Gz+Qf +T7GpmcNmSK8Anr7Em+2tUgnf6afbEUedVny2g5iqmkKcbg4A8py3EUW6Gns3J/ag +R+uddhtrC9TZoKToqlwafZj4RrqUcvBkAdhiErvLCiJJVoU/0aRy7FTc2o5/UnTm +T0Utou4d0PxyrPjUZB/lHOsdshPlx/Gplg8AH0dBEkmI2e1Pt3WCnnVj0Nol7kTY +9eFgma/3kaPPVTlSRovl4inxcf5qn0FhnzN0yd/YRsrM5RV5lHAtpyXSqtF3BYpJ +By7FxU8G7cnqto61l/KLwxdIeAZFui4= +-----END PRIVATE KEY----- diff --git a/mysql-test/std_data/cachain/unrelated_root.crt b/mysql-test/std_data/cachain/unrelated_root.crt new file mode 100644 index 00000000000..ae98e874942 --- /dev/null +++ b/mysql-test/std_data/cachain/unrelated_root.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFXTCCA0WgAwIBAgIUeQKcZZsEZFZt1xhFkSikz0lYh+kwDQYJKoZIhvcNAQEL +BQAwNjEQMA4GA1UECgwHTWFyaWFEQjEQMA4GA1UECwwHTWFyaWFEQjEQMA4GA1UE +AwwHUm9vdCBDQTAeFw0yNTEyMTIxNjU2NDVaFw00NTA4MjkxNjU2NDVaMDYxEDAO +BgNVBAoMB01hcmlhREIxEDAOBgNVBAsMB01hcmlhREIxEDAOBgNVBAMMB1Jvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDeG9rtI3IXA84EfEow +kBZYgmYRAHUoW2EaQP3KET8rDJhFfDmCrjLJF9wCJ0DVo64Hsf9dHE255+DvFJ2I +c2c5DwwKBqYVYOiiV8z54SJ+fmbtCvK/lU5PlbymNZhONZNxk4lYGWWH+tLLWndH +xHe/RgI5o4Ba8NQ2LrzpS4dYQzHfGqMmIea8blAcmtyqTwXZc1O1cswiIPD0pRlV +AomGc2AtTVU2IqCmdVvGz7N7ki8QO4AR/SLnT3SANuLUx/lpaGvNyGz2KB5NPxE2 +8VYzRIEsMPEtsDKnvW8qo8rIbuTJqQPJi/WI6D5BnFLiKpeQAUVZBYZlx5ntQHV1 +we4afaHCDU6SNRt7UfzI/SfHPRZPnzm6K/KOtzYdJzA1Ap8PD4ZwL7hg9yUxVzsl +jpySLW6+8PtxcMbkqZ8/nrTF0TH6qYszPitSNsAgB1zxSSteOJkwt9ou+WKGB0Tf +5yYJ6flSmz8GUzUovO/FUmJP8uoJOVGHOYuAGH1203fxBez6aKo+wbpk8r7S2/4Z +Ad4/OKgwU6ZaTKgIfzwkGuiR2kj6WKj+OLGN+fB9gTs+YTmb0wu+8sH9ub32y2ig +kNLyrALJWyQhbrJeLOdwqj0mJkY238OP0Y8LKwbWLZWmhAW8tGbDA76vceH1+Hb4 +AqqbK9+Uhh1JXfrU/USaExS1bwIDAQABo2MwYTAdBgNVHQ4EFgQUNhJVtzmAj38l +AL8UgHmb0i+xkQwwHwYDVR0jBBgwFoAUNhJVtzmAj38lAL8UgHmb0i+xkQwwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIB +AHi3EtRvq9Dq6SISzL52X0Lzycm9GJ6lZ5D/8nWP8wdMMWVi9OxDKV5KSIPFf2t2 +E6LS/bi45iBSsqrcJqr1NSf+nzvbhSWMbFcC/9xIwKaeRIj5JtafQHwpYX8uwAXr +nnN0GJ3tXd9Gc213ZjUsqCtPfStovDqchD58qIxGf1xUyWoOZwikmSjrbc2w75Zr +oBQ8yTUcdHiFNZqhRqKTe/rbxKIDbRnEV4dLoEfGJPaSygba/NPTxtmSX5vJbLLR +/l74jKOTxM+tNhSQ8e8HGo5yyunMWsnHtqvVl0JlQ9qKkF0wPPTJWkSeWh74e0Dd +mnnz0iDzkknguVwihvnuz0tFW5Cynsu4owVJpRSDd7C94f00S6lRQ10AWJiQLNhR +4370dVcbNCdIIwVOMQ0xA6fSKmOpLclIz1c6XHHuGR+klR1U8sX6dNOQMBRa8nZy +xGMXSQ0X05pqPnBBdZRmQDka1bWn/Uuk+2tY5CamWjF191EurtsZ1cJ7HQnPtheV +zdZxP+YSOvE5TJVnziqxlkIChxa+INBuaQoISxepBi2m2VPGj5SjAZcDPx9+uled +CzLan4jnrDW8d8HeiOleeWuQHp/mdKScKfsVD6yyoEQqVajBl4QV5QncgZQiH9LD +hzldPqgBh2P+MOiKGEPDPDe/+DSJOeASicDTeV5kFug7 +-----END CERTIFICATE----- diff --git a/mysql-test/suite.pm b/mysql-test/suite.pm index 7531c5ca8b7..e7797837686 100644 --- a/mysql-test/suite.pm +++ b/mysql-test/suite.pm @@ -109,6 +109,10 @@ sub skip_combinations { $skip{'include/check_windows_admin.inc'} = 'Requires admin privileges' unless IS_WINDOWS and Win32::IsAdminUser(); + # MDEV-38326. Chained TLS certificates fails verification for schannel clients + $skip{'main/chained_ssl_certificates.test'} = 'Skip if on Windows without OpenSSL' + unless !IS_WINDOWS || $openssl_ver; + %skip; } From 7093e1c4a9f96b3f9e43127407cbb7de337912da Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Fri, 19 Dec 2025 13:29:19 +1100 Subject: [PATCH 19/43] MDEV-37972 Do not emit XID not found error in spider_internal_xa_commit_by_xid This fixes "external" XA commits initiated by syncing binlog a slave with a master that has done XA commits. Note that spider_internal_xa_rollback_by_xid already did not emit the error in the same scenario. --- .../spider/bugfix/r/mdev_37972.result | 18 ++++++++++++++ .../mysql-test/spider/bugfix/t/mdev_37972.cnf | 1 + .../spider/bugfix/t/mdev_37972.test | 24 +++++++++++++++++++ storage/spider/spd_trx.cc | 2 -- 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 storage/spider/mysql-test/spider/bugfix/r/mdev_37972.result create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_37972.cnf create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_37972.test diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_37972.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_37972.result new file mode 100644 index 00000000000..13f5633217c --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_37972.result @@ -0,0 +1,18 @@ +include/master-slave.inc +[connection master] +connection slave; +INSTALL SONAME 'ha_spider'; +connection master; +CREATE TABLE t (a INT) ENGINE=InnoDB; +XA START 'x'; +INSERT INTO t VALUES (1),(2); +XA END 'x'; +XA PREPARE 'x'; +XA COMMIT 'x'; +connection slave; +connection master; +DROP TABLE t; +connection slave; +Warnings: +Warning 1620 Plugin is busy and will be uninstalled on shutdown +include/rpl_end.inc diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_37972.cnf b/storage/spider/mysql-test/spider/bugfix/t/mdev_37972.cnf new file mode 100644 index 00000000000..5339ea4cc89 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_37972.cnf @@ -0,0 +1 @@ +!include ../../../../../../mysql-test/suite/rpl/rpl_1slave_base.cnf diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_37972.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_37972.test new file mode 100644 index 00000000000..0341cd20aae --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_37972.test @@ -0,0 +1,24 @@ +--source include/have_innodb.inc +--source include/master-slave.inc + +--connection slave +INSTALL SONAME 'ha_spider'; +# set spider_internal_xa=1; # no use + +--connection master +CREATE TABLE t (a INT) ENGINE=InnoDB; +XA START 'x'; +INSERT INTO t VALUES (1),(2); +XA END 'x'; +XA PREPARE 'x'; +XA COMMIT 'x'; + +--sync_slave_with_master + +--connection master +DROP TABLE t; + +--connection slave +--disable_query_log +--source ../../include/clean_up_spider.inc +--source include/rpl_end.inc diff --git a/storage/spider/spd_trx.cc b/storage/spider/spd_trx.cc index 39432d0f6d2..9b25dd7c633 100644 --- a/storage/spider/spd_trx.cc +++ b/storage/spider/spd_trx.cc @@ -2418,8 +2418,6 @@ int spider_internal_xa_commit_by_xid( table_xa->file->print_error(error_num, MYF(0)); goto error; } - my_message(ER_SPIDER_XA_NOT_EXISTS_NUM, ER_SPIDER_XA_NOT_EXISTS_STR, - MYF(0)); error_num = ER_SPIDER_XA_NOT_EXISTS_NUM; goto error; } From 3c8a5ea12827041c86ebafeb4ac0a2e133416eb3 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Fri, 19 Dec 2025 13:30:00 +1100 Subject: [PATCH 20/43] MDEV-37972 Factor out duplicate code shared by spider_internal_xa_{commit,rollback}_by_xid --- storage/spider/spd_trx.cc | 304 ++++++-------------------------------- 1 file changed, 45 insertions(+), 259 deletions(-) diff --git a/storage/spider/spd_trx.cc b/storage/spider/spd_trx.cc index e41be358ee6..aaab62379bb 100644 --- a/storage/spider/spd_trx.cc +++ b/storage/spider/spd_trx.cc @@ -2286,15 +2286,17 @@ error_create_read_record: DBUG_RETURN(0); } -int spider_internal_xa_commit_by_xid( +static int spider_internal_xa_commit_or_rollback_by_xid( THD* thd, SPIDER_TRX *trx, - XID* xid + XID* xid, + bool is_rollback ) { TABLE *table_xa, *table_xa_member = 0; int error_num; char xa_key[MAX_KEY_LENGTH]; char xa_member_key[MAX_KEY_LENGTH]; + /* This share has only one link */ SPIDER_SHARE tmp_share; char *tmp_connect_info[SPIDER_TMP_SHARE_CHAR_PTR_COUNT]; uint tmp_connect_info_length[SPIDER_TMP_SHARE_UINT_COUNT]; @@ -2307,7 +2309,7 @@ int spider_internal_xa_commit_by_xid( bool table_xa_opened = FALSE; bool table_xa_member_opened = FALSE; bool created_tmp_thd = FALSE; - DBUG_ENTER("spider_internal_xa_commit_by_xid"); + DBUG_ENTER("spider_internal_xa_commit_or_rollback_by_xid"); if (!thd) { if (!(thd = spider_create_tmp_thd())) @@ -2347,36 +2349,40 @@ int spider_internal_xa_commit_by_xid( goto error; } SPD_INIT_ALLOC_ROOT(&mem_root, 4096, 0, MYF(MY_WME)); - if ( - force_commit != 2 && - (error_num = spider_check_sys_xa_status( - table_xa, - SPIDER_SYS_XA_PREPARED_STR, - SPIDER_SYS_XA_COMMIT_STR, - NULL, - ER_SPIDER_XA_NOT_PREPARED_NUM, - &mem_root)) - ) { - free_root(&mem_root, MYF(0)); - if (error_num == ER_SPIDER_XA_NOT_PREPARED_NUM) - my_message(error_num, ER_SPIDER_XA_NOT_PREPARED_STR, MYF(0)); - goto error; + if (force_commit != 2) + { + error_num = is_rollback ? + spider_check_sys_xa_status( + table_xa, SPIDER_SYS_XA_NOT_YET_STR, SPIDER_SYS_XA_PREPARED_STR, + SPIDER_SYS_XA_ROLLBACK_STR, ER_SPIDER_XA_PREPARED_NUM, &mem_root) : + spider_check_sys_xa_status( + table_xa, SPIDER_SYS_XA_PREPARED_STR, SPIDER_SYS_XA_COMMIT_STR, + NULL, ER_SPIDER_XA_NOT_PREPARED_NUM, &mem_root); + if (error_num) + { + free_root(&mem_root, MYF(0)); + if (error_num == ER_SPIDER_XA_PREPARED_NUM) + my_message(error_num, ER_SPIDER_XA_PREPARED_STR, MYF(0)); + if (error_num == ER_SPIDER_XA_NOT_PREPARED_NUM) + my_message(error_num, ER_SPIDER_XA_NOT_PREPARED_STR, MYF(0)); + goto error; + } } - /* update mysql.spider_xa set - status = 'COMMIT' + status = 'ROLLBACK' / 'COMMIT' where format_id = trx->xid.format_id and gtrid_length = trx->xid.gtrid_length and data = trx->xid.data */ - if ( - (error_num = spider_update_xa( - table_xa, xid, SPIDER_SYS_XA_COMMIT_STR)) - ) { + error_num= is_rollback ? + spider_update_xa(table_xa, xid, SPIDER_SYS_XA_ROLLBACK_STR) : + spider_update_xa(table_xa, xid, SPIDER_SYS_XA_COMMIT_STR); + if (error_num) + { free_root(&mem_root, MYF(0)); goto error; } @@ -2453,8 +2459,10 @@ int spider_internal_xa_commit_by_xid( } conn->error_mode &= spider_param_error_read_mode(thd, 0); conn->error_mode &= spider_param_error_write_mode(thd, 0); - if ( - (error_num = spider_db_xa_commit(conn, xid)) && + error_num = is_rollback ? + spider_db_xa_rollback(conn, xid) : + spider_db_xa_commit(conn, xid); + if (error_num && (force_commit == 0 || (force_commit == 1 && error_num != ER_XAER_NOTA)) ) { @@ -2527,246 +2535,24 @@ error_open_table: DBUG_RETURN(error_num); } +int spider_internal_xa_commit_by_xid( + THD* thd, + SPIDER_TRX *trx, + XID* xid +) { + DBUG_ENTER("spider_internal_xa_commit_by_xid"); + DBUG_RETURN(spider_internal_xa_commit_or_rollback_by_xid( + thd, trx, xid, false)); +} + int spider_internal_xa_rollback_by_xid( THD* thd, SPIDER_TRX *trx, XID* xid ) { - TABLE *table_xa, *table_xa_member = 0; - int error_num; - char xa_key[MAX_KEY_LENGTH]; - char xa_member_key[MAX_KEY_LENGTH]; - /* This share has only one link */ - SPIDER_SHARE tmp_share; - char *tmp_connect_info[SPIDER_TMP_SHARE_CHAR_PTR_COUNT]; - uint tmp_connect_info_length[SPIDER_TMP_SHARE_UINT_COUNT]; - long tmp_long[SPIDER_TMP_SHARE_LONG_COUNT]; - longlong tmp_longlong[SPIDER_TMP_SHARE_LONGLONG_COUNT]; - SPIDER_CONN *conn; - uint force_commit = spider_param_force_commit(thd); - MEM_ROOT mem_root; - SPIDER_Open_tables_backup open_tables_backup; - bool table_xa_opened = FALSE; - bool table_xa_member_opened = FALSE; - bool created_tmp_thd= FALSE; DBUG_ENTER("spider_internal_xa_rollback_by_xid"); - if (!thd) - { - if (!(thd = spider_create_tmp_thd())) - { - error_num = HA_ERR_OUT_OF_MEM; - goto error; - } - created_tmp_thd= TRUE; - } - /* - select - status - from - mysql.spider_xa - where - format_id = xid->format_id and - gtrid_length = xid->gtrid_length and - data = xid->data - */ - if ( - !(table_xa = spider_open_sys_table( - thd, SPIDER_SYS_XA_TABLE_NAME_STR, SPIDER_SYS_XA_TABLE_NAME_LEN, - TRUE, &open_tables_backup, TRUE, &error_num)) - ) - goto error_open_table; - table_xa_opened = TRUE; - spider_store_xa_pk(table_xa, xid); - if ( - (error_num = spider_check_sys_table(table_xa, xa_key)) - ) { - if (error_num != HA_ERR_KEY_NOT_FOUND && error_num != HA_ERR_END_OF_FILE) - { - table_xa->file->print_error(error_num, MYF(0)); - goto error; - } - error_num = ER_SPIDER_XA_NOT_EXISTS_NUM; - goto error; - } - SPD_INIT_ALLOC_ROOT(&mem_root, 4096, 0, MYF(MY_WME)); - if ( - force_commit != 2 && - (error_num = spider_check_sys_xa_status( - table_xa, - SPIDER_SYS_XA_NOT_YET_STR, - SPIDER_SYS_XA_PREPARED_STR, - SPIDER_SYS_XA_ROLLBACK_STR, - ER_SPIDER_XA_PREPARED_NUM, - &mem_root)) - ) { - free_root(&mem_root, MYF(0)); - if (error_num == ER_SPIDER_XA_PREPARED_NUM) - my_message(error_num, ER_SPIDER_XA_PREPARED_STR, MYF(0)); - goto error; - } - - /* - update - mysql.spider_xa - set - status = 'ROLLBACK' - where - format_id = trx->xid.format_id and - gtrid_length = trx->xid.gtrid_length and - data = trx->xid.data - */ - if ( - (error_num = spider_update_xa( - table_xa, xid, SPIDER_SYS_XA_ROLLBACK_STR)) - ) { - free_root(&mem_root, MYF(0)); - goto error; - } - spider_close_sys_table(thd, table_xa, &open_tables_backup, TRUE); - table_xa_opened = FALSE; - - /* - select - scheme tmp_share.tgt_wrappers, - host tmp_share.tgt_hosts, - port tmp_share.tgt_ports, - socket tmp_share.tgt_sockets, - username tmp_share.tgt_usernames, - password tmp_share.tgt_passwords - from - mysql.spider_xa_member - where - format_id = xid->format_id and - gtrid_length = xid->gtrid_length and - data = xid->data - */ - if ( - !(table_xa_member = spider_open_sys_table( - thd, SPIDER_SYS_XA_MEMBER_TABLE_NAME_STR, - SPIDER_SYS_XA_MEMBER_TABLE_NAME_LEN, TRUE, &open_tables_backup, TRUE, - &error_num)) - ) { - free_root(&mem_root, MYF(0)); - goto error_open_table; - } - table_xa_member_opened = TRUE; - spider_store_xa_pk(table_xa_member, xid); - if ( - (error_num = spider_get_sys_table_by_idx(table_xa_member, xa_member_key, 0, - SPIDER_SYS_XA_PK_COL_CNT)) - ) { - if (error_num != HA_ERR_KEY_NOT_FOUND && error_num != HA_ERR_END_OF_FILE) - { - free_root(&mem_root, MYF(0)); - table_xa_member->file->print_error(error_num, MYF(0)); - goto error; - } else { - free_root(&mem_root, MYF(0)); - spider_close_sys_table(thd, table_xa_member, &open_tables_backup, TRUE); - table_xa_member_opened = FALSE; - goto xa_delete; - } - } - - memset((void*)&tmp_share, 0, sizeof(SPIDER_SHARE)); - memset(&tmp_connect_info, 0, - sizeof(char *) * SPIDER_TMP_SHARE_CHAR_PTR_COUNT); - spider_set_tmp_share_pointer(&tmp_share, tmp_connect_info, - tmp_connect_info_length, tmp_long, tmp_longlong); - do { - SPIDER_BACKUP_DASTATUS; - spider_get_sys_server_info(table_xa_member, &tmp_share, 0, &mem_root); - if ((error_num = spider_create_conn_keys(&tmp_share))) - { - spider_sys_index_end(table_xa_member); - free_root(&mem_root, MYF(0)); - goto error; - } - - if (!(conn= spider_get_conn(&tmp_share, 0, tmp_share.conn_keys[0], trx, - NULL, FALSE, FALSE, &error_num)) && - (force_commit == 0 || - (force_commit == 1 && error_num != ER_XAER_NOTA))) - { - spider_sys_index_end(table_xa_member); - spider_free_tmp_share_alloc(&tmp_share); - free_root(&mem_root, MYF(0)); - goto error; - } - conn->error_mode &= spider_param_error_read_mode(thd, 0); - conn->error_mode &= spider_param_error_write_mode(thd, 0); - if ( - (error_num = spider_db_xa_rollback(conn, xid)) && - (force_commit == 0 || - (force_commit == 1 && error_num != ER_XAER_NOTA)) - ) { - SPIDER_CONN_RESTORE_DASTATUS_AND_RESET_ERROR_NUM; - if (error_num) - { - spider_sys_index_end(table_xa_member); - spider_free_tmp_share_alloc(&tmp_share); - free_root(&mem_root, MYF(0)); - goto error; - } - } - spider_free_tmp_share_alloc(&tmp_share); - error_num = spider_sys_index_next_same(table_xa_member, xa_member_key); - } while (error_num == 0); - if ((error_num = spider_sys_index_end(table_xa_member))) - { - free_root(&mem_root, MYF(0)); - goto error; - } - free_root(&mem_root, MYF(0)); - spider_reuse_trx_ha(trx); - spider_free_trx_conn(trx, FALSE); - - /* - delete from - mysql.spider_xa_member - where - format_id = xid->format_id and - gtrid_length = xid->gtrid_length and - data = xid->data - */ - if ((error_num = spider_delete_xa_member(table_xa_member, xid))) - goto error; - spider_close_sys_table(thd, table_xa_member, &open_tables_backup, TRUE); - table_xa_member_opened = FALSE; - -xa_delete: - /* - delete from - mysql.spider_xa - where - format_id = xid->format_id and - gtrid_length = xid->gtrid_length and - data = xid->data - */ - if ( - !(table_xa = spider_open_sys_table( - thd, SPIDER_SYS_XA_TABLE_NAME_STR, SPIDER_SYS_XA_TABLE_NAME_LEN, - TRUE, &open_tables_backup, TRUE, &error_num)) - ) - goto error_open_table; - table_xa_opened = TRUE; - if ((error_num = spider_delete_xa(table_xa, xid))) - goto error; - spider_close_sys_table(thd, table_xa, &open_tables_backup, TRUE); - table_xa_opened = FALSE; - if (created_tmp_thd) - spider_free_tmp_thd(thd); - DBUG_RETURN(0); - -error: - if (table_xa_opened) - spider_close_sys_table(thd, table_xa, &open_tables_backup, TRUE); - if (table_xa_member_opened) - spider_close_sys_table(thd, table_xa_member, &open_tables_backup, TRUE); -error_open_table: - if (created_tmp_thd) - spider_free_tmp_thd(thd); - DBUG_RETURN(error_num); + DBUG_RETURN(spider_internal_xa_commit_or_rollback_by_xid( + thd, trx, xid, true)); } int spider_start_consistent_snapshot( From 4deadb9b870d3146f2f6e8dac247f476262e7e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Fri, 19 Dec 2025 09:46:54 +0200 Subject: [PATCH 21/43] MDEV-38390 : Galera test failure on wsrep.wsrep_provider_plugin_defaults Test result depends on Galera library version that should be 26.4.25 for these test results. --- mysql-test/include/galera_variables_ok.inc | 2 +- mysql-test/suite/wsrep/r/variables.result | 1 + mysql-test/suite/wsrep/r/variables_debug.result | 1 + mysql-test/suite/wsrep/t/variables.test | 2 +- mysql-test/suite/wsrep/t/variables_debug.test | 3 ++- mysql-test/suite/wsrep/t/wsrep_provider_plugin_defaults.test | 2 +- 6 files changed, 7 insertions(+), 4 deletions(-) diff --git a/mysql-test/include/galera_variables_ok.inc b/mysql-test/include/galera_variables_ok.inc index 9bd31bb32b2..d10bf1fd36e 100644 --- a/mysql-test/include/galera_variables_ok.inc +++ b/mysql-test/include/galera_variables_ok.inc @@ -5,7 +5,7 @@ if (!$_galera_variables_delta) { --let $galera_variables_delta=0 } ---let $galera_variables_expected=`SELECT 51 + $galera_variables_delta` +--let $galera_variables_expected=`SELECT 50 + $galera_variables_delta` --let $galera_variables_count=`SELECT COUNT(*) FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep%'` diff --git a/mysql-test/suite/wsrep/r/variables.result b/mysql-test/suite/wsrep/r/variables.result index 15836b9a0c2..d29b28e9c37 100644 --- a/mysql-test/suite/wsrep/r/variables.result +++ b/mysql-test/suite/wsrep/r/variables.result @@ -44,6 +44,7 @@ wsrep_commit_window # wsrep_local_state # wsrep_local_state_comment # wsrep_cert_index_size # +wsrep_causal_reads # wsrep_cert_interval # wsrep_open_transactions # wsrep_open_connections # diff --git a/mysql-test/suite/wsrep/r/variables_debug.result b/mysql-test/suite/wsrep/r/variables_debug.result index 6c4f8877761..594a840ae2f 100644 --- a/mysql-test/suite/wsrep/r/variables_debug.result +++ b/mysql-test/suite/wsrep/r/variables_debug.result @@ -44,6 +44,7 @@ wsrep_commit_window # wsrep_local_state # wsrep_local_state_comment # wsrep_cert_index_size # +wsrep_causal_reads # wsrep_cert_interval # wsrep_open_transactions # wsrep_open_connections # diff --git a/mysql-test/suite/wsrep/t/variables.test b/mysql-test/suite/wsrep/t/variables.test index c82d0ae02c2..3121266f8ae 100644 --- a/mysql-test/suite/wsrep/t/variables.test +++ b/mysql-test/suite/wsrep/t/variables.test @@ -3,7 +3,7 @@ --source include/have_innodb.inc --source include/galera_no_debug_sync.inc ---let $galera_version=26.4.23 +--let $galera_version=26.4.25 source include/check_galera_version.inc; source include/galera_variables_ok.inc; diff --git a/mysql-test/suite/wsrep/t/variables_debug.test b/mysql-test/suite/wsrep/t/variables_debug.test index e50cee28a15..989b82a447d 100644 --- a/mysql-test/suite/wsrep/t/variables_debug.test +++ b/mysql-test/suite/wsrep/t/variables_debug.test @@ -5,9 +5,10 @@ --source include/have_debug_sync.inc --source include/galera_have_debug_sync.inc ---let $galera_version=26.4.23 +--let $galera_version=26.4.25 source include/check_galera_version.inc; +--let $galera_variables_delta=1 source include/galera_variables_ok_debug.inc; --replace_column 2 # diff --git a/mysql-test/suite/wsrep/t/wsrep_provider_plugin_defaults.test b/mysql-test/suite/wsrep/t/wsrep_provider_plugin_defaults.test index 1e0b6a49efc..44dc3f7cb72 100644 --- a/mysql-test/suite/wsrep/t/wsrep_provider_plugin_defaults.test +++ b/mysql-test/suite/wsrep/t/wsrep_provider_plugin_defaults.test @@ -1,7 +1,7 @@ --source include/have_wsrep.inc --source include/have_innodb.inc ---let $galera_version=26.4.24 +--let $galera_version=26.4.25 source include/check_galera_version.inc; SELECT COUNT(*) FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_provider%' AND VARIABLE_NAME NOT IN ( From 5d2da8ef1e6bcff57479240c1580c64906c15005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 22 Dec 2025 08:29:57 +0200 Subject: [PATCH 22/43] MDEV-38408 WITH_DBUG_TRACE=OFF doesn't disable DBUG_DUMP or DBUG_LOG DBUG_DUMP(), DBUG_LOG(): Null expand unless DBUG_TRACE is enabled. Fixes up commit a69cf6f07ee7286d4813ef5c602d5b79b39d3028 (MDEV-29613) --- include/my_dbug.h | 5 +++-- sql/log_event.cc | 2 +- sql/rpl_record.cc | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/my_dbug.h b/include/my_dbug.h index b4b16d653b5..18ea052da6a 100644 --- a/include/my_dbug.h +++ b/include/my_dbug.h @@ -79,6 +79,7 @@ extern int (*dbug_sanity)(void); #define DBUG_PRINT(keyword,arglist) \ do if (_db_pargs_(__LINE__,keyword)) _db_doprnt_ arglist; while(0) +#define DBUG_DUMP(keyword,a1,a2) _db_dump_(__LINE__,keyword,a1,a2) #ifdef HAVE_ATTRIBUTE_CLEANUP #define DBUG_ENTER(a) struct _db_stack_frame_ _db_stack_frame_ __attribute__((cleanup(_db_return_))); \ @@ -98,6 +99,7 @@ extern int (*dbug_sanity)(void); #define DBUG_RETURN(a1) return(a1) #define DBUG_VOID_RETURN return #define DBUG_PRINT(keyword,arglist) do{} while(0) +#define DBUG_DUMP(keyword,a1,a2) do{} while(0) #endif #define DBUG_EXECUTE(keyword,a1) \ @@ -116,7 +118,6 @@ extern int (*dbug_sanity)(void); #define DBUG_SET_INITIAL(a1) _db_set_init_ (a1) #define DBUG_PROCESS(a1) _db_process_(a1) #define DBUG_FILE _db_fp_() -#define DBUG_DUMP(keyword,a1,a2) _db_dump_(__LINE__,keyword,a1,a2) #define DBUG_END() _db_end_ () #define DBUG_LOCK_FILE _db_lock_file_() #define DBUG_UNLOCK_FILE _db_unlock_file_() @@ -236,7 +237,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout); one should #include . We intentionally avoid including it here to save compilation time. */ -# ifdef DBUG_OFF +# if defined DBUG_OFF || !defined DBUG_TRACE # define DBUG_LOG(keyword, v) do {} while (0) # else # define DBUG_LOG(keyword, v) do { \ diff --git a/sql/log_event.cc b/sql/log_event.cc index b1657275128..04385e886c9 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -216,7 +216,7 @@ private: Log_event *m_ev; // Used for Flashback }; -#ifndef DBUG_OFF +#if !defined DBUG_OFF && defined DBUG_TRACE #define DBUG_DUMP_EVENT_BUF(B,L) \ do { \ const uchar *_buf=(uchar*)(B); \ diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index 6266acf7805..194d16bf5b6 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -100,7 +100,7 @@ pack_row(TABLE *table, MY_BITMAP const* cols, length is stored in little-endian format, since this is the format used for the binlog. */ -#ifndef DBUG_OFF +#if !defined DBUG_OFF && defined DBUG_TRACE const uchar *old_pack_ptr= pack_ptr; #endif pack_ptr= field->pack(pack_ptr, field->ptr + offset, From 6892722577f3e37ea62506d4e38a7ff5ea8fe148 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Mon, 22 Dec 2025 11:57:34 +0530 Subject: [PATCH 23/43] MDEV-38271 Server hangs during concurrent set innodb_encryption_threads Problem: ======= - Multiple user threads waits for all encryption threads to start before returing the control to user. But in fil_crypt_thread(), InnoDB signals that thread is started after incrementing srv_n_fil_crypt_threads_started variable. For multiple waiters, pthread_cond_broadcast() would be more appropriate as it wakes all waiting threads. Solution: ======== fil_crypt_thread(): Use pthread_cond_broadcast instead of pthread_cond_signal(fil_crypt_cond) to wake multiple waiter threads --- storage/innobase/fil/fil0crypt.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/fil/fil0crypt.cc b/storage/innobase/fil/fil0crypt.cc index 28078747318..3d32d1c640b 100644 --- a/storage/innobase/fil/fil0crypt.cc +++ b/storage/innobase/fil/fil0crypt.cc @@ -2021,7 +2021,7 @@ static void fil_crypt_thread() #endif /* UNIV_PFS_THREAD */ mysql_mutex_lock(&fil_crypt_threads_mutex); rotate_thread_t thr(srv_n_fil_crypt_threads_started++); - pthread_cond_signal(&fil_crypt_cond); /* signal that we started */ + pthread_cond_broadcast(&fil_crypt_cond); if (!thr.should_shutdown()) { /* if we find a tablespace that is starting, skip over it @@ -2093,7 +2093,7 @@ wait_for_work: fil_crypt_return_iops(&thr); srv_n_fil_crypt_threads_started--; - pthread_cond_signal(&fil_crypt_cond); /* signal that we stopped */ + pthread_cond_broadcast(&fil_crypt_cond); mysql_mutex_unlock(&fil_crypt_threads_mutex); my_thread_end(); From 291cc20bf1bba64f735503c5e4a7c21c9a00f831 Mon Sep 17 00:00:00 2001 From: Rucha Deodhar Date: Mon, 22 Sep 2025 15:26:54 +0530 Subject: [PATCH 24/43] MDEV-33640: Server crashes at my_hash_free Analysis: While parsing the json schema, we dont initialize the hash that stores properties, but we try to free hash while cleanup. This happend because the variable that checks whether hash is initialized has garbage value. So the check becomes true and we end up deleting the uninitialized hash. Fix: Initialize the is_hash_inited to false. --- mysql-test/main/func_json.result | 5 +++++ mysql-test/main/func_json.test | 8 ++++++++ sql/json_schema.h | 5 +++++ 3 files changed, 18 insertions(+) diff --git a/mysql-test/main/func_json.result b/mysql-test/main/func_json.result index edb0ba36796..d0d186538a8 100644 --- a/mysql-test/main/func_json.result +++ b/mysql-test/main/func_json.result @@ -5379,4 +5379,9 @@ result [["2", "6"], ["3", "8"], ["4", "5"], ["1", "7"]] [["2", "6"], ["3", "8"], ["4", "5"], ["1", "7"]] [["2", "6"], ["3", "8"], ["4", "5"], ["1", "7"]] +# +# MDEV-33640: Server crashes at my_hash_free +# +SELECT JSON_SCHEMA_VALID('{"properties": "a_string": {"pattern": "^[5-9]$"}}}', '{"a_string": "8"}'); +ERROR HY000: Invalid value for keyword properties # End of 11.4 Test diff --git a/mysql-test/main/func_json.test b/mysql-test/main/func_json.test index 2045b5df09c..23a418f16de 100644 --- a/mysql-test/main/func_json.test +++ b/mysql-test/main/func_json.test @@ -4275,4 +4275,12 @@ SELECT ( WITH x AS ( WITH x ( x ) AS ( SELECT ( 1.000000 ) ) SELECT x FROM x ) S select json_array_intersect('[["1", "7"], ["2", "6"], ["4", "5"], ["3", "8"]]', '[["2","6"],["3","8"],["4","5"],["1","7"]]') as result from (values (1),(2),(3),(4),(5)) x; + +--echo # +--echo # MDEV-33640: Server crashes at my_hash_free +--echo # + +--error ER_JSON_INVALID_VALUE_FOR_KEYWORD +SELECT JSON_SCHEMA_VALID('{"properties": "a_string": {"pattern": "^[5-9]$"}}}', '{"a_string": "8"}'); + --echo # End of 11.4 Test diff --git a/sql/json_schema.h b/sql/json_schema.h index 28cd8b411b9..6063597ec56 100644 --- a/sql/json_schema.h +++ b/sql/json_schema.h @@ -475,6 +475,7 @@ class Json_schema_properties : public Json_schema_keyword Json_schema_properties() { priority= 1; + is_hash_inited= false; } bool validate(const json_engine_t *je, const uchar *k_start= NULL, const uchar *k_end= NULL) override; @@ -503,6 +504,10 @@ class Json_schema_dependent_schemas : public Json_schema_keyword if (is_hash_inited) my_hash_free(&properties); } + Json_schema_dependent_schemas() + { + is_hash_inited= false; + } bool validate(const json_engine_t *je, const uchar *k_start= NULL, const uchar *k_end= NULL) override; bool handle_keyword(THD *thd, json_engine_t *je, From dfd28e53242ae61d35072fdbd04e0e2943362231 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 23 Dec 2025 13:42:45 +0300 Subject: [PATCH 25/43] MDEV-29114 Pruning depends on current timestamp for partition by SYSTEM_TIME Wrong "fastpath" boundary conditions. The right boundary is open, the left boundary is closed. The condition range_value[loc_hist_id - 1] as left boundary must be checked with <= operator. The same with the right boundary range_value[loc_hist_id] as the correct way is to check it with > operator, the right boundary check was rewritten for clear understanding: ts < range_value[loc_hist_id] The below code confirms closed endpoint type for left boundary: if (range_value[loc_hist_id] <= ts) min_hist_id= loc_hist_id + 1; Also the endpoint type (closed for left, open for right) is confirmed by vers_set_hist_part() for DML: else if (vers_info->interval.is_set() && vers_info->hist_part->range_value <= thd->query_start()) and here (right boundary, rewritten for clarity): if (thd->query_start() < next->range_value) { error= false; break; } --- .../suite/versioning/r/partition.result | 63 +++++++++++++++++++ mysql-test/suite/versioning/t/partition.test | 46 ++++++++++++++ sql/partition_info.cc | 4 +- sql/sql_partition.cc | 5 +- 4 files changed, 115 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/versioning/r/partition.result b/mysql-test/suite/versioning/r/partition.result index 54b2ab1d469..337de9e15a7 100644 --- a/mysql-test/suite/versioning/r/partition.result +++ b/mysql-test/suite/versioning/r/partition.result @@ -3524,6 +3524,69 @@ alter table `t1` partition by system_time interval 7 year ; ERROR 22003: TIMESTAMP value is out of range in 'INTERVAL' drop table t1; # +# MDEV-29114 ALTER puts row into different partition for system_time +# interval partitioning +# +# CREATE case +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +create or replace table t1 (x int) with system versioning +partition by system_time interval 1 hour ( +partition p0 history, partition p1 history, partition pn current); +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (0); +set timestamp= unix_timestamp('2000-01-01 00:10:00'); +update t1 set x= 1; +set timestamp= unix_timestamp('2000-01-01 01:00:00'); +update t1 set x= 2; +set timestamp= unix_timestamp('2000-01-01 01:30:00'); +update t1 set x= 3; +# CREATE result: row 1 got into p1 +select *, row_start, row_end from t1 partition (p0); +x row_start row_end +0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.000000 +select *, row_start, row_end from t1 partition (p1); +x row_start row_end +1 2000-01-01 00:10:00.000000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +flush tables; +# For CREATE pruning is affected by current timestamp, but SELECT works +# in any case since row 1 got into p1 +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59'; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 p0,p1,pn ALL NULL NULL NULL NULL 4 Using where +select * from t1 for system_time as of '2000-01-01 00:59:59'; +x +1 +set timestamp= unix_timestamp('2020-01-01 00:00:00'); +explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59'; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 p0,p1,pn ALL NULL NULL NULL NULL 4 Using where +select * from t1 for system_time as of '2000-01-01 00:59:59'; +x +1 +# ALTER case +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +alter table t1 +partition by system_time interval 1 hour ( +partition p0 history, partition p1 history, partition pn current); +# ALTER result: row 1 got into p1 +Select *, row_start, row_end from t1 partition (p0); +x row_start row_end +0 2000-01-01 00:00:00.000000 2000-01-01 00:10:00.000000 +Select *, row_start, row_end from t1 partition (p1); +x row_start row_end +1 2000-01-01 00:10:00.000000 2000-01-01 01:00:00.000000 +2 2000-01-01 01:00:00.000000 2000-01-01 01:30:00.000000 +flush tables; +Explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59'; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 p0,p1,pn ALL NULL NULL NULL NULL 4 Using where +Select * from t1 for system_time as of '2000-01-01 00:59:59'; +x +1 +drop table t1; +# # End of 10.11 tests # set global innodb_stats_persistent= @save_persistent; diff --git a/mysql-test/suite/versioning/t/partition.test b/mysql-test/suite/versioning/t/partition.test index 245d3ff9323..7acc4c22f8d 100644 --- a/mysql-test/suite/versioning/t/partition.test +++ b/mysql-test/suite/versioning/t/partition.test @@ -2768,6 +2768,52 @@ partition by system_time interval 1 month ( alter table `t1` partition by system_time interval 7 year ; drop table t1; +--echo # +--echo # MDEV-29114 ALTER puts row into different partition for system_time +--echo # interval partitioning +--echo # +--echo # CREATE case +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +create or replace table t1 (x int) with system versioning +partition by system_time interval 1 hour ( + partition p0 history, partition p1 history, partition pn current); +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +insert t1 values (0); +set timestamp= unix_timestamp('2000-01-01 00:10:00'); +update t1 set x= 1; +set timestamp= unix_timestamp('2000-01-01 01:00:00'); +update t1 set x= 2; +set timestamp= unix_timestamp('2000-01-01 01:30:00'); +update t1 set x= 3; + +--echo # CREATE result: row 1 got into p1 +select *, row_start, row_end from t1 partition (p0); +select *, row_start, row_end from t1 partition (p1); +flush tables; +--echo # For CREATE pruning is affected by current timestamp, but SELECT works +--echo # in any case since row 1 got into p1 +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59'; +select * from t1 for system_time as of '2000-01-01 00:59:59'; +set timestamp= unix_timestamp('2020-01-01 00:00:00'); +explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59'; +select * from t1 for system_time as of '2000-01-01 00:59:59'; + +--echo # ALTER case +set timestamp= unix_timestamp('2000-01-01 00:00:00'); +alter table t1 +partition by system_time interval 1 hour ( + partition p0 history, partition p1 history, partition pn current); + +--echo # ALTER result: row 1 got into p1 +Select *, row_start, row_end from t1 partition (p0); +Select *, row_start, row_end from t1 partition (p1); +flush tables; +Explain partitions select * from t1 for system_time as of '2000-01-01 00:59:59'; +Select * from t1 for system_time as of '2000-01-01 00:59:59'; + +drop table t1; + --echo # --echo # End of 10.11 tests --echo # diff --git a/sql/partition_info.cc b/sql/partition_info.cc index f7fdaba5c7b..b459608c764 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -831,6 +831,7 @@ bool partition_info::vers_set_hist_part(THD *thd, uint *create_count) return 0; } else if (vers_info->interval.is_set() && + /* Left boundary is closed */ vers_info->hist_part->range_value <= thd->query_start()) { partition_element *next= NULL; @@ -842,7 +843,8 @@ bool partition_info::vers_set_hist_part(THD *thd, uint *create_count) while ((next= it++) != vers_info->now_part) { vers_info->hist_part= next; - if (next->range_value > thd->query_start()) + /* Right boundary is open */ + if (thd->query_start() < next->range_value) { error= false; break; diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 621ce730c7b..5dac3292eba 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -3511,13 +3511,14 @@ int vers_get_partition_id(partition_info *part_info, uint32 *part_id, goto done; // fastpath ts= row_end->get_timestamp(&unused); - if ((loc_hist_id == 0 || range_value[loc_hist_id - 1] < ts) && - (loc_hist_id == max_hist_id || range_value[loc_hist_id] >= ts)) + if ((loc_hist_id == 0 || range_value[loc_hist_id - 1] <= ts) && + (loc_hist_id == max_hist_id || ts < range_value[loc_hist_id])) goto done; // fastpath while (max_hist_id > min_hist_id) { loc_hist_id= (max_hist_id + min_hist_id) / 2; + /* Left boundary is closed */ if (range_value[loc_hist_id] <= ts) min_hist_id= loc_hist_id + 1; else From 9fad7ac3f7c87295e4a8a1939e913e8e76cf3764 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 23 Dec 2025 13:42:45 +0300 Subject: [PATCH 26/43] Comment cleanup Comment "Currently the field first_nested ..." is for first_nested, not for Name_resolution_context context. --- sql/sql_lex.h | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a6cf6303aa6..5c1bc968f44 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1113,9 +1113,20 @@ Field_pair *find_matching_field_pair(Item *item, List pair_list); class st_select_lex: public st_select_lex_node { public: + Name_resolution_context context; + LEX_CSTRING db; + + /* + Point to the LEX in which it was created, used in view subquery detection. + + TODO: make also st_select_lex::parent_stmt_lex (see LEX::stmt_lex) + and use st_select_lex::parent_lex & st_select_lex::parent_stmt_lex + instead of global (from THD) references where it is possible. + */ + LEX *parent_lex; /* Currently the field first_nested is used only by parser. - It containa either a reference to the first select + It contains either a reference to the first select of the nest of selects to which 'this' belongs to, or in the case of priority jump it contains a reference to the select to which the priority nest has to be attached to. @@ -1128,18 +1139,6 @@ public: while select3->first_nested points to select2 and select1->first_nested points to select1. */ - - Name_resolution_context context; - LEX_CSTRING db; - - /* - Point to the LEX in which it was created, used in view subquery detection. - - TODO: make also st_select_lex::parent_stmt_lex (see LEX::stmt_lex) - and use st_select_lex::parent_lex & st_select_lex::parent_stmt_lex - instead of global (from THD) references where it is possible. - */ - LEX *parent_lex; st_select_lex *first_nested; Item *where, *having; /* WHERE & HAVING clauses */ Item *prep_where; /* saved WHERE clause for prepared statement processing */ From 9f70e0a76ce2a9c577444e85fc6981bc3dd0f38d Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 23 Dec 2025 13:42:45 +0300 Subject: [PATCH 27/43] MDEV-32299 Segfault when preparing unreferenced select in recursive CTE With_element::prepare_unreferenced() contains query from recursive CTE which is upper owner of unreferenced CTE itself. Such recursive derived table is resolved into its expression and it tries to resolve fields or any other data from subqueries which are not yet prepared. These subqueries are preparing in upper frames of the same stack after prepare_unreferenced() finishes. That does not happen when recursion happens in referenced CTE because in that case there are links between the derived tables defined by the parser and descending into such recursion descends further into all the dependency queries. No such link exists in unreferenced query therefore it misses its upper dependencies. The fix moves prepare_unreferenced() later after JOIN::prepare() so it has the chance to access all the prepared data. There may be another fix of failing unreferenced queries with error as such kind of queries seem to be not usable. --- mysql-test/main/cte_recursive.result | 107 ++++++++++++++++++++++++ mysql-test/main/cte_recursive.test | 119 +++++++++++++++++++++++++++ sql/sql_cte.cc | 13 +++ sql/sql_cte.h | 4 +- sql/sql_lex.h | 1 + sql/sql_prepare.cc | 4 + sql/sql_select.cc | 8 +- 7 files changed, 249 insertions(+), 7 deletions(-) diff --git a/mysql-test/main/cte_recursive.result b/mysql-test/main/cte_recursive.result index 2fcf40bf7a8..e35d26f170c 100644 --- a/mysql-test/main/cte_recursive.result +++ b/mysql-test/main/cte_recursive.result @@ -5975,4 +5975,111 @@ drop table t1; # SELECT ( WITH RECURSIVE x AS ( WITH x AS ( SELECT 1 FROM t14 ) SELECT x ) , t14 AS ( SELECT 1 UNION SELECT 'x' FROM x ) SELECT x FROM x WHERE ( SELECT x FROM x ) ) ; ERROR 42S22: Unknown column 'x' in 'SELECT' +# +# MDEV-32299 Segfault when preparing unreferenced select in recursive CTE +# +SELECT ( WITH RECURSIVE x AS ( SELECT * FROM ( SELECT UTC_TIMESTAMP FROM ( SELECT ( WITH x AS ( WITH x AS ( SELECT * FROM x ) SELECT 1 ) SELECT 1 ) ) x ) x UNION SELECT NULL ) ( SELECT x FROM x ) ) ; +ERROR 42S22: Unknown column 'x' in 'SELECT' +select ( +with recursive x as ( +select * from ( +select utc_timestamp +from ( +select ( +with x as ( +with x as ( +select * from x +) +select 1 +) +select 1 +) s +) y +) z +union +select null +) +(select x from x) +); +ERROR 42S22: Unknown column 'x' in 'SELECT' +select ( +with recursive r as ( +select * from ( +with a as ( +select * from r +) +select 1 +) y +union +select 1 +) +(select * from r) +); +( +with recursive r as ( +select * from ( +with a as ( +select * from r +) +select 1 +) y +union +select 1 +) +(select * from r) +) +1 +create table t1 (x int); +with recursive r as ( +select f1 from ( +with a as ( +select x from t1 +) +select 1 as f1 +) y +union +select null +) +(select * from r); +f1 +1 +NULL +with recursive r as ( +select f1 from ( +with a as ( +select f2 from t1 +) +select 1 as f1 +) y +union +select null +) +(select * from r); +ERROR 42S22: Unknown column 'f2' in 'SELECT' +drop table t1; +with recursive r as ( +select 1 as f1 +union +select f1 from ( +with a as ( +select f1 from r +) +select 1 as f1 +) y +) +(select f1 from r); +f1 +1 +with recursive r as ( +select 1 as f1 +union +select f1 from ( +with a as ( +select f2 from r +) +select 1 as f1 +) y +) +(select f1 from r); +ERROR 42S22: Unknown column 'f2' in 'SELECT' # End of 10.6 tests diff --git a/mysql-test/main/cte_recursive.test b/mysql-test/main/cte_recursive.test index 35d3a8609d9..3447ced1a1c 100644 --- a/mysql-test/main/cte_recursive.test +++ b/mysql-test/main/cte_recursive.test @@ -3,6 +3,8 @@ --source include/no_msan_without_big.inc --source include/not_valgrind.inc --source include/have_innodb.inc +# --view-protocol fails due to MDEV-37119 for MDEV-32299 +--source include/no_view_protocol.inc create table t1 (a int, b varchar(32)); insert into t1 values @@ -4064,4 +4066,121 @@ drop table t1; --error ER_BAD_FIELD_ERROR SELECT ( WITH RECURSIVE x AS ( WITH x AS ( SELECT 1 FROM t14 ) SELECT x ) , t14 AS ( SELECT 1 UNION SELECT 'x' FROM x ) SELECT x FROM x WHERE ( SELECT x FROM x ) ) ; +--echo # +--echo # MDEV-32299 Segfault when preparing unreferenced select in recursive CTE +--echo # + +# As in bug report +--error ER_BAD_FIELD_ERROR +SELECT ( WITH RECURSIVE x AS ( SELECT * FROM ( SELECT UTC_TIMESTAMP FROM ( SELECT ( WITH x AS ( WITH x AS ( SELECT * FROM x ) SELECT 1 ) SELECT 1 ) ) x ) x UNION SELECT NULL ) ( SELECT x FROM x ) ) ; + +#0 0x00005555569dff7b in TABLE_LIST::reset_const_table (this=0x7fffe0019938) at ../src/sql/table.cc:9615 +#1 0x00005555569dffd4 in TABLE_LIST::reset_const_table (this=0x7fffe001a8c0) at ../src/sql/table.cc:9622 +--error ER_BAD_FIELD_ERROR +select ( + with recursive x as ( + select * from ( + select utc_timestamp + from ( + select ( + with x as ( + with x as ( + select * from x + ) + select 1 + ) + select 1 + ) s + ) y + ) z + + union + + select null + ) + (select x from x) +); + +#6 0x00007ffff7639206 in __assert_fail (assertion=0x555555e121b6 "table_ref->table || table_ref->view", file=0x555555cdf138 "../src/sql/table.cc", line=7305, function=0x555555e30e24 "void Field_iterator_table_ref::set_field_iterator()") at ./assert/assert.c:101 +#7 0x00005555569da013 in Field_iterator_table_ref::set_field_iterator (this=0x7ffff0a9afc0) at ../src/sql/table.cc:7305 +select ( + with recursive r as ( + select * from ( + with a as ( + select * from r + ) + select 1 + ) y + + union + + select 1 + ) + (select * from r) +); + +# Field resolution in unreferenced: +create table t1 (x int); + +with recursive r as ( + select f1 from ( + with a as ( + select x from t1 + ) + select 1 as f1 + ) y + + union + + select null +) +(select * from r); + +--error ER_BAD_FIELD_ERROR +with recursive r as ( + select f1 from ( + with a as ( + select f2 from t1 + ) + select 1 as f1 + ) y + + union + + select null +) +(select * from r); + +drop table t1; + +with recursive r as ( + select 1 as f1 + + union + + select f1 from ( + with a as ( + select f1 from r + ) + select 1 as f1 + ) y +) +(select f1 from r); + +--error ER_BAD_FIELD_ERROR +with recursive r as ( + select 1 as f1 + + union + + select f1 from ( + with a as ( + select f2 from r + ) + select 1 as f1 + ) y +) +(select f1 from r); + + --echo # End of 10.6 tests diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index 278bc38305f..92bb1dc596a 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -100,6 +100,19 @@ bool LEX::check_dependencies_in_with_clauses() } +bool LEX::prepare_unreferenced_in_with_clauses() +{ + for (With_clause *with_clause= with_clauses_list; + with_clause; + with_clause= with_clause->next_with_clause) + { + if (with_clause->prepare_unreferenced_elements(thd)) + return true; + } + return false; +} + + /** @brief Resolve table references to CTE from a sub-chain of table references diff --git a/sql/sql_cte.h b/sql/sql_cte.h index 1da7c6c323a..6d0e1bb41cb 100644 --- a/sql/sql_cte.h +++ b/sql/sql_cte.h @@ -438,9 +438,7 @@ public: void print(THD *thd, String *str, enum_query_type query_type); friend class With_element; - - friend - bool LEX::check_dependencies_in_with_clauses(); + friend struct LEX; }; inline diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 5c1bc968f44..4055e074f1d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -4954,6 +4954,7 @@ public: DDL_options ddl_options); bool check_dependencies_in_with_clauses(); + bool prepare_unreferenced_in_with_clauses(); bool check_cte_dependencies_and_resolve_references(); bool resolve_references_to_cte(TABLE_LIST *tables, TABLE_LIST **tables_last, diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 218d110e7c5..e62c9056804 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1628,6 +1628,10 @@ static int mysql_test_select(Prepared_statement *stmt, */ if (unit->prepare(unit->derived, 0, 0)) goto error; + + if (thd->lex->prepare_unreferenced_in_with_clauses()) + goto error; + if (!lex->describe && !thd->lex->analyze_stmt && !stmt->is_sql_prepare()) { /* Make copy of item list, as change_columns may change it */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 71715f743b6..a025b7484b5 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1645,10 +1645,6 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num, } } - With_clause *with_clause=select_lex->get_with_clause(); - if (with_clause && with_clause->prepare_unreferenced_elements(thd)) - DBUG_RETURN(1); - With_element *with_elem= select_lex->get_with_element(); if (with_elem && select_lex->check_unrestricted_recursive( @@ -5265,6 +5261,10 @@ mysql_select(THD *thd, TABLE_LIST *tables, List &fields, COND *conds, } thd->get_stmt_da()->reset_current_row_for_warning(1); + + if (thd->lex->prepare_unreferenced_in_with_clauses()) + goto err; + /* Look for a table owned by an engine with the select_handler interface */ select_lex->pushdown_select= find_select_handler(thd, select_lex); From 10643cffe98a42ee28a9f16b70ad7dff90563a03 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 23 Dec 2025 13:42:45 +0300 Subject: [PATCH 28/43] MDEV-37325 Incorrect results for INTERSECT ALL in ORACLE mode Oracle mode has no priority between set-operators but the current implementation with disable_index_if_needed() can not work with arbitrary mix of distinct/non-distinct set-operators as the algorithm is tied to the properly wrapped version of expression. Index can be disabled only once and cannot be reenabled afterwards. To adapt Oracle mode for the aforementioned implementation we prioritize each set operator with parens in the order of appearance. So the expression: S1 op1 S2 op2 S3 op3 S4 will be rewritten like this: (((S1 op1 S2) op2 S3) op3 S4). The rewritten expression is valid for both Oracle and default mode and renders the same result. Prioritizing is done with create_priority_nest() but it was tweaked to work with derived to be the first operand. Originally create_priority_nest() could wrap only second operand, like this: S1 op1 S2 op2 S3 -> S1 op1 (S2 op2 S3). Now this behaviour is kept by non-NULL attach_to argument. With NULL attach_to argument it produces this transformation: S1 op1 S2 -> (S1 op1 S2), but to be strict it will look like this: SELECT .. FROM (S1 op1 S2). This expression is treated as the first one in the select list, so nothing to "attach to". On importance of not making fake_select_lex after create_priority_nest() in parsed_select_expr_start(). This will fail nastily in Protocol::valid_handler() check in store_longlong() for EXPLAIN. This happens because select_options are inherited from newly made fake_select_lex but without SELECT_DESCRIBE flag which is meant to be assigned by mysql_explain_union() but union_needs_tmp_table() is false (because now top-level unit has no set-operator, it is just select from derived), so SELECT_DESCRIBE assignment is skipped. Depending on that flag send_result_set_metadata() is conditionally executed in JOIN::exec_inner(). And originally it is done via select_describe() branch for top-level select when there is no set operator (it is select from derived as explained above), not via direct send_result_set_metadata(). On IS_OR_WAS_ORACLE. Sql_mode_save_for_frm_handling removes MODE_ORACLE when view is registered or opened. We cannot return it back as this conflicts with MDEV-12478 (tested by compat/oracle.func_concat). So we are temporarily adding WAS_ORACLE flag instead. WAS_MODE_ORACLE is for testing both WAS_ORACLE and MODE_ORACLE simultaneously. --- mysql-test/main/intersect_all.result | 271 ++- mysql-test/main/intersect_all.test | 105 + mysql-test/main/set_operation_oracle.result | 102 +- .../oracle/r/empty_string_literal.result | 2 +- mysql-test/suite/compat/oracle/r/minus.result | 2 +- .../compat/oracle/r/table_value_constr.result | 1783 +++++++++++------ .../compat/oracle/t/table_value_constr.test | 12 + sql/sql_class.h | 13 +- sql/sql_lex.cc | 48 +- sql/sql_lex.h | 6 +- sql/sql_union.cc | 8 +- 11 files changed, 1615 insertions(+), 737 deletions(-) diff --git a/mysql-test/main/intersect_all.result b/mysql-test/main/intersect_all.result index eeb0c8b5892..028a76944b3 100644 --- a/mysql-test/main/intersect_all.result +++ b/mysql-test/main/intersect_all.result @@ -1023,23 +1023,31 @@ NULL UNION RESULT ALL NULL NULL NULL NULL NULL set sql_mode= 'oracle'; explain SELECT * from t3 union select * from u3 intersect all select * from i3; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t3 system NULL NULL NULL NULL 0 Const row not found -2 UNION u3 system NULL NULL NULL NULL 0 Const row not found -3 INTERSECT i3 system NULL NULL NULL NULL 0 Const row not found -NULL UNIT RESULT ALL NULL NULL NULL NULL NULL -select * from t3 union select * from u3 intersect select * from i3; +1 PRIMARY ALL NULL NULL NULL NULL 2 +4 DERIVED ALL NULL NULL NULL NULL 2 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL no matching row in const table +3 UNION NULL NULL NULL NULL NULL NULL NULL no matching row in const table +NULL UNION RESULT ALL NULL NULL NULL NULL NULL +5 INTERSECT NULL NULL NULL NULL NULL NULL NULL no matching row in const table +NULL INTERSECT RESULT ALL NULL NULL NULL NULL NULL +select x from t3 union select x from u3 intersect select x from i3; x -SELECT * from t3 union select * from u3 intersect all select * from i3; +SELECT x from t3 union select x from u3 intersect all select x from i3; x insert into t3 values (0); insert into i3 values (0); -Select * from t3 union select * from u3 intersect select * from i3; +Select x from t3 union select x from u3 intersect select x from i3; x 0 -SELECT * FROM t3 UNION SELECT * FROM u3 INTERSECT ALL SELECT * FROM i3; +SELECT x FROM t3 UNION SELECT x FROM u3 INTERSECT ALL SELECT x FROM i3; +x +0 +create view v as select * from t3 union select * from u3 intersect select * from i3; +select * from v; x 0 drop tables t3, u3, i3; +drop view v; # First line of these results is column names, not the result # (pay attention to "affected rows") values (1, 2) union all values (1, 2); @@ -1065,10 +1073,47 @@ values (1, 2) union all values (1, 2) union values (4, 3) union all values (4, 3 1 2 4 3 affected rows: 2 +set sql_mode= default; create table t1 (a int, b int); create table t2 like t1; insert t1 values (1, 2), (1, 2), (1, 2), (2, 3), (2, 3), (3, 4), (3, 4); insert t2 values (1, 2), (1, 2), (2, 3), (2, 3), (2, 3), (2, 3), (4, 5); +select * from t1 intersect all select * from t2; +a b +1 2 +2 3 +1 2 +2 3 +select * from t1 intersect all select * from t2 union values (1, 2); +a b +1 2 +2 3 +select * from t1 intersect all (select * from t2 union values (1, 2)); +a b +1 2 +2 3 +(select * from t1 intersect all select * from t2) union values (1, 2); +a b +1 2 +2 3 +create view v1 as select * from t1 intersect all select * from t2 union values (1, 2); +show create view v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` intersect all select `t2`.`a` AS `a`,`t2`.`b` AS `b` from `t2` union values (1,2) latin1 latin1_swedish_ci +select * from v1; +a b +1 2 +2 3 +create view v2 as select * from t1 union values (1, 2) intersect all select * from t2; +show create view v2; +View Create View character_set_client collation_connection +v2 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` union select `__5`.`1` AS `1`,`__5`.`2` AS `2` from (values (1,2) intersect all select `t2`.`a` AS `a`,`t2`.`b` AS `b` from `t2`) `__5` latin1 latin1_swedish_ci +select * from v2; +a b +1 2 +2 3 +3 4 +set sql_mode= 'oracle'; select * from t1 intersect select * from t2; a b 1 2 @@ -1098,8 +1143,6 @@ select * from t1 intersect all select * from t2 union values (1, 2); a b 1 2 2 3 -1 2 -2 3 select * from t1 intersect all (select * from t2 union values (1, 2)); a b 1 2 @@ -1110,9 +1153,209 @@ a b 2 3 explain select * from t1 intersect all select * from t2 union values (1, 2); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 7 -2 INTERSECT t2 ALL NULL NULL NULL NULL 7 -3 UNION NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNIT RESULT ALL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 8 +4 DERIVED ALL NULL NULL NULL NULL 7 +2 DERIVED t1 ALL NULL NULL NULL NULL 7 +3 INTERSECT t2 ALL NULL NULL NULL NULL 7 +NULL INTERSECT RESULT ALL NULL NULL NULL NULL NULL +5 UNION NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL +show create view v1; +View Create View character_set_client collation_connection +v1 CREATE VIEW "v1" AS select "__6"."a" AS "a","__6"."b" AS "b" from (select "__4"."a" AS "a","__4"."b" AS "b" from (select "t1"."a" AS "a","t1"."b" AS "b" from "t1" intersect all select "t2"."a" AS "a","t2"."b" AS "b" from "t2") "__4" union values (1,2)) "__6" latin1 latin1_swedish_ci +select * from v1; +a b +1 2 +2 3 +show create view v2; +View Create View character_set_client collation_connection +v2 CREATE VIEW "v2" AS select "__7"."a" AS "a","__7"."b" AS "b" from (select "t1"."a" AS "a","t1"."b" AS "b" from "t1" union select "__5"."1" AS "1","__5"."2" AS "2" from (select "__6"."1" AS "1","__6"."2" AS "2" from (values (1,2) intersect all select "t2"."a" AS "a","t2"."b" AS "b" from "t2") "__6") "__5") "__7" latin1 latin1_swedish_ci +select * from v2; +a b +1 2 +2 3 +3 4 +create view v3 as select * from t1 union values (1, 2) intersect all select * from t2; +show create view v3; +View Create View character_set_client collation_connection +v3 CREATE VIEW "v3" AS select "__6"."a" AS "a","__6"."b" AS "b" from (select "__8"."a" AS "a","__8"."b" AS "b" from (select "__4"."a" AS "a","__4"."b" AS "b" from (select "__6"."a" AS "a","__6"."b" AS "b" from (select "t1"."a" AS "a","t1"."b" AS "b" from "t1" union values (1,2)) "__6") "__4" intersect all select "t2"."a" AS "a","t2"."b" AS "b" from "t2") "__8") "__6" latin1 latin1_swedish_ci +select * from v3; +a b +1 2 +2 3 drop tables t1, t2; +drop view v1, v2, v3; +create table t3 (x int); +create table u3 (x int); +create table i3 (x int); +create view v4 as SELECT * from t3 union select * from u3 intersect all select * from i3; +select * from v4; +x +drop tables t3, u3, i3; +drop view v4; +set sql_mode= default; +# +# MDEV-37325 Incorrect results for INTERSECT ALL in ORACLE mode +# +create table t1 (a int, b int); +create table t2 like t1; +insert t1 values (1, 2), (1, 2), (1, 2), (1, 2); +insert t2 values (1, 2), (1, 2); +select * from t1 except all select * from t2 intersect all values (1, 2); +a b +1 2 +1 2 +1 2 +explain select * from t1 except all select * from t2 intersect all values (1, 2); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 4 +4 EXCEPT ALL NULL NULL NULL NULL 2 +2 DERIVED t2 ALL NULL NULL NULL NULL 2 +3 INTERSECT NULL NULL NULL NULL NULL NULL NULL No tables used +NULL INTERSECT RESULT ALL NULL NULL NULL NULL NULL +NULL EXCEPT RESULT ALL NULL NULL NULL NULL NULL +Explain select * from (select * from (select * from t1 except all select * from t2) __3 intersect all values (1,2)) __5; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY ALL NULL NULL NULL NULL 2 +2 DERIVED ALL NULL NULL NULL NULL 4 +3 DERIVED t1 ALL NULL NULL NULL NULL 4 +4 EXCEPT t2 ALL NULL NULL NULL NULL 2 +NULL EXCEPT RESULT ALL NULL NULL NULL NULL NULL +5 INTERSECT NULL NULL NULL NULL NULL NULL NULL No tables used +NULL INTERSECT RESULT ALL NULL NULL NULL NULL NULL +create view v1 as select * from t1 except all select * from t2 intersect all values (1, 2); +show create view v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` except all select `__5`.`a` AS `a`,`__5`.`b` AS `b` from (select `t2`.`a` AS `a`,`t2`.`b` AS `b` from `t2` intersect all values (1,2)) `__5` latin1 latin1_swedish_ci +select * from v1; +a b +1 2 +1 2 +1 2 +drop view v1; +create view v1 as ((select * from t1 except all select * from t2) intersect all values (1, 2)); +show create view v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `__5`.`a` AS `a`,`__5`.`b` AS `b` from (select `t1`.`a` AS `a`,`t1`.`b` AS `b` from `t1` except all select `t2`.`a` AS `a`,`t2`.`b` AS `b` from `t2`) `__5` intersect all values (1,2) latin1 latin1_swedish_ci +select * from v1; +a b +1 2 +drop view v1; +explain select * from ((select * from t1 except all select * from t2) intersect all values (1, 2)) v; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY ALL NULL NULL NULL NULL 2 +5 DERIVED ALL NULL NULL NULL NULL 4 +2 DERIVED t1 ALL NULL NULL NULL NULL 4 +3 EXCEPT t2 ALL NULL NULL NULL NULL 2 +NULL EXCEPT RESULT ALL NULL NULL NULL NULL NULL +4 INTERSECT NULL NULL NULL NULL NULL NULL NULL No tables used +NULL INTERSECT RESULT ALL NULL NULL NULL NULL NULL +# Oracle: first UNION, then INTERSECT ALL +set sql_mode= 'oracle'; +create view v2 as select * from t1 except all select * from t2 intersect all values (1, 2); +show create view v2; +View Create View character_set_client collation_connection +v2 CREATE VIEW "v2" AS select "__6"."a" AS "a","__6"."b" AS "b" from (select "__8"."a" AS "a","__8"."b" AS "b" from (select "__4"."a" AS "a","__4"."b" AS "b" from (select "__6"."a" AS "a","__6"."b" AS "b" from (select "t1"."a" AS "a","t1"."b" AS "b" from "t1" except all select "t2"."a" AS "a","t2"."b" AS "b" from "t2") "__6") "__4" intersect all values (1,2)) "__8") "__6" latin1 latin1_swedish_ci +select * from v2; +a b +1 2 +drop view v2; +EXPLAIN select * from t1 except all select * from t2 intersect all values (1, 2); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY ALL NULL NULL NULL NULL 2 +4 DERIVED ALL NULL NULL NULL NULL 4 +2 DERIVED t1 ALL NULL NULL NULL NULL 4 +3 EXCEPT t2 ALL NULL NULL NULL NULL 2 +NULL EXCEPT RESULT ALL NULL NULL NULL NULL NULL +5 INTERSECT NULL NULL NULL NULL NULL NULL NULL No tables used +NULL INTERSECT RESULT ALL NULL NULL NULL NULL NULL +select * from t2 union all select * from t2; +a b +1 2 +1 2 +1 2 +1 2 +select * from t1 except all select * from t2 intersect all values (1, 2) union values (2, 3); +a b +1 2 +2 3 +select * from t2 union all select * from t2 except all select * from t2 intersect all values (1, 2) union values (2, 3); +a b +1 2 +2 3 +select * from t1 except all select * from t2 union values (2, 3); +a b +1 2 +2 3 +# Default: first INTERSECT ALL, then UNION +select * from t1 union values (1, 2) intersect all select * from t2; +a b +1 2 +select * from t1 union (values (1, 2) intersect all select * from t2); +a b +1 2 +set sql_mode= default; +select * from t1 union values (1, 2) intersect all select * from t2; +a b +1 2 +select * from t1 union (values (1, 2) intersect all select * from t2); +a b +1 2 +set sql_mode= 'oracle'; +create or replace table t2 like t1; +insert t2 values (1, 2), (2, 3); +create table t3 select * from t1 except all select * from t2; +select * from t3; +a b +1 2 +1 2 +1 2 +select * from t3 intersect all select * from t1; +a b +1 2 +1 2 +1 2 +show create table t2; +Table Create Table +t2 CREATE TABLE "t2" ( + "a" int(11) DEFAULT NULL, + "b" int(11) DEFAULT NULL +) +select * from t2 order by a desc; +a b +2 3 +1 2 +select * from t1 except all select * from t2; +a b +1 2 +1 2 +1 2 +select * from t1 except all select * from t2 union all values (0, 1); +a b +1 2 +1 2 +1 2 +0 1 +select * from t1 except all select * from t2 union all values (0, 1) order by a limit 2; +a b +0 1 +1 2 +select * from t1 except all select * from t2 intersect all select * from t1; +a b +1 2 +1 2 +1 2 +select * from t1 except all select * from t2 intersect all select * from t1 union all select * from t2; +a b +1 2 +1 2 +1 2 +1 2 +2 3 +select * from t1 except all select * from t2 intersect all select * from t1 union all select * from t2 order by a desc limit 3; +a b +2 3 +1 2 +1 2 +drop tables t1, t2, t3; set sql_mode= default; diff --git a/mysql-test/main/intersect_all.test b/mysql-test/main/intersect_all.test index 8217e4ebcbc..c3dc4e123f0 100644 --- a/mysql-test/main/intersect_all.test +++ b/mysql-test/main/intersect_all.test @@ -336,13 +336,22 @@ create table i3 (x int); explain SELECT * from t3 union select * from u3 intersect all select * from i3; set sql_mode= 'oracle'; explain SELECT * from t3 union select * from u3 intersect all select * from i3; +# TODO: there is a bug in --view protocol, it displays column name as '*' instead of 'x' +--replace_result * x select * from t3 union select * from u3 intersect select * from i3; +--replace_result * x SELECT * from t3 union select * from u3 intersect all select * from i3; insert into t3 values (0); insert into i3 values (0); +--replace_result * x Select * from t3 union select * from u3 intersect select * from i3; +--replace_result * x SELECT * FROM t3 UNION SELECT * FROM u3 INTERSECT ALL SELECT * FROM i3; +# CREATE VIEW still works good +create view v as select * from t3 union select * from u3 intersect select * from i3; +select * from v; drop tables t3, u3, i3; +drop view v; --enable_info --echo # First line of these results is column names, not the result @@ -372,10 +381,26 @@ values (1, 2) union all values (1, 2) union values (4, 3) union all values (4, 3 values (1, 2) union all values (1, 2) union values (4, 3) union all values (4, 3) union all values (1, 2) union values (1, 2); --disable_info +set sql_mode= default; create table t1 (a int, b int); create table t2 like t1; insert t1 values (1, 2), (1, 2), (1, 2), (2, 3), (2, 3), (3, 4), (3, 4); insert t2 values (1, 2), (1, 2), (2, 3), (2, 3), (2, 3), (2, 3), (4, 5); + +select * from t1 intersect all select * from t2; +select * from t1 intersect all select * from t2 union values (1, 2); +select * from t1 intersect all (select * from t2 union values (1, 2)); +(select * from t1 intersect all select * from t2) union values (1, 2); + +create view v1 as select * from t1 intersect all select * from t2 union values (1, 2); +show create view v1; +select * from v1; + +create view v2 as select * from t1 union values (1, 2) intersect all select * from t2; +show create view v2; +select * from v2; + +set sql_mode= 'oracle'; select * from t1 intersect select * from t2; select * from t1 intersect all select * from t2; --echo # Default: first INTERSECT ALL, then UNION @@ -390,5 +415,85 @@ select * from t1 intersect all select * from t2 union values (1, 2); select * from t1 intersect all (select * from t2 union values (1, 2)); (select * from t1 intersect all select * from t2) union values (1, 2); explain select * from t1 intersect all select * from t2 union values (1, 2); + +show create view v1; +select * from v1; + +show create view v2; +select * from v2; + +create view v3 as select * from t1 union values (1, 2) intersect all select * from t2; +show create view v3; +select * from v3; + drop tables t1, t2; +drop view v1, v2, v3; + +create table t3 (x int); +create table u3 (x int); +create table i3 (x int); +create view v4 as SELECT * from t3 union select * from u3 intersect all select * from i3; +select * from v4; +drop tables t3, u3, i3; +drop view v4; +set sql_mode= default; + +--echo # +--echo # MDEV-37325 Incorrect results for INTERSECT ALL in ORACLE mode +--echo # +create table t1 (a int, b int); +create table t2 like t1; +insert t1 values (1, 2), (1, 2), (1, 2), (1, 2); +insert t2 values (1, 2), (1, 2); +select * from t1 except all select * from t2 intersect all values (1, 2); +explain select * from t1 except all select * from t2 intersect all values (1, 2); +Explain select * from (select * from (select * from t1 except all select * from t2) __3 intersect all values (1,2)) __5; + +create view v1 as select * from t1 except all select * from t2 intersect all values (1, 2); +show create view v1; +select * from v1; +drop view v1; + +create view v1 as ((select * from t1 except all select * from t2) intersect all values (1, 2)); +show create view v1; +select * from v1; +drop view v1; + +explain select * from ((select * from t1 except all select * from t2) intersect all values (1, 2)) v; + +--echo # Oracle: first UNION, then INTERSECT ALL +set sql_mode= 'oracle'; +create view v2 as select * from t1 except all select * from t2 intersect all values (1, 2); +show create view v2; +select * from v2; +drop view v2; + +EXPLAIN select * from t1 except all select * from t2 intersect all values (1, 2); + +select * from t2 union all select * from t2; +select * from t1 except all select * from t2 intersect all values (1, 2) union values (2, 3); +select * from t2 union all select * from t2 except all select * from t2 intersect all values (1, 2) union values (2, 3); +select * from t1 except all select * from t2 union values (2, 3); +--echo # Default: first INTERSECT ALL, then UNION +select * from t1 union values (1, 2) intersect all select * from t2; +select * from t1 union (values (1, 2) intersect all select * from t2); +set sql_mode= default; +select * from t1 union values (1, 2) intersect all select * from t2; +select * from t1 union (values (1, 2) intersect all select * from t2); + +set sql_mode= 'oracle'; +create or replace table t2 like t1; +insert t2 values (1, 2), (2, 3); +create table t3 select * from t1 except all select * from t2; +select * from t3; +select * from t3 intersect all select * from t1; +show create table t2; +select * from t2 order by a desc; +select * from t1 except all select * from t2; +select * from t1 except all select * from t2 union all values (0, 1); +select * from t1 except all select * from t2 union all values (0, 1) order by a limit 2; +select * from t1 except all select * from t2 intersect all select * from t1; +select * from t1 except all select * from t2 intersect all select * from t1 union all select * from t2; +select * from t1 except all select * from t2 intersect all select * from t1 union all select * from t2 order by a desc limit 3; +drop tables t1, t2, t3; set sql_mode= default; diff --git a/mysql-test/main/set_operation_oracle.result b/mysql-test/main/set_operation_oracle.result index 947e5e9a11c..95cc7dda806 100644 --- a/mysql-test/main/set_operation_oracle.result +++ b/mysql-test/main/set_operation_oracle.result @@ -7,34 +7,44 @@ insert into t3 values (1,1),(3,3); set SQL_MODE=ORACLE; (select a,b from t1) union (select c,d from t2) intersect (select e,f from t3) union (select 4,4); a b -4 4 3 3 +4 4 explain extended (select a,b from t1) union (select c,d from t2) intersect (select e,f from t3) union (select 4,4); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00 -2 UNION t2 ALL NULL NULL NULL NULL 2 100.00 -3 INTERSECT t3 ALL NULL NULL NULL NULL 2 100.00 -4 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNIT RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +6 DERIVED ALL NULL NULL NULL NULL 2 100.00 +4 DERIVED ALL NULL NULL NULL NULL 4 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 2 100.00 +3 UNION t2 ALL NULL NULL NULL NULL 2 100.00 +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +5 INTERSECT t3 ALL NULL NULL NULL NULL 2 100.00 +NULL INTERSECT RESULT ALL NULL NULL NULL NULL NULL NULL +7 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 (/* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1") union (/* select#2 */ select "test"."t2"."c" AS "c","test"."t2"."d" AS "d" from "test"."t2") intersect (/* select#3 */ select "test"."t3"."e" AS "e","test"."t3"."f" AS "f" from "test"."t3") union (/* select#4 */ select 4 AS "4",4 AS "4") +Note 1003 /* select#1 */ select "__7"."a" AS "a","__7"."b" AS "b" from (/* select#6 */ select "__5"."a" AS "a","__5"."b" AS "b" from (/* select#4 */ select "__3"."a" AS "a","__3"."b" AS "b" from ((/* select#2 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1") union (/* select#3 */ select "test"."t2"."c" AS "c","test"."t2"."d" AS "d" from "test"."t2")) "__3" intersect (/* select#5 */ select "test"."t3"."e" AS "e","test"."t3"."f" AS "f" from "test"."t3")) "__5" union (/* select#7 */ select 4 AS "4",4 AS "4")) "__7" (select e,f from t3) intersect (select c,d from t2) union (select a,b from t1) union (select 4,4); e f -5 5 3 3 +5 5 6 6 4 4 explain extended (select e,f from t3) intersect (select c,d from t2) union (select a,b from t1) union (select 4,4); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t3 ALL NULL NULL NULL NULL 2 100.00 -2 INTERSECT t2 ALL NULL NULL NULL NULL 2 100.00 -3 UNION t1 ALL NULL NULL NULL NULL 2 100.00 -4 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNIT RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 +6 DERIVED ALL NULL NULL NULL NULL 4 100.00 +4 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t3 ALL NULL NULL NULL NULL 2 100.00 +3 INTERSECT t2 ALL NULL NULL NULL NULL 2 100.00 +NULL INTERSECT RESULT ALL NULL NULL NULL NULL NULL NULL +5 UNION t1 ALL NULL NULL NULL NULL 2 100.00 +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +7 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 (/* select#1 */ select "test"."t3"."e" AS "e","test"."t3"."f" AS "f" from "test"."t3") intersect (/* select#2 */ select "test"."t2"."c" AS "c","test"."t2"."d" AS "d" from "test"."t2") union (/* select#3 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1") union (/* select#4 */ select 4 AS "4",4 AS "4") +Note 1003 /* select#1 */ select "__7"."e" AS "e","__7"."f" AS "f" from (/* select#6 */ select "__5"."e" AS "e","__5"."f" AS "f" from (/* select#4 */ select "__3"."e" AS "e","__3"."f" AS "f" from ((/* select#2 */ select "test"."t3"."e" AS "e","test"."t3"."f" AS "f" from "test"."t3") intersect (/* select#3 */ select "test"."t2"."c" AS "c","test"."t2"."d" AS "d" from "test"."t2")) "__3" union (/* select#5 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1")) "__5" union (/* select#7 */ select 4 AS "4",4 AS "4")) "__7" create table t12(c1 int); insert into t12 values(1); insert into t12 values(2); @@ -61,18 +71,21 @@ insert into t3 values (1,1),(3,3); set SQL_MODE=ORACLE; select a,b from t1 union all select c,d from t2 intersect select e,f from t3 union all select 4,'4' from dual; a b -4 4 3 3 +4 4 explain extended select a,b from t1 union all select c,d from t2 intersect select e,f from t3 union all select 4,'4' from dual; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00 -2 UNION t2 ALL NULL NULL NULL NULL 2 100.00 -3 INTERSECT t3 ALL NULL NULL NULL NULL 2 100.00 -4 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNIT RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +6 DERIVED ALL NULL NULL NULL NULL 2 100.00 +4 DERIVED ALL NULL NULL NULL NULL 4 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 2 100.00 +3 UNION t2 ALL NULL NULL NULL NULL 2 100.00 +5 INTERSECT t3 ALL NULL NULL NULL NULL 2 100.00 +NULL INTERSECT RESULT ALL NULL NULL NULL NULL NULL NULL +7 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" union all /* select#2 */ select "test"."t2"."c" AS "c","test"."t2"."d" AS "d" from "test"."t2" intersect /* select#3 */ select "test"."t3"."e" AS "e","test"."t3"."f" AS "f" from "test"."t3" union all /* select#4 */ select 4 AS "4",'4' AS "4" +Note 1003 /* select#1 */ select "__7"."a" AS "a","__7"."b" AS "b" from (/* select#6 */ select "__5"."a" AS "a","__5"."b" AS "b" from (/* select#4 */ select "__3"."a" AS "a","__3"."b" AS "b" from (/* select#2 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" union all /* select#3 */ select "test"."t2"."c" AS "c","test"."t2"."d" AS "d" from "test"."t2") "__3" intersect /* select#5 */ select "test"."t3"."e" AS "e","test"."t3"."f" AS "f" from "test"."t3") "__5" union all /* select#7 */ select 4 AS "4",'4' AS "4") "__7" select a,b from t1 union all select c,d from t2 intersect all select e,f from t3 union all select 4,'4' from dual; a b 3 3 @@ -80,29 +93,35 @@ a b explain extended select a,b from t1 union all select c,d from t2 intersect all select e,f from t3 union all select 4,'4' from dual; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00 -2 UNION t2 ALL NULL NULL NULL NULL 2 100.00 -3 INTERSECT t3 ALL NULL NULL NULL NULL 2 100.00 -4 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNIT RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +6 DERIVED ALL NULL NULL NULL NULL 2 100.00 +4 DERIVED ALL NULL NULL NULL NULL 4 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 2 100.00 +3 UNION t2 ALL NULL NULL NULL NULL 2 100.00 +5 INTERSECT t3 ALL NULL NULL NULL NULL 2 100.00 +NULL INTERSECT RESULT ALL NULL NULL NULL NULL NULL NULL +7 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" union all /* select#2 */ select "test"."t2"."c" AS "c","test"."t2"."d" AS "d" from "test"."t2" intersect all /* select#3 */ select "test"."t3"."e" AS "e","test"."t3"."f" AS "f" from "test"."t3" union all /* select#4 */ select 4 AS "4",'4' AS "4" +Note 1003 /* select#1 */ select "__7"."a" AS "a","__7"."b" AS "b" from (/* select#6 */ select "__5"."a" AS "a","__5"."b" AS "b" from (/* select#4 */ select "__3"."a" AS "a","__3"."b" AS "b" from (/* select#2 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" union all /* select#3 */ select "test"."t2"."c" AS "c","test"."t2"."d" AS "d" from "test"."t2") "__3" intersect all /* select#5 */ select "test"."t3"."e" AS "e","test"."t3"."f" AS "f" from "test"."t3") "__5" union all /* select#7 */ select 4 AS "4",'4' AS "4") "__7" select e,f from t3 intersect select c,d from t2 union all select a,b from t1 union all select 4,'4' from dual; e f -5 5 3 3 +5 5 6 6 4 4 explain extended select e,f from t3 intersect select c,d from t2 union all select a,b from t1 union all select 4,'4' from dual; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t3 ALL NULL NULL NULL NULL 2 100.00 -2 INTERSECT t2 ALL NULL NULL NULL NULL 2 100.00 -3 UNION t1 ALL NULL NULL NULL NULL 2 100.00 -4 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNIT RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 +6 DERIVED ALL NULL NULL NULL NULL 4 100.00 +4 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t3 ALL NULL NULL NULL NULL 2 100.00 +3 INTERSECT t2 ALL NULL NULL NULL NULL 2 100.00 +NULL INTERSECT RESULT ALL NULL NULL NULL NULL NULL NULL +5 UNION t1 ALL NULL NULL NULL NULL 2 100.00 +7 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: -Note 1003 /* select#1 */ select "test"."t3"."e" AS "e","test"."t3"."f" AS "f" from "test"."t3" intersect /* select#2 */ select "test"."t2"."c" AS "c","test"."t2"."d" AS "d" from "test"."t2" union all /* select#3 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" union all /* select#4 */ select 4 AS "4",'4' AS "4" +Note 1003 /* select#1 */ select "__7"."e" AS "e","__7"."f" AS "f" from (/* select#6 */ select "__5"."e" AS "e","__5"."f" AS "f" from (/* select#4 */ select "__3"."e" AS "e","__3"."f" AS "f" from (/* select#2 */ select "test"."t3"."e" AS "e","test"."t3"."f" AS "f" from "test"."t3" intersect /* select#3 */ select "test"."t2"."c" AS "c","test"."t2"."d" AS "d" from "test"."t2") "__3" union all /* select#5 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1") "__5" union all /* select#7 */ select 4 AS "4",'4' AS "4") "__7" select e,f from t3 intersect all select c,d from t2 union all select a,b from t1 union all select 4,'4' from dual; e f 3 3 @@ -112,13 +131,16 @@ e f explain extended select e,f from t3 intersect all select c,d from t2 union all select a,b from t1 union all select 4,'4' from dual; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t3 ALL NULL NULL NULL NULL 2 100.00 -2 INTERSECT t2 ALL NULL NULL NULL NULL 2 100.00 -3 UNION t1 ALL NULL NULL NULL NULL 2 100.00 -4 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNIT RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 +6 DERIVED ALL NULL NULL NULL NULL 4 100.00 +4 DERIVED ALL NULL NULL NULL NULL 2 100.00 +2 DERIVED t3 ALL NULL NULL NULL NULL 2 100.00 +3 INTERSECT t2 ALL NULL NULL NULL NULL 2 100.00 +NULL INTERSECT RESULT ALL NULL NULL NULL NULL NULL NULL +5 UNION t1 ALL NULL NULL NULL NULL 2 100.00 +7 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: -Note 1003 /* select#1 */ select "test"."t3"."e" AS "e","test"."t3"."f" AS "f" from "test"."t3" intersect all /* select#2 */ select "test"."t2"."c" AS "c","test"."t2"."d" AS "d" from "test"."t2" union all /* select#3 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" union all /* select#4 */ select 4 AS "4",'4' AS "4" +Note 1003 /* select#1 */ select "__7"."e" AS "e","__7"."f" AS "f" from (/* select#6 */ select "__5"."e" AS "e","__5"."f" AS "f" from (/* select#4 */ select "__3"."e" AS "e","__3"."f" AS "f" from (/* select#2 */ select "test"."t3"."e" AS "e","test"."t3"."f" AS "f" from "test"."t3" intersect all /* select#3 */ select "test"."t2"."c" AS "c","test"."t2"."d" AS "d" from "test"."t2") "__3" union all /* select#5 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1") "__5" union all /* select#7 */ select 4 AS "4",'4' AS "4") "__7" set SQL_MODE=default; drop table t1,t2,t3; set SQL_MODE=oracle; diff --git a/mysql-test/suite/compat/oracle/r/empty_string_literal.result b/mysql-test/suite/compat/oracle/r/empty_string_literal.result index 1260c7aa75d..4ac87bf6f24 100644 --- a/mysql-test/suite/compat/oracle/r/empty_string_literal.result +++ b/mysql-test/suite/compat/oracle/r/empty_string_literal.result @@ -156,7 +156,7 @@ c1 c2 1 one SHOW CREATE VIEW v1; View Create View character_set_client collation_connection -v1 CREATE VIEW "v1" AS select "t1"."c1" AS "c1","t1"."c2" AS "c2" from "t1" union select "t1"."c1" AS "c1",NULL AS "NULL" from "t1" order by 1,2 cp1250 latin2_general_ci +v1 CREATE VIEW "v1" AS select "__4"."c1" AS "c1","__4"."c2" AS "c2" from (select "__5"."c1" AS "c1","__5"."c2" AS "c2" from (select "t1"."c1" AS "c1","t1"."c2" AS "c2" from "t1" union select "t1"."c1" AS "c1",NULL AS "NULL" from "t1") "__5") "__4" order by 1,2 cp1250 latin2_general_ci DROP VIEW v1; DROP TABLE t1; EXPLAIN EXTENDED SELECT ''; diff --git a/mysql-test/suite/compat/oracle/r/minus.result b/mysql-test/suite/compat/oracle/r/minus.result index 67b7e5347c3..9ce10820f90 100644 --- a/mysql-test/suite/compat/oracle/r/minus.result +++ b/mysql-test/suite/compat/oracle/r/minus.result @@ -37,9 +37,9 @@ c2 menny SELECT c2 FROM tx1 MINUS SELECT c2 from tx2 UNION SELECT c2 from tx3; c2 -jim menny linda +jim kris create table MINUS (a int); 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 'MINUS (a int)' at line 1 diff --git a/mysql-test/suite/compat/oracle/r/table_value_constr.result b/mysql-test/suite/compat/oracle/r/table_value_constr.result index 8b377a2e95b..439b44b1f64 100644 --- a/mysql-test/suite/compat/oracle/r/table_value_constr.result +++ b/mysql-test/suite/compat/oracle/r/table_value_constr.result @@ -247,16 +247,16 @@ except select 1,2 union values (1,2); 1 2 -1 2 3 4 +1 2 values (1,2),(3,4) except values (1,2) union values (1,2); 1 2 -1 2 3 4 +1 2 values (1,2),(3,4) except values (1,2) @@ -278,16 +278,16 @@ select 1,2 union all values (1,2); 1 2 -1 2 3 4 +1 2 values (1,2),(3,4) except values (1,2) union all values (1,2); 1 2 -1 2 3 4 +1 2 values (1,2),(3,4) except values (1,2) @@ -774,23 +774,24 @@ explain extended select * from t1 where a in (values (1) union select 2); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -4 DEPENDENT SUBQUERY ref key0 key0 4 func 2 100.00 +1 PRIMARY ref key0 key0 4 test.t1.a 1 100.00 FirstMatch(t1) 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where <"test"."t1"."a">(("test"."t1"."a",(/* select#4 */ select "tvc_0"."1" from (values (1)) "tvc_0" where ("test"."t1"."a") = "tvc_0"."1" union /* select#3 */ select 2 having ("test"."t1"."a") = (2)))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((values (1) union /* select#3 */ select 2 AS "2") "__4") where "__4"."1" = "test"."t1"."a" explain extended select * from t1 where a in (select * from (values (1)) as tvc_0 union select 2); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY ref key0 key0 4 func 2 100.00 +1 PRIMARY ref key0 key0 4 test.t1.a 1 100.00 FirstMatch(t1) +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -4 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +4 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where <"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select "tvc_0"."1" from (values (1)) "tvc_0" where ("test"."t1"."a") = "tvc_0"."1" union /* select#4 */ select 2 having ("test"."t1"."a") = (2)))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((/* select#2 */ select "tvc_0"."1" AS "1" from (values (1)) "tvc_0" union /* select#4 */ select 2 AS "2") "__5") where "__5"."1" = "test"."t1"."a" # IN-subquery with VALUES structure(s) : UNION with VALUES on the second place select * from t1 where a in (select 2 union values (1)); @@ -809,23 +810,24 @@ explain extended select * from t1 where a in (select 2 union values (1)); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -4 DEPENDENT UNION ref key0 key0 4 func 2 100.00 -3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ref key0 key0 4 test.t1.a 1 100.00 FirstMatch(t1) +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where <"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select 2 having ("test"."t1"."a") = (2) union /* select#4 */ select "tvc_0"."1" from (values (1)) "tvc_0" where ("test"."t1"."a") = "tvc_0"."1"))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((/* select#2 */ select 2 AS "2" union values (1)) "__4") where "__4"."2" = "test"."t1"."a" explain extended select * from t1 where a in (select 2 union select * from (values (1)) tvc_0); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 DEPENDENT UNION ref key0 key0 4 func 2 100.00 +1 PRIMARY ref key0 key0 4 test.t1.a 1 100.00 FirstMatch(t1) +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION ALL NULL NULL NULL NULL 2 100.00 4 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where <"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select 2 having ("test"."t1"."a") = (2) union /* select#3 */ select "tvc_0"."1" from (values (1)) "tvc_0" where ("test"."t1"."a") = "tvc_0"."1"))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((/* select#2 */ select 2 AS "2" union /* select#3 */ select "tvc_0"."1" AS "1" from (values (1)) "tvc_0") "__5") where "__5"."2" = "test"."t1"."a" # IN-subquery with VALUES structure(s) : UNION ALL select * from t1 where a in (values (1) union all select b from t1); @@ -846,21 +848,22 @@ explain extended select * from t1 where a in (values (1) union all select b from t1); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -4 DEPENDENT SUBQUERY ref key0 key0 4 func 2 100.00 +1 PRIMARY ref key0 key0 5 test.t1.a 2 100.00 FirstMatch(t1) 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 DEPENDENT UNION t1 ALL NULL NULL NULL NULL 6 100.00 Using where +3 UNION t1 ALL NULL NULL NULL NULL 6 100.00 Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where <"test"."t1"."a">(("test"."t1"."a",(/* select#4 */ select "tvc_0"."1" from (values (1)) "tvc_0" where ("test"."t1"."a") = "tvc_0"."1" union all /* select#3 */ select "test"."t1"."b" from "test"."t1" where ("test"."t1"."a") = "test"."t1"."b"))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((values (1) union all /* select#3 */ select "test"."t1"."b" AS "b" from "test"."t1") "__4") where "__4"."1" = "test"."t1"."a" explain extended select * from t1 where a in (select * from (values (1)) as tvc_0 union all select b from t1); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY ref key0 key0 4 func 2 100.00 +1 PRIMARY ref key0 key0 5 test.t1.a 2 100.00 FirstMatch(t1) +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -4 DEPENDENT UNION t1 ALL NULL NULL NULL NULL 6 100.00 Using where +4 UNION t1 ALL NULL NULL NULL NULL 6 100.00 Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where <"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select "tvc_0"."1" from (values (1)) "tvc_0" where ("test"."t1"."a") = "tvc_0"."1" union all /* select#4 */ select "test"."t1"."b" from "test"."t1" where ("test"."t1"."a") = "test"."t1"."b"))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((/* select#2 */ select "tvc_0"."1" AS "1" from (values (1)) "tvc_0" union all /* select#4 */ select "test"."t1"."b" AS "b" from "test"."t1") "__5") where "__5"."1" = "test"."t1"."a" # NOT IN subquery with VALUES structure(s) : simple case select * from t1 where a not in (values (1),(2)); @@ -908,23 +911,24 @@ explain extended select * from t1 where a not in (values (1) union select 2); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -4 DEPENDENT SUBQUERY ALL NULL NULL NULL NULL 2 100.00 Using where +4 DEPENDENT SUBQUERY index_subquery key0 key0 4 func 1 100.00 Using where; Full scan on NULL key 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where !<"test"."t1"."a">(("test"."t1"."a",(/* select#4 */ select "tvc_0"."1" from (values (1)) "tvc_0" where trigcond(("test"."t1"."a") = "tvc_0"."1") union /* select#3 */ select 2 having trigcond(("test"."t1"."a") = (2))))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where !<"test"."t1"."a">(("test"."t1"."a",((("test"."t1"."a") in (temporary) on key0 where trigcond(("test"."t1"."a") = "__4"."1"))))) explain extended select * from t1 where a not in (select * from (values (1)) as tvc_0 union select 2); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY ALL NULL NULL NULL NULL 2 100.00 Using where +5 DEPENDENT SUBQUERY index_subquery key0 key0 4 func 1 100.00 Using where; Full scan on NULL key +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -4 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +4 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where !<"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select "tvc_0"."1" from (values (1)) "tvc_0" where trigcond(("test"."t1"."a") = "tvc_0"."1") union /* select#4 */ select 2 having trigcond(("test"."t1"."a") = (2))))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where !<"test"."t1"."a">(("test"."t1"."a",((("test"."t1"."a") in (temporary) on key0 where trigcond(("test"."t1"."a") = "__5"."1"))))) # NOT IN subquery with VALUES structure(s) : UNION with VALUES on the second place select * from t1 where a not in (select 2 union values (1)); @@ -943,23 +947,24 @@ explain extended select * from t1 where a not in (select 2 union values (1)); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -4 DEPENDENT UNION ALL NULL NULL NULL NULL 2 100.00 Using where -3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +4 DEPENDENT SUBQUERY index_subquery key0 key0 4 func 1 100.00 Using where; Full scan on NULL key +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where !<"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select 2 having trigcond(("test"."t1"."a") = (2)) union /* select#4 */ select "tvc_0"."1" from (values (1)) "tvc_0" where trigcond(("test"."t1"."a") = "tvc_0"."1")))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where !<"test"."t1"."a">(("test"."t1"."a",((("test"."t1"."a") in (temporary) on key0 where trigcond(("test"."t1"."a") = "__4"."2"))))) explain extended select * from t1 where a not in (select 2 union select * from (values (1)) as tvc_0); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 DEPENDENT UNION ALL NULL NULL NULL NULL 2 100.00 Using where +5 DEPENDENT SUBQUERY index_subquery key0 key0 4 func 1 100.00 Using where; Full scan on NULL key +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION ALL NULL NULL NULL NULL 2 100.00 4 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where !<"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select 2 having trigcond(("test"."t1"."a") = (2)) union /* select#3 */ select "tvc_0"."1" from (values (1)) "tvc_0" where trigcond(("test"."t1"."a") = "tvc_0"."1")))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where !<"test"."t1"."a">(("test"."t1"."a",((("test"."t1"."a") in (temporary) on key0 where trigcond(("test"."t1"."a") = "__5"."2"))))) # ANY-subquery with VALUES structure(s) : simple case select * from t1 where a = any (values (1),(2)); @@ -1009,23 +1014,24 @@ explain extended select * from t1 where a = any (values (1) union select 2); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -4 DEPENDENT SUBQUERY ref key0 key0 4 func 2 100.00 +1 PRIMARY ref key0 key0 4 test.t1.a 1 100.00 FirstMatch(t1) 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where <"test"."t1"."a">(("test"."t1"."a",(/* select#4 */ select "tvc_0"."1" from (values (1)) "tvc_0" where ("test"."t1"."a") = "tvc_0"."1" union /* select#3 */ select 2 having ("test"."t1"."a") = (2)))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((values (1) union /* select#3 */ select 2 AS "2") "__4") where "__4"."1" = "test"."t1"."a" explain extended select * from t1 where a = any (select * from (values (1)) as tvc_0 union select 2); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY ref key0 key0 4 func 2 100.00 +1 PRIMARY ref key0 key0 4 test.t1.a 1 100.00 FirstMatch(t1) +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -4 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +4 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where <"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select "tvc_0"."1" from (values (1)) "tvc_0" where ("test"."t1"."a") = "tvc_0"."1" union /* select#4 */ select 2 having ("test"."t1"."a") = (2)))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((/* select#2 */ select "tvc_0"."1" AS "1" from (values (1)) "tvc_0" union /* select#4 */ select 2 AS "2") "__5") where "__5"."1" = "test"."t1"."a" # ANY-subquery with VALUES structure(s) : UNION with VALUES on the second place select * from t1 where a = any (select 2 union values (1)); @@ -1044,23 +1050,24 @@ explain extended select * from t1 where a = any (select 2 union values (1)); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -4 DEPENDENT UNION ref key0 key0 4 func 2 100.00 -3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ref key0 key0 4 test.t1.a 1 100.00 FirstMatch(t1) +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where <"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select 2 having ("test"."t1"."a") = (2) union /* select#4 */ select "tvc_0"."1" from (values (1)) "tvc_0" where ("test"."t1"."a") = "tvc_0"."1"))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((/* select#2 */ select 2 AS "2" union values (1)) "__4") where "__4"."2" = "test"."t1"."a" explain extended select * from t1 where a = any (select 2 union select * from (values (1)) as tvc_0); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 DEPENDENT UNION ref key0 key0 4 func 2 100.00 +1 PRIMARY ref key0 key0 4 test.t1.a 1 100.00 FirstMatch(t1) +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION ALL NULL NULL NULL NULL 2 100.00 4 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where <"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select 2 having ("test"."t1"."a") = (2) union /* select#3 */ select "tvc_0"."1" from (values (1)) "tvc_0" where ("test"."t1"."a") = "tvc_0"."1"))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((/* select#2 */ select 2 AS "2" union /* select#3 */ select "tvc_0"."1" AS "1" from (values (1)) "tvc_0") "__5") where "__5"."2" = "test"."t1"."a" # ALL-subquery with VALUES structure(s) : simple case select * from t1 where a = all (values (1)); @@ -1106,21 +1113,22 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where 4 DEPENDENT SUBQUERY ALL NULL NULL NULL NULL 2 100.00 Using where 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where (<"test"."t1"."a">(("test"."t1"."a",(/* select#4 */ select "tvc_0"."1" from (values (1)) "tvc_0" where trigcond(("test"."t1"."a") <> "tvc_0"."1") union /* select#3 */ select 1 having trigcond(("test"."t1"."a") <> (1)))))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where (<"test"."t1"."a">(("test"."t1"."a",(/* select#4 */ select "__4"."1" from (values (1) union /* select#3 */ select 1 AS "1") "__4" where trigcond(("test"."t1"."a") <> "__4"."1"))))) explain extended select * from t1 where a = all (select * from (values (1)) as tvc_0 union select 1); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY ALL NULL NULL NULL NULL 2 100.00 Using where +5 DEPENDENT SUBQUERY ALL NULL NULL NULL NULL 2 100.00 Using where +2 DERIVED ALL NULL NULL NULL NULL 2 100.00 3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -4 DEPENDENT UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +4 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where (<"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select "tvc_0"."1" from (values (1)) "tvc_0" where trigcond(("test"."t1"."a") <> "tvc_0"."1") union /* select#4 */ select 1 having trigcond(("test"."t1"."a") <> (1)))))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where (<"test"."t1"."a">(("test"."t1"."a",(/* select#5 */ select "__5"."1" from (/* select#2 */ select "tvc_0"."1" AS "1" from (values (1)) "tvc_0" union /* select#4 */ select 1 AS "1") "__5" where trigcond(("test"."t1"."a") <> "__5"."1"))))) # ALL-subquery with VALUES structure(s) : UNION with VALUES on the second place select * from t1 where a = any (select 1 union values (1)); @@ -1137,23 +1145,24 @@ explain extended select * from t1 where a = any (select 1 union values (1)); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -4 DEPENDENT UNION ref key0 key0 4 func 2 100.00 -3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ref key0 key0 4 test.t1.a 1 100.00 FirstMatch(t1) +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where <"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select 1 having ("test"."t1"."a") = (1) union /* select#4 */ select "tvc_0"."1" from (values (1)) "tvc_0" where ("test"."t1"."a") = "tvc_0"."1"))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((/* select#2 */ select 1 AS "1" union values (1)) "__4") where "__4"."1" = "test"."t1"."a" explain extended select * from t1 where a = any (select 1 union select * from (values (1)) as tvc_0); id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 6 100.00 Using where -2 DEPENDENT SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 DEPENDENT UNION ref key0 key0 4 func 2 100.00 +1 PRIMARY ref key0 key0 4 test.t1.a 1 100.00 FirstMatch(t1) +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION ALL NULL NULL NULL NULL 2 100.00 4 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" where <"test"."t1"."a">(("test"."t1"."a",(/* select#2 */ select 1 having ("test"."t1"."a") = (1) union /* select#3 */ select "tvc_0"."1" from (values (1)) "tvc_0" where ("test"."t1"."a") = "tvc_0"."1"))) +Note 1003 /* select#1 */ select "test"."t1"."a" AS "a","test"."t1"."b" AS "b" from "test"."t1" semi join ((/* select#2 */ select 1 AS "1" union /* select#3 */ select "tvc_0"."1" AS "1" from (values (1)) "tvc_0") "__5") where "__5"."1" = "test"."t1"."a" # prepare statement that uses VALUES structure(s): single VALUES structure prepare stmt1 from ' values (1,2); @@ -1321,132 +1330,183 @@ select 1,2 union values (1,2),(3,4); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL -explain -values (1,2),(3,4) -union -select 1,2; -id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL -explain -values (5,6) -union -values (1,2),(3,4); -id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL -explain format=json -select 1,2 -union -values (1,2),(3,4); -EXPLAIN -{ - "query_block": { - "union_result": { - "table_name": "", - "access_type": "ALL", - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" - } - } - } - ] - } - } -} -explain format=json -values (1,2),(3,4) -union -select 1,2; -EXPLAIN -{ - "query_block": { - "union_result": { - "table_name": "", - "access_type": "ALL", - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" - } - } - } - ] - } - } -} -explain format=json -values (5,6) -union -values (1,2),(3,4); -EXPLAIN -{ - "query_block": { - "union_result": { - "table_name": "", - "access_type": "ALL", - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" - } - } - } - ] - } - } -} -explain -select 1,2 -union -values (3,4) -union -values (1,2); -id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL No tables used +1 PRIMARY ALL NULL NULL NULL NULL 2 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used 3 UNION NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL +NULL UNION RESULT ALL NULL NULL NULL NULL NULL +explain +values (1,2),(3,4) +union +select 1,2; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY ALL NULL NULL NULL NULL 2 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL +explain +values (5,6) +union +values (1,2),(3,4); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY ALL NULL NULL NULL NULL 3 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL +explain format=json +select 1,2 +union +values (1,2),(3,4); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "rows": 2, + "filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "table_name": "", + "access_type": "ALL", + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } + } + } + } + } + ] + } +} +explain format=json +values (1,2),(3,4) +union +select 1,2; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "rows": 2, + "filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "table_name": "", + "access_type": "ALL", + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } + } + } + } + } + ] + } +} +explain format=json +values (5,6) +union +values (1,2),(3,4); +EXPLAIN +{ + "query_block": { + "select_id": 1, + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "rows": 3, + "filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "table_name": "", + "access_type": "ALL", + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } + } + } + } + } + ] + } +} +explain +select 1,2 +union +values (3,4) +union +values (1,2); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY ALL NULL NULL NULL NULL 3 +4 DERIVED ALL NULL NULL NULL NULL 2 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL +5 UNION NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL explain format=json select 1,2 union @@ -1456,38 +1516,78 @@ values (1,2); EXPLAIN { "query_block": { - "union_result": { - "table_name": "", - "access_type": "ALL", - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 3, - "operation": "UNION", - "table": { - "message": "No tables used" + "select_id": 1, + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "rows": 3, + "filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "table_name": "", + "access_type": "ALL", + "query_specifications": [ + { + "query_block": { + "select_id": 4, + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "rows": 2, + "filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "table_name": "", + "access_type": "ALL", + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } + } + } + } + } + ] + } + }, + { + "query_block": { + "select_id": 5, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } } } } - ] - } + } + ] } } # explain query that uses VALUES structure(s): UNION ALL with VALUES structure(s) @@ -1496,23 +1596,26 @@ select 1,2 union values (1,2),(3,4); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 2 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL explain values (1,2),(3,4) union all select 1,2; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL No tables used +1 PRIMARY ALL NULL NULL NULL NULL 2 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL No tables used explain values (1,2) union all values (1,2),(3,4); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL No tables used +1 PRIMARY ALL NULL NULL NULL NULL 3 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL No tables used explain format=json values (1,2),(3,4) union all @@ -1520,27 +1623,42 @@ select 1,2; EXPLAIN { "query_block": { - "union_result": { - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" + "select_id": 1, + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "rows": 2, + "filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } } } } - ] - } + } + ] } } explain format=json @@ -1550,29 +1668,44 @@ values (1,2),(3,4); EXPLAIN { "query_block": { - "union_result": { - "table_name": "", - "access_type": "ALL", - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" + "select_id": 1, + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "rows": 2, + "filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "table_name": "", + "access_type": "ALL", + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } } } } - ] - } + } + ] } } explain format=json @@ -1582,27 +1715,42 @@ values (1,2),(3,4); EXPLAIN { "query_block": { - "union_result": { - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" + "select_id": 1, + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "rows": 3, + "filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } } } } - ] - } + } + ] } } explain @@ -1612,9 +1760,11 @@ values (3,4) union all values (1,2); id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL No tables used +1 PRIMARY ALL NULL NULL NULL NULL 3 +4 DERIVED ALL NULL NULL NULL NULL 2 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL No tables used 3 UNION NULL NULL NULL NULL NULL NULL NULL No tables used +5 UNION NULL NULL NULL NULL NULL NULL NULL No tables used explain format=json select 1,2 union all @@ -1624,36 +1774,74 @@ values (1,2); EXPLAIN { "query_block": { - "union_result": { - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 3, - "operation": "UNION", - "table": { - "message": "No tables used" + "select_id": 1, + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "rows": 3, + "filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "query_specifications": [ + { + "query_block": { + "select_id": 4, + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "rows": 2, + "filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } + } + } + } + } + ] + } + }, + { + "query_block": { + "select_id": 5, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } } } } - ] - } + } + ] } } # analyze query that uses VALUES structure(s): single VALUES structure @@ -1689,147 +1877,219 @@ select 1,2 union values (1,2),(3,4); id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL 2.00 NULL NULL -analyze -values (1,2),(3,4) -union -select 1,2; -id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL 2.00 NULL NULL -analyze -values (5,6) -union -values (1,2),(3,4); -id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL 3.00 NULL NULL -analyze format=json -select 1,2 -union -values (1,2),(3,4); -ANALYZE -{ - "query_optimization": { - "r_total_time_ms": "REPLACED" - }, - "query_block": { - "union_result": { - "table_name": "", - "access_type": "ALL", - "r_loops": 1, - "r_rows": 2, - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" - } - } - } - ] - } - } -} -analyze format=json -values (1,2),(3,4) -union -select 1,2; -ANALYZE -{ - "query_optimization": { - "r_total_time_ms": "REPLACED" - }, - "query_block": { - "union_result": { - "table_name": "", - "access_type": "ALL", - "r_loops": 1, - "r_rows": 2, - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" - } - } - } - ] - } - } -} -analyze format=json -values (5,6) -union -values (1,2),(3,4); -ANALYZE -{ - "query_optimization": { - "r_total_time_ms": "REPLACED" - }, - "query_block": { - "union_result": { - "table_name": "", - "access_type": "ALL", - "r_loops": 1, - "r_rows": 3, - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" - } - } - } - ] - } - } -} -analyze -select 1,2 -union -values (3,4) -union -values (1,2); -id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +1 PRIMARY ALL NULL NULL NULL NULL 3 2.00 100.00 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used 3 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL 2.00 NULL NULL +NULL UNION RESULT ALL NULL NULL NULL NULL NULL 2.00 NULL NULL +analyze +values (1,2),(3,4) +union +select 1,2; +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 3 2.00 100.00 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL 2.00 NULL NULL +analyze +values (5,6) +union +values (1,2),(3,4); +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 3 3.00 100.00 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL 3.00 NULL NULL +analyze format=json +select 1,2 +union +values (1,2),(3,4); +ANALYZE +{ + "query_optimization": { + "r_total_time_ms": "REPLACED" + }, + "query_block": { + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "rows": 3, + "r_rows": 2, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "r_rows": 2, + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } + } + } + } + } + ] + } +} +analyze format=json +values (1,2),(3,4) +union +select 1,2; +ANALYZE +{ + "query_optimization": { + "r_total_time_ms": "REPLACED" + }, + "query_block": { + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "rows": 3, + "r_rows": 2, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "r_rows": 2, + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } + } + } + } + } + ] + } +} +analyze format=json +values (5,6) +union +values (1,2),(3,4); +ANALYZE +{ + "query_optimization": { + "r_total_time_ms": "REPLACED" + }, + "query_block": { + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "rows": 3, + "r_rows": 3, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "r_rows": 3, + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } + } + } + } + } + ] + } +} +analyze +select 1,2 +union +values (3,4) +union +values (1,2); +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 3 2.00 100.00 100.00 +4 DERIVED ALL NULL NULL NULL NULL 2 2.00 100.00 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL 2.00 NULL NULL +5 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL 2.00 NULL NULL analyze format=json select 1,2 union @@ -1842,40 +2102,96 @@ ANALYZE "r_total_time_ms": "REPLACED" }, "query_block": { - "union_result": { - "table_name": "", - "access_type": "ALL", - "r_loops": 1, - "r_rows": 2, - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 3, - "operation": "UNION", - "table": { - "message": "No tables used" + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "rows": 3, + "r_rows": 2, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "r_rows": 2, + "query_specifications": [ + { + "query_block": { + "select_id": 4, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "rows": 2, + "r_rows": 2, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "r_rows": 2, + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } + } + } + } + } + ] + } + }, + { + "query_block": { + "select_id": 5, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } } } } - ] - } + } + ] } } # analyze query that uses VALUES structure(s): UNION ALL with VALUES structure(s) @@ -1884,23 +2200,26 @@ select 1,2 union values (1,2),(3,4); id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL 2.00 NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 3 2.00 100.00 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL 2.00 NULL NULL analyze values (1,2),(3,4) union all select 1,2; id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +1 PRIMARY ALL NULL NULL NULL NULL 3 3.00 100.00 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used analyze values (1,2) union all values (1,2),(3,4); id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +1 PRIMARY ALL NULL NULL NULL NULL 3 3.00 100.00 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used analyze format=json values (1,2),(3,4) union all @@ -1911,27 +2230,49 @@ ANALYZE "r_total_time_ms": "REPLACED" }, "query_block": { - "union_result": { - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "rows": 3, + "r_rows": 3, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } } } } - ] - } + } + ] } } analyze format=json @@ -1944,31 +2285,53 @@ ANALYZE "r_total_time_ms": "REPLACED" }, "query_block": { - "union_result": { - "table_name": "", - "access_type": "ALL", - "r_loops": 1, - "r_rows": 2, - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "rows": 3, + "r_rows": 2, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "r_rows": 2, + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } } } } - ] - } + } + ] } } analyze format=json @@ -1981,27 +2344,49 @@ ANALYZE "r_total_time_ms": "REPLACED" }, "query_block": { - "union_result": { - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "rows": 3, + "r_rows": 3, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } } } } - ] - } + } + ] } } analyze @@ -2011,9 +2396,11 @@ values (3,4) union all values (1,2); id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +1 PRIMARY ALL NULL NULL NULL NULL 3 3.00 100.00 100.00 +4 DERIVED ALL NULL NULL NULL NULL 2 2.00 100.00 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used 3 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used +5 UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL No tables used analyze format=json select 1,2 union all @@ -2026,36 +2413,88 @@ ANALYZE "r_total_time_ms": "REPLACED" }, "query_block": { - "union_result": { - "query_specifications": [ - { - "query_block": { - "select_id": 1, - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 2, - "operation": "UNION", - "table": { - "message": "No tables used" - } - } - }, - { - "query_block": { - "select_id": 3, - "operation": "UNION", - "table": { - "message": "No tables used" + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "rows": 3, + "r_rows": 3, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "query_specifications": [ + { + "query_block": { + "select_id": 4, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "nested_loop": [ + { + "table": { + "table_name": "", + "access_type": "ALL", + "r_loops": 1, + "rows": 2, + "r_rows": 2, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100, + "materialized": { + "query_block": { + "union_result": { + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } + } + } + } + } + ] + } + }, + { + "query_block": { + "select_id": 5, + "operation": "UNION", + "table": { + "message": "No tables used" + } + } + } + ] + } } } } - ] - } + } + ] } } # different number of values in TVC @@ -2260,11 +2699,12 @@ select 2 union (values (5), (7), (1), (3), (4) limit 2); 7 explain extended select 2 union (values (5), (7), (1), (3), (4) limit 2); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select 2 AS "2" union (values (5),(7),(1),(3),(4) limit 2) +Note 1003 /* select#1 */ select "__3"."2" AS "2" from (/* select#2 */ select 2 AS "2" union (values (5),(7),(1),(3),(4) limit 2)) "__3" select 2 union (values (5), (7), (1), (3), (4) limit 2 offset 1); 2 2 @@ -2272,11 +2712,12 @@ select 2 union (values (5), (7), (1), (3), (4) limit 2 offset 1); 1 explain extended select 2 union (values (5), (7), (1), (3), (4) limit 2 offset 1); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select 2 AS "2" union (values (5),(7),(1),(3),(4) limit 1,2) +Note 1003 /* select#1 */ select "__3"."2" AS "2" from (/* select#2 */ select 2 AS "2" union (values (5),(7),(1),(3),(4) limit 1,2)) "__3" select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2); 2 2 @@ -2284,12 +2725,13 @@ select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2); 3 explain extended select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 UNION ALL NULL NULL NULL NULL 5 100.00 Using filesort +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +4 UNION ALL NULL NULL NULL NULL 5 100.00 Using filesort +3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select 2 AS "2" union (/* select#3 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 2) +Note 1003 /* select#1 */ select "__3"."2" AS "2" from (/* select#2 */ select 2 AS "2" union (/* select#4 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 2)) "__3" select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1); 2 2 @@ -2297,12 +2739,13 @@ select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1); 4 explain extended select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 UNION ALL NULL NULL NULL NULL 5 100.00 Using filesort +1 PRIMARY ALL NULL NULL NULL NULL 3 100.00 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +4 UNION ALL NULL NULL NULL NULL 5 100.00 Using filesort +3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select 2 AS "2" union (/* select#3 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 1,2) +Note 1003 /* select#1 */ select "__3"."2" AS "2" from (/* select#2 */ select 2 AS "2" union (/* select#4 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 1,2)) "__3" (values (5), (7), (1), (3), (4) limit 2) union select 2; 5 5 @@ -2310,11 +2753,12 @@ Note 1003 /* select#1 */ select 2 AS "2" union (/* select#3 */ select "tvc_0"."5 2 explain extended (values (5), (7), (1), (3), (4) limit 2) union select 2; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 (values (5),(7),(1),(3),(4) limit 2) union /* select#2 */ select 2 AS "2" +Note 1003 /* select#1 */ select "__3"."5" AS "5" from ((values (5),(7),(1),(3),(4) limit 2) union /* select#3 */ select 2 AS "2") "__3" (values (5), (7), (1), (3), (4) limit 2 offset 1) union select 2; 5 7 @@ -2322,11 +2766,12 @@ Note 1003 (values (5),(7),(1),(3),(4) limit 2) union /* select#2 */ select 2 AS 2 explain extended (values (5), (7), (1), (3), (4) limit 2 offset 1) union select 2; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 (values (5),(7),(1),(3),(4) limit 1,2) union /* select#2 */ select 2 AS "2" +Note 1003 /* select#1 */ select "__3"."5" AS "5" from ((values (5),(7),(1),(3),(4) limit 1,2) union /* select#3 */ select 2 AS "2") "__3" (values (5), (7), (1), (3), (4) order by 1 limit 2) union select 2; 5 1 @@ -2334,12 +2779,13 @@ Note 1003 (values (5),(7),(1),(3),(4) limit 1,2) union /* select#2 */ select 2 A 2 explain extended (values (5), (7), (1), (3), (4) order by 1 limit 2) union select 2; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 Using filesort -3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +4 DERIVED ALL NULL NULL NULL NULL 5 100.00 Using filesort +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 (/* select#1 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 2) union /* select#2 */ select 2 AS "2" +Note 1003 /* select#1 */ select "__3"."5" AS "5" from ((/* select#4 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 2) union /* select#3 */ select 2 AS "2") "__3" (values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1) union select 2; 5 3 @@ -2347,12 +2793,13 @@ Note 1003 (/* select#1 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3) 2 explain extended (values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1) union select 2; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 Using filesort -3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +1 PRIMARY ALL NULL NULL NULL NULL 3 100.00 +4 DERIVED ALL NULL NULL NULL NULL 5 100.00 Using filesort +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 (/* select#1 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 1,2) union /* select#2 */ select 2 AS "2" +Note 1003 /* select#1 */ select "__3"."5" AS "5" from ((/* select#4 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 1,2) union /* select#3 */ select 2 AS "2") "__3" select 3 union all (values (5), (7), (1), (3), (4) limit 2 offset 3); 3 3 @@ -2360,10 +2807,11 @@ select 3 union all (values (5), (7), (1), (3), (4) limit 2 offset 3); 4 explain extended select 3 union all (values (5), (7), (1), (3), (4) limit 2 offset 3); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: -Note 1003 /* select#1 */ select 3 AS "3" union all (values (5),(7),(1),(3),(4) limit 3,2) +Note 1003 /* select#1 */ select "__3"."3" AS "3" from (/* select#2 */ select 3 AS "3" union all (values (5),(7),(1),(3),(4) limit 3,2)) "__3" (values (5), (7), (1), (3), (4) limit 2 offset 3) union all select 3; 5 3 @@ -2371,10 +2819,11 @@ Note 1003 /* select#1 */ select 3 AS "3" union all (values (5),(7),(1),(3),(4) l 3 explain extended (values (5), (7), (1), (3), (4) limit 2 offset 3) union all select 3; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: -Note 1003 (values (5),(7),(1),(3),(4) limit 3,2) union all /* select#2 */ select 3 AS "3" +Note 1003 /* select#1 */ select "__3"."5" AS "5" from ((values (5),(7),(1),(3),(4) limit 3,2) union all /* select#3 */ select 3 AS "3") "__3" select 3 union all (values (5), (7), (1), (3), (4) order by 1 limit 2); 3 3 @@ -2382,12 +2831,13 @@ select 3 union all (values (5), (7), (1), (3), (4) order by 1 limit 2); 3 explain extended select 3 union all (values (5), (7), (1), (3), (4) order by 1 limit 2); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 UNION ALL NULL NULL NULL NULL 5 100.00 Using filesort +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +4 UNION ALL NULL NULL NULL NULL 5 100.00 Using filesort +3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 /* select#1 */ select 3 AS "3" union all (/* select#3 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 2) +Note 1003 /* select#1 */ select "__3"."3" AS "3" from (/* select#2 */ select 3 AS "3" union all (/* select#4 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 2)) "__3" (values (5), (7), (1), (3), (4) order by 1 limit 2) union all select 3; 5 1 @@ -2395,29 +2845,31 @@ Note 1003 /* select#1 */ select 3 AS "3" union all (/* select#3 */ select "tvc_0 3 explain extended (values (5), (7), (1), (3), (4) order by 1 limit 2) union all select 3; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 Using filesort +1 PRIMARY ALL NULL NULL NULL NULL 2 100.00 +4 DERIVED ALL NULL NULL NULL NULL 5 100.00 Using filesort +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 /* select#1 */ select "__3"."5" AS "5" from ((/* select#4 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 2) union all /* select#3 */ select 3 AS "3") "__3" +( values (5), (7), (1), (3), (4) limit 2 offset 1 ) +union +( values (5), (7), (1), (3), (4) order by 1 limit 2 ); +5 +7 +1 +3 +explain extended ( values (5), (7), (1), (3), (4) limit 2 offset 1 ) +union +( values (5), (7), (1), (3), (4) order by 1 limit 2 ); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 7 100.00 +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +4 UNION ALL NULL NULL NULL NULL 5 100.00 Using filesort 3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 (/* select#1 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 2) union all /* select#2 */ select 3 AS "3" -( values (5), (7), (1), (3), (4) limit 2 offset 1 ) -union -( values (5), (7), (1), (3), (4) order by 1 limit 2 ); -5 -7 -1 -3 -explain extended ( values (5), (7), (1), (3), (4) limit 2 offset 1 ) -union -( values (5), (7), (1), (3), (4) order by 1 limit 2 ); -id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 UNION ALL NULL NULL NULL NULL 5 100.00 Using filesort -2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL -Warnings: -Note 1003 (values (5),(7),(1),(3),(4) limit 1,2) union (/* select#3 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 2) +Note 1003 /* select#1 */ select "__3"."5" AS "5" from ((values (5),(7),(1),(3),(4) limit 1,2) union (/* select#4 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 2)) "__3" ( values (5), (7), (1), (3), (4) limit 2 offset 1 ) union all ( values (5), (7), (1), (3), (4) order by 1 limit 2 ); @@ -2430,12 +2882,13 @@ explain extended ( values (5), (7), (1), (3), (4) limit 2 offset 1 ) union all ( values (5), (7), (1), (3), (4) order by 1 limit 2 ); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -3 UNION ALL NULL NULL NULL NULL 5 100.00 Using filesort +1 PRIMARY ALL NULL NULL NULL NULL 7 100.00 2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +4 UNION ALL NULL NULL NULL NULL 5 100.00 Using filesort +3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 (values (5),(7),(1),(3),(4) limit 1,2) union all (/* select#3 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 2) +Note 1003 /* select#1 */ select "__3"."5" AS "5" from ((values (5),(7),(1),(3),(4) limit 1,2) union all (/* select#4 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 2)) "__3" (values (5), (7), (1), (3), (4) limit 2 offset 3) union all select 3 order by 1; 5 3 @@ -2443,11 +2896,11 @@ Note 1003 (values (5),(7),(1),(3),(4) limit 1,2) union all (/* select#3 */ selec 4 explain extended (values (5), (7), (1), (3), (4) limit 2 offset 3) union all select 3 order by 1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Using filesort +1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 Using filesort +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used Warnings: -Note 1003 (values (5),(7),(1),(3),(4) limit 3,2) union all /* select#2 */ select 3 AS "3" order by 1 +Note 1003 /* select#1 */ select "__3"."5" AS "5" from ((values (5),(7),(1),(3),(4) limit 3,2) union all /* select#3 */ select 3 AS "3") "__3" order by 1 (values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3 order by 1; 5 3 @@ -2456,12 +2909,13 @@ Note 1003 (values (5),(7),(1),(3),(4) limit 3,2) union all /* select#2 */ select 5 explain extended (values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3 order by 1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 Using filesort -3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Using filesort +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 Using filesort +4 DERIVED ALL NULL NULL NULL NULL 5 100.00 Using filesort +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 (/* select#1 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 1,3) union all /* select#2 */ select 3 AS "3" order by 1 +Note 1003 /* select#1 */ select "__3"."5" AS "5" from ((/* select#4 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 1,3) union all /* select#3 */ select 3 AS "3") "__3" order by 1 (values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3 order by 1 limit 2 offset 1; 5 @@ -2470,12 +2924,13 @@ order by 1 limit 2 offset 1; explain extended (values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3 order by 1 limit 2 offset 1; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 Using filesort -3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used -2 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used -NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Using filesort +1 PRIMARY ALL NULL NULL NULL NULL 4 100.00 Using filesort +4 DERIVED ALL NULL NULL NULL NULL 5 100.00 Using filesort +2 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL No tables used +3 UNION NULL NULL NULL NULL NULL NULL NULL NULL No tables used +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL Warnings: -Note 1003 (/* select#1 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 1,3) union all /* select#2 */ select 3 AS "3" order by 1 limit 1,2 +Note 1003 /* select#1 */ select "__3"."5" AS "5" from ((/* select#4 */ select "tvc_0"."5" AS "5" from (values (5),(7),(1),(3),(4)) "tvc_0" order by 1 limit 1,3) union all /* select#3 */ select 3 AS "3") "__3" order by 1 limit 1,2 values (5,90), (7,20), (1,70), (3,50), (4,10) order by 3; ERROR 42S22: Unknown column '3' in 'ORDER BY' create view v1 as values (5), (7), (1), (3), (4) order by 1 limit 2; @@ -2493,7 +2948,7 @@ union ( values (5), (7), (1), (3), (4) order by 1 limit 2 ); show create view v1; View Create View character_set_client collation_connection -v1 CREATE VIEW "v1" AS (values (5),(7),(1),(3),(4) limit 1,2) union (values (5),(7),(1),(3),(4) order by 1 limit 2) latin1 latin1_swedish_ci +v1 CREATE VIEW "v1" AS select "__4"."5" AS "5" from (select "__5"."5" AS "5" from ((values (5),(7),(1),(3),(4) limit 1,2) union (values (5),(7),(1),(3),(4) order by 1 limit 2)) "__5") "__4" latin1 latin1_swedish_ci select * from v1; 5 7 diff --git a/mysql-test/suite/compat/oracle/t/table_value_constr.test b/mysql-test/suite/compat/oracle/t/table_value_constr.test index ca3c40bb7f9..8e19a10d434 100644 --- a/mysql-test/suite/compat/oracle/t/table_value_constr.test +++ b/mysql-test/suite/compat/oracle/t/table_value_constr.test @@ -1171,21 +1171,25 @@ eval explain extended $q; let $q= select 2 union (values (5), (7), (1), (3), (4) limit 2); +--replace_result * 2 eval $q; eval explain extended $q; let $q= select 2 union (values (5), (7), (1), (3), (4) limit 2 offset 1); +--replace_result * 2 eval $q; eval explain extended $q; let $q= select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2); +--replace_result * 2 eval $q; eval explain extended $q; let $q= select 2 union (values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1); +--replace_result * 2 eval $q; eval explain extended $q; @@ -1193,6 +1197,7 @@ eval explain extended $q; let $q= (values (5), (7), (1), (3), (4) limit 2) union select 2; eval $q; +--replace_result derived4 derived2 eval explain extended $q; let $q= @@ -1203,16 +1208,19 @@ eval explain extended $q; let $q= (values (5), (7), (1), (3), (4) order by 1 limit 2) union select 2; eval $q; +--replace_result derived4 derived2 eval explain extended $q; let $q= (values (5), (7), (1), (3), (4) order by 1 limit 2 offset 1) union select 2; eval $q; +--replace_result derived4 derived2 eval explain extended $q; let $q= select 3 union all (values (5), (7), (1), (3), (4) limit 2 offset 3); +--replace_result * 3 eval $q; eval explain extended $q; @@ -1223,12 +1231,14 @@ eval explain extended $q; let $q= select 3 union all (values (5), (7), (1), (3), (4) order by 1 limit 2); +--replace_result * 3 eval $q; eval explain extended $q; let $q= (values (5), (7), (1), (3), (4) order by 1 limit 2) union all select 3; eval $q; +--replace_result derived4 derived2 eval explain extended $q; let $q= @@ -1253,12 +1263,14 @@ eval explain extended $q; let $q= (values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3 order by 1; eval $q; +--replace_result derived4 derived2 eval explain extended $q; let $q= (values (5), (7), (1), (3), (4) order by 1 limit 3 offset 1) union all select 3 order by 1 limit 2 offset 1; eval $q; +--replace_result derived4 derived2 eval explain extended $q; --error ER_BAD_FIELD_ERROR diff --git a/sql/sql_class.h b/sql/sql_class.h index 119a036fd98..42a8567ea5a 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -194,6 +194,8 @@ enum enum_binlog_row_image { #define MODE_TIME_ROUND_FRACTIONAL (1ULL << 34) /* The following modes are specific to MySQL */ #define MODE_MYSQL80_TIME_TRUNCATE_FRACTIONAL (1ULL << 32) +#define WAS_ORACLE (1ULL << 35) +#define IS_OR_WAS_ORACLE (MODE_ORACLE | WAS_ORACLE) /* Bits for different old style modes */ @@ -7833,7 +7835,7 @@ class Sql_mode_save Sql_mode_save(THD *thd) : thd(thd), old_mode(thd->variables.sql_mode) {} ~Sql_mode_save() { thd->variables.sql_mode = old_mode; } - private: + protected: THD *thd; sql_mode_t old_mode; // SQL mode saved at construction time. }; @@ -7850,6 +7852,9 @@ public: Sql_mode_save_for_frm_handling(THD *thd) :Sql_mode_save(thd) { + if (thd->variables.sql_mode & MODE_ORACLE) + thd->variables.sql_mode|= IS_OR_WAS_ORACLE; + /* - MODE_REAL_AS_FLOAT affect only CREATE TABLE parsing + MODE_PIPES_AS_CONCAT affect expression parsing @@ -7879,6 +7884,12 @@ public: MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES | MODE_ORACLE | MODE_EMPTY_STRING_IS_NULL); }; + + ~Sql_mode_save_for_frm_handling() + { + if (thd->variables.sql_mode & IS_OR_WAS_ORACLE) + thd->variables.sql_mode&= ~IS_OR_WAS_ORACLE; + } }; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index f2f1dca8de5..15391c37e6b 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -6303,6 +6303,7 @@ LEX::create_unit(SELECT_LEX *first_sel) DBUG_RETURN(NULL); unit->register_select_chain(first_sel); + /* TODO: Why this condition? Explain in comment */ if (first_sel->next_select()) { unit->reset_distinct(); @@ -6485,22 +6486,29 @@ Name_resolution_context *LEX::pop_context() } -SELECT_LEX *LEX::create_priority_nest(SELECT_LEX *first_in_nest) +SELECT_LEX *LEX::create_priority_nest(SELECT_LEX *first_in_nest, SELECT_LEX *attach_to) { DBUG_ENTER("LEX::create_priority_nest"); DBUG_ASSERT(first_in_nest->first_nested); enum sub_select_type wr_unit_type= first_in_nest->get_linkage(); bool wr_distinct= first_in_nest->distinct; - SELECT_LEX *attach_to= first_in_nest->first_nested; - attach_to->cut_next(); + if (attach_to) + attach_to->cut_next(); SELECT_LEX *wrapper= wrap_select_chain_into_derived(first_in_nest); if (wrapper) { first_in_nest->first_nested= NULL; wrapper->set_linkage_and_distinct(wr_unit_type, wr_distinct); - wrapper->first_nested= attach_to->first_nested; - wrapper->set_master_unit(attach_to->master_unit()); - attach_to->link_neighbour(wrapper); + if (attach_to) + { + wrapper->first_nested= attach_to->first_nested; + wrapper->set_master_unit(attach_to->master_unit()); + attach_to->link_neighbour(wrapper); + } + else + { + wrapper->first_nested= wrapper; + } } DBUG_RETURN(wrapper); } @@ -10412,11 +10420,30 @@ SELECT_LEX_UNIT *LEX::parsed_select_expr_start(SELECT_LEX *s1, SELECT_LEX *s2, sel1->link_neighbour(sel2); sel2->set_linkage_and_distinct(unit_type, distinct); sel2->first_nested= sel1->first_nested= sel1; + const bool oracle= (thd->variables.sql_mode & IS_OR_WAS_ORACLE); + if (oracle && + /* + Recursive CTE must have anchor defined as non-recursive set attached + via UNION, such anchor would be lost in wrapping. But in recursive CTE + all set operations either distinct or non-distinct + (see ER_NOT_SUPPORTED_YET limitation in st_select_lex_unit::prepare()), + so explicit prioritization via wrapping is not required. + + Test: compat/oracle.func_concat + */ + !(curr_with_clause && curr_with_clause->with_recursive) && + !(sel1= create_priority_nest(sel1, NULL))) + { + return NULL; + } res= create_unit(sel1); if (res == NULL) return NULL; res->pre_last_parse= sel1; - push_select(res->fake_select_lex); + if (oracle) + push_select(sel1); + else + push_select(res->fake_select_lex); return res; } @@ -10446,7 +10473,8 @@ SELECT_LEX_UNIT *LEX::parsed_select_expr_cont(SELECT_LEX_UNIT *unit, if (first_in_nest->first_nested != first_in_nest) { /* There is a priority jump starting from first_in_nest */ - if ((last= create_priority_nest(first_in_nest)) == NULL) + if ((last= create_priority_nest(first_in_nest, + first_in_nest->first_nested)) == NULL) return NULL; unit->fix_distinct(); } @@ -10496,7 +10524,7 @@ LEX::add_primary_to_query_expression_body(SELECT_LEX_UNIT *unit, { return add_primary_to_query_expression_body(unit, sel, unit_type, distinct, - thd->variables.sql_mode & MODE_ORACLE); + thd->variables.sql_mode & IS_OR_WAS_ORACLE); } /** @@ -10543,7 +10571,7 @@ bool LEX::parsed_multi_operand_query_expression_body(SELECT_LEX_UNIT *unit) if (first_in_nest->first_nested != first_in_nest) { /* There is a priority jump starting from first_in_nest */ - if (create_priority_nest(first_in_nest) == NULL) + if (create_priority_nest(first_in_nest, first_in_nest->first_nested) == NULL) return true; unit->fix_distinct(); } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 4055e074f1d..490ba9df6ce 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1683,7 +1683,8 @@ public: DBUG_ENTER("SELECT_LEX::set_linkage_and_distinct"); DBUG_PRINT("info", ("select: %p distinct %d", this, d)); set_linkage(l); - DBUG_ASSERT(l == UNION_TYPE || + DBUG_ASSERT(l == UNSPECIFIED_TYPE || + l == UNION_TYPE || l == INTERSECT_TYPE || l == EXCEPT_TYPE); if (d && master_unit() && master_unit()->union_distinct != this) @@ -3794,6 +3795,7 @@ public: select_stack_head()->select_number : 0), select_lex, select_lex->select_number)); + DBUG_ASSERT(select_lex); if (unlikely(select_stack_top > MAX_SELECT_NESTING)) { my_error(ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT, MYF(0)); @@ -4785,7 +4787,7 @@ public: } bool main_select_push(bool service= false); bool insert_select_hack(SELECT_LEX *sel); - SELECT_LEX *create_priority_nest(SELECT_LEX *first_in_nest); + SELECT_LEX *create_priority_nest(SELECT_LEX *first_in_nest, SELECT_LEX *attach_to); bool set_main_unit(st_select_lex_unit *u) { diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 1a5c729859e..e0503f9b0a8 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -479,8 +479,8 @@ int select_unit::update_counter(Field* counter, longlong value) */ bool select_unit_ext::disable_index_if_needed(SELECT_LEX *curr_sl) -{ - const bool oracle_mode= thd->variables.sql_mode & MODE_ORACLE; +{ + const bool oracle_mode= (thd->variables.sql_mode & IS_OR_WAS_ORACLE); if (is_index_enabled && ((!oracle_mode && curr_sl == curr_sl->master_unit()->union_distinct) || @@ -497,7 +497,7 @@ bool select_unit_ext::disable_index_if_needed(SELECT_LEX *curr_sl) table->no_keyread=1; /* In case of Oracle mode we unfold at the last operator */ DBUG_ASSERT(!oracle_mode || !curr_sl->next_select()); - return oracle_mode || !curr_sl->distinct; + return !curr_sl->distinct; } return false; } @@ -1928,7 +1928,7 @@ void st_select_lex_unit::optimize_bag_operation(bool is_outer_distinct) PREPARE ... FROM recursive */ - if ((thd->variables.sql_mode & MODE_ORACLE) || + if ((thd->variables.sql_mode & IS_OR_WAS_ORACLE) || (thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) || (fake_select_lex != NULL && thd->stmt_arena->is_stmt_prepare()) || (with_element && with_element->is_recursive )) From ca63e2c6276e5ea121d456524446f4f22b5b7e56 Mon Sep 17 00:00:00 2001 From: Rucha Deodhar Date: Thu, 4 Dec 2025 18:35:50 +0530 Subject: [PATCH 29/43] MDEV-37950: INSERT ... RETURNING exposes columns for which the user lacks SELECT privilege Analysis: When setup_fields() is called, the want_privilege is set to NO_ACL, so correct priveleges are not checked. Fix: Since RETURNING requires SELECT_ACL privelige, when we are setting up the returning fields for the given query, set want_privilege to SELECT_ACL. Reset to original value of want_privilege once done. --- mysql-test/main/insert_notembedded.result | 43 +++++++++++++++++ mysql-test/main/insert_notembedded.test | 57 +++++++++++++++++++++++ sql/sql_base.cc | 19 ++++++-- 3 files changed, 115 insertions(+), 4 deletions(-) diff --git a/mysql-test/main/insert_notembedded.result b/mysql-test/main/insert_notembedded.result index 5df69cc8ffe..6c5e8057500 100644 --- a/mysql-test/main/insert_notembedded.result +++ b/mysql-test/main/insert_notembedded.result @@ -121,3 +121,46 @@ connection default; DROP DATABASE meow; set local sql_mode=default; set global sql_mode=default; +# +# MDEV-37950: INSERT ... RETURNING exposes columns for which +# the user lacks SELECT privilege +# +CREATE USER regular; +GRANT INSERT ON *.* TO regular; +GRANT DELETE ON *.* TO regular; +CREATE DATABASE test1; +DROP TABLE IF EXISTS test1.t_trigger_test1; +Warnings: +Note 1051 Unknown table 'test1.t_trigger_test1' +CREATE TABLE test1.t_trigger_test1 ( +id INT AUTO_INCREMENT PRIMARY KEY, +name VARCHAR(50), +note VARCHAR(100) +); +CREATE TRIGGER test1.trg_before_insert +BEFORE INSERT ON test1.t_trigger_test1 +FOR EACH ROW +BEGIN +SET NEW.name = CONCAT('BEFORE_', NEW.name); +END | +INSERT INTO test1.t_trigger_test1 (name) VALUES ('Alice') RETURNING *; +ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1' +INSERT INTO test1.t_trigger_test1 (name) VALUES ('Alice') RETURNING id, name, note; +ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1' +# same for DELETE because delete with "WHERE" still requires to +# read from the table, which basically means having select privileges +DELETE FROM test1.t_trigger_test1 WHERE id=1; +ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1' +DELETE FROM test1.t_trigger_test1 WHERE id=1 RETURNING id; +ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1' +DELETE FROM test1.t_trigger_test1 WHERE id=1 RETURNING *; +ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1' +DELETE FROM test1.t_trigger_test1 RETURNING *; +ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1' +DELETE FROM test1.t_trigger_test1 RETURNING id; +ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1' +DELETE FROM test1.t_trigger_test1; +DROP TRIGGER test1.trg_before_insert; +DROP TABLE test1.t_trigger_test1; +DROP USER regular; +DROP DATABASE test1; diff --git a/mysql-test/main/insert_notembedded.test b/mysql-test/main/insert_notembedded.test index 2769aee8d8a..09f5b18531d 100644 --- a/mysql-test/main/insert_notembedded.test +++ b/mysql-test/main/insert_notembedded.test @@ -158,3 +158,60 @@ DROP DATABASE meow; set local sql_mode=default; set global sql_mode=default; + +--echo # +--echo # MDEV-37950: INSERT ... RETURNING exposes columns for which +--echo # the user lacks SELECT privilege +--echo # +CREATE USER regular; +GRANT INSERT ON *.* TO regular; +GRANT DELETE ON *.* TO regular; + +CREATE DATABASE test1; +DROP TABLE IF EXISTS test1.t_trigger_test1; +CREATE TABLE test1.t_trigger_test1 ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(50), + note VARCHAR(100) +); +DELIMITER |; +CREATE TRIGGER test1.trg_before_insert +BEFORE INSERT ON test1.t_trigger_test1 +FOR EACH ROW +BEGIN + SET NEW.name = CONCAT('BEFORE_', NEW.name); +END | +DELIMITER ;| + +change_user regular; + +--error ER_COLUMNACCESS_DENIED_ERROR +INSERT INTO test1.t_trigger_test1 (name) VALUES ('Alice') RETURNING *; +--error ER_COLUMNACCESS_DENIED_ERROR +INSERT INTO test1.t_trigger_test1 (name) VALUES ('Alice') RETURNING id, name, note; + +--echo # same for DELETE because delete with "WHERE" still requires to +--echo # read from the table, which basically means having select privileges + +--error ER_COLUMNACCESS_DENIED_ERROR +DELETE FROM test1.t_trigger_test1 WHERE id=1; + +--error ER_COLUMNACCESS_DENIED_ERROR +DELETE FROM test1.t_trigger_test1 WHERE id=1 RETURNING id; + +--error ER_COLUMNACCESS_DENIED_ERROR +DELETE FROM test1.t_trigger_test1 WHERE id=1 RETURNING *; + +--error ER_COLUMNACCESS_DENIED_ERROR +DELETE FROM test1.t_trigger_test1 RETURNING *; + +--error ER_COLUMNACCESS_DENIED_ERROR +DELETE FROM test1.t_trigger_test1 RETURNING id; + +DELETE FROM test1.t_trigger_test1; + +change_user root; +DROP TRIGGER test1.trg_before_insert; +DROP TABLE test1.t_trigger_test1; +DROP USER regular; +DROP DATABASE test1; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index aeb829c60e5..00a2fb90c58 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7909,12 +7909,23 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array, int setup_returning_fields(THD* thd, TABLE_LIST* table_list) { + GRANT_INFO *saved_grant; + int res= 0; + if (!thd->lex->has_returning()) return 0; - return setup_wild(thd, table_list, thd->lex->returning()->item_list, NULL, - thd->lex->returning(), true) - || setup_fields(thd, Ref_ptr_array(), thd->lex->returning()->item_list, - MARK_COLUMNS_READ, NULL, NULL, 0, THD_WHERE::RETURNING); + + saved_grant= &table_list->table->grant; + table_list->table->grant.want_privilege|= SELECT_ACL; + + res= setup_wild(thd, table_list, thd->lex->returning()->item_list, NULL, + thd->lex->returning(), true) + || setup_fields(thd, Ref_ptr_array(), thd->lex->returning()->item_list, + MARK_COLUMNS_READ, NULL, NULL, 0, THD_WHERE::RETURNING); + + table_list->table->grant= *saved_grant; + + return res; } From 0cd55ca5a575f41c84959067d84162608d268794 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 3 Dec 2025 17:10:26 +0100 Subject: [PATCH 30/43] MDEV-38242 crash when first COM_STMT_EXECUTE doesn't send types all params are initialized to use Type_handler_null. NULL params do not have a value to read from the packet buffer, so their Item_param_set_param_func() should do nothing. Type_handler_null inherits from Type_handler_string_result, that is, Type_handler_null *must* override parent's Item_param_set_param_func(). --- sql/sql_type.cc | 6 ++++++ sql/sql_type.h | 2 ++ tests/mysql_client_test.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/sql/sql_type.cc b/sql/sql_type.cc index 49fd38ec33f..0783e25d0e2 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -8080,6 +8080,12 @@ void Type_handler_string_result::Item_param_set_param_func(Item_param *param, } +void Type_handler_null::Item_param_set_param_func(Item_param *param, + uchar **pos, ulong len) const +{ +} + + void Type_handler_time_common::Item_param_set_param_func(Item_param *param, uchar **pos, ulong len) const diff --git a/sql/sql_type.h b/sql/sql_type.h index f334bc02534..e9ba2d04261 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -6998,6 +6998,8 @@ public: const Bit_addr &bit, const Column_definition_attributes *attr, uint32 flags) const override; + void Item_param_set_param_func(Item_param *param, + uchar **pos, ulong len) const override; }; diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 48f8410e1e8..dc6b8308b5b 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -22956,6 +22956,39 @@ static void test_cache_metadata() mysql_stmt_close(stmt); } +static void test_mdev_38242() +{ + MYSQL_STMT *stmt; + int rc; + MYSQL_BIND param[2]; + int val1=117440769, val2; + my_bool null1=0, null2=1; + + myheader("test_mdev_38242"); + + stmt= mysql_stmt_init(mysql); + check_stmt(stmt); + + rc= mysql_stmt_prepare(stmt, STRING_WITH_LEN("SELECT 1 WHERE ?=0 AND ?=0")); + check_execute(stmt, rc); + + bzero(param, sizeof(param)); + + param[0].buffer= &val1; + param[0].buffer_type= MYSQL_TYPE_LONG; + param[0].is_null= &null1; + param[1].buffer= &val2; + param[1].buffer_type= MYSQL_TYPE_LONG; + param[1].is_null= &null2; + + rc= mysql_stmt_bind_param(stmt, param); + check_execute(stmt, rc); + + stmt->send_types_to_server= 0; + rc= mysql_stmt_execute(stmt); + check_execute_r(stmt, rc); + mysql_stmt_close(stmt); +} static struct my_tests_st my_tests[]= { { "test_mdev_20516", test_mdev_20516 }, @@ -23266,6 +23299,7 @@ static struct my_tests_st my_tests[]= { { "test_connect_autocommit", test_connect_autocommit}, { "test_execute_direct", test_execute_direct }, { "test_cache_metadata", test_cache_metadata}, + { "test_mdev_38242", test_mdev_38242 }, #ifndef EMBEDDED_LIBRARY { "test_mdev_24411", test_mdev_24411}, { "test_mdev_34718_bu", test_mdev_34718_bu }, From 64c9efbfa5748b6ff419ebd00e60decc6bdf9639 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 18 Dec 2025 11:01:29 +0100 Subject: [PATCH 31/43] MDEV-38368 Spider crash on fake loop detection variables use my_safe_alloca() when the size might be large --- .../spider/include/have_group_by_handler.inc | 1 + .../spider/include/have_group_by_handler.opt | 1 + .../spider/r/loop_check_long_var.result | 28 +++++++++++++++ .../spider/t/loop_check_long_var.test | 36 +++++++++++++++++++ storage/spider/spd_conn.cc | 14 ++++---- 5 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 storage/spider/mysql-test/spider/include/have_group_by_handler.inc create mode 100644 storage/spider/mysql-test/spider/include/have_group_by_handler.opt create mode 100644 storage/spider/mysql-test/spider/r/loop_check_long_var.result create mode 100644 storage/spider/mysql-test/spider/t/loop_check_long_var.test diff --git a/storage/spider/mysql-test/spider/include/have_group_by_handler.inc b/storage/spider/mysql-test/spider/include/have_group_by_handler.inc new file mode 100644 index 00000000000..b9e467a836f --- /dev/null +++ b/storage/spider/mysql-test/spider/include/have_group_by_handler.inc @@ -0,0 +1 @@ +# see .opt file diff --git a/storage/spider/mysql-test/spider/include/have_group_by_handler.opt b/storage/spider/mysql-test/spider/include/have_group_by_handler.opt new file mode 100644 index 00000000000..fdce7f08d6a --- /dev/null +++ b/storage/spider/mysql-test/spider/include/have_group_by_handler.opt @@ -0,0 +1 @@ +--loose_spider_disable_group_by_handler=0 diff --git a/storage/spider/mysql-test/spider/r/loop_check_long_var.result b/storage/spider/mysql-test/spider/r/loop_check_long_var.result new file mode 100644 index 00000000000..3fc277bc0bc --- /dev/null +++ b/storage/spider/mysql-test/spider/r/loop_check_long_var.result @@ -0,0 +1,28 @@ +install plugin spider soname 'ha_spider'; +# +# MDEV-38368 Spider crash on fake loop detection variables +# +set global spider_same_server_link=1; +create server srv1 +foreign data wrapper mysql +options ( +host 'localhost', +user 'root', +socket '$MASTER_1_MYSOCK' + ); +create table t1 (id int primary key, data varchar(255)); +insert into t1 values (1, 'test'), (2, 'data'); +create table st1 (id int primary key, data varchar(255)) engine=spider +comment='wrapper "mysql", srv "srv1", table "t1"'; +set @`spider_lc_./test/st1` = concat('-', repeat('x', 900000), '-pid-tbl-'); +select * from st1 limit 1; +id data +1 test +drop table st1, t1; +drop server srv1; +uninstall plugin spider; +Warnings: +Warning 1620 Plugin is busy and will be uninstalled on shutdown +drop tables mysql.spider_link_failed_log, mysql.spider_link_mon_servers, mysql.spider_tables, mysql.spider_table_crd, mysql.spider_table_position_for_recovery, mysql.spider_table_sts, mysql.spider_xa, mysql.spider_xa_failed_log, mysql.spider_xa_member; +truncate mysql.func; +# End of 10.6 tests diff --git a/storage/spider/mysql-test/spider/t/loop_check_long_var.test b/storage/spider/mysql-test/spider/t/loop_check_long_var.test new file mode 100644 index 00000000000..426c52243fb --- /dev/null +++ b/storage/spider/mysql-test/spider/t/loop_check_long_var.test @@ -0,0 +1,36 @@ +# group by hander doesn't matter here, let's not run this test twice +source include/have_group_by_handler.inc; + +install plugin spider soname 'ha_spider'; + +--echo # +--echo # MDEV-38368 Spider crash on fake loop detection variables +--echo # + +set global spider_same_server_link=1; + +evalp create server srv1 + foreign data wrapper mysql + options ( + host 'localhost', + user 'root', + socket '$MASTER_1_MYSOCK' + ); + +create table t1 (id int primary key, data varchar(255)); +insert into t1 values (1, 'test'), (2, 'data'); + +create table st1 (id int primary key, data varchar(255)) engine=spider +comment='wrapper "mysql", srv "srv1", table "t1"'; + +set @`spider_lc_./test/st1` = concat('-', repeat('x', 900000), '-pid-tbl-'); +select * from st1 limit 1; + +drop table st1, t1; +drop server srv1; + +uninstall plugin spider; +drop tables mysql.spider_link_failed_log, mysql.spider_link_mon_servers, mysql.spider_tables, mysql.spider_table_crd, mysql.spider_table_position_for_recovery, mysql.spider_table_sts, mysql.spider_xa, mysql.spider_xa_failed_log, mysql.spider_xa_member; +truncate mysql.func; + +--echo # End of 10.6 tests diff --git a/storage/spider/spd_conn.cc b/storage/spider/spd_conn.cc index 951fb497a9a..631e6c3f4a9 100644 --- a/storage/spider/spd_conn.cc +++ b/storage/spider/spd_conn.cc @@ -1242,7 +1242,7 @@ int spider_conn_queue_loop_check( */ lex_str.length = top_share->path.length + SPIDER_SQL_LOP_CHK_PRM_PRF_LEN; buf_sz = lex_str.length + 2; - loop_check_buf = (char *) my_alloca(buf_sz); + loop_check_buf = (char *) my_safe_alloca(buf_sz); if (unlikely(!loop_check_buf)) { DBUG_RETURN(HA_ERR_OUT_OF_MEM); @@ -1309,7 +1309,7 @@ int spider_conn_queue_loop_check( from_str.length = tmp_name - lex_str.str + 1; } } - my_afree(loop_check_buf); + my_safe_afree(loop_check_buf, buf_sz); /* construct loop_check_buf as -- e.g. "---./test/t0--./test/t1-./test/t2", later used as @@ -1325,7 +1325,7 @@ int spider_conn_queue_loop_check( to_str.str = path; DBUG_PRINT("info", ("spider to=%s", to_str.str)); buf_sz = from_str.length + top_share->path.length + to_str.length + 3; - loop_check_buf = (char *) my_alloca(buf_sz); + loop_check_buf = (char *) my_safe_alloca(buf_sz); if (unlikely(!loop_check_buf)) { DBUG_RETURN(HA_ERR_OUT_OF_MEM); @@ -1376,7 +1376,7 @@ int spider_conn_queue_loop_check( lex_str.length + 2), NullS) )) { - my_afree(loop_check_buf); + my_safe_afree(loop_check_buf, buf_sz); goto error_alloc_loop_check; } lcptr->flag = 0; @@ -1401,7 +1401,7 @@ int spider_conn_queue_loop_check( (uchar *) to_str.str, to_str.length); if (unlikely(my_hash_insert(&conn->loop_checked, (uchar *) lcptr))) { - my_afree(loop_check_buf); + my_safe_afree(loop_check_buf, buf_sz); goto error_hash_insert; } } else { @@ -1412,11 +1412,11 @@ int spider_conn_queue_loop_check( lcptr->flag |= SPIDER_LOP_CHK_IGNORED; } pthread_mutex_unlock(&conn->loop_check_mutex); - my_afree(loop_check_buf); + my_safe_afree(loop_check_buf, buf_sz); DBUG_PRINT("info", ("spider be sent or queued already")); DBUG_RETURN(0); } - my_afree(loop_check_buf); + my_safe_afree(loop_check_buf, buf_sz); if ((error_num = spider_conn_queue_and_merge_loop_check(conn, lcptr))) { From 9fc46b885763a87d0e33264e0c2e6d12f81e7f3f Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 23 Dec 2025 01:26:19 +0400 Subject: [PATCH 32/43] MDEV-38372 ASAN error in InnoDB on malformed WKB Geometry::is_binary_valid() method added, and used to check WKB before storing in the engine. --- mysql-test/suite/funcs_1/r/storedproc.result | 6 ++---- mysql-test/suite/innodb_gis/r/0.result | 12 +++++++++++ mysql-test/suite/innodb_gis/t/0.test | 13 ++++++++++++ sql/spatial.h | 13 ++++++++++++ sql/sql_type_geom.cc | 21 ++++++++++---------- 5 files changed, 50 insertions(+), 15 deletions(-) diff --git a/mysql-test/suite/funcs_1/r/storedproc.result b/mysql-test/suite/funcs_1/r/storedproc.result index 45bcc882f3c..8b1cb5e8b47 100644 --- a/mysql-test/suite/funcs_1/r/storedproc.result +++ b/mysql-test/suite/funcs_1/r/storedproc.result @@ -17283,8 +17283,7 @@ set f1 = f1; return f1; END// SELECT fn104('\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0?\0\0\0\0\0\0?\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0@\0\0\0\0\0\0@'); -fn104('\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0?\0\0\0\0\0\0?\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0@\0\0\0\0\0\0@') -??@@@@ +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field DROP FUNCTION IF EXISTS fn105; CREATE FUNCTION fn105( f1 point) returns point BEGIN @@ -18418,8 +18417,7 @@ set f1 = f1; SELECT f1; END// CALL sp104('\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0?\0\0\0\0\0\0?\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0@\0\0\0\0\0\0@'); -f1 -??@@@@ +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field DROP PROCEDURE IF EXISTS sp105; CREATE PROCEDURE sp105( f1 point) BEGIN diff --git a/mysql-test/suite/innodb_gis/r/0.result b/mysql-test/suite/innodb_gis/r/0.result index c9864fad830..9d4773b0b9b 100644 --- a/mysql-test/suite/innodb_gis/r/0.result +++ b/mysql-test/suite/innodb_gis/r/0.result @@ -617,3 +617,15 @@ commit; disconnect a; connection default; drop table t1; +# +# MDEV-38372 ASAN error in InnoDB on malformed WKB +# +create table t1 ( +id int auto_increment primary key, +geom geometry not null, +spatial index (geom) +) engine=innodb; +insert t1 (geom) values (X'000000000107000000FF000000'); +ERROR 22003: Cannot get geometry object from data you send to the GEOMETRY field +drop table t1; +# End of 10.6 tests diff --git a/mysql-test/suite/innodb_gis/t/0.test b/mysql-test/suite/innodb_gis/t/0.test index c482d881732..39feb8eb087 100644 --- a/mysql-test/suite/innodb_gis/t/0.test +++ b/mysql-test/suite/innodb_gis/t/0.test @@ -40,5 +40,18 @@ disconnect a; connection default; drop table t1; +--echo # +--echo # MDEV-38372 ASAN error in InnoDB on malformed WKB +--echo # +create table t1 ( + id int auto_increment primary key, + geom geometry not null, + spatial index (geom) +) engine=innodb; +--error ER_CANT_CREATE_GEOMETRY_OBJECT +insert t1 (geom) values (X'000000000107000000FF000000'); +drop table t1; + +--echo # End of 10.6 tests diff --git a/sql/spatial.h b/sql/spatial.h index d775e14aea4..c215003b321 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -269,6 +269,19 @@ public: virtual const Class_info *get_class_info() const=0; virtual uint32 get_data_size() const=0; + + /* + The 'binary_valid' spatial object can be stored in the database + and be the correct argument to the spatial functions. + It can still be not valid by the OPENGIS standard like + having self-intersecting borders. + */ + bool is_binary_valid() const + { + uint32 data_size= get_data_size(); + + return (data_size == GET_SIZE_ERROR) ? false : !no_data(m_data, data_size); + } virtual bool init_from_wkt(Gis_read_stream *trs, String *wkb)=0; /* returns the length of the wkb that was read */ virtual uint init_from_wkb(const char *wkb, uint len, wkbByteOrder bo, diff --git a/sql/sql_type_geom.cc b/sql/sql_type_geom.cc index 74d17fd21eb..45a7e752c85 100644 --- a/sql/sql_type_geom.cc +++ b/sql/sql_type_geom.cc @@ -854,6 +854,12 @@ int Field_geom::store(const char *from, size_t length, CHARSET_INFO *cs) wkb_type > (uint32) Geometry::wkb_last) goto err; + const char *dummy; + Geometry_buffer buffer; + Geometry *geom= Geometry::construct(&buffer, from, uint32(length)); + if (!geom || !geom->is_binary_valid()) + goto err; + if (m_type_handler->geometry_type() != Type_handler_geometry::GEOM_GEOMETRY && m_type_handler->geometry_type() != Type_handler_geometry::GEOM_GEOMETRYCOLLECTION && (uint32) m_type_handler->geometry_type() != wkb_type) @@ -866,21 +872,14 @@ int Field_geom::store(const char *from, size_t length, CHARSET_INFO *cs) if (!tab_name) tab_name= ""; - Geometry_buffer buffer; - Geometry *geom= NULL; - String wkt; - const char *dummy; - wkt.set_charset(&my_charset_latin1); - if (!(geom= Geometry::construct(&buffer, from, uint32(length))) || - geom->as_wkt(&wkt, &dummy)) + StringBuffer wkt(&my_charset_latin1); + if (geom->as_wkt(&wkt, &dummy)) goto err; my_error(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, MYF(0), Geometry::ci_collection[m_type_handler->geometry_type()]->m_name.str, - wkt.c_ptr_safe(), - db, tab_name, field_name.str, - (ulong) table->in_use->get_stmt_da()-> - current_row_for_warning()); + wkt.c_ptr_safe(), db, tab_name, field_name.str, + (ulong) table->in_use->get_stmt_da()->current_row_for_warning()); goto err_exit; } From 2dcd3cefe1f50366ab9f4cce5325483b43e5efcc Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 18 Dec 2025 16:14:26 +0100 Subject: [PATCH 33/43] MDEV-38374 nonsense code in wsrep_store_key_val_for_row() fix copy-paste error. See the code for MYSQL_TYPE_VARCHAR --- .../suite/galera/r/galera_blob_key.result | 14 ++++++++++++++ mysql-test/suite/galera/t/galera_blob_key.test | 17 +++++++++++++++++ storage/innobase/handler/ha_innodb.cc | 2 +- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/galera/r/galera_blob_key.result create mode 100644 mysql-test/suite/galera/t/galera_blob_key.test diff --git a/mysql-test/suite/galera/r/galera_blob_key.result b/mysql-test/suite/galera/r/galera_blob_key.result new file mode 100644 index 00000000000..e0e2aa1013a --- /dev/null +++ b/mysql-test/suite/galera/r/galera_blob_key.result @@ -0,0 +1,14 @@ +connection node_2; +connection node_1; +# +# MDEV-38374 nonsense code in wsrep_store_key_val_for_row() +# +create table t1 ( +id int auto_increment primary key, +blob_data longblob not null, +unique key idx_blob_prefix (blob_data(3072)) +) engine=innodb; +insert into t1 (blob_data) values ('a'); +update t1 set blob_data = repeat('x', 4000) where id = 1; +drop table t1; +# End of 10.6 tests diff --git a/mysql-test/suite/galera/t/galera_blob_key.test b/mysql-test/suite/galera/t/galera_blob_key.test new file mode 100644 index 00000000000..1c83120a062 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_blob_key.test @@ -0,0 +1,17 @@ +source include/galera_cluster.inc; +source include/have_innodb.inc; + +--echo # +--echo # MDEV-38374 nonsense code in wsrep_store_key_val_for_row() +--echo # + +create table t1 ( + id int auto_increment primary key, + blob_data longblob not null, + unique key idx_blob_prefix (blob_data(3072)) +) engine=innodb; +insert into t1 (blob_data) values ('a'); +update t1 set blob_data = repeat('x', 4000) where id = 1; +drop table t1; + +--echo # End of 10.6 tests diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 7ce5b4983fc..50cfc5570d2 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -6918,12 +6918,12 @@ wsrep_store_key_val_for_row( wsrep_thd_query(thd)); true_len = buff_space; } + memcpy(buff, sorted, true_len); buff += true_len; buff_space -= true_len; } else { buff += key_len; } - memcpy(buff, sorted, true_len); } else { /* Here we handle all other data types except the true VARCHAR, BLOB and TEXT. Note that the column From 635559a2ad68a5a6d1a354e8209c58323dba0261 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 18 Dec 2025 16:52:17 +0100 Subject: [PATCH 34/43] MDEV-38375 audit plugin implements its own (simple) SQL parser remove the parser, use thd_sql_command(thd) --- .../suite/plugins/r/server_audit.result | 2 - .../plugins/r/thread_pool_server_audit.result | 2 - plugin/server_audit/server_audit.c | 417 ++++-------------- sql/sql_cmd.h | 99 +---- sql/sql_command.h | 112 +++++ 5 files changed, 205 insertions(+), 427 deletions(-) create mode 100644 sql/sql_command.h diff --git a/mysql-test/suite/plugins/r/server_audit.result b/mysql-test/suite/plugins/r/server_audit.result index 6c9a9e0ff2e..588563b1b0e 100644 --- a/mysql-test/suite/plugins/r/server_audit.result +++ b/mysql-test/suite/plugins/r/server_audit.result @@ -443,8 +443,6 @@ TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'/*! select 2*/',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'/*comment*/ select 2',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'CREATE USER u1 IDENTIFIED BY *****',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'GRANT ALL ON sa_db TO u2 IDENTIFIED BY *****',0 -TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'SET PASSWORD \n# comment\nFOR u1 = PASSWORD(*****)',0 -TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'SET PASSWORD FOR u1=',ID TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'CREATE USER u3 IDENTIFIED BY *****',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'drop user u1, u2, u3',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'insert into t1 values (1), (2)',0 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 8c6ece53227..19a3163d209 100644 --- a/mysql-test/suite/plugins/r/thread_pool_server_audit.result +++ b/mysql-test/suite/plugins/r/thread_pool_server_audit.result @@ -361,8 +361,6 @@ TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'/*! select 2*/',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'/*comment*/ select 2',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'CREATE USER u1 IDENTIFIED BY *****',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'GRANT ALL ON sa_db TO u2 IDENTIFIED BY *****',0 -TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'SET PASSWORD\n# comment\nFOR u1 = PASSWORD(*****)',0 -TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'SET PASSWORD FOR u1=',ID TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'CREATE USER u3 IDENTIFIED BY *****',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'drop user u1, u2, u3',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'set global server_audit_events=\'\'',0 diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index fee20a1a543..7175239be82 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -22,6 +22,7 @@ #include #include +#include "sql_command.h" #ifndef _WIN32 #define DO_SYSLOG @@ -811,189 +812,6 @@ static int user_coll_fill(struct user_coll *c, char *users, } -enum sa_keywords -{ - SQLCOM_NOTHING=0, - SQLCOM_DDL, - SQLCOM_DML, - SQLCOM_GRANT, - SQLCOM_CREATE_USER, - SQLCOM_ALTER_USER, - SQLCOM_CHANGE_MASTER, - SQLCOM_CREATE_SERVER, - SQLCOM_SET_OPTION, - SQLCOM_ALTER_SERVER, - SQLCOM_TRUNCATE, - SQLCOM_QUERY_ADMIN, - SQLCOM_DCL, - SQLCOM_FOUND=-1, -}; - -struct sa_keyword -{ - int length; - const char *wd; - struct sa_keyword *next; - enum sa_keywords type; -}; - - -struct sa_keyword xml_word[]= -{ - {3, "XML", 0, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; -struct sa_keyword user_word[]= -{ - {4, "USER", 0, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; -struct sa_keyword data_word[]= -{ - {4, "DATA", 0, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; -struct sa_keyword server_word[]= -{ - {6, "SERVER", 0, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; -struct sa_keyword master_word[]= -{ - {6, "MASTER", 0, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; -struct sa_keyword password_word[]= -{ - {8, "PASSWORD", 0, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; -struct sa_keyword function_word[]= -{ - {8, "FUNCTION", 0, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; -struct sa_keyword statement_word[]= -{ - {9, "STATEMENT", 0, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; -struct sa_keyword procedure_word[]= -{ - {9, "PROCEDURE", 0, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; -struct sa_keyword replace_user_word[]= -{ - {7, "REPLACE", user_word, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; -struct sa_keyword or_replace_user_word[]= -{ - {2, "OR", replace_user_word, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; -struct sa_keyword replace_server_word[]= -{ - {7, "REPLACE", server_word, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; -struct sa_keyword or_replace_server_word[]= -{ - {2, "OR", replace_server_word, SQLCOM_FOUND}, - {0, NULL, 0, SQLCOM_NOTHING} -}; - - -struct sa_keyword keywords_to_skip[]= -{ - {3, "SET", statement_word, SQLCOM_QUERY_ADMIN}, - {0, NULL, 0, SQLCOM_NOTHING} -}; - - -struct sa_keyword not_ddl_keywords[]= -{ - {4, "DROP", user_word, SQLCOM_DCL}, - {6, "CREATE", user_word, SQLCOM_DCL}, - {6, "CREATE", or_replace_user_word, SQLCOM_DCL}, - {6, "RENAME", user_word, SQLCOM_DCL}, - {0, NULL, 0, SQLCOM_NOTHING} -}; - - -struct sa_keyword ddl_keywords[]= -{ - {4, "DROP", 0, SQLCOM_DDL}, - {5, "ALTER", 0, SQLCOM_DDL}, - {6, "CREATE", 0, SQLCOM_DDL}, - {6, "RENAME", 0, SQLCOM_DDL}, - {8, "TRUNCATE", 0, SQLCOM_DDL}, - {0, NULL, 0, SQLCOM_NOTHING} -}; - - -struct sa_keyword dml_keywords[]= -{ - {2, "DO", 0, SQLCOM_DML}, - {4, "CALL", 0, SQLCOM_DML}, - {4, "LOAD", data_word, SQLCOM_DML}, - {4, "LOAD", xml_word, SQLCOM_DML}, - {6, "DELETE", 0, SQLCOM_DML}, - {6, "INSERT", 0, SQLCOM_DML}, - {6, "SELECT", 0, SQLCOM_DML}, - {6, "UPDATE", 0, SQLCOM_DML}, - {7, "HANDLER", 0, SQLCOM_DML}, - {7, "REPLACE", 0, SQLCOM_DML}, - {0, NULL, 0, SQLCOM_NOTHING} -}; - - -struct sa_keyword dml_no_select_keywords[]= -{ - {2, "DO", 0, SQLCOM_DML}, - {4, "CALL", 0, SQLCOM_DML}, - {4, "LOAD", data_word, SQLCOM_DML}, - {4, "LOAD", xml_word, SQLCOM_DML}, - {6, "DELETE", 0, SQLCOM_DML}, - {6, "INSERT", 0, SQLCOM_DML}, - {6, "UPDATE", 0, SQLCOM_DML}, - {7, "HANDLER", 0, SQLCOM_DML}, - {7, "REPLACE", 0, SQLCOM_DML}, - {0, NULL, 0, SQLCOM_NOTHING} -}; - - -struct sa_keyword dcl_keywords[]= -{ - {6, "CREATE", user_word, SQLCOM_DCL}, - {6, "CREATE", or_replace_user_word, SQLCOM_DCL}, - {4, "DROP", user_word, SQLCOM_DCL}, - {6, "RENAME", user_word, SQLCOM_DCL}, - {5, "GRANT", 0, SQLCOM_DCL}, - {6, "REVOKE", 0, SQLCOM_DCL}, - {3, "SET", password_word, SQLCOM_DCL}, - {0, NULL, 0, SQLCOM_NOTHING} -}; - - -struct sa_keyword passwd_keywords[]= -{ - {3, "SET", password_word, SQLCOM_SET_OPTION}, - {5, "ALTER", server_word, SQLCOM_ALTER_SERVER}, - {5, "ALTER", user_word, SQLCOM_ALTER_USER}, - {5, "GRANT", 0, SQLCOM_GRANT}, - {6, "CREATE", user_word, SQLCOM_CREATE_USER}, - {6, "CREATE", or_replace_user_word, SQLCOM_CREATE_USER}, - {6, "CREATE", server_word, SQLCOM_CREATE_SERVER}, - {6, "CREATE", or_replace_server_word, SQLCOM_CREATE_SERVER}, - {6, "CHANGE", master_word, SQLCOM_CHANGE_MASTER}, - {0, NULL, 0, SQLCOM_NOTHING} -}; - -#define MAX_KEYWORD 9 - - static void error_header() { struct tm tm_time; @@ -1107,6 +925,78 @@ static int get_user_host(const char *uh_line, unsigned int uh_len, return 0; } + +static int sql_command_to_cmdtype(int sql_command) +{ + switch (sql_command) + { + case SQLCOM_ALTER_DB: + case SQLCOM_ALTER_DB_UPGRADE: + case SQLCOM_ALTER_FUNCTION: + case SQLCOM_ALTER_PROCEDURE: + case SQLCOM_ALTER_SEQUENCE: + case SQLCOM_ALTER_TABLE: + case SQLCOM_CREATE_DB: + case SQLCOM_CREATE_INDEX: + case SQLCOM_CREATE_PACKAGE: + case SQLCOM_CREATE_PACKAGE_BODY: + case SQLCOM_CREATE_PROCEDURE: + case SQLCOM_CREATE_SEQUENCE: + case SQLCOM_CREATE_SPFUNCTION: + case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_TRIGGER: + case SQLCOM_CREATE_VIEW: + case SQLCOM_DROP_DB: + case SQLCOM_DROP_FUNCTION: + case SQLCOM_DROP_INDEX: + case SQLCOM_DROP_PACKAGE: + case SQLCOM_DROP_PACKAGE_BODY: + case SQLCOM_DROP_PROCEDURE: + case SQLCOM_DROP_SEQUENCE: + case SQLCOM_DROP_TABLE: + case SQLCOM_DROP_TRIGGER: + case SQLCOM_DROP_VIEW: + case SQLCOM_RENAME_TABLE: + case SQLCOM_TRUNCATE: + return EVENT_QUERY_DDL; + + case SQLCOM_DO: + case SQLCOM_CALL: + case SQLCOM_LOAD: + case SQLCOM_DELETE: + case SQLCOM_DELETE_MULTI: + case SQLCOM_INSERT: + case SQLCOM_INSERT_SELECT: + case SQLCOM_UPDATE: + case SQLCOM_UPDATE_MULTI: + case SQLCOM_HA_OPEN: + case SQLCOM_HA_READ: + case SQLCOM_HA_CLOSE: + case SQLCOM_REPLACE: + case SQLCOM_REPLACE_SELECT: + return EVENT_QUERY_DML | EVENT_QUERY_DML_NO_SELECT; + + case SQLCOM_SELECT: + return EVENT_QUERY_DML; + + case SQLCOM_CREATE_USER: + case SQLCOM_CREATE_ROLE: + case SQLCOM_DROP_ROLE: + case SQLCOM_DROP_USER: + case SQLCOM_RENAME_USER: + case SQLCOM_GRANT: + case SQLCOM_GRANT_ROLE: + case SQLCOM_REVOKE: + case SQLCOM_REVOKE_ROLE: + case SQLCOM_REVOKE_ALL: + return EVENT_QUERY_DCL; + + default: + return EVENT_QUERY_ALL; + }; +} + + #if defined(_WIN32) && !defined(S_ISDIR) #define S_ISDIR(x) ((x) & _S_IFDIR) #endif /*_WIN32 && !S_ISDIR*/ @@ -1755,118 +1645,8 @@ static int do_log_user(const char *name, int len, } -static int get_next_word(const char *query, char *word) -{ - int len= 0; - char c; - while ((c= query[len])) - { - if (c >= 'a' && c <= 'z') - word[len]= 'A' + (c-'a'); - else if (c >= 'A' && c <= 'Z') - word[len]= c; - else - break; - - if (len++ == MAX_KEYWORD) - return 0; - } - word[len]= 0; - return len; -} - - -static int filter_query_type(const char *query, struct sa_keyword *kwd) -{ - int qwe_in_list; - char fword[MAX_KEYWORD + 1], nword[MAX_KEYWORD + 1]; - int len, nlen= 0; - const struct sa_keyword *l_keywords; - if (!query) - return SQLCOM_NOTHING; - - while (*query && (is_space(*query) || *query == '(' || *query == '/')) - { - /* comment handling */ - if (*query == '/' && query[1] == '*') - { - if (query[2] == '!') - { - query+= 3; - while (*query >= '0' && *query <= '9') - query++; - continue; - } - query+= 2; - while (*query) - { - if (*query=='*' && query[1] == '/') - { - query+= 2; - break; - } - query++; - } - continue; - } - query++; - } - - qwe_in_list= SQLCOM_NOTHING; - if (!(len= get_next_word(query, fword))) - goto not_in_list; - query+= len+1; - - l_keywords= kwd; - while (l_keywords->length) - { - if (l_keywords->length == len && strncmp(l_keywords->wd, fword, len) == 0) - { - if (l_keywords->next) - { - if (nlen == 0) - { - while (*query && is_space(*query)) - query++; - nlen= get_next_word(query, nword); - } - if (filter_query_type(query, l_keywords->next) == SQLCOM_NOTHING) - goto do_loop; - } - - qwe_in_list= l_keywords->type; - break; - }; -do_loop: - l_keywords++; - } - -not_in_list: - return qwe_in_list; -} - -static const char *skip_set_statement(const char *query) -{ - if (filter_query_type(query, keywords_to_skip)) - { - char fword[MAX_KEYWORD + 1]; - int len; - do - { - len= get_next_word(query, fword); - query+= len ? len : 1; - if (len == 3 && strncmp(fword, "FOR", 3) == 0) - break; - } while (*query); - - if (*query == 0) - return 0; - } - return query; -} - static int log_statement_ex(const struct connection_info *cn, - time_t ev_time, unsigned long thd_id, + time_t ev_time, unsigned long thd_id, int sql_cmd, const char *query, unsigned int query_len, int error_code, const char *type, int take_lock) { @@ -1881,6 +1661,7 @@ static int log_statement_ex(const struct connection_info *cn, long long query_id; int result; char *big_buffer= NULL; + int cmdtype= sql_command_to_cmdtype(sql_cmd); if ((db= cn->db)) db_length= cn->db_length; @@ -1907,30 +1688,14 @@ static int log_statement_ex(const struct connection_info *cn, { const char *orig_query= query; - if ((query= skip_set_statement(query)) == SQLCOM_NOTHING) - return 0; - - if (events & EVENT_QUERY_DDL) - { - if (!filter_query_type(query, not_ddl_keywords) && - filter_query_type(query, ddl_keywords)) - goto do_log_query; - } - if (events & EVENT_QUERY_DML) - { - if (filter_query_type(query, dml_keywords)) - goto do_log_query; - } - if (events & EVENT_QUERY_DML_NO_SELECT) - { - if (filter_query_type(query, dml_no_select_keywords)) - goto do_log_query; - } - if (events & EVENT_QUERY_DCL) - { - if (filter_query_type(query, dcl_keywords)) - goto do_log_query; - } + if (events & EVENT_QUERY_DDL && cmdtype & EVENT_QUERY_DDL) + goto do_log_query; + if (events & EVENT_QUERY_DML && cmdtype & EVENT_QUERY_DML) + goto do_log_query; + if (events & EVENT_QUERY_DML_NO_SELECT && cmdtype & EVENT_QUERY_DML_NO_SELECT) + goto do_log_query; + if (events & EVENT_QUERY_DCL && cmdtype & EVENT_QUERY_DCL) + goto do_log_query; return 0; do_log_query: @@ -1964,7 +1729,7 @@ do_log_query: if (query_log_limit > 0 && uh_buffer_size > query_log_limit+2) uh_buffer_size= query_log_limit+2; - switch (filter_query_type(skip_set_statement(query), passwd_keywords)) + switch (sql_cmd) { case SQLCOM_GRANT: case SQLCOM_CREATE_USER: @@ -1990,8 +1755,8 @@ do_log_query: case SQLCOM_SET_OPTION: csize+= escape_string_hide_passwords(query, query_len, uh_buffer, uh_buffer_size, - NULL, 0, NULL, 0, - "=", 1, 0); + "PASSWORD", 8, "=", 1, + "PASSWORD", 8, '('); break; default: csize+= escape_string(query, query_len, @@ -2011,10 +1776,11 @@ do_log_query: static int log_statement(const struct connection_info *cn, const struct mysql_event_general *event, - const char *type) + const char *type, int sql_command) { return log_statement_ex(cn, event->general_time, event->general_thread_id, - event->general_query, event->general_query_length, + sql_command, event->general_query, + event->general_query_length, event->general_error_code, type, 1); } @@ -2305,7 +2071,7 @@ void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev) if (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS && event_query_command(event)) { - log_statement(cn, event, "QUERY"); + log_statement(cn, event, "QUERY", thd_sql_command(thd)); cn->query_length= 0; /* So the log_current_query() won't log this again. */ cn->log_always= 0; } @@ -2843,7 +2609,8 @@ static void log_current_query(MYSQL_THD thd) { cn->log_always= 1; log_statement_ex(cn, cn->query_time, thd_get_thread_id(thd), - cn->query, cn->query_length, 0, "QUERY", 0); + thd_sql_command(thd), cn->query, cn->query_length, 0, + "QUERY", 0); cn->log_always= 0; } } diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index e1ce96efb29..f94ffee2f9f 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -20,104 +20,7 @@ #ifndef SQL_CMD_INCLUDED #define SQL_CMD_INCLUDED -/* - When a command is added here, be sure it's also added in mysqld.cc - in "struct show_var_st status_vars[]= {" ... - - If the command returns a result set or is not allowed in stored - functions or triggers, please also make sure that - sp_get_flags_for_command (sp_head.cc) returns proper flags for the - added SQLCOM_. -*/ - -enum enum_sql_command { - SQLCOM_SELECT, SQLCOM_CREATE_TABLE, SQLCOM_CREATE_INDEX, SQLCOM_ALTER_TABLE, - SQLCOM_UPDATE, SQLCOM_INSERT, SQLCOM_INSERT_SELECT, - SQLCOM_DELETE, SQLCOM_TRUNCATE, SQLCOM_DROP_TABLE, SQLCOM_DROP_INDEX, - - SQLCOM_SHOW_DATABASES, SQLCOM_SHOW_TABLES, SQLCOM_SHOW_FIELDS, - SQLCOM_SHOW_KEYS, SQLCOM_SHOW_VARIABLES, SQLCOM_SHOW_STATUS, - SQLCOM_SHOW_ENGINE_LOGS, SQLCOM_SHOW_ENGINE_STATUS, SQLCOM_SHOW_ENGINE_MUTEX, - SQLCOM_SHOW_PROCESSLIST, SQLCOM_SHOW_BINLOG_STAT, SQLCOM_SHOW_SLAVE_STAT, - SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS, - SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_TABLE_STATUS, - SQLCOM_SHOW_TRIGGERS, - - SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES, - SQLCOM_GRANT, - SQLCOM_CHANGE_DB, SQLCOM_CREATE_DB, SQLCOM_DROP_DB, SQLCOM_ALTER_DB, - SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT, - SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION, - SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK, - SQLCOM_ASSIGN_TO_KEYCACHE, SQLCOM_PRELOAD_KEYS, - SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE, - SQLCOM_ROLLBACK, SQLCOM_ROLLBACK_TO_SAVEPOINT, - SQLCOM_COMMIT, SQLCOM_SAVEPOINT, SQLCOM_RELEASE_SAVEPOINT, - SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP, - SQLCOM_BEGIN, SQLCOM_CHANGE_MASTER, - SQLCOM_RENAME_TABLE, - SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_PURGE_BEFORE, SQLCOM_SHOW_BINLOGS, - SQLCOM_SHOW_OPEN_TABLES, - SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ, - SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_UPDATE_MULTI, - SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_DO, - SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS, - SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES, - SQLCOM_HELP, SQLCOM_CREATE_USER, SQLCOM_DROP_USER, SQLCOM_RENAME_USER, - SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM, - SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL, - SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION, - SQLCOM_SHOW_CREATE_PROC, SQLCOM_SHOW_CREATE_FUNC, - SQLCOM_SHOW_STATUS_PROC, SQLCOM_SHOW_STATUS_FUNC, - SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE, - SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW, - SQLCOM_CREATE_TRIGGER, SQLCOM_DROP_TRIGGER, - SQLCOM_XA_START, SQLCOM_XA_END, SQLCOM_XA_PREPARE, - SQLCOM_XA_COMMIT, SQLCOM_XA_ROLLBACK, SQLCOM_XA_RECOVER, - SQLCOM_SHOW_PROC_CODE, SQLCOM_SHOW_FUNC_CODE, - SQLCOM_ALTER_TABLESPACE, - SQLCOM_INSTALL_PLUGIN, SQLCOM_UNINSTALL_PLUGIN, - SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT, - SQLCOM_SHOW_PLUGINS, SQLCOM_SHOW_CONTRIBUTORS, - SQLCOM_CREATE_SERVER, SQLCOM_DROP_SERVER, SQLCOM_ALTER_SERVER, - SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT, - SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS, - SQLCOM_SHOW_CREATE_TRIGGER, - SQLCOM_ALTER_DB_UPGRADE, - SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES, - SQLCOM_SIGNAL, SQLCOM_RESIGNAL, - SQLCOM_SHOW_RELAYLOG_EVENTS, - SQLCOM_GET_DIAGNOSTICS, - SQLCOM_SLAVE_ALL_START, SQLCOM_SLAVE_ALL_STOP, - SQLCOM_SHOW_EXPLAIN, SQLCOM_SHUTDOWN, - SQLCOM_CREATE_ROLE, SQLCOM_DROP_ROLE, SQLCOM_GRANT_ROLE, SQLCOM_REVOKE_ROLE, - SQLCOM_COMPOUND, - SQLCOM_SHOW_GENERIC, - SQLCOM_ALTER_USER, - SQLCOM_SHOW_CREATE_USER, - SQLCOM_EXECUTE_IMMEDIATE, - SQLCOM_CREATE_SEQUENCE, - SQLCOM_DROP_SEQUENCE, - SQLCOM_ALTER_SEQUENCE, - SQLCOM_CREATE_PACKAGE, - SQLCOM_DROP_PACKAGE, - SQLCOM_CREATE_PACKAGE_BODY, - SQLCOM_DROP_PACKAGE_BODY, - SQLCOM_SHOW_CREATE_PACKAGE, - SQLCOM_SHOW_CREATE_PACKAGE_BODY, - SQLCOM_SHOW_STATUS_PACKAGE, - SQLCOM_SHOW_STATUS_PACKAGE_BODY, - SQLCOM_SHOW_PACKAGE_BODY_CODE, - SQLCOM_BACKUP, SQLCOM_BACKUP_LOCK, - - /* - When a command is added here, be sure it's also added in mysqld.cc - in "struct show_var_st com_status_vars[]= {" ... - */ - /* This should be the last !!! */ - SQLCOM_END -}; - +#include "sql_command.h" class Storage_engine_name { diff --git a/sql/sql_command.h b/sql/sql_command.h new file mode 100644 index 00000000000..a8accca1a7c --- /dev/null +++ b/sql/sql_command.h @@ -0,0 +1,112 @@ +/* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/* + When a command is added here, be sure it's also added in mysqld.cc + in "struct show_var_st status_vars[]= {" ... + + If the command returns a result set or is not allowed in stored + functions or triggers, please also make sure that + sp_get_flags_for_command (sp_head.cc) returns proper flags for the + added SQLCOM_. +*/ + +enum enum_sql_command { + SQLCOM_SELECT, SQLCOM_CREATE_TABLE, SQLCOM_CREATE_INDEX, SQLCOM_ALTER_TABLE, + SQLCOM_UPDATE, SQLCOM_INSERT, SQLCOM_INSERT_SELECT, + SQLCOM_DELETE, SQLCOM_TRUNCATE, SQLCOM_DROP_TABLE, SQLCOM_DROP_INDEX, + + SQLCOM_SHOW_DATABASES, SQLCOM_SHOW_TABLES, SQLCOM_SHOW_FIELDS, + SQLCOM_SHOW_KEYS, SQLCOM_SHOW_VARIABLES, SQLCOM_SHOW_STATUS, + SQLCOM_SHOW_ENGINE_LOGS, SQLCOM_SHOW_ENGINE_STATUS, SQLCOM_SHOW_ENGINE_MUTEX, + SQLCOM_SHOW_PROCESSLIST, SQLCOM_SHOW_BINLOG_STAT, SQLCOM_SHOW_SLAVE_STAT, + SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS, + SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_TABLE_STATUS, + SQLCOM_SHOW_TRIGGERS, + + SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES, + SQLCOM_GRANT, + SQLCOM_CHANGE_DB, SQLCOM_CREATE_DB, SQLCOM_DROP_DB, SQLCOM_ALTER_DB, + SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT, + SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION, + SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK, + SQLCOM_ASSIGN_TO_KEYCACHE, SQLCOM_PRELOAD_KEYS, + SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE, + SQLCOM_ROLLBACK, SQLCOM_ROLLBACK_TO_SAVEPOINT, + SQLCOM_COMMIT, SQLCOM_SAVEPOINT, SQLCOM_RELEASE_SAVEPOINT, + SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP, + SQLCOM_BEGIN, SQLCOM_CHANGE_MASTER, + SQLCOM_RENAME_TABLE, + SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_PURGE_BEFORE, SQLCOM_SHOW_BINLOGS, + SQLCOM_SHOW_OPEN_TABLES, + SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ, + SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_UPDATE_MULTI, + SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_DO, + SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS, + SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES, + SQLCOM_HELP, SQLCOM_CREATE_USER, SQLCOM_DROP_USER, SQLCOM_RENAME_USER, + SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM, + SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL, + SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION, + SQLCOM_SHOW_CREATE_PROC, SQLCOM_SHOW_CREATE_FUNC, + SQLCOM_SHOW_STATUS_PROC, SQLCOM_SHOW_STATUS_FUNC, + SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE, + SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW, + SQLCOM_CREATE_TRIGGER, SQLCOM_DROP_TRIGGER, + SQLCOM_XA_START, SQLCOM_XA_END, SQLCOM_XA_PREPARE, + SQLCOM_XA_COMMIT, SQLCOM_XA_ROLLBACK, SQLCOM_XA_RECOVER, + SQLCOM_SHOW_PROC_CODE, SQLCOM_SHOW_FUNC_CODE, + SQLCOM_ALTER_TABLESPACE, + SQLCOM_INSTALL_PLUGIN, SQLCOM_UNINSTALL_PLUGIN, + SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT, + SQLCOM_SHOW_PLUGINS, SQLCOM_SHOW_CONTRIBUTORS, + SQLCOM_CREATE_SERVER, SQLCOM_DROP_SERVER, SQLCOM_ALTER_SERVER, + SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT, + SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS, + SQLCOM_SHOW_CREATE_TRIGGER, + SQLCOM_ALTER_DB_UPGRADE, + SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES, + SQLCOM_SIGNAL, SQLCOM_RESIGNAL, + SQLCOM_SHOW_RELAYLOG_EVENTS, + SQLCOM_GET_DIAGNOSTICS, + SQLCOM_SLAVE_ALL_START, SQLCOM_SLAVE_ALL_STOP, + SQLCOM_SHOW_EXPLAIN, SQLCOM_SHUTDOWN, + SQLCOM_CREATE_ROLE, SQLCOM_DROP_ROLE, SQLCOM_GRANT_ROLE, SQLCOM_REVOKE_ROLE, + SQLCOM_COMPOUND, + SQLCOM_SHOW_GENERIC, + SQLCOM_ALTER_USER, + SQLCOM_SHOW_CREATE_USER, + SQLCOM_EXECUTE_IMMEDIATE, + SQLCOM_CREATE_SEQUENCE, + SQLCOM_DROP_SEQUENCE, + SQLCOM_ALTER_SEQUENCE, + SQLCOM_CREATE_PACKAGE, + SQLCOM_DROP_PACKAGE, + SQLCOM_CREATE_PACKAGE_BODY, + SQLCOM_DROP_PACKAGE_BODY, + SQLCOM_SHOW_CREATE_PACKAGE, + SQLCOM_SHOW_CREATE_PACKAGE_BODY, + SQLCOM_SHOW_STATUS_PACKAGE, + SQLCOM_SHOW_STATUS_PACKAGE_BODY, + SQLCOM_SHOW_PACKAGE_BODY_CODE, + SQLCOM_BACKUP, SQLCOM_BACKUP_LOCK, + + /* + When a command is added here, be sure it's also added in mysqld.cc + in "struct show_var_st com_status_vars[]= {" ... + */ + /* This should be the last !!! */ + SQLCOM_END +}; From 317fb109153b45091003f0b1380b5d68522fce29 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sat, 27 Dec 2025 11:35:01 +0100 Subject: [PATCH 35/43] MDEV-38375 do MYSQL_AUDIT_GENERAL_STATUS on ps execute too just like slow logging, MYSQL_AUDIT_GENERAL_STATUS should be done inside Prepared_statement::execute() --- sql/sql_parse.cc | 23 ++++++++++++----------- sql/sql_prepare.cc | 5 +++++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f5cbce26031..9c0af6ef115 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1594,6 +1594,7 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD * NET *net= &thd->net; bool error= 0; bool do_end_of_statement= true; + bool log_slow_done= false; DBUG_ENTER("dispatch_command"); DBUG_PRINT("info", ("command: %d %s", command, (command_name[command].str != 0 ? @@ -1820,6 +1821,7 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD * case COM_STMT_BULK_EXECUTE: { mysqld_stmt_bulk_execute(thd, packet, packet_length); + log_slow_done= true; #ifdef WITH_WSREP if (WSREP(thd)) { @@ -1831,6 +1833,7 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD * case COM_STMT_EXECUTE: { mysqld_stmt_execute(thd, packet, packet_length); + log_slow_done= true; #ifdef WITH_WSREP if (WSREP(thd)) { @@ -2007,6 +2010,7 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD * mysql_parse(thd, beginning_of_next_stmt, length, &parser_state); } + log_slow_done= thd->lex->sql_command == SQLCOM_EXECUTE; DBUG_PRINT("info",("query ready")); break; @@ -2460,22 +2464,19 @@ resume: if (likely(!thd->is_error() && !thd->killed_errno())) mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0); - mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS, - thd->get_stmt_da()->is_error() ? - thd->get_stmt_da()->sql_errno() : 0, - command_name[command].str); + if (!log_slow_done) + mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS, + thd->get_stmt_da()->is_error() ? + thd->get_stmt_da()->sql_errno() : 0, + command_name[command].str); thd->update_all_stats(); /* - Write to slow query log only those statements that received via the text - protocol except the EXECUTE statement. The reason we do that way is - that for statements received via binary protocol and for the EXECUTE - statement, the slow statements have been already written to slow query log - inside the method Prepared_statement::execute(). + for backward compatibility we only log COM_QUERY here, + as if COM_STMT_PREPARE or COM_FIELD_LIST couldn't be slow. */ - if(command == COM_QUERY && - thd->lex->sql_command != SQLCOM_EXECUTE) + if (command == COM_QUERY && !log_slow_done) log_slow_statement(thd); else delete_explain_query(thd->lex); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index b2bcf03820c..9a666b4754c 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -5316,6 +5316,11 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) */ cleanup_stmt(false); + mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS, + thd->get_stmt_da()->is_error() ? + thd->get_stmt_da()->sql_errno() : 0, + command_name[thd->get_command()].str); + /* Log the statement to slow query log if it passes filtering. We do it here for prepared statements despite of the fact that the function From 4da80ba8413ec866e614a3e612e649fc13736825 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Wed, 3 Dec 2025 15:46:11 +0200 Subject: [PATCH 36/43] MDEV-38240: Selectivity sampling not performed when the table has no indexed conditions Don't skip range analysis step in such cases. (Backport to 11.4, cherry picked from 2ce3f0d76cb6ae0dc6df1f257f28a4b7b1a00244) --- mysql-test/main/selectivity.result | 28 +++++++++++++++++++++++ mysql-test/main/selectivity.test | 22 +++++++++++++++++- mysql-test/main/selectivity_innodb.result | 28 +++++++++++++++++++++++ sql/sql_select.cc | 22 +++++++++++------- 4 files changed, 91 insertions(+), 9 deletions(-) diff --git a/mysql-test/main/selectivity.result b/mysql-test/main/selectivity.result index 923967766e5..ef2d2e85b86 100644 --- a/mysql-test/main/selectivity.result +++ b/mysql-test/main/selectivity.result @@ -2113,3 +2113,31 @@ a b c d e 2 NULL NULL NULL NULL set join_cache_level= default; DROP TABLE t1, t2, t3; +# +# MDEV-38240: Selectivity sampling not performed when the table has no indexed conditions +# +set use_stat_tables=PREFERABLY; +create table t1 (a varchar(100), b int); +insert into t1 select seq, seq from seq_1_to_1000; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +set optimizer_use_condition_selectivity=5; +# Must produce filtered=1% or so: +explain extended +select * from t1 where a like '%99%'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 1000 1.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where `test`.`t1`.`a` like '%99%' +# Must have same filtered as above +explain extended +select * from t1 where a like '%99%' and (b is null or b is not null); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 1000 1.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where `test`.`t1`.`a` like '%99%' and (`test`.`t1`.`b` is null or `test`.`t1`.`b` is not null) +drop table t1; +set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +set use_stat_tables= @save_use_stat_tables; diff --git a/mysql-test/main/selectivity.test b/mysql-test/main/selectivity.test index 9c68dbe5061..7caa2503a33 100644 --- a/mysql-test/main/selectivity.test +++ b/mysql-test/main/selectivity.test @@ -1453,4 +1453,24 @@ explain SELECT * FROM t1 LEFT JOIN (t2 JOIN t3 ON t2.b = t3.e) ON t2.c = 1; SELECT * FROM t1 LEFT JOIN (t2 JOIN t3 ON t2.b = t3.e) ON t2.c = 1; set join_cache_level= default; -DROP TABLE t1, t2, t3; \ No newline at end of file +DROP TABLE t1, t2, t3; +--echo # +--echo # MDEV-38240: Selectivity sampling not performed when the table has no indexed conditions +--echo # +set use_stat_tables=PREFERABLY; +create table t1 (a varchar(100), b int); +insert into t1 select seq, seq from seq_1_to_1000; +analyze table t1; + +set optimizer_use_condition_selectivity=5; +--echo # Must produce filtered=1% or so: +explain extended +select * from t1 where a like '%99%'; + +--echo # Must have same filtered as above +explain extended +select * from t1 where a like '%99%' and (b is null or b is not null); +drop table t1; + +set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +set use_stat_tables= @save_use_stat_tables; diff --git a/mysql-test/main/selectivity_innodb.result b/mysql-test/main/selectivity_innodb.result index 5c3c88c9dd5..66bef413c3a 100644 --- a/mysql-test/main/selectivity_innodb.result +++ b/mysql-test/main/selectivity_innodb.result @@ -2120,6 +2120,34 @@ a b c d e 2 NULL NULL NULL NULL set join_cache_level= default; DROP TABLE t1, t2, t3; +# +# MDEV-38240: Selectivity sampling not performed when the table has no indexed conditions +# +set use_stat_tables=PREFERABLY; +create table t1 (a varchar(100), b int); +insert into t1 select seq, seq from seq_1_to_1000; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +set optimizer_use_condition_selectivity=5; +# Must produce filtered=1% or so: +explain extended +select * from t1 where a like '%99%'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 1000 1.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where `test`.`t1`.`a` like '%99%' +# Must have same filtered as above +explain extended +select * from t1 where a like '%99%' and (b is null or b is not null); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 1000 1.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where `test`.`t1`.`a` like '%99%' and (`test`.`t1`.`b` is null or `test`.`t1`.`b` is not null) +drop table t1; +set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +set use_stat_tables= @save_use_stat_tables; set optimizer_switch=@save_optimizer_switch_for_selectivity_test; set @tmp_ust= @@use_stat_tables; set @tmp_oucs= @@optimizer_use_condition_selectivity; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8e693ea1734..c8dc944817e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6227,15 +6227,21 @@ make_join_statistics(JOIN *join, List &tables_list, s->table->opt_range_condition_rows / s->table->used_stat_records); /* - Perform range analysis if there are keys it could use (1). - Don't do range analysis for materialized subqueries (2). - Don't do range analysis for materialized derived tables/views (3) + Perform range analysis if we could infer something from it. + (1) There are indexes for which we have range conditions, + (2) Or there are sargable conditions on the table's columns that we + could use for selectivity estimation, + (3) Or selectivity estimation via sampling is enabled. + + (4) Don't do range analysis for materialized subqueries. + (5) Don't do range analysis for materialized derived tables/views. */ - if ((!s->const_keys.is_clear_all() || - !bitmap_is_clear_all(&s->table->cond_set)) && // (1) - !s->table->is_filled_at_execution() && // (2) - !(s->table->pos_in_table_list->derived && // (3) - s->table->pos_in_table_list->is_materialized_derived())) // (3) + if ((!s->const_keys.is_clear_all() || // (1) + !bitmap_is_clear_all(&s->table->cond_set) || // (2) + thd->variables.optimizer_use_condition_selectivity >= 5) && // (3) + !s->table->is_filled_at_execution() && // (4) + !(s->table->pos_in_table_list->derived && // (5) + s->table->pos_in_table_list->is_materialized_derived())) // (5) { bool impossible_range= FALSE; ha_rows records= HA_ROWS_MAX; From 224efb91b4fc6c0df59abe6516a4931e08096d16 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Sat, 27 Dec 2025 15:43:39 +0200 Subject: [PATCH 37/43] MDEV-38426: Clustered PK not used for Loose Index Scan optimization Loose Index Scan optimization can only be used when the index it is uses covers all columns used by the query. So, it would not be attempted for indexes that are not present in table->covering_keys. Before commit ed0a72356691 (pushed to 11.0), Clustered PK would be present in table->covering_keys if primary key columns are covering. However, that commit gave Clustered PKs special treatment: they are no longer present in table->covering_keys. This caused Loose Scan not to be considered for Clustered PKs. Fixed this by making the check in get_best_group_min_max() to allow Loose Scan if all fields used by the query are parts of the Clustered PK. Also made changes: - The logic in JOIN_TAB::save_explain_data() assumed that Loose Scan could only be used for index-only scans. This is not the case for clustered PKs. - One query in group_min_max_innodb started to use Loose Scan. It did so in 10.11, too. --- mysql-test/main/group_min_max_innodb.result | 39 ++++++++++++++++++++- mysql-test/main/group_min_max_innodb.test | 34 ++++++++++++++++++ sql/opt_range.cc | 35 +++++++++++++++++- sql/sql_select.cc | 23 +++++++----- 4 files changed, 120 insertions(+), 11 deletions(-) diff --git a/mysql-test/main/group_min_max_innodb.result b/mysql-test/main/group_min_max_innodb.result index c14fed88666..44905a80811 100644 --- a/mysql-test/main/group_min_max_innodb.result +++ b/mysql-test/main/group_min_max_innodb.result @@ -180,7 +180,7 @@ F 17 EXPLAIN SELECT c1, max(i2) FROM t1 WHERE (c1 = 'C' OR c1 = 'F' ) AND ( i2 = 17 ) GROUP BY c1; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range k1 k1 5 NULL 2 Using where +1 SIMPLE t1 range k1 k1 5 NULL 2 Using where; Using index for group-by SELECT c1, max(i2) FROM t1 WHERE (c1 = 'C' OR c1 = 'F' ) AND ( i2 = 17 ) GROUP BY c1; c1 max(i2) @@ -438,6 +438,43 @@ disconnect con1; connection default; DROP TABLE t1; SET GLOBAL innodb_lru_scan_depth= @lru_depth.save; +# +# MDEV-38426: Clustered PK not used for Loose Index Scan optimization +# +create table t1 ( +col1 int not null, +col2 int not null, +col3 int, +primary key(col1, col2) +) engine=innodb; +insert into t1 select +A.seq, +B.seq, +A.seq +from +seq_1_to_10 A, +seq_1_to_1000 B; +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +# This must use Loose Scan: +explain select distinct col1 from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL PRIMARY 4 NULL 11 Using index for group-by +# This must use Loose Scan: +explain select col1 from t1 group by col1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL PRIMARY 4 NULL 11 Using index for group-by +# This must use Loose Scan: +explain select col1, min(col2) from t1 group by col1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL PRIMARY 4 NULL 11 Using index for group-by +# This will not use Loose Index Scan as col3 is not in PK: +explain select col1, min(col2), col3 from t1 group by col1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL PRIMARY 8 NULL 10000 +drop table t1; set global innodb_stats_persistent= @innodb_stats_persistent_save; set global innodb_stats_persistent_sample_pages= @innodb_stats_persistent_sample_pages_save; diff --git a/mysql-test/main/group_min_max_innodb.test b/mysql-test/main/group_min_max_innodb.test index 419c53dbd18..6d2389d22c8 100644 --- a/mysql-test/main/group_min_max_innodb.test +++ b/mysql-test/main/group_min_max_innodb.test @@ -323,6 +323,40 @@ while ($run) DROP TABLE t1; SET GLOBAL innodb_lru_scan_depth= @lru_depth.save; +--echo # +--echo # MDEV-38426: Clustered PK not used for Loose Index Scan optimization +--echo # +--source include/have_sequence.inc + +create table t1 ( + col1 int not null, + col2 int not null, + col3 int, + primary key(col1, col2) +) engine=innodb; +insert into t1 select + A.seq, + B.seq, + A.seq +from + seq_1_to_10 A, + seq_1_to_1000 B; +analyze table t1; + +--echo # This must use Loose Scan: +explain select distinct col1 from t1; + +--echo # This must use Loose Scan: +explain select col1 from t1 group by col1; + +--echo # This must use Loose Scan: +explain select col1, min(col2) from t1 group by col1; + +--echo # This will not use Loose Index Scan as col3 is not in PK: +explain select col1, min(col2), col3 from t1 group by col1; + +drop table t1; + set global innodb_stats_persistent= @innodb_stats_persistent_save; set global innodb_stats_persistent_sample_pages= @innodb_stats_persistent_sample_pages_save; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index fa58bc13c04..15035f94e4d 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -14091,6 +14091,38 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, double *read_cost, ha_rows *records); +/* + @brief + Check if index keynr is clustered and the query only uses its explicitly + defined parts. + + @detail + Clustered PK indexes are not present in table->covering_keys (one can say + a clustered index is always covering as it has all table's fields in it). + This function checks if the set of columns in the index definition + PRIMARY KEY(col1, ... colN) is covering for the quey. +*/ + +static bool index_is_clustered_covering(const TABLE *table, uint keynr) +{ + if (!table->file->is_clustering_key(keynr)) + return false; + + /* + Check that all fields used by the query are explicitly present + in the index. + */ + for (uint i= bitmap_get_first_set(table->read_set); + i != MY_BIT_NONE; + i= bitmap_get_next_set(table->read_set, i)) + { + if (!table->field[i]->part_of_key.is_set(keynr)) + return false; + } + return true; +} + + /** Test if this access method is applicable to a GROUP query with MIN/MAX functions, and if so, construct a new TRP object. @@ -14446,7 +14478,8 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time) (was also: "Exclude UNIQUE indexes ..." but this was removed because there are cases Loose Scan over a multi-part index is useful). */ - if (!table->covering_keys.is_set(cur_index) || + if ((!table->covering_keys.is_set(cur_index) && + !index_is_clustered_covering(table, cur_index)) || !table->keys_in_use_for_group_by.is_set(cur_index)) { cause= "not covering"; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c8dc944817e..470414f1c6f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -31089,16 +31089,21 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta, else eta->push_extra(ET_SCANNED_ALL_DATABASES); } - if (key_read) + + if (quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) { - if (quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX) - { - QUICK_GROUP_MIN_MAX_SELECT *qgs= - (QUICK_GROUP_MIN_MAX_SELECT *) tab_select->quick; - eta->push_extra(ET_USING_INDEX_FOR_GROUP_BY); - eta->loose_scan_is_scanning= qgs->loose_scan_is_scanning(); - } - else + QUICK_GROUP_MIN_MAX_SELECT *qgs= + (QUICK_GROUP_MIN_MAX_SELECT *) tab_select->quick; + eta->push_extra(ET_USING_INDEX_FOR_GROUP_BY); + eta->loose_scan_is_scanning= qgs->loose_scan_is_scanning(); + } + else + { + /* + Print "Using index" if we haven't already printed "Using index for + group by". + */ + if (key_read) eta->push_extra(ET_USING_INDEX); } if (table->reginfo.not_exists_optimize) From c7313daf8c32c3ab8fe180aa0e813fde6f208692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 2 Jan 2026 09:25:46 +0200 Subject: [PATCH 38/43] MDEV-38419: Fix some clang-21 -fsanitize=memory failures fil_space_t::free_page(): Turn a parameter into a template parameter, and remove some duplicated code. This fixes an error that was flagged by clang++-21 -fsanitize=memory in buf_page_create(). fseg_create(): Merge the parameter reserved_extent to n_reserved. This fixes an error about n_reserved being uninitialized. fseg_alloc_free_page_low(): Simplify a debug assertion. flst_add_last(), flst_remove(): Reduce the scope of a conditionally initialized variable. Reviewed by: Thirunarayanan Balathandayuthapani --- storage/innobase/buf/buf0buf.cc | 2 +- storage/innobase/fsp/fsp0fsp.cc | 14 ++++++------ storage/innobase/fut/fut0lst.cc | 36 ++++++++++++++++++------------ storage/innobase/include/fil0fil.h | 17 ++++++-------- storage/innobase/log/log0recv.cc | 6 +++-- 5 files changed, 41 insertions(+), 34 deletions(-) diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index c053970b090..67bac28ce3f 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -3474,7 +3474,7 @@ buf_block_t* buf_page_create(fil_space_t *space, uint32_t offset, ulint zip_size, mtr_t *mtr, buf_block_t *free_block) noexcept { - space->free_page(offset, false); + space->free_page(offset); return buf_page_create_low({space->id, offset}, zip_size, mtr, free_block); } diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index 40bfca1388c..a36143c3bcc 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -1683,8 +1683,7 @@ fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr, dberr_t *err, { fseg_inode_t* inode; ib_id_t seg_id; - uint32_t n_reserved; - bool reserved_extent = false; + uint32_t n_reserved = 0; DBUG_ENTER("fseg_create"); @@ -1712,16 +1711,16 @@ inode_alloc: if (!inode) { block = nullptr; reserve_extent: - if (!has_done_reservation && !reserved_extent) { + if (!has_done_reservation && !n_reserved) { *err = fsp_reserve_free_extents(&n_reserved, space, 2, FSP_NORMAL, mtr); if (UNIV_UNLIKELY(*err != DB_SUCCESS)) { DBUG_RETURN(nullptr); } + ut_ad(n_reserved > 0); /* Extents reserved successfully. So try allocating the page or inode */ - reserved_extent = true; if (inode) { goto page_alloc; } @@ -1791,7 +1790,8 @@ page_alloc: + block->page.frame, space->id); funct_exit: - if (!has_done_reservation && reserved_extent) { + if (n_reserved) { + ut_ad(!has_done_reservation); space->release_free_extents(n_reserved); } @@ -2173,8 +2173,6 @@ take_hinted_page: buf_block_t* block = fsp_alloc_free_page( space, hint, mtr, init_mtr, err); - ut_ad(block || !has_done_reservation || *err); - if (block) { /* Put the page in the fragment page array of the segment */ @@ -2187,6 +2185,8 @@ take_hinted_page: fseg_set_nth_frag_page_no( seg_inode, iblock, n, block->page.id().page_no(), mtr); + } else { + ut_ad(*err != DB_SUCCESS); } /* fsp_alloc_free_page() invoked fsp_init_file_page() diff --git a/storage/innobase/fut/fut0lst.cc b/storage/innobase/fut/fut0lst.cc index 8fe21e74b73..c421d070b66 100644 --- a/storage/innobase/fut/fut0lst.cc +++ b/storage/innobase/fut/fut0lst.cc @@ -281,12 +281,15 @@ dberr_t flst_add_last(buf_block_t *base, uint16_t boffset, return DB_CORRUPTION; buf_block_t *cur= add; - dberr_t err; - if (addr.page != add->page.id().page_no() && - !(cur= buf_page_get_gen(page_id_t{add->page.id().space(), addr.page}, - add->zip_size(), RW_SX_LATCH, nullptr, - BUF_GET_POSSIBLY_FREED, mtr, &err))) - return err; + if (addr.page != add->page.id().page_no()) + { + dberr_t err; + cur= buf_page_get_gen(page_id_t{add->page.id().space(), addr.page}, + add->zip_size(), RW_SX_LATCH, nullptr, + BUF_GET_POSSIBLY_FREED, mtr, &err); + if (!cur) + return err; + } return flst_insert_after(base, boffset, cur, addr.boffset, add, aoffset, limit, mtr); } @@ -382,16 +385,21 @@ dberr_t flst_remove(buf_block_t *base, uint16_t boffset, prev_addr.page, prev_addr.boffset, mtr); else { - dberr_t err2; - if (next_addr.page == cur->page.id().page_no() || - (cur= buf_page_get_gen(page_id_t(cur->page.id().space(), - next_addr.page), - cur->zip_size(), RW_SX_LATCH, nullptr, - BUF_GET_POSSIBLY_FREED, mtr, &err2))) + if (next_addr.page == cur->page.id().page_no()) + write_addr: flst_write_addr(*cur, cur->page.frame + next_addr.boffset + FLST_PREV, prev_addr.page, prev_addr.boffset, mtr); - else if (err == DB_SUCCESS) - err= err2; + else + { + dberr_t err2; + cur= buf_page_get_gen(page_id_t(cur->page.id().space(), next_addr.page), + cur->zip_size(), RW_SX_LATCH, nullptr, + BUF_GET_POSSIBLY_FREED, mtr, &err2); + if (cur) + goto write_addr; + if (err == DB_SUCCESS) + err= err2; + } } byte *len= &base->page.frame[boffset + FLST_LEN]; diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index e757231a64e..8c957e64cbb 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -995,19 +995,16 @@ public: @retval nullptr if the tablespace is missing or inaccessible */ static fil_space_t *get_for_write(ulint id) noexcept; - /** Add/remove the free page in the freed ranges list. - @param[in] offset page number to be added - @param[in] free true if page to be freed */ - void free_page(uint32_t offset, bool add=true) + /** Add/remove a page in freed_ranges. + @tparam add true=add, false=remove + @param offset page number */ + template void free_page(uint32_t offset) noexcept { std::lock_guard freed_lock(freed_range_mutex); if (add) - return freed_ranges.add_value(offset); - - if (freed_ranges.empty()) - return; - - return freed_ranges.remove_value(offset); + freed_ranges.add_value(offset); + else + freed_ranges.remove_value(offset); } /** Add the range of freed pages */ diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 52a5a0a457c..effb3ced673 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -2358,8 +2358,10 @@ static void store_freed_or_init_rec(page_id_t page_id, bool freed) space= fil_system.sys_space; else space= fil_space_get(space_id); - - space->free_page(page_no, freed); + if (freed) + space->free_page(page_no); + else + space->free_page(page_no); return; } From 37dce6b547decb7f21c80b00c8552ddb3f900b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 2 Jan 2026 10:25:13 +0200 Subject: [PATCH 39/43] MDEV-38463: subtraction of unsigned offset ... overflowed row_log_table_apply_ops(), row_log_apply_ops(): When switching buffers, do not subtract addresses of unrelated buffers, but simply set mrec_end relative to mrec. This makes the code more readable and will avoid a runtime error from clang -fsanitize=undefined depending on what kind of addresses are being returned by row_log_block_allocate(). --- storage/innobase/row/row0log.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index daa2036ee39..4598a62c9af 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -2806,10 +2806,10 @@ process_next_block: ut_ad(0); goto unexpected_eof; } else { - memcpy(index->online_log->head.buf, mrec, - ulint(mrec_end - mrec)); - mrec_end -= ulint(mrec - index->online_log->head.buf); + const size_t s = size_t(mrec_end - mrec); + memcpy(index->online_log->head.buf, mrec, s); mrec = index->online_log->head.buf; + mrec_end = mrec + s; goto process_next_block; } } @@ -3698,10 +3698,10 @@ process_next_block: ut_ad(0); goto unexpected_eof; } else { - memcpy(index->online_log->head.buf, mrec, - ulint(mrec_end - mrec)); - mrec_end -= ulint(mrec - index->online_log->head.buf); + const size_t s = size_t(mrec_end - mrec); + memcpy(index->online_log->head.buf, mrec, s); mrec = index->online_log->head.buf; + mrec_end = mrec + s; goto process_next_block; } } From 3b2169f0d1e116bf3f648b7f961bb5b5bde9133c Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Thu, 1 Jan 2026 13:10:39 +0100 Subject: [PATCH 40/43] MDEV-37862: innodb.gap_locks test failure: 0 lock structs, 0 row locks The test fails sporadically due to an insufficient --replace_regex. The output of SHOW ENGINE INNODB STATUS can have some "not started" transactions in additional to the "ACTIVE" transaction the test wants to display, and the --replace_regex would wrongly pick one of the "not started" transactions to show if it occured after the "ACTIVE" one in the output. Signed-off-by: Kristian Nielsen --- mysql-test/suite/innodb/t/gap_locks.test | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/innodb/t/gap_locks.test b/mysql-test/suite/innodb/t/gap_locks.test index 77ce2c842b1..e0af735ef67 100644 --- a/mysql-test/suite/innodb/t/gap_locks.test +++ b/mysql-test/suite/innodb/t/gap_locks.test @@ -18,7 +18,23 @@ ELSE 'SERIALIZABLE' END`; eval SET TRANSACTION ISOLATION LEVEL $tx_isolation; BEGIN; DELETE FROM t1 WHERE b='2' AND c=2; ---replace_regex /.*(\d+ lock struct...), heap size \d+(, \d+ row lock...).*/\1\2/ +# The SHOW ENGINE INNODB STATUS looks like this: +# +# ... +# +LIST OF TRANSACTIONS FOR EACH SESSION: +# +---TRANSACTION (0x7f1e08168300), not started +# +0 lock struct(s), heap size 1144, 0 row lock(s) +# +---TRANSACTION (0x7f1e08167780), not started +# +0 lock struct(s), heap size 1144, 0 row lock(s) +# +---TRANSACTION 25, ACTIVE 0 sec +# +2 lock struct(s), heap size 1144, 1 row lock(s) +# ... +# +# We want the lock struct(s) and row lock(s) of the ACTIVE transaction, but +# not of any inactive ones. Note the use of non-greedy match ".*?", so that +# we get the values for the ACTIVE transaction, not the last values in the +# list. +--replace_regex /.*---TRANSACTION \d+, ACTIVE.*?(\d+ lock struct...), heap size \d+(, \d+ row lock...).*/\1\2/ SHOW ENGINE INNODB STATUS; ROLLBACK; From b64db51ad89d78e6a6f8bc238dd2e208b5f7aa92 Mon Sep 17 00:00:00 2001 From: Kristian Nielsen Date: Fri, 2 Jan 2026 11:37:37 +0100 Subject: [PATCH 41/43] Fix leaving system table as InnoDB engine Fix the test case main.servers to restore the table mysql.servers back to Aria engine, not leave it changed to InnoDB. This was causing the test case innodb.innodb_stats to fail sporadically if it was run after main.servers. Signed-off-by: Kristian Nielsen --- mysql-test/main/servers.result | 1 + mysql-test/main/servers.test | 2 ++ 2 files changed, 3 insertions(+) diff --git a/mysql-test/main/servers.result b/mysql-test/main/servers.result index a02c3d6ac74..2e3697b6a2c 100644 --- a/mysql-test/main/servers.result +++ b/mysql-test/main/servers.result @@ -50,3 +50,4 @@ CREATE SERVER s1 FOREIGN DATA WRAPPER mysql OPTIONS (HOST '127.0.0.1'); ALTER TABLE mysql.servers ENGINE=innodb; FLUSH PRIVILEGES; drop server s1; +ALTER TABLE mysql.servers ENGINE=Aria; diff --git a/mysql-test/main/servers.test b/mysql-test/main/servers.test index f46041bb21c..f83a28ff33a 100644 --- a/mysql-test/main/servers.test +++ b/mysql-test/main/servers.test @@ -50,3 +50,5 @@ CREATE SERVER s1 FOREIGN DATA WRAPPER mysql OPTIONS (HOST '127.0.0.1'); ALTER TABLE mysql.servers ENGINE=innodb; FLUSH PRIVILEGES; drop server s1; +# Restore the table back to original engine, to not disrupt following tests. +ALTER TABLE mysql.servers ENGINE=Aria; From 037a0aa96f7ac0389b4e78fa3b482a08d83dce2a Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Tue, 25 Nov 2025 00:07:07 +0400 Subject: [PATCH 42/43] MDEV-38182 - main.xa fails sporadically Test was affected by incompletely closed preceding connections such that count_sessions.inc records value larger than 1 (for the default connection). This causes subsequent wait_until_count_sessions.inc not to wait for connections it was supposed to wait for. Replaced count_sessions.inc with hardcoded 1. --- mysql-test/main/xa.test | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mysql-test/main/xa.test b/mysql-test/main/xa.test index 104509b6cc6..289f8e7d47c 100644 --- a/mysql-test/main/xa.test +++ b/mysql-test/main/xa.test @@ -5,7 +5,7 @@ --source include/not_embedded.inc # Save the initial number of concurrent sessions ---source include/count_sessions.inc +let $count_sessions=1; call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction"); --disable_query_log @@ -694,7 +694,6 @@ xa rollback 'test1'; xa recover; drop table asd; disconnect con_tmp; ---source include/wait_until_disconnected.inc connection default; --echo # MDEV-30978: a read-only server should still allow the commit of @@ -718,7 +717,6 @@ xa rollback '2'; --echo # Read-only disconnect case ---source include/count_sessions.inc connect (con1_ro,localhost,root,,); xa start '3'; select 0; @@ -732,7 +730,6 @@ xa recover; --error ER_XA_RBROLLBACK xa commit '3'; ---source include/count_sessions.inc connect (con2_ro,localhost,root,,); xa start '4'; select 0; @@ -781,6 +778,7 @@ xa end 'r:foo'; xa prepare 'r:foo'; disconnect con1; connection default; +--source include/wait_until_count_sessions.inc xa recover; xa start 'r:bar'; From faf1a76516626fb3393cb6501f319bc146f44d97 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Sat, 27 Dec 2025 23:57:32 +0400 Subject: [PATCH 43/43] MDEV-38437 - main.ssl fails sporadically Test was affected by incompletely closed preceding connections. Make test agnostic to concurrent connections by querying performance_schema.status_by_thread only for connections that it uses. --- mysql-test/main/ssl.result | 2 +- mysql-test/main/ssl.test | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mysql-test/main/ssl.result b/mysql-test/main/ssl.result index c402f02b859..c1d2ee54c0f 100644 --- a/mysql-test/main/ssl.result +++ b/mysql-test/main/ssl.result @@ -1,5 +1,5 @@ connect ssl_con,localhost,root,,,,,SSL; -select variable_name from performance_schema.status_by_thread where VARIABLE_NAME LIKE 'Ssl%'; +select variable_name from performance_schema.status_by_thread where VARIABLE_NAME LIKE 'Ssl%' and thread_id in($default_id, $ssl_con_id); variable_name Ssl_cipher Ssl_cipher_list diff --git a/mysql-test/main/ssl.test b/mysql-test/main/ssl.test index 53d93ff1593..83bae7bde04 100644 --- a/mysql-test/main/ssl.test +++ b/mysql-test/main/ssl.test @@ -12,9 +12,12 @@ --source include/default_charset.inc --source include/have_perfschema.inc +let $default_id=`select thread_id from performance_schema.threads where processlist_id=connection_id()`; + connect (ssl_con,localhost,root,,,,,SSL); -select variable_name from performance_schema.status_by_thread where VARIABLE_NAME LIKE 'Ssl%'; +let $ssl_con_id=`select thread_id from performance_schema.threads where processlist_id=connection_id()`; +evalp select variable_name from performance_schema.status_by_thread where VARIABLE_NAME LIKE 'Ssl%' and thread_id in($default_id, $ssl_con_id); # Check ssl turned on SELECT (VARIABLE_VALUE <> '') AS have_ssl FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME='Ssl_cipher';