
[Commits] 31fabc1: MDEV-29139 Crash when using ANY predicand with redundant subquery in GROUP BY clause
by IgorBabaev 21 Jul '22
by IgorBabaev 21 Jul '22
21 Jul '22
revision-id: 31fabc1f4d6518d0102d6c2b9075f71198d5ca58 (mariadb-10.3.35-73-g31fabc1)
parent(s): 1848804840f5595f982c4cd502ba2112f6dd7911
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-07-21 14:57:35 -0700
message:
MDEV-29139 Crash when using ANY predicand with redundant subquery in GROUP BY clause
This bug could cause a crash of the server when executing queries containing
ANY/ALL predicands with redundant subqueries in GROUP BY clauses.
These subqueries are eliminated by remove_redundant_subquery_clause()
together with elimination of GROUP BY list containing these subqueries.
However the references to the elements of the GROUP BY remained in the
JOIN::all_fields list of the right operand of of the ALL/ANY predicand.
Later these references confused make_aggr_tables_info() when forming
proper execution structures after ALL/ANY predicands had been replaced
with expressions containing MIN/MAX set functions.
The patch just removes these references from JOIN::all_fields list used
by the subquery of the ALL/ANY predicand when its GROUP BY clause is
eliminated.
Approved by Oleksandr Byelkin <sanja(a)mariadb.com>
---
mysql-test/main/subselect4.result | 57 +++++++++++++++++++++++++++++++++++++++
mysql-test/main/subselect4.test | 39 +++++++++++++++++++++++++++
sql/sql_select.cc | 18 ++++++++++++-
3 files changed, 113 insertions(+), 1 deletion(-)
diff --git a/mysql-test/main/subselect4.result b/mysql-test/main/subselect4.result
index ba5a6bb..c374040 100644
--- a/mysql-test/main/subselect4.result
+++ b/mysql-test/main/subselect4.result
@@ -2981,4 +2981,61 @@ ANALYZE
}
DROP TABLE t1;
# End of 10.2 tests
+#
+# MDEV-29139: Redundannt subquery in GROUP BY clause of ANY/ALL subquery
+#
+create table t1 (a int);
+insert into t1 values (3), (1), (2);
+create table t2 (b int not null);
+insert into t2 values (4), (2);
+create table t3 (c int);
+insert into t3 values (7), (1);
+explain extended select a from t1
+where a >= any (select b from t2 group by (select c from t3 where c = 1));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 2 100.00
+Warnings:
+Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where <nop>(<in_optimizer>(`test`.`t1`.`a`,(/* select#2 */ select min(`test`.`t2`.`b`) from `test`.`t2`) <= <cache>(`test`.`t1`.`a`)))
+select a from t1
+where a >= any (select b from t2 group by (select c from t3 where c = 1));
+a
+3
+2
+prepare stmt from "select a from t1
+where a >= any (select b from t2 group by (select c from t3 where c = 1))";
+execute stmt;
+a
+3
+2
+execute stmt;
+a
+3
+2
+deallocate prepare stmt;
+explain extended select a from t1
+where a <= all (select b from t2 group by (select c from t3 where c = 1));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 2 100.00
+Warnings:
+Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where <not>(<in_optimizer>(`test`.`t1`.`a`,(/* select#2 */ select min(`test`.`t2`.`b`) from `test`.`t2`) < <cache>(`test`.`t1`.`a`)))
+select a from t1
+where a <= all (select b from t2 group by (select c from t3 where c = 1));
+a
+1
+2
+explain extended select a from t1
+where a >= any (select b from t2 group by 1 + (select c from t3 where c = 1));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 2 100.00
+Warnings:
+Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where <nop>(<in_optimizer>(`test`.`t1`.`a`,(/* select#2 */ select min(`test`.`t2`.`b`) from `test`.`t2`) <= <cache>(`test`.`t1`.`a`)))
+select a from t1
+where a >= any (select b from t2 group by 1 + (select c from t3 where c = 1));
+a
+3
+2
+drop table t1,t2,t3;
# End of 10.3 tests
diff --git a/mysql-test/main/subselect4.test b/mysql-test/main/subselect4.test
index aeef884..2faede5 100644
--- a/mysql-test/main/subselect4.test
+++ b/mysql-test/main/subselect4.test
@@ -2438,4 +2438,43 @@ DROP TABLE t1;
--echo # End of 10.2 tests
+
+--echo #
+--echo # MDEV-29139: Redundannt subquery in GROUP BY clause of ANY/ALL subquery
+--echo #
+
+create table t1 (a int);
+insert into t1 values (3), (1), (2);
+create table t2 (b int not null);
+insert into t2 values (4), (2);
+create table t3 (c int);
+insert into t3 values (7), (1);
+
+let $q1=
+select a from t1
+ where a >= any (select b from t2 group by (select c from t3 where c = 1));
+
+eval explain extended $q1;
+eval $q1;
+
+eval prepare stmt from "$q1";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+let $q2=
+select a from t1
+ where a <= all (select b from t2 group by (select c from t3 where c = 1));
+
+eval explain extended $q2;
+eval $q2;
+
+let $q3=
+select a from t1
+ where a >= any (select b from t2 group by 1 + (select c from t3 where c = 1));
+eval explain extended $q3;
+eval $q3;
+
+drop table t1,t2,t3;
+
--echo # End of 10.3 tests
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 0803e78..049d976 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -544,6 +544,7 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
return false;
}
+
/**
The following clauses are redundant for subqueries:
@@ -605,7 +606,22 @@ void remove_redundant_subquery_clauses(st_select_lex *subq_select_lex)
Here SUBQ cannot be removed.
*/
if (!ord->in_field_list)
+ {
(*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL);
+ /*
+ Remove from the JOIN::all_fields list any reference to the elements
+ of the eliminated GROUP BY list unless it is 'in_field_list'.
+ This is needed in order not to confuse JOIN::make_aggr_tables_info()
+ when it constructs different structure for execution phase.
+ */
+ List_iterator<Item> li(subq_select_lex->join->all_fields);
+ Item *item;
+ while ((item= li++))
+ {
+ if (item == *ord->item)
+ li.remove();
+ }
+ }
}
subq_select_lex->join->group_list= NULL;
subq_select_lex->group_list.empty();
@@ -2874,7 +2890,7 @@ bool JOIN::make_aggr_tables_info()
DBUG_ENTER("JOIN::make_aggr_tables_info");
const bool has_group_by= this->group;
-
+
sort_and_group_aggr_tab= NULL;
if (group_optimized_away)
1
0

[Commits] 660269c: MDEV-29139 Crash when using ANY predicand with redundant subquery in GROUP BY clause
by IgorBabaev 21 Jul '22
by IgorBabaev 21 Jul '22
21 Jul '22
revision-id: 660269c6b75d81b9ee9cbe935af00779b04bf30a (mariadb-10.3.35-73-g660269c)
parent(s): 1848804840f5595f982c4cd502ba2112f6dd7911
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-07-20 16:00:20 -0700
message:
MDEV-29139 Crash when using ANY predicand with redundant subquery in GROUP BY clause
This bug could cause a crash of the server when executing queries containing
ANY/ALL predicands with redundant subqueries in GROUP BY clauses.
These subqueries are eliminated by remove_redundant_subquery_clause().
However the references to them remained in the JOIN::all_fields list of the
ALL/ANY predicand. Later these references confused make_aggr_tables_info()
when forming proper execution structures after ALL/ANY predicands had been
replaced with expressions containing MIN/MAX set functions.
The patch just removes these references from JOIN::all_fields list used
by the subquery of the ALL/ANY predicand after its GROUP BY clause has been
altogether eliminated.
Approved by Oleksandr Byelkin <sanja(a)mariadb.com>
---
mysql-test/main/subselect4.result | 45 +++++++++++++++++++++++++++++++++++++++
mysql-test/main/subselect4.test | 33 ++++++++++++++++++++++++++++
sql/sql_select.cc | 26 +++++++++++++++++++++-
sql/sql_select.h | 1 +
4 files changed, 104 insertions(+), 1 deletion(-)
diff --git a/mysql-test/main/subselect4.result b/mysql-test/main/subselect4.result
index ba5a6bb..932ca1e 100644
--- a/mysql-test/main/subselect4.result
+++ b/mysql-test/main/subselect4.result
@@ -2981,4 +2981,49 @@ ANALYZE
}
DROP TABLE t1;
# End of 10.2 tests
+#
+# MDEV-29139: Redundannt subquery in GROUP BY clause of ANY/ALL subquery
+#
+create table t1 (a int);
+insert into t1 values (3), (1), (2);
+create table t2 (b int not null);
+insert into t2 values (4), (2);
+create table t3 (c int);
+insert into t3 values (7), (1);
+explain extended select a from t1
+where a >= any (select b from t2 group by (select c from t3 where c = 1));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 2 100.00
+Warnings:
+Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where <nop>(<in_optimizer>(`test`.`t1`.`a`,(/* select#2 */ select min(`test`.`t2`.`b`) from `test`.`t2`) <= <cache>(`test`.`t1`.`a`)))
+select a from t1
+where a >= any (select b from t2 group by (select c from t3 where c = 1));
+a
+3
+2
+prepare stmt from "select a from t1
+where a >= any (select b from t2 group by (select c from t3 where c = 1))";
+execute stmt;
+a
+3
+2
+execute stmt;
+a
+3
+2
+deallocate prepare stmt;
+explain extended select a from t1
+where a <= all (select b from t2 group by (select c from t3 where c = 1));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 2 100.00
+Warnings:
+Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where <not>(<in_optimizer>(`test`.`t1`.`a`,(/* select#2 */ select min(`test`.`t2`.`b`) from `test`.`t2`) < <cache>(`test`.`t1`.`a`)))
+select a from t1
+where a <= all (select b from t2 group by (select c from t3 where c = 1));
+a
+1
+2
+drop table t1,t2,t3;
# End of 10.3 tests
diff --git a/mysql-test/main/subselect4.test b/mysql-test/main/subselect4.test
index aeef884..a59f4bb 100644
--- a/mysql-test/main/subselect4.test
+++ b/mysql-test/main/subselect4.test
@@ -2438,4 +2438,37 @@ DROP TABLE t1;
--echo # End of 10.2 tests
+
+--echo #
+--echo # MDEV-29139: Redundannt subquery in GROUP BY clause of ANY/ALL subquery
+--echo #
+
+create table t1 (a int);
+insert into t1 values (3), (1), (2);
+create table t2 (b int not null);
+insert into t2 values (4), (2);
+create table t3 (c int);
+insert into t3 values (7), (1);
+
+let $q1=
+select a from t1
+ where a >= any (select b from t2 group by (select c from t3 where c = 1));
+
+eval explain extended $q1;
+eval $q1;
+
+eval prepare stmt from "$q1";
+execute stmt;
+execute stmt;
+deallocate prepare stmt;
+
+let $q2=
+select a from t1
+ where a <= all (select b from t2 group by (select c from t3 where c = 1));
+
+eval explain extended $q2;
+eval $q2;
+
+drop table t1,t2,t3;
+
--echo # End of 10.3 tests
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 0803e78..7a66f77 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -544,6 +544,28 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
return false;
}
+
+/**
+ @brief
+ Remove elements that are not actual anymore in the list of all fields
+
+ @note
+ This is needed in order not to confuse JOIN::make_aggr_tables_info().
+*/
+
+void JOIN::remove_eliminated_subqueries_from_all_fields_list()
+{
+ List_iterator<Item> li(all_fields);
+ Item *item;
+ while ((item= li++))
+ {
+ if (item->type() == Item::SUBSELECT_ITEM &&
+ ((Item_subselect *) item)->eliminated)
+ li.remove();
+ }
+}
+
+
/**
The following clauses are redundant for subqueries:
@@ -1177,8 +1199,10 @@ JOIN::prepare(TABLE_LIST *tables_init,
!thd->lex->is_view_context_analysis()) // 3)
{
remove_redundant_subquery_clauses(select_lex);
+ remove_eliminated_subqueries_from_all_fields_list();
}
+
/* Resolve the ORDER BY that was skipped, then remove it. */
if (skip_order_by && select_lex !=
select_lex->master_unit()->global_parameters())
@@ -2874,7 +2898,7 @@ bool JOIN::make_aggr_tables_info()
DBUG_ENTER("JOIN::make_aggr_tables_info");
const bool has_group_by= this->group;
-
+
sort_and_group_aggr_tab= NULL;
if (group_optimized_away)
diff --git a/sql/sql_select.h b/sql/sql_select.h
index c8ff8b2..7932b5d 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -1804,6 +1804,7 @@ class JOIN :public Sql_alloc
void cleanup_item_list(List<Item> &items) const;
bool add_having_as_table_cond(JOIN_TAB *tab);
+ void remove_eliminated_subqueries_from_all_fields_list();
bool make_aggr_tables_info();
bool add_fields_for_current_rowid(JOIN_TAB *cur, List<Item> *fields);
};
1
0

[Commits] 6570149: MDEV-29088 Server crash upon CREATE VIEW with unknown column in ON condition
by IgorBabaev 13 Jul '22
by IgorBabaev 13 Jul '22
13 Jul '22
revision-id: 65701497583f86c5958f6c91e42b5c504eb54b9e (mariadb-10.4.25-26-g6570149)
parent(s): 9a0cbd31ce8576468981b14b066dea155cb922d9
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-07-13 11:13:06 -0700
message:
MDEV-29088 Server crash upon CREATE VIEW with unknown column in ON condition
This bug caused crashes when the server executed such a CREATE VIEW
statement whose view specification contained a reference to an unknown
column in a subquery used in ON condition.
The cause of this bug is quite similar to the cause of the bug MDEV-26412.
The fix of this bug is quite similar to the fix for MDEV-26412.
Approved by Sergey Petrunia <sergey(a)mariadb.com>
---
mysql-test/main/view.result | 13 +++++++++++++
mysql-test/main/view.test | 18 ++++++++++++++++++
sql/sql_yacc.yy | 2 ++
3 files changed, 33 insertions(+)
diff --git a/mysql-test/main/view.result b/mysql-test/main/view.result
index 11686fe..e92ba3f 100644
--- a/mysql-test/main/view.result
+++ b/mysql-test/main/view.result
@@ -6896,4 +6896,17 @@ ERROR 42S22: Unknown column 't1.x' in 'on clause'
CREATE TABLE t4 AS SELECT * FROM t1 JOIN t2 ON t1.x > t2.b;
ERROR 42S22: Unknown column 't1.x' in 'on clause'
DROP TABLE t1,t2,t3;
+#
+# MDEV-29088: view specification contains unknown column in ON condition
+#
+create table t1 (a int);
+create table t2 (b int);
+create table t3 (c int);
+create view v as
+select * from t1 left join t2 on t1.a=t2.b and t1.a in (select d from t3);
+ERROR 42S22: Unknown column 'd' in 'field list'
+create algorithm=merge view v as
+select * from t1 left join t2 on t1.a=t2.b and t1.a in (select d from t3);
+ERROR 42S22: Unknown column 'd' in 'field list'
+drop table t1,t2,t3;
# End of 10.4 tests
diff --git a/mysql-test/main/view.test b/mysql-test/main/view.test
index 5b813b4..6e65666 100644
--- a/mysql-test/main/view.test
+++ b/mysql-test/main/view.test
@@ -6626,4 +6626,22 @@ CREATE TABLE t4 AS SELECT * FROM t1 JOIN t2 ON t1.x > t2.b;
DROP TABLE t1,t2,t3;
+--echo #
+--echo # MDEV-29088: view specification contains unknown column in ON condition
+--echo #
+
+create table t1 (a int);
+create table t2 (b int);
+create table t3 (c int);
+
+--error ER_BAD_FIELD_ERROR
+create view v as
+ select * from t1 left join t2 on t1.a=t2.b and t1.a in (select d from t3);
+
+--error ER_BAD_FIELD_ERROR
+create algorithm=merge view v as
+ select * from t1 left join t2 on t1.a=t2.b and t1.a in (select d from t3);
+
+drop table t1,t2,t3;
+
--echo # End of 10.4 tests
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index ff91a64..57540f9 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -2829,6 +2829,7 @@ create:
{
if (Lex->main_select_push())
MYSQL_YYABORT;
+ Lex->inc_select_stack_outer_barrier();
if (Lex->add_create_view(thd, $1 | $5,
DTYPE_ALGORITHM_UNDEFINED, $3, $6))
MYSQL_YYABORT;
@@ -2844,6 +2845,7 @@ create:
MYSQL_YYABORT;
if (Lex->main_select_push())
MYSQL_YYABORT;
+ Lex->inc_select_stack_outer_barrier();
}
view_list_opt AS view_select
{
1
0

[Commits] fadf955: MDEV-29088 Server crash upon CREATE VIEW with unknown column in ON condition
by IgorBabaev 13 Jul '22
by IgorBabaev 13 Jul '22
13 Jul '22
revision-id: fadf9556aaa005855b2a6546783ee9483c2e0616 (mariadb-10.4.25-26-gfadf955)
parent(s): 9a0cbd31ce8576468981b14b066dea155cb922d9
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-07-12 15:35:11 -0700
message:
MDEV-29088 Server crash upon CREATE VIEW with unknown column in ON condition
This bug caused crashes when the server executed such a CREATE VIEW
statement whose view specification contained a reference to an unknown
column in a subquery used in ON condition.
The cause of this bug is quite similar to the cause of the bug MDEV-26412.
The fix of this bug is quite similar to the fix for MDEV-26412.
---
mysql-test/main/view.result | 13 +++++++++++++
mysql-test/main/view.test | 18 ++++++++++++++++++
sql/sql_yacc.yy | 2 ++
3 files changed, 33 insertions(+)
diff --git a/mysql-test/main/view.result b/mysql-test/main/view.result
index 11686fe..e92ba3f 100644
--- a/mysql-test/main/view.result
+++ b/mysql-test/main/view.result
@@ -6896,4 +6896,17 @@ ERROR 42S22: Unknown column 't1.x' in 'on clause'
CREATE TABLE t4 AS SELECT * FROM t1 JOIN t2 ON t1.x > t2.b;
ERROR 42S22: Unknown column 't1.x' in 'on clause'
DROP TABLE t1,t2,t3;
+#
+# MDEV-29088: view specification contains unknown column in ON condition
+#
+create table t1 (a int);
+create table t2 (b int);
+create table t3 (c int);
+create view v as
+select * from t1 left join t2 on t1.a=t2.b and t1.a in (select d from t3);
+ERROR 42S22: Unknown column 'd' in 'field list'
+create algorithm=merge view v as
+select * from t1 left join t2 on t1.a=t2.b and t1.a in (select d from t3);
+ERROR 42S22: Unknown column 'd' in 'field list'
+drop table t1,t2,t3;
# End of 10.4 tests
diff --git a/mysql-test/main/view.test b/mysql-test/main/view.test
index 5b813b4..6e65666 100644
--- a/mysql-test/main/view.test
+++ b/mysql-test/main/view.test
@@ -6626,4 +6626,22 @@ CREATE TABLE t4 AS SELECT * FROM t1 JOIN t2 ON t1.x > t2.b;
DROP TABLE t1,t2,t3;
+--echo #
+--echo # MDEV-29088: view specification contains unknown column in ON condition
+--echo #
+
+create table t1 (a int);
+create table t2 (b int);
+create table t3 (c int);
+
+--error ER_BAD_FIELD_ERROR
+create view v as
+ select * from t1 left join t2 on t1.a=t2.b and t1.a in (select d from t3);
+
+--error ER_BAD_FIELD_ERROR
+create algorithm=merge view v as
+ select * from t1 left join t2 on t1.a=t2.b and t1.a in (select d from t3);
+
+drop table t1,t2,t3;
+
--echo # End of 10.4 tests
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index ff91a64..57540f9 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -2829,6 +2829,7 @@ create:
{
if (Lex->main_select_push())
MYSQL_YYABORT;
+ Lex->inc_select_stack_outer_barrier();
if (Lex->add_create_view(thd, $1 | $5,
DTYPE_ALGORITHM_UNDEFINED, $3, $6))
MYSQL_YYABORT;
@@ -2844,6 +2845,7 @@ create:
MYSQL_YYABORT;
if (Lex->main_select_push())
MYSQL_YYABORT;
+ Lex->inc_select_stack_outer_barrier();
}
view_list_opt AS view_select
{
1
0

[Commits] cd8b27b: MDEV-28965 Assertion failure when preparing UPDATE with derived table in WHERE
by IgorBabaev 12 Jul '22
by IgorBabaev 12 Jul '22
12 Jul '22
revision-id: cd8b27bb537d03ed4042cdca3176ad7134b892a7 (mariadb-10.6.1-477-gcd8b27b)
parent(s): 2db18fdb3d68d906fbd188ec570a64502ba55849
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-07-11 16:57:37 -0700
message:
MDEV-28965 Assertion failure when preparing UPDATE with derived table in WHERE
This patch fixes not only the assertion failure in the function
Field_iterator_table_ref::set_field_iterator() but also:
- fixes the problem of forced materialization of derived tables used
in subqueries contained in WHERE clauses of single-table and multi-table
UPDATE and DELETE statements
- fixes the problem of MDEV-17954 that prevented execution of multi-table
DELETE statements if they use in their WHERE clauses references to
the tables that are updated.
The patch must be considered a complement to the patch for MDEV-28883.
Approved by Oleksandr Byelkin <sanja(a)mariadb.com>
---
mysql-test/main/delete_use_source.result | 184 ++++++++++-
mysql-test/main/delete_use_source.test | 120 ++++++++
mysql-test/main/derived.result | 336 +++++++++++++++++++++
mysql-test/main/derived.test | 210 +++++++++++++
mysql-test/main/derived_cond_pushdown.result | 27 +-
mysql-test/main/derived_cond_pushdown.test | 2 +-
mysql-test/main/multi_update.result | 2 -
mysql-test/main/multi_update.test | 2 -
mysql-test/main/subselect.result | 1 -
mysql-test/main/subselect.test | 1 -
mysql-test/main/subselect_no_exists_to_in.result | 1 -
mysql-test/main/subselect_no_mat.result | 1 -
mysql-test/main/subselect_no_opts.result | 1 -
mysql-test/main/subselect_no_scache.result | 1 -
mysql-test/main/subselect_no_semijoin.result | 1 -
.../engines/iuds/r/update_delete_number.result | 22 +-
.../suite/engines/iuds/t/update_delete_number.test | 20 +-
sql/opt_subselect.cc | 56 +++-
sql/sql_base.cc | 38 ++-
sql/sql_delete.cc | 63 ++--
sql/sql_delete.h | 15 +-
sql/sql_lex.cc | 28 +-
sql/sql_lex.h | 1 +
sql/sql_update.cc | 21 +-
sql/sql_update.h | 16 +-
sql/sql_yacc.yy | 15 +
sql/table.cc | 10 +-
27 files changed, 1107 insertions(+), 88 deletions(-)
diff --git a/mysql-test/main/delete_use_source.result b/mysql-test/main/delete_use_source.result
index 0990a55..329203a 100644
--- a/mysql-test/main/delete_use_source.result
+++ b/mysql-test/main/delete_use_source.result
@@ -49,7 +49,7 @@ rollback;
start transaction;
explain delete from v1 where (select count(*) from t1 b where b.c1=v1.c1) = 500 limit 1;
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY t1 range c1 c1 4 NULL 600 Using where
+1 PRIMARY t1 range c1 c1 4 NULL 600 Using index condition; Using where
2 DEPENDENT SUBQUERY b ref c1 c1 4 test.t1.c1 167 Using index
delete from v1 where (select count(*) from t1 b where b.c1=v1.c1) = 500 limit 1;
affected rows: 1
@@ -65,7 +65,7 @@ rollback;
start transaction;
explain delete from v1 where (select count(*) from t1 b where b.c1=v1.c1) = 500;
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY t1 ALL c1 NULL NULL NULL # Using where
+1 PRIMARY t1 range c1 c1 4 NULL # Using index condition; Using where
2 DEPENDENT SUBQUERY b ref c1 c1 4 test.t1.c1 # Using index
delete from v1 where (select count(*) from t1 b where b.c1=v1.c1) = 500 ;
affected rows: 500
@@ -154,3 +154,183 @@ set session sort_buffer_size = 1024;
delete from t1 where c1=0 and exists(select 'x' from t1 b where b.c1<10);
affected rows: 128000
drop table t1;
+#
+# MDEV-17954: multi-table DELETE with the same source and target
+#
+create table t1 (c1 int, c2 int, c3 int);
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+#
+# Single-table DELETE with the same source and target
+# handled as multi-table DELETE
+#
+explain delete from t1
+where c2 in (select distinct a.c2 from t1 a where t1.c1=a.c1 and a.c2 < 3);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 8 Using where
+1 PRIMARY a ALL NULL NULL NULL NULL 8 Using where; FirstMatch(t1)
+delete from t1
+where c2 in (select distinct a.c2 from t1 a where t1.c1=a.c1 and a.c2 < 3);
+select * from t1;
+c1 c2 c3
+1 3 3
+2 3 6
+2 4 7
+2 5 8
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+prepare stmt from "delete from t1
+where c2 in (select distinct a.c2 from t1 a where t1.c1=a.c1 and a.c2 < 3)";
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 3 3
+2 3 6
+2 4 7
+2 5 8
+delete from t1;
+insert into t1 values
+(2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+c1 c2 c3
+2 3 6
+2 4 7
+2 5 8
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+#
+# Multi-table DELETE with the same source and target
+#
+create table t2 (c1 int, c2 int, c3 int);
+insert into t2 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,5,8);
+explain delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 7
+1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using where
+delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+prepare stmt from "delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1";
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+delete from t1;
+insert into t1 values
+(2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+c1 c2 c3
+2 4 7
+deallocate prepare stmt;
+explain delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 system NULL NULL NULL NULL 1
+1 SIMPLE t2 ALL NULL NULL NULL NULL 7 Using where
+delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1;
+select * from t1;
+c1 c2 c3
+2 4 7
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+prepare stmt from "delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1";
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+delete from t1;
+insert into t1 values
+(2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+c1 c2 c3
+2 4 7
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+explain delete from t1,t2 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1 and t2.c1 > 1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 7 Using where
+1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using where
+delete from t1,t2 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1 and t2.c1 > 1;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+select * from t2;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+delete from t2;
+insert into t2 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,5,8);
+prepare stmt from "delete from t1,t2 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1 and t2.c1 > 1";
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+select * from t2;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+delete from t1;
+insert into t1 values
+(1,2,2), (1,3,3), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+delete from t2;
+insert into t2 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5);
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 2 2
+1 3 3
+2 3 6
+2 4 7
+2 5 8
+select * from t2;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 1 4
+deallocate prepare stmt;
+drop table t1,t2;
diff --git a/mysql-test/main/delete_use_source.test b/mysql-test/main/delete_use_source.test
index 4aed00d..9625431 100644
--- a/mysql-test/main/delete_use_source.test
+++ b/mysql-test/main/delete_use_source.test
@@ -135,3 +135,123 @@ set session sort_buffer_size = 1024;
delete from t1 where c1=0 and exists(select 'x' from t1 b where b.c1<10);
drop table t1;
+
+--echo #
+--echo # MDEV-17954: multi-table DELETE with the same source and target
+--echo #
+
+create table t1 (c1 int, c2 int, c3 int);
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+
+--echo #
+--echo # Single-table DELETE with the same source and target
+--echo # handled as multi-table DELETE
+--echo #
+
+let $q1=
+delete from t1
+ where c2 in (select distinct a.c2 from t1 a where t1.c1=a.c1 and a.c2 < 3);
+
+eval explain $q1;
+eval $q1;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+eval prepare stmt from "$q1";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+
+--echo #
+--echo # Multi-table DELETE with the same source and target
+--echo #
+
+create table t2 (c1 int, c2 int, c3 int);
+insert into t2 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,5,8);
+
+let $q2=
+delete from t1 using t1,t2
+ where t1.c2 = t2.c2 and t1.c1 > 1;
+
+eval explain $q2;
+eval $q2;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+eval prepare stmt from "$q2";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+let $q2=
+delete from t1 using t1,t2
+ where t1.c2 = t2.c2 and t1.c1 > 1;
+
+eval explain $q2;
+eval $q2;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+eval prepare stmt from "$q2";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+
+let $q3=
+delete from t1,t2 using t1,t2
+ where t1.c2 = t2.c2 and t1.c1 > 1 and t2.c1 > 1;
+
+eval explain $q3;
+eval $q3;
+select * from t1;
+select * from t2;
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+delete from t2;
+insert into t2 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,5,8);
+eval prepare stmt from "$q3";
+execute stmt;
+select * from t1;
+select * from t2;
+delete from t1;
+insert into t1 values
+ (1,2,2), (1,3,3), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+delete from t2;
+insert into t2 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5);
+execute stmt;
+select * from t1;
+select * from t2;
+deallocate prepare stmt;
+
+drop table t1,t2;
diff --git a/mysql-test/main/derived.result b/mysql-test/main/derived.result
index b6310f1..c28e01d 100644
--- a/mysql-test/main/derived.result
+++ b/mysql-test/main/derived.result
@@ -1316,3 +1316,339 @@ a a
4 4
6 6
drop table t1,t2,t3;
+# End of 10.3 tests
+#
+# MDEV-28883: single/multi-table UPDATE/DELETE whose WHERE condition
+# contains subquery from mergeable derived table
+# that uses the updated/deleted table
+#
+create table t1 (pk int, a int);
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain update t1 set a = 10
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 Using where
+update t1 set a = 10
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 10
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "update t1 set a = 10
+where a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 10
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 10
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+explain update t1 set a = 10
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where; Using filesort
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 Using where
+update t1 set a = 10
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+select * from t1;
+pk a
+4 9
+3 10
+1 10
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+prepare stmt from "update t1 set a = 10
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2";
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 10
+1 10
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 10
+1 10
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 Using where
+delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+explain delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where
+2 SUBQUERY <derived3> ALL NULL NULL NULL NULL 3 Using where
+3 DERIVED t1 ALL NULL NULL NULL NULL 3 Using where
+delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a;
+pk a
+4 9
+select * from t1;
+pk a
+3 1
+1 3
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+prepare stmt from "delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a";
+execute stmt;
+pk a
+4 9
+select * from t1;
+pk a
+3 1
+1 3
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+pk a
+4 9
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain delete from t1
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where; Using filesort
+2 SUBQUERY <derived3> ALL NULL NULL NULL NULL 4 Using where
+3 DERIVED t1 ALL NULL NULL NULL NULL 4 Using where
+delete from t1
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+select * from t1;
+pk a
+2 7
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2";
+execute stmt;
+select * from t1;
+pk a
+2 7
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+execute stmt;
+select * from t1;
+pk a
+2 7
+4 9
+deallocate prepare stmt;
+create table t2 (pk int, a int);
+insert into t2 values (1,3), (2, 7), (3,1), (4,9);
+create table t3 (a int);
+insert into t3 VALUES (0),(1);
+explain update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 2 Using where
+1 PRIMARY t3 ALL NULL NULL NULL NULL 2 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where
+update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+2 7
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 9
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t3 ALL NULL NULL NULL NULL 2 Using where
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 4 Using where
+update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 9
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+insert into t3 values (9), (10), (7);
+explain delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+1 PRIMARY t3 ALL NULL NULL NULL NULL 5 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 Using where
+delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+1 PRIMARY t3 ALL NULL NULL NULL NULL 5 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 4 Using where
+delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+drop table t1,t2,t3;
+# End of MariaDB 10.10 tests
diff --git a/mysql-test/main/derived.test b/mysql-test/main/derived.test
index 904114e..d3b13ce 100644
--- a/mysql-test/main/derived.test
+++ b/mysql-test/main/derived.test
@@ -1126,3 +1126,213 @@ analyze select * from t1 , ( (select t2.a from t2 order by c) union all (select
select * from t1 , ( (select t2.a from t2 order by c) union all (select t2.a from t2 order by c) except(select t3.a from t3 order by b))q where t1.a=q.a;
drop table t1,t2,t3;
+
+--echo # End of 10.3 tests
+
+--echo #
+--echo # MDEV-28883: single/multi-table UPDATE/DELETE whose WHERE condition
+--echo # contains subquery from mergeable derived table
+--echo # that uses the updated/deleted table
+--echo #
+
+create table t1 (pk int, a int);
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q1=
+update t1 set a = 10
+ where a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q1;
+eval $q1;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q1";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+
+let $q2=
+update t1 set a = 10
+ where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+eval explain $q2;
+eval $q2;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+eval prepare stmt from "$q2";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q3=
+delete from t1
+ where a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q3;
+eval $q3;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q3";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+
+let $q4=
+delete from t1
+ where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a;
+
+eval explain $q4;
+eval $q4;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+eval prepare stmt from "$q4";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q5=
+delete from t1
+ where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+eval explain $q5;
+eval $q5;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q5";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+create table t2 (pk int, a int);
+insert into t2 values (1,3), (2, 7), (3,1), (4,9);
+create table t3 (a int);
+insert into t3 VALUES (0),(1);
+
+let $q6=
+update t1,t3 set t1.a = 1
+ where t1.a=t3.a and
+ t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q6;
+eval $q6;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q6";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q7=
+update t1,t3 set t1.a = 1
+ where t1.a=t3.a and
+ t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+
+eval explain $q7;
+eval $q7;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q7";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+insert into t3 values (9), (10), (7);
+
+let $q8=
+delete from t1 using t1,t3
+ where t1.a=t3.a and
+ t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q8;
+eval $q8;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q8";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q9=
+delete from t1 using t1,t3
+ where t1.a=t3.a and
+ t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+
+eval explain $q9;
+eval $q9;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q9";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+
+drop table t1,t2,t3;
+
+--echo # End of MariaDB 10.10 tests
diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result
index 9bbd32a..a7d7e87 100644
--- a/mysql-test/main/derived_cond_pushdown.result
+++ b/mysql-test/main/derived_cond_pushdown.result
@@ -11908,7 +11908,7 @@ DROP TABLE t1;
#
CREATE TABLE t1 (f1 text, f2 int);
INSERT INTO t1 VALUES ('x',1), ('y',2);
-CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 ) AS t;
+CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 GROUP BY f2) AS t;
UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2;
EXPLAIN FORMAT=JSON UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2;
EXPLAIN
@@ -11939,17 +11939,22 @@ EXPLAIN
"materialized": {
"query_block": {
"select_id": 3,
- "nested_loop": [
- {
- "table": {
- "table_name": "t1",
- "access_type": "ALL",
- "rows": 2,
- "filtered": 100,
- "attached_condition": "t1.f2 < 2"
- }
+ "filesort": {
+ "sort_key": "t1.f2",
+ "temporary_table": {
+ "nested_loop": [
+ {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 2,
+ "filtered": 100,
+ "attached_condition": "t1.f2 < 2"
+ }
+ }
+ ]
}
- ]
+ }
}
}
}
diff --git a/mysql-test/main/derived_cond_pushdown.test b/mysql-test/main/derived_cond_pushdown.test
index 39e8221..e88fae7 100644
--- a/mysql-test/main/derived_cond_pushdown.test
+++ b/mysql-test/main/derived_cond_pushdown.test
@@ -2089,7 +2089,7 @@ DROP TABLE t1;
CREATE TABLE t1 (f1 text, f2 int);
INSERT INTO t1 VALUES ('x',1), ('y',2);
-CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 ) AS t;
+CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 GROUP BY f2) AS t;
let $q1 =
UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2;
eval $q1;
diff --git a/mysql-test/main/multi_update.result b/mysql-test/main/multi_update.result
index 674dc79..ae661fa 100644
--- a/mysql-test/main/multi_update.result
+++ b/mysql-test/main/multi_update.result
@@ -441,12 +441,10 @@ create table t1 (col1 int);
create table t2 (col1 int);
update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1;
delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1;
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
drop table t1,t2;
create table t1(a int);
create table t2(a int);
delete from t1,t2 using t1,t2 where t1.a=(select a from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
drop table t1, t2;
create table t1 (a int, b int);
insert into t1 values (1, 2), (2, 3), (3, 4);
diff --git a/mysql-test/main/multi_update.test b/mysql-test/main/multi_update.test
index 5f4b5fc..839cebf 100644
--- a/mysql-test/main/multi_update.test
+++ b/mysql-test/main/multi_update.test
@@ -390,7 +390,6 @@ drop table t1, t2, t3;
create table t1 (col1 int);
create table t2 (col1 int);
update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1;
--- error ER_UPDATE_TABLE_USED
delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1;
drop table t1,t2;
@@ -399,7 +398,6 @@ drop table t1,t2;
#
create table t1(a int);
create table t2(a int);
---error ER_UPDATE_TABLE_USED
delete from t1,t2 using t1,t2 where t1.a=(select a from t1);
drop table t1, t2;
# End of 4.1 tests
diff --git a/mysql-test/main/subselect.result b/mysql-test/main/subselect.result
index 22a814c..5e8265c 100644
--- a/mysql-test/main/subselect.result
+++ b/mysql-test/main/subselect.result
@@ -634,7 +634,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect.test b/mysql-test/main/subselect.test
index 19c30bd..7e29660 100644
--- a/mysql-test/main/subselect.test
+++ b/mysql-test/main/subselect.test
@@ -366,7 +366,6 @@ insert into t12 values (33, 10),(22, 11),(2, 12);
insert into t2 values (1, 21),(2, 12),(3, 23);
select * from t11;
select * from t12;
--- error ER_UPDATE_TABLE_USED
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-- error ER_SUBQUERY_NO_1_ROW
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
diff --git a/mysql-test/main/subselect_no_exists_to_in.result b/mysql-test/main/subselect_no_exists_to_in.result
index def116c..3c18950 100644
--- a/mysql-test/main/subselect_no_exists_to_in.result
+++ b/mysql-test/main/subselect_no_exists_to_in.result
@@ -638,7 +638,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_mat.result b/mysql-test/main/subselect_no_mat.result
index 7eb3734..c1b0c67 100644
--- a/mysql-test/main/subselect_no_mat.result
+++ b/mysql-test/main/subselect_no_mat.result
@@ -641,7 +641,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_opts.result b/mysql-test/main/subselect_no_opts.result
index f2981c0..13af2f4 100644
--- a/mysql-test/main/subselect_no_opts.result
+++ b/mysql-test/main/subselect_no_opts.result
@@ -637,7 +637,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_scache.result b/mysql-test/main/subselect_no_scache.result
index 17bec03..38d5665 100644
--- a/mysql-test/main/subselect_no_scache.result
+++ b/mysql-test/main/subselect_no_scache.result
@@ -640,7 +640,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_semijoin.result b/mysql-test/main/subselect_no_semijoin.result
index cb3620f..cab4e6f 100644
--- a/mysql-test/main/subselect_no_semijoin.result
+++ b/mysql-test/main/subselect_no_semijoin.result
@@ -637,7 +637,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/suite/engines/iuds/r/update_delete_number.result b/mysql-test/suite/engines/iuds/r/update_delete_number.result
index 1cd2a62..1534f93 100644
--- a/mysql-test/suite/engines/iuds/r/update_delete_number.result
+++ b/mysql-test/suite/engines/iuds/r/update_delete_number.result
@@ -739,8 +739,21 @@ c1 c2 c3 c1 c2 c3
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
-DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+INSERT INTO t1 VALUES(254,127,1),(0,-128,2),(1,127,3),(3,NULL,5);
+INSERT INTO t2 VALUES(127,255,1),(127,1,2),(-128,0,3),(-1,NULL,5);
+DELETE FROM t1,t2 using t1,t2
+where t1.c1=(select c1 from t1 where c2 < 10) and t2.c2 < 10;
+SELECT * FROM t1;
+c1 c2 c3
+1 127 3
+254 127 1
+3 NULL 5
+SELECT * FROM t2;
+c1 c2 c3
+-1 NULL 5
+127 255 1
CREATE TABLE t3(c1 INT UNSIGNED NOT NULL PRIMARY KEY, c2 INT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 INT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
@@ -981,7 +994,6 @@ create table mt1 (col1 int);
create table mt2 (col1 int);
update mt1,mt2 set mt1.col1 = (select max(col1) from mt1) where mt1.col1 = mt2.col1;
delete mt1 from mt1,mt2 where mt1.col1 < (select max(col1) from mt1) and mt1.col1 = mt2.col1;
-ERROR HY000: Table 'mt1' is specified twice, both as a target for 'DELETE' and as a separate source for data
drop table mt1,mt2;
CREATE TABLE IF NOT EXISTS `mt1` (`id` int(11) NOT NULL auto_increment, `tst` text, `tsmt1` text, PRIMARY KEY (`id`));
CREATE TABLE IF NOT EXISTS `mt2` (`ID` int(11) NOT NULL auto_increment, `ParId` int(11) default NULL, `tst` text, `tsmt1` text, PRIMARY KEY (`ID`), KEY `IX_ParId_mt2` (`ParId`), FOREIGN KEY (`ParId`) REFERENCES `mt1` (`id`));
@@ -1852,7 +1864,6 @@ DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
CREATE TABLE t3(c1 TINYINT UNSIGNED NOT NULL PRIMARY KEY, c2 TINYINT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 TINYINT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
@@ -2599,7 +2610,6 @@ DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
CREATE TABLE t3(c1 SMALLINT UNSIGNED NOT NULL PRIMARY KEY, c2 SMALLINT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 SMALLINT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
@@ -3346,7 +3356,6 @@ DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
CREATE TABLE t3(c1 MEDIUMINT UNSIGNED NOT NULL PRIMARY KEY, c2 MEDIUMINT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 MEDIUMINT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
@@ -4093,7 +4102,6 @@ DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
CREATE TABLE t3(c1 BIGINT UNSIGNED NOT NULL PRIMARY KEY, c2 BIGINT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 BIGINT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
diff --git a/mysql-test/suite/engines/iuds/t/update_delete_number.test b/mysql-test/suite/engines/iuds/t/update_delete_number.test
index ce3f901..4347d06 100644
--- a/mysql-test/suite/engines/iuds/t/update_delete_number.test
+++ b/mysql-test/suite/engines/iuds/t/update_delete_number.test
@@ -285,8 +285,18 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
-DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+INSERT INTO t1 VALUES(254,127,1),(0,-128,2),(1,127,3),(3,NULL,5);
+INSERT INTO t2 VALUES(127,255,1),(127,1,2),(-128,0,3),(-1,NULL,5);
+# After the patch for MDEV-28883 this should not report
+# ER_UPDATE_TABLE_USED anymore
+DELETE FROM t1,t2 using t1,t2
+ where t1.c1=(select c1 from t1 where c2 < 10) and t2.c2 < 10;
+--sorted_result
+SELECT * FROM t1;
+--sorted_result
+SELECT * FROM t2;
# eq-ref join
CREATE TABLE t3(c1 INT UNSIGNED NOT NULL PRIMARY KEY, c2 INT SIGNED NULL, c3 INT);
@@ -496,7 +506,7 @@ drop table mt1, mt2, mt3;
create table mt1 (col1 int);
create table mt2 (col1 int);
update mt1,mt2 set mt1.col1 = (select max(col1) from mt1) where mt1.col1 = mt2.col1;
--- error ER_UPDATE_TABLE_USED
+# -- error ER_UPDATE_TABLE_USED
delete mt1 from mt1,mt2 where mt1.col1 < (select max(col1) from mt1) and mt1.col1 = mt2.col1;
drop table mt1,mt2;
@@ -865,7 +875,6 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
# eq-ref join
@@ -1166,7 +1175,6 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
# eq-ref join
@@ -1467,7 +1475,6 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
# eq-ref join
@@ -1768,7 +1775,6 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
# eq-ref join
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index fa338f0..1e94f0a 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -30,6 +30,8 @@
#include "sql_base.h"
#include "sql_const.h"
#include "sql_select.h"
+#include "sql_update.h" // class Sql_cmd_update
+#include "sql_delete.h" // class Sql_cmd_delete
#include "filesort.h"
#include "opt_subselect.h"
#include "sql_test.h"
@@ -532,6 +534,48 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs,
return FALSE;
}
+/**
+ @brief Check whether an IN subquery must be excluded from conversion to SJ
+
+ @param thd global context the processed statement
+ @returns true if the IN subquery must be excluded from conversion to SJ
+
+ @note
+ Currently a top level IN subquery of an delete statement is not converted
+ to SJ if the statement contains ORDER BY ... LIMIT or contains RETURNING.
+
+ @todo
+ The disjunctive members
+ !((Sql_cmd_update *) cmd)->is_multitable()
+ !((Sql_cmd_delete *) cmd)->is_multitable()
+ will be removed when conversions of IN predicands to semi-joins are
+ fully supported for single-table UPDATE/DELETE statements.
+*/
+
+bool SELECT_LEX::is_sj_conversion_prohibited(THD *thd)
+{
+ DBUG_ASSERT(master_unit()->item->substype() == Item_subselect::IN_SUBS);
+
+ SELECT_LEX *outer_sl= outer_select();
+ if (outer_sl->outer_select())
+ return false;
+
+ Sql_cmd *cmd= thd->lex->m_sql_cmd;
+
+ switch (thd->lex->sql_command) {
+ case SQLCOM_UPDATE:
+ return
+ !((Sql_cmd_update *) cmd)->is_multitable() ||
+ ((Sql_cmd_update *) cmd)->processing_as_multitable_update_prohibited(thd);
+ case SQLCOM_DELETE:
+ return
+ !((Sql_cmd_delete *) cmd)->is_multitable() ||
+ ((Sql_cmd_delete *) cmd)->processing_as_multitable_delete_prohibited(thd);
+ default:
+ return false;
+ }
+}
+
/*
Check if we need JOIN::prepare()-phase subquery rewrites and if yes, do them
@@ -675,9 +719,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
3. Subquery does not have GROUP BY or ORDER BY
4. Subquery does not use aggregate functions or HAVING
5. Subquery predicate is at the AND-top-level of ON/WHERE clause
- 6. We are not in a subquery of a single table UPDATE/DELETE that
- doesn't have a JOIN (TODO: We should handle this at some
- point by switching to multi-table UPDATE/DELETE)
+ 6. We are not in a subquery of a single-table UPDATE/DELETE that
+ does not allow conversion to multi-table UPDATE/DELETE
7. We're not in a table-less subquery like "SELECT 1"
8. No execution method was already chosen (by a prepared statement)
9. Parent select is not a table-less select
@@ -692,9 +735,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
!select_lex->group_list.elements && !join->order && // 3
!join->having && !select_lex->with_sum_func && // 4
in_subs->emb_on_expr_nest && // 5
- select_lex->outer_select()->join && // 6
- (!thd->lex->m_sql_cmd ||
- thd->lex->m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI) &&
+ !select_lex->is_sj_conversion_prohibited(thd) && // 6
parent_unit->first_select()->leaf_tables.elements && // 7
!in_subs->has_strategy() && // 8
select_lex->outer_select()->table_list.first && // 9
@@ -754,7 +795,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
*/
if (in_subs && !in_subs->has_strategy())
{
- if (is_materialization_applicable(thd, in_subs, select_lex))
+ if (!select_lex->is_sj_conversion_prohibited(thd) &&
+ is_materialization_applicable(thd, in_subs, select_lex))
{
in_subs->add_strategy(SUBS_MATERIALIZATION);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 2b41b78..725a0ff 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -47,6 +47,8 @@
#include "sql_prepare.h"
#include "sql_statistics.h"
#include "sql_cte.h"
+#include "sql_update.h" // class Sql_cmd_update
+#include "sql_delete.h" // class Sql_cmd_delete
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
@@ -1175,16 +1177,42 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
We come here for queries of type:
INSERT INTO t1 (SELECT tmp.a FROM (select * FROM t1) as tmp);
- Try to fix by materializing the derived table
+ Try to fix by materializing the derived table if one can't do without it.
*/
TABLE_LIST *derived= res->belong_to_derived;
if (derived->is_merged_derived() && !derived->derived->is_excluded())
{
- DBUG_PRINT("info",
+ bool materialize= true;
+ if (thd->lex->sql_command == SQLCOM_UPDATE)
+ {
+ Sql_cmd_update *cmd= (Sql_cmd_update *) (thd->lex->m_sql_cmd);
+ if (cmd->is_multitable())
+ materialize= false;
+ else if (!cmd->processing_as_multitable_update_prohibited(thd))
+ {
+ cmd->set_as_multitable();
+ materialize= false;
+ }
+ }
+ else if (thd->lex->sql_command == SQLCOM_DELETE)
+ {
+ Sql_cmd_delete *cmd= (Sql_cmd_delete *) (thd->lex->m_sql_cmd);
+ if (cmd->is_multitable())
+ materialize= false;
+ if (!cmd->processing_as_multitable_delete_prohibited(thd))
+ {
+ cmd->set_as_multitable();
+ materialize= false;
+ }
+ }
+ if (materialize)
+ {
+ DBUG_PRINT("info",
("convert merged to materialization to resolve the conflict"));
- derived->change_refs_to_fields();
- derived->set_materialized_derived();
- goto retry;
+ derived->change_refs_to_fields();
+ derived->set_materialized_derived();
+ goto retry;
+ }
}
}
DBUG_RETURN(res);
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 2ff4331..2db662c 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -1109,6 +1109,8 @@ multi_delete::~multi_delete()
table_being_deleted= table_being_deleted->next_local)
{
TABLE *table= table_being_deleted->table;
+ if (!table)
+ continue;
table->no_keyread=0;
table->no_cache= 0;
}
@@ -1440,6 +1442,34 @@ bool multi_delete::send_eof()
}
+/**
+ @brief Check whether processing to multi-table delete is prohibited
+
+ @param thd global context the processed statement
+ @returns true if processing as multitable is prohibited, false otherwise
+
+ @todo
+ Introduce handler level flag for storage engines that would prohibit
+ such conversion for any single-table delete.
+*/
+
+bool Sql_cmd_delete::processing_as_multitable_delete_prohibited(THD *thd)
+{
+ SELECT_LEX *const select_lex = thd->lex->first_select_lex();
+ return
+ ((select_lex->order_list.elements &&
+ select_lex->limit_params.select_limit) ||
+ thd->lex->has_returning());
+}
+
+
+/**
+ @brief Perform precheck of table privileges for delete statements
+
+ @param thd global context the processed statement
+ @returns false on success, true on error
+*/
+
bool Sql_cmd_delete::precheck(THD *thd)
{
if (!multitable)
@@ -1543,6 +1573,20 @@ bool Sql_cmd_delete::prepare_inner(THD *thd)
DBUG_RETURN(true);
}
+ if (!multitable)
+ {
+ TABLE_LIST *update_source_table= 0;
+ if (((update_source_table=unique_table(thd, table_list,
+ table_list->next_global, 0)) ||
+ table_list->is_multitable()))
+ {
+ DBUG_ASSERT(update_source_table || table_list->view != 0);
+ if (!table_list->is_multitable() &&
+ !processing_as_multitable_delete_prohibited(thd))
+ multitable= true;
+ }
+ }
+
if (table_list->has_period())
{
if (table_list->is_view_or_derived())
@@ -1627,25 +1671,6 @@ bool Sql_cmd_delete::prepare_inner(THD *thd)
}
}
- for (target_tbl= (TABLE_LIST*) aux_tables;
- target_tbl;
- target_tbl= target_tbl->next_local)
- {
- /*
- Check that table from which we delete is not used somewhere
- inside subqueries/view.
- */
- {
- TABLE_LIST *duplicate;
- if ((duplicate= unique_table(thd, target_tbl->correspondent_table,
- lex->query_tables, 0)))
- {
- update_non_unique_table_error(target_tbl->correspondent_table,
- "DELETE", duplicate);
- DBUG_RETURN(TRUE);
- }
- }
- }
/*
Reset the exclude flag to false so it doesn't interfare
with further calls to unique_table
diff --git a/sql/sql_delete.h b/sql/sql_delete.h
index e1d5044..463cb19 100644
--- a/sql/sql_delete.h
+++ b/sql/sql_delete.h
@@ -44,11 +44,13 @@ class Sql_cmd_delete final : public Sql_cmd_dml
{
public:
Sql_cmd_delete(bool multitable_arg)
- : multitable(multitable_arg), save_protocol(NULL) {}
+ : orig_multitable(multitable_arg), multitable(multitable_arg),
+ save_protocol(NULL)
+ {}
enum_sql_command sql_command_code() const override
{
- return multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE;
+ return orig_multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE;
}
DML_prelocking_strategy *get_dml_prelocking_strategy()
@@ -56,6 +58,12 @@ class Sql_cmd_delete final : public Sql_cmd_dml
return &dml_prelocking_strategy;
}
+ bool processing_as_multitable_delete_prohibited(THD *thd);
+
+ bool is_multitable() { return multitable; }
+
+ void set_as_multitable() { multitable= true; }
+
protected:
/**
@brief Perform precheck of table privileges for delete statements
@@ -78,6 +86,9 @@ class Sql_cmd_delete final : public Sql_cmd_dml
*/
bool delete_from_single_table(THD *thd);
+ /* Original value of the 'multitable' flag set by constructor */
+ const bool orig_multitable;
+
/*
True if the statement is a multitable delete or converted to such.
For a single-table delete this flag is set to true if the statement
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 78c067a..c0f91a0 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -37,6 +37,8 @@
#include "sql_partition.h"
#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part
#include "event_parse_data.h"
+#include "sql_update.h" // class Sql_cmd_update
+#include "sql_delete.h" // class Sql_cmd_delete
void LEX::parse_error(uint err_number)
{
@@ -4037,9 +4039,8 @@ bool LEX::can_use_merged()
SYNOPSIS
LEX::can_not_use_merged()
- @param no_update_or_delete Set to 1 if we can't use merge with multiple-table
- updates, like when used from
- TALE_LIST::init_derived()
+ @param forced_no_merge_for_update_delete Set to 1 if we can't use merge with
+ multiple-table updates/deletes
DESCRIPTION
Temporary table algorithm will be used on all SELECT levels for queries
@@ -4050,7 +4051,7 @@ bool LEX::can_use_merged()
TRUE - VIEWs with MERGE algorithms can be used
*/
-bool LEX::can_not_use_merged(bool no_update_or_delete)
+bool LEX::can_not_use_merged(bool forced_no_merge_for_update_delete)
{
switch (sql_command) {
case SQLCOM_CREATE_VIEW:
@@ -4064,18 +4065,29 @@ bool LEX::can_not_use_merged(bool no_update_or_delete)
return TRUE;
case SQLCOM_UPDATE_MULTI:
- case SQLCOM_DELETE_MULTI:
- if (no_update_or_delete)
+ if (forced_no_merge_for_update_delete)
return TRUE;
/* Fall through */
case SQLCOM_UPDATE:
- if (no_update_or_delete && m_sql_cmd &&
- (m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI ||
+ if (forced_no_merge_for_update_delete &&
+ (((Sql_cmd_update *) m_sql_cmd)->is_multitable() ||
query_tables->is_multitable()))
return TRUE;
+ return FALSE;
+
+ case SQLCOM_DELETE_MULTI:
+ if (forced_no_merge_for_update_delete)
+ return TRUE;
/* Fall through */
+ case SQLCOM_DELETE:
+ if (forced_no_merge_for_update_delete &&
+ (((Sql_cmd_delete *) m_sql_cmd)->is_multitable() ||
+ query_tables->is_multitable()))
+ return TRUE;
+ return FALSE;
+
default:
return FALSE;
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index e733b4f..402e3bd 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1642,6 +1642,7 @@ class st_select_lex: public st_select_lex_node
void lex_start(LEX *plex);
bool is_unit_nest() { return (nest_flags & UNIT_NEST_FL); }
void mark_as_unit_nest() { nest_flags= UNIT_NEST_FL; }
+ bool is_sj_conversion_prohibited(THD *thd);
};
typedef class st_select_lex SELECT_LEX;
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 6b14c4f..7769777 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -2807,6 +2807,23 @@ bool multi_update::send_eof()
/**
+ @brief Check whether conversion to multi-table update is prohibited
+
+ @param thd global context the processed statement
+ @returns true if conversion is prohibited, false otherwise
+
+ @todo
+ Introduce handler level flag for storage engines that would prohibit
+ such conversion for any single-table update.
+*/
+
+bool Sql_cmd_update::processing_as_multitable_update_prohibited(THD *thd)
+{
+ return false;
+}
+
+
+/**
@brief Perform precheck of table privileges for update statements
@param thd global context the processed statement
@@ -2889,7 +2906,9 @@ bool Sql_cmd_update::prepare_inner(THD *thd)
"updating and querying the same temporal periods table");
DBUG_RETURN(TRUE);
}
- multitable= true;
+ if (!table_list->is_multitable() &&
+ !processing_as_multitable_update_prohibited(thd))
+ multitable= true;
}
}
diff --git a/sql/sql_update.h b/sql/sql_update.h
index d0fc7cb..bd7d58c 100644
--- a/sql/sql_update.h
+++ b/sql/sql_update.h
@@ -46,12 +46,12 @@ class Sql_cmd_update final : public Sql_cmd_dml
{
public:
Sql_cmd_update(bool multitable_arg)
- : multitable(multitable_arg)
- { }
+ : orig_multitable(multitable_arg), multitable(multitable_arg)
+ {}
enum_sql_command sql_command_code() const override
{
- return multitable ? SQLCOM_UPDATE_MULTI : SQLCOM_UPDATE;
+ return orig_multitable ? SQLCOM_UPDATE_MULTI : SQLCOM_UPDATE;
}
DML_prelocking_strategy *get_dml_prelocking_strategy()
@@ -59,6 +59,12 @@ class Sql_cmd_update final : public Sql_cmd_dml
return &multiupdate_prelocking_strategy;
}
+ bool processing_as_multitable_update_prohibited(THD *thd);
+
+ bool is_multitable() { return multitable; }
+
+ void set_as_multitable() { multitable= true; }
+
protected:
/**
@brief Perform precheck of table privileges for update statements
@@ -82,6 +88,9 @@ class Sql_cmd_update final : public Sql_cmd_dml
*/
bool update_single_table(THD *thd);
+ /* Original value of the 'multitable' flag set by constructor */
+ const bool orig_multitable;
+
/*
True if the statement is a multi-table update or converted to such.
For a single-table update this flag is set to true if the statement
@@ -95,7 +104,6 @@ class Sql_cmd_update final : public Sql_cmd_dml
public:
/* The list of the updating expressions used in the set clause */
List<Item> *update_value_list;
-
};
#endif /* SQL_UPDATE_INCLUDED */
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index a587a37..f0d9e69 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -13370,8 +13370,23 @@ delete_single_table:
YYPS->m_lock_type,
YYPS->m_mdl_type,
NULL,
+ 0)))
+ MYSQL_YYABORT;
+ Select->table_list.save_and_clear(&Lex->auxiliary_table_list);
+ /* Save the number of auxiliary tables */
+ Lex->table_count= 1;
+
+ Lex->query_tables= 0;
+ Lex->query_tables_last= &Lex->query_tables;
+ if (unlikely(!Select->
+ add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING,
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type,
+ NULL,
$3)))
MYSQL_YYABORT;
+ Lex->auxiliary_table_list.first->correspondent_table=
+ Lex->query_tables;
YYPS->m_lock_type= TL_READ_DEFAULT;
YYPS->m_mdl_type= MDL_SHARED_READ;
}
diff --git a/sql/table.cc b/sql/table.cc
index f0d5149..44eb9f6 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -7059,7 +7059,8 @@ void Field_iterator_table_ref::set_field_iterator()
table_ref->alias.str));
}
/* This is a merge view, so use field_translation. */
- else if (table_ref->field_translation)
+ else if (table_ref->field_translation &&
+ !table_ref->is_materialized_derived())
{
DBUG_ASSERT(table_ref->is_merged_derived());
field_it= &view_field_it;
@@ -7069,7 +7070,7 @@ void Field_iterator_table_ref::set_field_iterator()
/* This is a base table or stored view. */
else
{
- DBUG_ASSERT(table_ref->table || table_ref->view);
+ DBUG_ASSERT(table_ref->table || table_ref->is_materialized_derived());
field_it= &table_field_it;
DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_table",
table_ref->alias.str));
@@ -9518,13 +9519,16 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
!derived_table_optimization_done(this))
{
/* A subquery might be forced to be materialized due to a side-effect. */
+ bool forced_no_merge_for_update_delete=
+ belong_to_view ? belong_to_view->updating :
+ !unit->outer_select()->outer_select();
if (!is_materialized_derived() && first_select->is_mergeable() &&
(unit->outer_select() && !unit->outer_select()->with_rownum) &&
(!thd->lex->with_rownum ||
(!first_select->group_list.elements &&
!first_select->order_list.elements)) &&
optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE) &&
- !thd->lex->can_not_use_merged(1) &&
+ !thd->lex->can_not_use_merged(forced_no_merge_for_update_delete) &&
!is_recursive_with_table())
set_merged_derived();
else
1
0

[Commits] 0c3b1a5: MDEV-28965 Assertion failure when preparing UPDATE with derived table in WHERE
by IgorBabaev 12 Jul '22
by IgorBabaev 12 Jul '22
12 Jul '22
revision-id: 0c3b1a545d0163d7641ebcc8317d85e633252d4a (mariadb-10.6.1-477-g0c3b1a5)
parent(s): 2db18fdb3d68d906fbd188ec570a64502ba55849
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-07-11 16:39:23 -0700
message:
MDEV-28965 Assertion failure when preparing UPDATE with derived table in WHERE
This patch fixes not only the assertion failure in the function
Field_iterator_table_ref::set_field_iterator() but also:
- fixes the problem of forced materialization of derived tables used
in subqueries contained in WHERE clauses of single-table and multi-table
UPDATE and DELETE statements
- fixes the problem of MDEV-17954 that prevented execution of multi-table
DELETE statements if they use in their WHERE clauses references to
the tables that are updated.
The patch must be considered a complement to the patch for MDEV-28883.
---
mysql-test/main/delete_use_source.result | 184 ++++++++++-
mysql-test/main/delete_use_source.test | 120 ++++++++
mysql-test/main/derived.result | 336 +++++++++++++++++++++
mysql-test/main/derived.test | 210 +++++++++++++
mysql-test/main/derived_cond_pushdown.result | 27 +-
mysql-test/main/derived_cond_pushdown.test | 2 +-
mysql-test/main/multi_update.result | 2 -
mysql-test/main/multi_update.test | 2 -
mysql-test/main/subselect.result | 1 -
mysql-test/main/subselect.test | 1 -
mysql-test/main/subselect_no_exists_to_in.result | 1 -
mysql-test/main/subselect_no_mat.result | 1 -
mysql-test/main/subselect_no_opts.result | 1 -
mysql-test/main/subselect_no_scache.result | 1 -
mysql-test/main/subselect_no_semijoin.result | 1 -
.../engines/iuds/r/update_delete_number.result | 22 +-
.../suite/engines/iuds/t/update_delete_number.test | 20 +-
sql/opt_subselect.cc | 56 +++-
sql/sql_base.cc | 38 ++-
sql/sql_delete.cc | 63 ++--
sql/sql_delete.h | 15 +-
sql/sql_lex.cc | 28 +-
sql/sql_lex.h | 1 +
sql/sql_update.cc | 21 +-
sql/sql_update.h | 16 +-
sql/sql_yacc.yy | 15 +
sql/table.cc | 10 +-
27 files changed, 1107 insertions(+), 88 deletions(-)
diff --git a/mysql-test/main/delete_use_source.result b/mysql-test/main/delete_use_source.result
index 0990a55..329203a 100644
--- a/mysql-test/main/delete_use_source.result
+++ b/mysql-test/main/delete_use_source.result
@@ -49,7 +49,7 @@ rollback;
start transaction;
explain delete from v1 where (select count(*) from t1 b where b.c1=v1.c1) = 500 limit 1;
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY t1 range c1 c1 4 NULL 600 Using where
+1 PRIMARY t1 range c1 c1 4 NULL 600 Using index condition; Using where
2 DEPENDENT SUBQUERY b ref c1 c1 4 test.t1.c1 167 Using index
delete from v1 where (select count(*) from t1 b where b.c1=v1.c1) = 500 limit 1;
affected rows: 1
@@ -65,7 +65,7 @@ rollback;
start transaction;
explain delete from v1 where (select count(*) from t1 b where b.c1=v1.c1) = 500;
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY t1 ALL c1 NULL NULL NULL # Using where
+1 PRIMARY t1 range c1 c1 4 NULL # Using index condition; Using where
2 DEPENDENT SUBQUERY b ref c1 c1 4 test.t1.c1 # Using index
delete from v1 where (select count(*) from t1 b where b.c1=v1.c1) = 500 ;
affected rows: 500
@@ -154,3 +154,183 @@ set session sort_buffer_size = 1024;
delete from t1 where c1=0 and exists(select 'x' from t1 b where b.c1<10);
affected rows: 128000
drop table t1;
+#
+# MDEV-17954: multi-table DELETE with the same source and target
+#
+create table t1 (c1 int, c2 int, c3 int);
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+#
+# Single-table DELETE with the same source and target
+# handled as multi-table DELETE
+#
+explain delete from t1
+where c2 in (select distinct a.c2 from t1 a where t1.c1=a.c1 and a.c2 < 3);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 8 Using where
+1 PRIMARY a ALL NULL NULL NULL NULL 8 Using where; FirstMatch(t1)
+delete from t1
+where c2 in (select distinct a.c2 from t1 a where t1.c1=a.c1 and a.c2 < 3);
+select * from t1;
+c1 c2 c3
+1 3 3
+2 3 6
+2 4 7
+2 5 8
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+prepare stmt from "delete from t1
+where c2 in (select distinct a.c2 from t1 a where t1.c1=a.c1 and a.c2 < 3)";
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 3 3
+2 3 6
+2 4 7
+2 5 8
+delete from t1;
+insert into t1 values
+(2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+c1 c2 c3
+2 3 6
+2 4 7
+2 5 8
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+#
+# Multi-table DELETE with the same source and target
+#
+create table t2 (c1 int, c2 int, c3 int);
+insert into t2 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,5,8);
+explain delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 7
+1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using where
+delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+prepare stmt from "delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1";
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+delete from t1;
+insert into t1 values
+(2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+c1 c2 c3
+2 4 7
+deallocate prepare stmt;
+explain delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 system NULL NULL NULL NULL 1
+1 SIMPLE t2 ALL NULL NULL NULL NULL 7 Using where
+delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1;
+select * from t1;
+c1 c2 c3
+2 4 7
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+prepare stmt from "delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1";
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+delete from t1;
+insert into t1 values
+(2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+c1 c2 c3
+2 4 7
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+explain delete from t1,t2 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1 and t2.c1 > 1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 7 Using where
+1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using where
+delete from t1,t2 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1 and t2.c1 > 1;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+select * from t2;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+delete from t2;
+insert into t2 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,5,8);
+prepare stmt from "delete from t1,t2 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1 and t2.c1 > 1";
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+select * from t2;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+delete from t1;
+insert into t1 values
+(1,2,2), (1,3,3), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+delete from t2;
+insert into t2 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5);
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 2 2
+1 3 3
+2 3 6
+2 4 7
+2 5 8
+select * from t2;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 1 4
+deallocate prepare stmt;
+drop table t1,t2;
diff --git a/mysql-test/main/delete_use_source.test b/mysql-test/main/delete_use_source.test
index 4aed00d..9625431 100644
--- a/mysql-test/main/delete_use_source.test
+++ b/mysql-test/main/delete_use_source.test
@@ -135,3 +135,123 @@ set session sort_buffer_size = 1024;
delete from t1 where c1=0 and exists(select 'x' from t1 b where b.c1<10);
drop table t1;
+
+--echo #
+--echo # MDEV-17954: multi-table DELETE with the same source and target
+--echo #
+
+create table t1 (c1 int, c2 int, c3 int);
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+
+--echo #
+--echo # Single-table DELETE with the same source and target
+--echo # handled as multi-table DELETE
+--echo #
+
+let $q1=
+delete from t1
+ where c2 in (select distinct a.c2 from t1 a where t1.c1=a.c1 and a.c2 < 3);
+
+eval explain $q1;
+eval $q1;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+eval prepare stmt from "$q1";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+
+--echo #
+--echo # Multi-table DELETE with the same source and target
+--echo #
+
+create table t2 (c1 int, c2 int, c3 int);
+insert into t2 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,5,8);
+
+let $q2=
+delete from t1 using t1,t2
+ where t1.c2 = t2.c2 and t1.c1 > 1;
+
+eval explain $q2;
+eval $q2;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+eval prepare stmt from "$q2";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+let $q2=
+delete from t1 using t1,t2
+ where t1.c2 = t2.c2 and t1.c1 > 1;
+
+eval explain $q2;
+eval $q2;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+eval prepare stmt from "$q2";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+
+let $q3=
+delete from t1,t2 using t1,t2
+ where t1.c2 = t2.c2 and t1.c1 > 1 and t2.c1 > 1;
+
+eval explain $q3;
+eval $q3;
+select * from t1;
+select * from t2;
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+delete from t2;
+insert into t2 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,5,8);
+eval prepare stmt from "$q3";
+execute stmt;
+select * from t1;
+select * from t2;
+delete from t1;
+insert into t1 values
+ (1,2,2), (1,3,3), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+delete from t2;
+insert into t2 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5);
+execute stmt;
+select * from t1;
+select * from t2;
+deallocate prepare stmt;
+
+drop table t1,t2;
diff --git a/mysql-test/main/derived.result b/mysql-test/main/derived.result
index b6310f1..c28e01d 100644
--- a/mysql-test/main/derived.result
+++ b/mysql-test/main/derived.result
@@ -1316,3 +1316,339 @@ a a
4 4
6 6
drop table t1,t2,t3;
+# End of 10.3 tests
+#
+# MDEV-28883: single/multi-table UPDATE/DELETE whose WHERE condition
+# contains subquery from mergeable derived table
+# that uses the updated/deleted table
+#
+create table t1 (pk int, a int);
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain update t1 set a = 10
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 Using where
+update t1 set a = 10
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 10
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "update t1 set a = 10
+where a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 10
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 10
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+explain update t1 set a = 10
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where; Using filesort
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 Using where
+update t1 set a = 10
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+select * from t1;
+pk a
+4 9
+3 10
+1 10
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+prepare stmt from "update t1 set a = 10
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2";
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 10
+1 10
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 10
+1 10
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 Using where
+delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+explain delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where
+2 SUBQUERY <derived3> ALL NULL NULL NULL NULL 3 Using where
+3 DERIVED t1 ALL NULL NULL NULL NULL 3 Using where
+delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a;
+pk a
+4 9
+select * from t1;
+pk a
+3 1
+1 3
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+prepare stmt from "delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a";
+execute stmt;
+pk a
+4 9
+select * from t1;
+pk a
+3 1
+1 3
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+pk a
+4 9
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain delete from t1
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where; Using filesort
+2 SUBQUERY <derived3> ALL NULL NULL NULL NULL 4 Using where
+3 DERIVED t1 ALL NULL NULL NULL NULL 4 Using where
+delete from t1
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+select * from t1;
+pk a
+2 7
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2";
+execute stmt;
+select * from t1;
+pk a
+2 7
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+execute stmt;
+select * from t1;
+pk a
+2 7
+4 9
+deallocate prepare stmt;
+create table t2 (pk int, a int);
+insert into t2 values (1,3), (2, 7), (3,1), (4,9);
+create table t3 (a int);
+insert into t3 VALUES (0),(1);
+explain update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 2 Using where
+1 PRIMARY t3 ALL NULL NULL NULL NULL 2 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where
+update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+2 7
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 9
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t3 ALL NULL NULL NULL NULL 2 Using where
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 4 Using where
+update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 9
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+insert into t3 values (9), (10), (7);
+explain delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+1 PRIMARY t3 ALL NULL NULL NULL NULL 5 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 Using where
+delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+1 PRIMARY t3 ALL NULL NULL NULL NULL 5 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 4 Using where
+delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+drop table t1,t2,t3;
+# End of MariaDB 10.10 tests
diff --git a/mysql-test/main/derived.test b/mysql-test/main/derived.test
index 904114e..d3b13ce 100644
--- a/mysql-test/main/derived.test
+++ b/mysql-test/main/derived.test
@@ -1126,3 +1126,213 @@ analyze select * from t1 , ( (select t2.a from t2 order by c) union all (select
select * from t1 , ( (select t2.a from t2 order by c) union all (select t2.a from t2 order by c) except(select t3.a from t3 order by b))q where t1.a=q.a;
drop table t1,t2,t3;
+
+--echo # End of 10.3 tests
+
+--echo #
+--echo # MDEV-28883: single/multi-table UPDATE/DELETE whose WHERE condition
+--echo # contains subquery from mergeable derived table
+--echo # that uses the updated/deleted table
+--echo #
+
+create table t1 (pk int, a int);
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q1=
+update t1 set a = 10
+ where a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q1;
+eval $q1;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q1";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+
+let $q2=
+update t1 set a = 10
+ where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+eval explain $q2;
+eval $q2;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+eval prepare stmt from "$q2";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q3=
+delete from t1
+ where a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q3;
+eval $q3;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q3";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+
+let $q4=
+delete from t1
+ where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a;
+
+eval explain $q4;
+eval $q4;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+eval prepare stmt from "$q4";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q5=
+delete from t1
+ where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+eval explain $q5;
+eval $q5;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q5";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+create table t2 (pk int, a int);
+insert into t2 values (1,3), (2, 7), (3,1), (4,9);
+create table t3 (a int);
+insert into t3 VALUES (0),(1);
+
+let $q6=
+update t1,t3 set t1.a = 1
+ where t1.a=t3.a and
+ t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q6;
+eval $q6;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q6";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q7=
+update t1,t3 set t1.a = 1
+ where t1.a=t3.a and
+ t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+
+eval explain $q7;
+eval $q7;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q7";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+insert into t3 values (9), (10), (7);
+
+let $q8=
+delete from t1 using t1,t3
+ where t1.a=t3.a and
+ t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q8;
+eval $q8;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q8";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q9=
+delete from t1 using t1,t3
+ where t1.a=t3.a and
+ t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+
+eval explain $q9;
+eval $q9;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q9";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+
+drop table t1,t2,t3;
+
+--echo # End of MariaDB 10.10 tests
diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result
index 9bbd32a..a7d7e87 100644
--- a/mysql-test/main/derived_cond_pushdown.result
+++ b/mysql-test/main/derived_cond_pushdown.result
@@ -11908,7 +11908,7 @@ DROP TABLE t1;
#
CREATE TABLE t1 (f1 text, f2 int);
INSERT INTO t1 VALUES ('x',1), ('y',2);
-CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 ) AS t;
+CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 GROUP BY f2) AS t;
UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2;
EXPLAIN FORMAT=JSON UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2;
EXPLAIN
@@ -11939,17 +11939,22 @@ EXPLAIN
"materialized": {
"query_block": {
"select_id": 3,
- "nested_loop": [
- {
- "table": {
- "table_name": "t1",
- "access_type": "ALL",
- "rows": 2,
- "filtered": 100,
- "attached_condition": "t1.f2 < 2"
- }
+ "filesort": {
+ "sort_key": "t1.f2",
+ "temporary_table": {
+ "nested_loop": [
+ {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 2,
+ "filtered": 100,
+ "attached_condition": "t1.f2 < 2"
+ }
+ }
+ ]
}
- ]
+ }
}
}
}
diff --git a/mysql-test/main/derived_cond_pushdown.test b/mysql-test/main/derived_cond_pushdown.test
index 39e8221..e88fae7 100644
--- a/mysql-test/main/derived_cond_pushdown.test
+++ b/mysql-test/main/derived_cond_pushdown.test
@@ -2089,7 +2089,7 @@ DROP TABLE t1;
CREATE TABLE t1 (f1 text, f2 int);
INSERT INTO t1 VALUES ('x',1), ('y',2);
-CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 ) AS t;
+CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 GROUP BY f2) AS t;
let $q1 =
UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2;
eval $q1;
diff --git a/mysql-test/main/multi_update.result b/mysql-test/main/multi_update.result
index 674dc79..ae661fa 100644
--- a/mysql-test/main/multi_update.result
+++ b/mysql-test/main/multi_update.result
@@ -441,12 +441,10 @@ create table t1 (col1 int);
create table t2 (col1 int);
update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1;
delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1;
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
drop table t1,t2;
create table t1(a int);
create table t2(a int);
delete from t1,t2 using t1,t2 where t1.a=(select a from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
drop table t1, t2;
create table t1 (a int, b int);
insert into t1 values (1, 2), (2, 3), (3, 4);
diff --git a/mysql-test/main/multi_update.test b/mysql-test/main/multi_update.test
index 5f4b5fc..839cebf 100644
--- a/mysql-test/main/multi_update.test
+++ b/mysql-test/main/multi_update.test
@@ -390,7 +390,6 @@ drop table t1, t2, t3;
create table t1 (col1 int);
create table t2 (col1 int);
update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1;
--- error ER_UPDATE_TABLE_USED
delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1;
drop table t1,t2;
@@ -399,7 +398,6 @@ drop table t1,t2;
#
create table t1(a int);
create table t2(a int);
---error ER_UPDATE_TABLE_USED
delete from t1,t2 using t1,t2 where t1.a=(select a from t1);
drop table t1, t2;
# End of 4.1 tests
diff --git a/mysql-test/main/subselect.result b/mysql-test/main/subselect.result
index 22a814c..5e8265c 100644
--- a/mysql-test/main/subselect.result
+++ b/mysql-test/main/subselect.result
@@ -634,7 +634,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect.test b/mysql-test/main/subselect.test
index 19c30bd..7e29660 100644
--- a/mysql-test/main/subselect.test
+++ b/mysql-test/main/subselect.test
@@ -366,7 +366,6 @@ insert into t12 values (33, 10),(22, 11),(2, 12);
insert into t2 values (1, 21),(2, 12),(3, 23);
select * from t11;
select * from t12;
--- error ER_UPDATE_TABLE_USED
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-- error ER_SUBQUERY_NO_1_ROW
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
diff --git a/mysql-test/main/subselect_no_exists_to_in.result b/mysql-test/main/subselect_no_exists_to_in.result
index def116c..3c18950 100644
--- a/mysql-test/main/subselect_no_exists_to_in.result
+++ b/mysql-test/main/subselect_no_exists_to_in.result
@@ -638,7 +638,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_mat.result b/mysql-test/main/subselect_no_mat.result
index 7eb3734..c1b0c67 100644
--- a/mysql-test/main/subselect_no_mat.result
+++ b/mysql-test/main/subselect_no_mat.result
@@ -641,7 +641,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_opts.result b/mysql-test/main/subselect_no_opts.result
index f2981c0..13af2f4 100644
--- a/mysql-test/main/subselect_no_opts.result
+++ b/mysql-test/main/subselect_no_opts.result
@@ -637,7 +637,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_scache.result b/mysql-test/main/subselect_no_scache.result
index 17bec03..38d5665 100644
--- a/mysql-test/main/subselect_no_scache.result
+++ b/mysql-test/main/subselect_no_scache.result
@@ -640,7 +640,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_semijoin.result b/mysql-test/main/subselect_no_semijoin.result
index cb3620f..cab4e6f 100644
--- a/mysql-test/main/subselect_no_semijoin.result
+++ b/mysql-test/main/subselect_no_semijoin.result
@@ -637,7 +637,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/suite/engines/iuds/r/update_delete_number.result b/mysql-test/suite/engines/iuds/r/update_delete_number.result
index 1cd2a62..1534f93 100644
--- a/mysql-test/suite/engines/iuds/r/update_delete_number.result
+++ b/mysql-test/suite/engines/iuds/r/update_delete_number.result
@@ -739,8 +739,21 @@ c1 c2 c3 c1 c2 c3
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
-DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+INSERT INTO t1 VALUES(254,127,1),(0,-128,2),(1,127,3),(3,NULL,5);
+INSERT INTO t2 VALUES(127,255,1),(127,1,2),(-128,0,3),(-1,NULL,5);
+DELETE FROM t1,t2 using t1,t2
+where t1.c1=(select c1 from t1 where c2 < 10) and t2.c2 < 10;
+SELECT * FROM t1;
+c1 c2 c3
+1 127 3
+254 127 1
+3 NULL 5
+SELECT * FROM t2;
+c1 c2 c3
+-1 NULL 5
+127 255 1
CREATE TABLE t3(c1 INT UNSIGNED NOT NULL PRIMARY KEY, c2 INT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 INT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
@@ -981,7 +994,6 @@ create table mt1 (col1 int);
create table mt2 (col1 int);
update mt1,mt2 set mt1.col1 = (select max(col1) from mt1) where mt1.col1 = mt2.col1;
delete mt1 from mt1,mt2 where mt1.col1 < (select max(col1) from mt1) and mt1.col1 = mt2.col1;
-ERROR HY000: Table 'mt1' is specified twice, both as a target for 'DELETE' and as a separate source for data
drop table mt1,mt2;
CREATE TABLE IF NOT EXISTS `mt1` (`id` int(11) NOT NULL auto_increment, `tst` text, `tsmt1` text, PRIMARY KEY (`id`));
CREATE TABLE IF NOT EXISTS `mt2` (`ID` int(11) NOT NULL auto_increment, `ParId` int(11) default NULL, `tst` text, `tsmt1` text, PRIMARY KEY (`ID`), KEY `IX_ParId_mt2` (`ParId`), FOREIGN KEY (`ParId`) REFERENCES `mt1` (`id`));
@@ -1852,7 +1864,6 @@ DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
CREATE TABLE t3(c1 TINYINT UNSIGNED NOT NULL PRIMARY KEY, c2 TINYINT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 TINYINT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
@@ -2599,7 +2610,6 @@ DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
CREATE TABLE t3(c1 SMALLINT UNSIGNED NOT NULL PRIMARY KEY, c2 SMALLINT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 SMALLINT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
@@ -3346,7 +3356,6 @@ DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
CREATE TABLE t3(c1 MEDIUMINT UNSIGNED NOT NULL PRIMARY KEY, c2 MEDIUMINT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 MEDIUMINT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
@@ -4093,7 +4102,6 @@ DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
CREATE TABLE t3(c1 BIGINT UNSIGNED NOT NULL PRIMARY KEY, c2 BIGINT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 BIGINT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
diff --git a/mysql-test/suite/engines/iuds/t/update_delete_number.test b/mysql-test/suite/engines/iuds/t/update_delete_number.test
index ce3f901..4347d06 100644
--- a/mysql-test/suite/engines/iuds/t/update_delete_number.test
+++ b/mysql-test/suite/engines/iuds/t/update_delete_number.test
@@ -285,8 +285,18 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
-DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+INSERT INTO t1 VALUES(254,127,1),(0,-128,2),(1,127,3),(3,NULL,5);
+INSERT INTO t2 VALUES(127,255,1),(127,1,2),(-128,0,3),(-1,NULL,5);
+# After the patch for MDEV-28883 this should not report
+# ER_UPDATE_TABLE_USED anymore
+DELETE FROM t1,t2 using t1,t2
+ where t1.c1=(select c1 from t1 where c2 < 10) and t2.c2 < 10;
+--sorted_result
+SELECT * FROM t1;
+--sorted_result
+SELECT * FROM t2;
# eq-ref join
CREATE TABLE t3(c1 INT UNSIGNED NOT NULL PRIMARY KEY, c2 INT SIGNED NULL, c3 INT);
@@ -496,7 +506,7 @@ drop table mt1, mt2, mt3;
create table mt1 (col1 int);
create table mt2 (col1 int);
update mt1,mt2 set mt1.col1 = (select max(col1) from mt1) where mt1.col1 = mt2.col1;
--- error ER_UPDATE_TABLE_USED
+# -- error ER_UPDATE_TABLE_USED
delete mt1 from mt1,mt2 where mt1.col1 < (select max(col1) from mt1) and mt1.col1 = mt2.col1;
drop table mt1,mt2;
@@ -865,7 +875,6 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
# eq-ref join
@@ -1166,7 +1175,6 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
# eq-ref join
@@ -1467,7 +1475,6 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
# eq-ref join
@@ -1768,7 +1775,6 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
# eq-ref join
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index fa338f0..1e94f0a 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -30,6 +30,8 @@
#include "sql_base.h"
#include "sql_const.h"
#include "sql_select.h"
+#include "sql_update.h" // class Sql_cmd_update
+#include "sql_delete.h" // class Sql_cmd_delete
#include "filesort.h"
#include "opt_subselect.h"
#include "sql_test.h"
@@ -532,6 +534,48 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs,
return FALSE;
}
+/**
+ @brief Check whether an IN subquery must be excluded from conversion to SJ
+
+ @param thd global context the processed statement
+ @returns true if the IN subquery must be excluded from conversion to SJ
+
+ @note
+ Currently a top level IN subquery of an delete statement is not converted
+ to SJ if the statement contains ORDER BY ... LIMIT or contains RETURNING.
+
+ @todo
+ The disjunctive members
+ !((Sql_cmd_update *) cmd)->is_multitable()
+ !((Sql_cmd_delete *) cmd)->is_multitable()
+ will be removed when conversions of IN predicands to semi-joins are
+ fully supported for single-table UPDATE/DELETE statements.
+*/
+
+bool SELECT_LEX::is_sj_conversion_prohibited(THD *thd)
+{
+ DBUG_ASSERT(master_unit()->item->substype() == Item_subselect::IN_SUBS);
+
+ SELECT_LEX *outer_sl= outer_select();
+ if (outer_sl->outer_select())
+ return false;
+
+ Sql_cmd *cmd= thd->lex->m_sql_cmd;
+
+ switch (thd->lex->sql_command) {
+ case SQLCOM_UPDATE:
+ return
+ !((Sql_cmd_update *) cmd)->is_multitable() ||
+ ((Sql_cmd_update *) cmd)->processing_as_multitable_update_prohibited(thd);
+ case SQLCOM_DELETE:
+ return
+ !((Sql_cmd_delete *) cmd)->is_multitable() ||
+ ((Sql_cmd_delete *) cmd)->processing_as_multitable_delete_prohibited(thd);
+ default:
+ return false;
+ }
+}
+
/*
Check if we need JOIN::prepare()-phase subquery rewrites and if yes, do them
@@ -675,9 +719,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
3. Subquery does not have GROUP BY or ORDER BY
4. Subquery does not use aggregate functions or HAVING
5. Subquery predicate is at the AND-top-level of ON/WHERE clause
- 6. We are not in a subquery of a single table UPDATE/DELETE that
- doesn't have a JOIN (TODO: We should handle this at some
- point by switching to multi-table UPDATE/DELETE)
+ 6. We are not in a subquery of a single-table UPDATE/DELETE that
+ does not allow conversion to multi-table UPDATE/DELETE
7. We're not in a table-less subquery like "SELECT 1"
8. No execution method was already chosen (by a prepared statement)
9. Parent select is not a table-less select
@@ -692,9 +735,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
!select_lex->group_list.elements && !join->order && // 3
!join->having && !select_lex->with_sum_func && // 4
in_subs->emb_on_expr_nest && // 5
- select_lex->outer_select()->join && // 6
- (!thd->lex->m_sql_cmd ||
- thd->lex->m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI) &&
+ !select_lex->is_sj_conversion_prohibited(thd) && // 6
parent_unit->first_select()->leaf_tables.elements && // 7
!in_subs->has_strategy() && // 8
select_lex->outer_select()->table_list.first && // 9
@@ -754,7 +795,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
*/
if (in_subs && !in_subs->has_strategy())
{
- if (is_materialization_applicable(thd, in_subs, select_lex))
+ if (!select_lex->is_sj_conversion_prohibited(thd) &&
+ is_materialization_applicable(thd, in_subs, select_lex))
{
in_subs->add_strategy(SUBS_MATERIALIZATION);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 2b41b78..725a0ff 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -47,6 +47,8 @@
#include "sql_prepare.h"
#include "sql_statistics.h"
#include "sql_cte.h"
+#include "sql_update.h" // class Sql_cmd_update
+#include "sql_delete.h" // class Sql_cmd_delete
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
@@ -1175,16 +1177,42 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
We come here for queries of type:
INSERT INTO t1 (SELECT tmp.a FROM (select * FROM t1) as tmp);
- Try to fix by materializing the derived table
+ Try to fix by materializing the derived table if one can't do without it.
*/
TABLE_LIST *derived= res->belong_to_derived;
if (derived->is_merged_derived() && !derived->derived->is_excluded())
{
- DBUG_PRINT("info",
+ bool materialize= true;
+ if (thd->lex->sql_command == SQLCOM_UPDATE)
+ {
+ Sql_cmd_update *cmd= (Sql_cmd_update *) (thd->lex->m_sql_cmd);
+ if (cmd->is_multitable())
+ materialize= false;
+ else if (!cmd->processing_as_multitable_update_prohibited(thd))
+ {
+ cmd->set_as_multitable();
+ materialize= false;
+ }
+ }
+ else if (thd->lex->sql_command == SQLCOM_DELETE)
+ {
+ Sql_cmd_delete *cmd= (Sql_cmd_delete *) (thd->lex->m_sql_cmd);
+ if (cmd->is_multitable())
+ materialize= false;
+ if (!cmd->processing_as_multitable_delete_prohibited(thd))
+ {
+ cmd->set_as_multitable();
+ materialize= false;
+ }
+ }
+ if (materialize)
+ {
+ DBUG_PRINT("info",
("convert merged to materialization to resolve the conflict"));
- derived->change_refs_to_fields();
- derived->set_materialized_derived();
- goto retry;
+ derived->change_refs_to_fields();
+ derived->set_materialized_derived();
+ goto retry;
+ }
}
}
DBUG_RETURN(res);
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 2ff4331..2db662c 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -1109,6 +1109,8 @@ multi_delete::~multi_delete()
table_being_deleted= table_being_deleted->next_local)
{
TABLE *table= table_being_deleted->table;
+ if (!table)
+ continue;
table->no_keyread=0;
table->no_cache= 0;
}
@@ -1440,6 +1442,34 @@ bool multi_delete::send_eof()
}
+/**
+ @brief Check whether processing to multi-table delete is prohibited
+
+ @param thd global context the processed statement
+ @returns true if processing as multitable is prohibited, false otherwise
+
+ @todo
+ Introduce handler level flag for storage engines that would prohibit
+ such conversion for any single-table delete.
+*/
+
+bool Sql_cmd_delete::processing_as_multitable_delete_prohibited(THD *thd)
+{
+ SELECT_LEX *const select_lex = thd->lex->first_select_lex();
+ return
+ ((select_lex->order_list.elements &&
+ select_lex->limit_params.select_limit) ||
+ thd->lex->has_returning());
+}
+
+
+/**
+ @brief Perform precheck of table privileges for delete statements
+
+ @param thd global context the processed statement
+ @returns false on success, true on error
+*/
+
bool Sql_cmd_delete::precheck(THD *thd)
{
if (!multitable)
@@ -1543,6 +1573,20 @@ bool Sql_cmd_delete::prepare_inner(THD *thd)
DBUG_RETURN(true);
}
+ if (!multitable)
+ {
+ TABLE_LIST *update_source_table= 0;
+ if (((update_source_table=unique_table(thd, table_list,
+ table_list->next_global, 0)) ||
+ table_list->is_multitable()))
+ {
+ DBUG_ASSERT(update_source_table || table_list->view != 0);
+ if (!table_list->is_multitable() &&
+ !processing_as_multitable_delete_prohibited(thd))
+ multitable= true;
+ }
+ }
+
if (table_list->has_period())
{
if (table_list->is_view_or_derived())
@@ -1627,25 +1671,6 @@ bool Sql_cmd_delete::prepare_inner(THD *thd)
}
}
- for (target_tbl= (TABLE_LIST*) aux_tables;
- target_tbl;
- target_tbl= target_tbl->next_local)
- {
- /*
- Check that table from which we delete is not used somewhere
- inside subqueries/view.
- */
- {
- TABLE_LIST *duplicate;
- if ((duplicate= unique_table(thd, target_tbl->correspondent_table,
- lex->query_tables, 0)))
- {
- update_non_unique_table_error(target_tbl->correspondent_table,
- "DELETE", duplicate);
- DBUG_RETURN(TRUE);
- }
- }
- }
/*
Reset the exclude flag to false so it doesn't interfare
with further calls to unique_table
diff --git a/sql/sql_delete.h b/sql/sql_delete.h
index e1d5044..463cb19 100644
--- a/sql/sql_delete.h
+++ b/sql/sql_delete.h
@@ -44,11 +44,13 @@ class Sql_cmd_delete final : public Sql_cmd_dml
{
public:
Sql_cmd_delete(bool multitable_arg)
- : multitable(multitable_arg), save_protocol(NULL) {}
+ : orig_multitable(multitable_arg), multitable(multitable_arg),
+ save_protocol(NULL)
+ {}
enum_sql_command sql_command_code() const override
{
- return multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE;
+ return orig_multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE;
}
DML_prelocking_strategy *get_dml_prelocking_strategy()
@@ -56,6 +58,12 @@ class Sql_cmd_delete final : public Sql_cmd_dml
return &dml_prelocking_strategy;
}
+ bool processing_as_multitable_delete_prohibited(THD *thd);
+
+ bool is_multitable() { return multitable; }
+
+ void set_as_multitable() { multitable= true; }
+
protected:
/**
@brief Perform precheck of table privileges for delete statements
@@ -78,6 +86,9 @@ class Sql_cmd_delete final : public Sql_cmd_dml
*/
bool delete_from_single_table(THD *thd);
+ /* Original value of the 'multitable' flag set by constructor */
+ const bool orig_multitable;
+
/*
True if the statement is a multitable delete or converted to such.
For a single-table delete this flag is set to true if the statement
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 78c067a..c0f91a0 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -37,6 +37,8 @@
#include "sql_partition.h"
#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part
#include "event_parse_data.h"
+#include "sql_update.h" // class Sql_cmd_update
+#include "sql_delete.h" // class Sql_cmd_delete
void LEX::parse_error(uint err_number)
{
@@ -4037,9 +4039,8 @@ bool LEX::can_use_merged()
SYNOPSIS
LEX::can_not_use_merged()
- @param no_update_or_delete Set to 1 if we can't use merge with multiple-table
- updates, like when used from
- TALE_LIST::init_derived()
+ @param forced_no_merge_for_update_delete Set to 1 if we can't use merge with
+ multiple-table updates/deletes
DESCRIPTION
Temporary table algorithm will be used on all SELECT levels for queries
@@ -4050,7 +4051,7 @@ bool LEX::can_use_merged()
TRUE - VIEWs with MERGE algorithms can be used
*/
-bool LEX::can_not_use_merged(bool no_update_or_delete)
+bool LEX::can_not_use_merged(bool forced_no_merge_for_update_delete)
{
switch (sql_command) {
case SQLCOM_CREATE_VIEW:
@@ -4064,18 +4065,29 @@ bool LEX::can_not_use_merged(bool no_update_or_delete)
return TRUE;
case SQLCOM_UPDATE_MULTI:
- case SQLCOM_DELETE_MULTI:
- if (no_update_or_delete)
+ if (forced_no_merge_for_update_delete)
return TRUE;
/* Fall through */
case SQLCOM_UPDATE:
- if (no_update_or_delete && m_sql_cmd &&
- (m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI ||
+ if (forced_no_merge_for_update_delete &&
+ (((Sql_cmd_update *) m_sql_cmd)->is_multitable() ||
query_tables->is_multitable()))
return TRUE;
+ return FALSE;
+
+ case SQLCOM_DELETE_MULTI:
+ if (forced_no_merge_for_update_delete)
+ return TRUE;
/* Fall through */
+ case SQLCOM_DELETE:
+ if (forced_no_merge_for_update_delete &&
+ (((Sql_cmd_delete *) m_sql_cmd)->is_multitable() ||
+ query_tables->is_multitable()))
+ return TRUE;
+ return FALSE;
+
default:
return FALSE;
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index e733b4f..402e3bd 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1642,6 +1642,7 @@ class st_select_lex: public st_select_lex_node
void lex_start(LEX *plex);
bool is_unit_nest() { return (nest_flags & UNIT_NEST_FL); }
void mark_as_unit_nest() { nest_flags= UNIT_NEST_FL; }
+ bool is_sj_conversion_prohibited(THD *thd);
};
typedef class st_select_lex SELECT_LEX;
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 6b14c4f..7769777 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -2807,6 +2807,23 @@ bool multi_update::send_eof()
/**
+ @brief Check whether conversion to multi-table update is prohibited
+
+ @param thd global context the processed statement
+ @returns true if conversion is prohibited, false otherwise
+
+ @todo
+ Introduce handler level flag for storage engines that would prohibit
+ such conversion for any single-table update.
+*/
+
+bool Sql_cmd_update::processing_as_multitable_update_prohibited(THD *thd)
+{
+ return false;
+}
+
+
+/**
@brief Perform precheck of table privileges for update statements
@param thd global context the processed statement
@@ -2889,7 +2906,9 @@ bool Sql_cmd_update::prepare_inner(THD *thd)
"updating and querying the same temporal periods table");
DBUG_RETURN(TRUE);
}
- multitable= true;
+ if (!table_list->is_multitable() &&
+ !processing_as_multitable_update_prohibited(thd))
+ multitable= true;
}
}
diff --git a/sql/sql_update.h b/sql/sql_update.h
index d0fc7cb..bd7d58c 100644
--- a/sql/sql_update.h
+++ b/sql/sql_update.h
@@ -46,12 +46,12 @@ class Sql_cmd_update final : public Sql_cmd_dml
{
public:
Sql_cmd_update(bool multitable_arg)
- : multitable(multitable_arg)
- { }
+ : orig_multitable(multitable_arg), multitable(multitable_arg)
+ {}
enum_sql_command sql_command_code() const override
{
- return multitable ? SQLCOM_UPDATE_MULTI : SQLCOM_UPDATE;
+ return orig_multitable ? SQLCOM_UPDATE_MULTI : SQLCOM_UPDATE;
}
DML_prelocking_strategy *get_dml_prelocking_strategy()
@@ -59,6 +59,12 @@ class Sql_cmd_update final : public Sql_cmd_dml
return &multiupdate_prelocking_strategy;
}
+ bool processing_as_multitable_update_prohibited(THD *thd);
+
+ bool is_multitable() { return multitable; }
+
+ void set_as_multitable() { multitable= true; }
+
protected:
/**
@brief Perform precheck of table privileges for update statements
@@ -82,6 +88,9 @@ class Sql_cmd_update final : public Sql_cmd_dml
*/
bool update_single_table(THD *thd);
+ /* Original value of the 'multitable' flag set by constructor */
+ const bool orig_multitable;
+
/*
True if the statement is a multi-table update or converted to such.
For a single-table update this flag is set to true if the statement
@@ -95,7 +104,6 @@ class Sql_cmd_update final : public Sql_cmd_dml
public:
/* The list of the updating expressions used in the set clause */
List<Item> *update_value_list;
-
};
#endif /* SQL_UPDATE_INCLUDED */
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index a587a37..f0d9e69 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -13370,8 +13370,23 @@ delete_single_table:
YYPS->m_lock_type,
YYPS->m_mdl_type,
NULL,
+ 0)))
+ MYSQL_YYABORT;
+ Select->table_list.save_and_clear(&Lex->auxiliary_table_list);
+ /* Save the number of auxiliary tables */
+ Lex->table_count= 1;
+
+ Lex->query_tables= 0;
+ Lex->query_tables_last= &Lex->query_tables;
+ if (unlikely(!Select->
+ add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING,
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type,
+ NULL,
$3)))
MYSQL_YYABORT;
+ Lex->auxiliary_table_list.first->correspondent_table=
+ Lex->query_tables;
YYPS->m_lock_type= TL_READ_DEFAULT;
YYPS->m_mdl_type= MDL_SHARED_READ;
}
diff --git a/sql/table.cc b/sql/table.cc
index f0d5149..44eb9f6 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -7059,7 +7059,8 @@ void Field_iterator_table_ref::set_field_iterator()
table_ref->alias.str));
}
/* This is a merge view, so use field_translation. */
- else if (table_ref->field_translation)
+ else if (table_ref->field_translation &&
+ !table_ref->is_materialized_derived())
{
DBUG_ASSERT(table_ref->is_merged_derived());
field_it= &view_field_it;
@@ -7069,7 +7070,7 @@ void Field_iterator_table_ref::set_field_iterator()
/* This is a base table or stored view. */
else
{
- DBUG_ASSERT(table_ref->table || table_ref->view);
+ DBUG_ASSERT(table_ref->table || table_ref->is_materialized_derived());
field_it= &table_field_it;
DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_table",
table_ref->alias.str));
@@ -9518,13 +9519,16 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
!derived_table_optimization_done(this))
{
/* A subquery might be forced to be materialized due to a side-effect. */
+ bool forced_no_merge_for_update_delete=
+ belong_to_view ? belong_to_view->updating :
+ !unit->outer_select()->outer_select();
if (!is_materialized_derived() && first_select->is_mergeable() &&
(unit->outer_select() && !unit->outer_select()->with_rownum) &&
(!thd->lex->with_rownum ||
(!first_select->group_list.elements &&
!first_select->order_list.elements)) &&
optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE) &&
- !thd->lex->can_not_use_merged(1) &&
+ !thd->lex->can_not_use_merged(forced_no_merge_for_update_delete) &&
!is_recursive_with_table())
set_merged_derived();
else
1
0

[Commits] 78370ea: MDEV-28965 Assertion failure when preparing UPDATE with derived table in WHERE
by IgorBabaev 11 Jul '22
by IgorBabaev 11 Jul '22
11 Jul '22
revision-id: 78370ea0fd21f66a4084d488ddcebac541249241 (mariadb-10.6.1-477-g78370ea)
parent(s): 2db18fdb3d68d906fbd188ec570a64502ba55849
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-07-06 21:57:23 -0700
message:
MDEV-28965 Assertion failure when preparing UPDATE with derived table in WHERE
This patch fixes not only the assertion failure in the function
Field_iterator_table_ref::set_field_iterator() but also:
- fixes the problem of forced materialization of derived tables used
in subqueries contained in WHERE clauses of single-table and multi-table
UPDATE and DELETE statements
- fixes the problem of MDEV-17954 that prevented execution of multi-table
DELETE statements if they use in their WHERE clauses references to
the tables that are updated.
The patch must be considered a complement to the patch for MDEV-28883.
---
mysql-test/main/delete_use_source.result | 184 ++++++++++-
mysql-test/main/delete_use_source.test | 120 ++++++++
mysql-test/main/derived.result | 336 +++++++++++++++++++++
mysql-test/main/derived.test | 210 +++++++++++++
mysql-test/main/derived_cond_pushdown.result | 27 +-
mysql-test/main/derived_cond_pushdown.test | 2 +-
mysql-test/main/multi_update.result | 2 -
mysql-test/main/multi_update.test | 2 -
mysql-test/main/subselect.result | 1 -
mysql-test/main/subselect.test | 1 -
mysql-test/main/subselect_no_exists_to_in.result | 1 -
mysql-test/main/subselect_no_mat.result | 1 -
mysql-test/main/subselect_no_opts.result | 1 -
mysql-test/main/subselect_no_scache.result | 1 -
mysql-test/main/subselect_no_semijoin.result | 1 -
.../engines/iuds/r/update_delete_number.result | 22 +-
.../suite/engines/iuds/t/update_delete_number.test | 20 +-
sql/opt_subselect.cc | 56 +++-
sql/sql_base.cc | 41 ++-
sql/sql_delete.cc | 63 ++--
sql/sql_delete.h | 14 +-
sql/sql_lex.cc | 28 +-
sql/sql_lex.h | 1 +
sql/sql_update.cc | 21 +-
sql/sql_update.h | 16 +-
sql/sql_yacc.yy | 13 +
sql/table.cc | 10 +-
27 files changed, 1106 insertions(+), 89 deletions(-)
diff --git a/mysql-test/main/delete_use_source.result b/mysql-test/main/delete_use_source.result
index 0990a55..329203a 100644
--- a/mysql-test/main/delete_use_source.result
+++ b/mysql-test/main/delete_use_source.result
@@ -49,7 +49,7 @@ rollback;
start transaction;
explain delete from v1 where (select count(*) from t1 b where b.c1=v1.c1) = 500 limit 1;
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY t1 range c1 c1 4 NULL 600 Using where
+1 PRIMARY t1 range c1 c1 4 NULL 600 Using index condition; Using where
2 DEPENDENT SUBQUERY b ref c1 c1 4 test.t1.c1 167 Using index
delete from v1 where (select count(*) from t1 b where b.c1=v1.c1) = 500 limit 1;
affected rows: 1
@@ -65,7 +65,7 @@ rollback;
start transaction;
explain delete from v1 where (select count(*) from t1 b where b.c1=v1.c1) = 500;
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY t1 ALL c1 NULL NULL NULL # Using where
+1 PRIMARY t1 range c1 c1 4 NULL # Using index condition; Using where
2 DEPENDENT SUBQUERY b ref c1 c1 4 test.t1.c1 # Using index
delete from v1 where (select count(*) from t1 b where b.c1=v1.c1) = 500 ;
affected rows: 500
@@ -154,3 +154,183 @@ set session sort_buffer_size = 1024;
delete from t1 where c1=0 and exists(select 'x' from t1 b where b.c1<10);
affected rows: 128000
drop table t1;
+#
+# MDEV-17954: multi-table DELETE with the same source and target
+#
+create table t1 (c1 int, c2 int, c3 int);
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+#
+# Single-table DELETE with the same source and target
+# handled as multi-table DELETE
+#
+explain delete from t1
+where c2 in (select distinct a.c2 from t1 a where t1.c1=a.c1 and a.c2 < 3);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 8 Using where
+1 PRIMARY a ALL NULL NULL NULL NULL 8 Using where; FirstMatch(t1)
+delete from t1
+where c2 in (select distinct a.c2 from t1 a where t1.c1=a.c1 and a.c2 < 3);
+select * from t1;
+c1 c2 c3
+1 3 3
+2 3 6
+2 4 7
+2 5 8
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+prepare stmt from "delete from t1
+where c2 in (select distinct a.c2 from t1 a where t1.c1=a.c1 and a.c2 < 3)";
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 3 3
+2 3 6
+2 4 7
+2 5 8
+delete from t1;
+insert into t1 values
+(2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+c1 c2 c3
+2 3 6
+2 4 7
+2 5 8
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+#
+# Multi-table DELETE with the same source and target
+#
+create table t2 (c1 int, c2 int, c3 int);
+insert into t2 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,5,8);
+explain delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 7
+1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using where
+delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+prepare stmt from "delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1";
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+delete from t1;
+insert into t1 values
+(2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+c1 c2 c3
+2 4 7
+deallocate prepare stmt;
+explain delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t1 system NULL NULL NULL NULL 1
+1 SIMPLE t2 ALL NULL NULL NULL NULL 7 Using where
+delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1;
+select * from t1;
+c1 c2 c3
+2 4 7
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+prepare stmt from "delete from t1 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1";
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+delete from t1;
+insert into t1 values
+(2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+c1 c2 c3
+2 4 7
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+explain delete from t1,t2 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1 and t2.c1 > 1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE t2 ALL NULL NULL NULL NULL 7 Using where
+1 SIMPLE t1 ALL NULL NULL NULL NULL 8 Using where
+delete from t1,t2 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1 and t2.c1 > 1;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+select * from t2;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+delete from t1;
+insert into t1 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+delete from t2;
+insert into t2 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,5,8);
+prepare stmt from "delete from t1,t2 using t1,t2
+where t1.c2 = t2.c2 and t1.c1 > 1 and t2.c1 > 1";
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 4 7
+select * from t2;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+delete from t1;
+insert into t1 values
+(1,2,2), (1,3,3), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+delete from t2;
+insert into t2 values
+(1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5);
+execute stmt;
+select * from t1;
+c1 c2 c3
+1 2 2
+1 3 3
+2 3 6
+2 4 7
+2 5 8
+select * from t2;
+c1 c2 c3
+1 1 1
+1 2 2
+1 3 3
+2 1 4
+deallocate prepare stmt;
+drop table t1,t2;
diff --git a/mysql-test/main/delete_use_source.test b/mysql-test/main/delete_use_source.test
index 4aed00d..9625431 100644
--- a/mysql-test/main/delete_use_source.test
+++ b/mysql-test/main/delete_use_source.test
@@ -135,3 +135,123 @@ set session sort_buffer_size = 1024;
delete from t1 where c1=0 and exists(select 'x' from t1 b where b.c1<10);
drop table t1;
+
+--echo #
+--echo # MDEV-17954: multi-table DELETE with the same source and target
+--echo #
+
+create table t1 (c1 int, c2 int, c3 int);
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+
+--echo #
+--echo # Single-table DELETE with the same source and target
+--echo # handled as multi-table DELETE
+--echo #
+
+let $q1=
+delete from t1
+ where c2 in (select distinct a.c2 from t1 a where t1.c1=a.c1 and a.c2 < 3);
+
+eval explain $q1;
+eval $q1;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+eval prepare stmt from "$q1";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+
+--echo #
+--echo # Multi-table DELETE with the same source and target
+--echo #
+
+create table t2 (c1 int, c2 int, c3 int);
+insert into t2 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,5,8);
+
+let $q2=
+delete from t1 using t1,t2
+ where t1.c2 = t2.c2 and t1.c1 > 1;
+
+eval explain $q2;
+eval $q2;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+eval prepare stmt from "$q2";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+let $q2=
+delete from t1 using t1,t2
+ where t1.c2 = t2.c2 and t1.c1 > 1;
+
+eval explain $q2;
+eval $q2;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+eval prepare stmt from "$q2";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values
+ (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+
+let $q3=
+delete from t1,t2 using t1,t2
+ where t1.c2 = t2.c2 and t1.c1 > 1 and t2.c1 > 1;
+
+eval explain $q3;
+eval $q3;
+select * from t1;
+select * from t2;
+delete from t1;
+insert into t1 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+delete from t2;
+insert into t2 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5), (2,3,6), (2,5,8);
+eval prepare stmt from "$q3";
+execute stmt;
+select * from t1;
+select * from t2;
+delete from t1;
+insert into t1 values
+ (1,2,2), (1,3,3), (2,2,5), (2,3,6), (2,4,7), (2,5,8);
+delete from t2;
+insert into t2 values
+ (1,1,1), (1,2,2), (1,3,3), (2,1,4), (2,2,5);
+execute stmt;
+select * from t1;
+select * from t2;
+deallocate prepare stmt;
+
+drop table t1,t2;
diff --git a/mysql-test/main/derived.result b/mysql-test/main/derived.result
index b6310f1..c28e01d 100644
--- a/mysql-test/main/derived.result
+++ b/mysql-test/main/derived.result
@@ -1316,3 +1316,339 @@ a a
4 4
6 6
drop table t1,t2,t3;
+# End of 10.3 tests
+#
+# MDEV-28883: single/multi-table UPDATE/DELETE whose WHERE condition
+# contains subquery from mergeable derived table
+# that uses the updated/deleted table
+#
+create table t1 (pk int, a int);
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain update t1 set a = 10
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 Using where
+update t1 set a = 10
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 10
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "update t1 set a = 10
+where a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 10
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 10
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+explain update t1 set a = 10
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where; Using filesort
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 Using where
+update t1 set a = 10
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+select * from t1;
+pk a
+4 9
+3 10
+1 10
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+prepare stmt from "update t1 set a = 10
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2";
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 10
+1 10
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 10
+1 10
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 Using where
+delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+explain delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where
+2 SUBQUERY <derived3> ALL NULL NULL NULL NULL 3 Using where
+3 DERIVED t1 ALL NULL NULL NULL NULL 3 Using where
+delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a;
+pk a
+4 9
+select * from t1;
+pk a
+3 1
+1 3
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+prepare stmt from "delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a";
+execute stmt;
+pk a
+4 9
+select * from t1;
+pk a
+3 1
+1 3
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+pk a
+4 9
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain delete from t1
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where; Using filesort
+2 SUBQUERY <derived3> ALL NULL NULL NULL NULL 4 Using where
+3 DERIVED t1 ALL NULL NULL NULL NULL 4 Using where
+delete from t1
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+select * from t1;
+pk a
+2 7
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2";
+execute stmt;
+select * from t1;
+pk a
+2 7
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+execute stmt;
+select * from t1;
+pk a
+2 7
+4 9
+deallocate prepare stmt;
+create table t2 (pk int, a int);
+insert into t2 values (1,3), (2, 7), (3,1), (4,9);
+create table t3 (a int);
+insert into t3 VALUES (0),(1);
+explain update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 2 Using where
+1 PRIMARY t3 ALL NULL NULL NULL NULL 2 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where
+update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+2 7
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 9
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t3 ALL NULL NULL NULL NULL 2 Using where
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 4 Using where
+update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "update t1,t3 set t1.a = 1
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 9
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+insert into t3 values (9), (10), (7);
+explain delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+1 PRIMARY t3 ALL NULL NULL NULL NULL 5 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 Using where
+delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+1 PRIMARY t3 ALL NULL NULL NULL NULL 5 Using where
+2 SUBQUERY t2 ALL NULL NULL NULL NULL 4 Using where
+delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1 using t1,t3
+where t1.a=t3.a and
+t1.a = ( select * from (select a from t2) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+drop table t1,t2,t3;
+# End of MariaDB 10.10 tests
diff --git a/mysql-test/main/derived.test b/mysql-test/main/derived.test
index 904114e..d3b13ce 100644
--- a/mysql-test/main/derived.test
+++ b/mysql-test/main/derived.test
@@ -1126,3 +1126,213 @@ analyze select * from t1 , ( (select t2.a from t2 order by c) union all (select
select * from t1 , ( (select t2.a from t2 order by c) union all (select t2.a from t2 order by c) except(select t3.a from t3 order by b))q where t1.a=q.a;
drop table t1,t2,t3;
+
+--echo # End of 10.3 tests
+
+--echo #
+--echo # MDEV-28883: single/multi-table UPDATE/DELETE whose WHERE condition
+--echo # contains subquery from mergeable derived table
+--echo # that uses the updated/deleted table
+--echo #
+
+create table t1 (pk int, a int);
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q1=
+update t1 set a = 10
+ where a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q1;
+eval $q1;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q1";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+
+let $q2=
+update t1 set a = 10
+ where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+eval explain $q2;
+eval $q2;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+eval prepare stmt from "$q2";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q3=
+delete from t1
+ where a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q3;
+eval $q3;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q3";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+
+let $q4=
+delete from t1
+ where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a;
+
+eval explain $q4;
+eval $q4;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+eval prepare stmt from "$q4";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q5=
+delete from t1
+ where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+eval explain $q5;
+eval $q5;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q5";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+create table t2 (pk int, a int);
+insert into t2 values (1,3), (2, 7), (3,1), (4,9);
+create table t3 (a int);
+insert into t3 VALUES (0),(1);
+
+let $q6=
+update t1,t3 set t1.a = 1
+ where t1.a=t3.a and
+ t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q6;
+eval $q6;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q6";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q7=
+update t1,t3 set t1.a = 1
+ where t1.a=t3.a and
+ t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+
+eval explain $q7;
+eval $q7;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q7";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+insert into t3 values (9), (10), (7);
+
+let $q8=
+delete from t1 using t1,t3
+ where t1.a=t3.a and
+ t1.a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q8;
+eval $q8;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q8";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q9=
+delete from t1 using t1,t3
+ where t1.a=t3.a and
+ t1.a = ( select * from (select a from t2) dt where dt.a > 7);
+
+eval explain $q9;
+eval $q9;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q9";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+
+drop table t1,t2,t3;
+
+--echo # End of MariaDB 10.10 tests
diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result
index 9bbd32a..a7d7e87 100644
--- a/mysql-test/main/derived_cond_pushdown.result
+++ b/mysql-test/main/derived_cond_pushdown.result
@@ -11908,7 +11908,7 @@ DROP TABLE t1;
#
CREATE TABLE t1 (f1 text, f2 int);
INSERT INTO t1 VALUES ('x',1), ('y',2);
-CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 ) AS t;
+CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 GROUP BY f2) AS t;
UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2;
EXPLAIN FORMAT=JSON UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2;
EXPLAIN
@@ -11939,17 +11939,22 @@ EXPLAIN
"materialized": {
"query_block": {
"select_id": 3,
- "nested_loop": [
- {
- "table": {
- "table_name": "t1",
- "access_type": "ALL",
- "rows": 2,
- "filtered": 100,
- "attached_condition": "t1.f2 < 2"
- }
+ "filesort": {
+ "sort_key": "t1.f2",
+ "temporary_table": {
+ "nested_loop": [
+ {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 2,
+ "filtered": 100,
+ "attached_condition": "t1.f2 < 2"
+ }
+ }
+ ]
}
- ]
+ }
}
}
}
diff --git a/mysql-test/main/derived_cond_pushdown.test b/mysql-test/main/derived_cond_pushdown.test
index 39e8221..e88fae7 100644
--- a/mysql-test/main/derived_cond_pushdown.test
+++ b/mysql-test/main/derived_cond_pushdown.test
@@ -2089,7 +2089,7 @@ DROP TABLE t1;
CREATE TABLE t1 (f1 text, f2 int);
INSERT INTO t1 VALUES ('x',1), ('y',2);
-CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 ) AS t;
+CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 GROUP BY f2) AS t;
let $q1 =
UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2;
eval $q1;
diff --git a/mysql-test/main/multi_update.result b/mysql-test/main/multi_update.result
index 674dc79..ae661fa 100644
--- a/mysql-test/main/multi_update.result
+++ b/mysql-test/main/multi_update.result
@@ -441,12 +441,10 @@ create table t1 (col1 int);
create table t2 (col1 int);
update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1;
delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1;
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
drop table t1,t2;
create table t1(a int);
create table t2(a int);
delete from t1,t2 using t1,t2 where t1.a=(select a from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
drop table t1, t2;
create table t1 (a int, b int);
insert into t1 values (1, 2), (2, 3), (3, 4);
diff --git a/mysql-test/main/multi_update.test b/mysql-test/main/multi_update.test
index 5f4b5fc..839cebf 100644
--- a/mysql-test/main/multi_update.test
+++ b/mysql-test/main/multi_update.test
@@ -390,7 +390,6 @@ drop table t1, t2, t3;
create table t1 (col1 int);
create table t2 (col1 int);
update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1;
--- error ER_UPDATE_TABLE_USED
delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1;
drop table t1,t2;
@@ -399,7 +398,6 @@ drop table t1,t2;
#
create table t1(a int);
create table t2(a int);
---error ER_UPDATE_TABLE_USED
delete from t1,t2 using t1,t2 where t1.a=(select a from t1);
drop table t1, t2;
# End of 4.1 tests
diff --git a/mysql-test/main/subselect.result b/mysql-test/main/subselect.result
index 22a814c..5e8265c 100644
--- a/mysql-test/main/subselect.result
+++ b/mysql-test/main/subselect.result
@@ -634,7 +634,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect.test b/mysql-test/main/subselect.test
index 19c30bd..7e29660 100644
--- a/mysql-test/main/subselect.test
+++ b/mysql-test/main/subselect.test
@@ -366,7 +366,6 @@ insert into t12 values (33, 10),(22, 11),(2, 12);
insert into t2 values (1, 21),(2, 12),(3, 23);
select * from t11;
select * from t12;
--- error ER_UPDATE_TABLE_USED
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-- error ER_SUBQUERY_NO_1_ROW
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
diff --git a/mysql-test/main/subselect_no_exists_to_in.result b/mysql-test/main/subselect_no_exists_to_in.result
index def116c..3c18950 100644
--- a/mysql-test/main/subselect_no_exists_to_in.result
+++ b/mysql-test/main/subselect_no_exists_to_in.result
@@ -638,7 +638,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_mat.result b/mysql-test/main/subselect_no_mat.result
index 7eb3734..c1b0c67 100644
--- a/mysql-test/main/subselect_no_mat.result
+++ b/mysql-test/main/subselect_no_mat.result
@@ -641,7 +641,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_opts.result b/mysql-test/main/subselect_no_opts.result
index f2981c0..13af2f4 100644
--- a/mysql-test/main/subselect_no_opts.result
+++ b/mysql-test/main/subselect_no_opts.result
@@ -637,7 +637,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_scache.result b/mysql-test/main/subselect_no_scache.result
index 17bec03..38d5665 100644
--- a/mysql-test/main/subselect_no_scache.result
+++ b/mysql-test/main/subselect_no_scache.result
@@ -640,7 +640,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_semijoin.result b/mysql-test/main/subselect_no_semijoin.result
index cb3620f..cab4e6f 100644
--- a/mysql-test/main/subselect_no_semijoin.result
+++ b/mysql-test/main/subselect_no_semijoin.result
@@ -637,7 +637,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/suite/engines/iuds/r/update_delete_number.result b/mysql-test/suite/engines/iuds/r/update_delete_number.result
index 1cd2a62..1534f93 100644
--- a/mysql-test/suite/engines/iuds/r/update_delete_number.result
+++ b/mysql-test/suite/engines/iuds/r/update_delete_number.result
@@ -739,8 +739,21 @@ c1 c2 c3 c1 c2 c3
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
-DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+INSERT INTO t1 VALUES(254,127,1),(0,-128,2),(1,127,3),(3,NULL,5);
+INSERT INTO t2 VALUES(127,255,1),(127,1,2),(-128,0,3),(-1,NULL,5);
+DELETE FROM t1,t2 using t1,t2
+where t1.c1=(select c1 from t1 where c2 < 10) and t2.c2 < 10;
+SELECT * FROM t1;
+c1 c2 c3
+1 127 3
+254 127 1
+3 NULL 5
+SELECT * FROM t2;
+c1 c2 c3
+-1 NULL 5
+127 255 1
CREATE TABLE t3(c1 INT UNSIGNED NOT NULL PRIMARY KEY, c2 INT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 INT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
@@ -981,7 +994,6 @@ create table mt1 (col1 int);
create table mt2 (col1 int);
update mt1,mt2 set mt1.col1 = (select max(col1) from mt1) where mt1.col1 = mt2.col1;
delete mt1 from mt1,mt2 where mt1.col1 < (select max(col1) from mt1) and mt1.col1 = mt2.col1;
-ERROR HY000: Table 'mt1' is specified twice, both as a target for 'DELETE' and as a separate source for data
drop table mt1,mt2;
CREATE TABLE IF NOT EXISTS `mt1` (`id` int(11) NOT NULL auto_increment, `tst` text, `tsmt1` text, PRIMARY KEY (`id`));
CREATE TABLE IF NOT EXISTS `mt2` (`ID` int(11) NOT NULL auto_increment, `ParId` int(11) default NULL, `tst` text, `tsmt1` text, PRIMARY KEY (`ID`), KEY `IX_ParId_mt2` (`ParId`), FOREIGN KEY (`ParId`) REFERENCES `mt1` (`id`));
@@ -1852,7 +1864,6 @@ DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
CREATE TABLE t3(c1 TINYINT UNSIGNED NOT NULL PRIMARY KEY, c2 TINYINT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 TINYINT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
@@ -2599,7 +2610,6 @@ DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
CREATE TABLE t3(c1 SMALLINT UNSIGNED NOT NULL PRIMARY KEY, c2 SMALLINT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 SMALLINT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
@@ -3346,7 +3356,6 @@ DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
CREATE TABLE t3(c1 MEDIUMINT UNSIGNED NOT NULL PRIMARY KEY, c2 MEDIUMINT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 MEDIUMINT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
@@ -4093,7 +4102,6 @@ DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
c1 c2 c3 c1 c2 c3
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
CREATE TABLE t3(c1 BIGINT UNSIGNED NOT NULL PRIMARY KEY, c2 BIGINT SIGNED NULL, c3 INT);
CREATE TABLE t4(c1 BIGINT UNSIGNED, c2 INT);
INSERT INTO t3 VALUES(200,126,1),(250,-127,2);
diff --git a/mysql-test/suite/engines/iuds/t/update_delete_number.test b/mysql-test/suite/engines/iuds/t/update_delete_number.test
index ce3f901..23a7a10 100644
--- a/mysql-test/suite/engines/iuds/t/update_delete_number.test
+++ b/mysql-test/suite/engines/iuds/t/update_delete_number.test
@@ -285,8 +285,18 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
-DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
+TRUNCATE TABLE t1;
+TRUNCATE TABLE t2;
+INSERT INTO t1 VALUES(254,127,1),(0,-128,2),(1,127,3),(3,NULL,5);
+INSERT INTO t2 VALUES(127,255,1),(127,1,2),(-128,0,3),(-1,NULL,5);
+# After the patch for MDEV-28883 this should not report
+# --error ER_UPDATE_TABLE_USED anymore
+DELETE FROM t1,t2 using t1,t2
+ where t1.c1=(select c1 from t1 where c2 < 10) and t2.c2 < 10;
+--sorted_result
+SELECT * FROM t1;
+--sorted_result
+SELECT * FROM t2;
# eq-ref join
CREATE TABLE t3(c1 INT UNSIGNED NOT NULL PRIMARY KEY, c2 INT SIGNED NULL, c3 INT);
@@ -496,7 +506,7 @@ drop table mt1, mt2, mt3;
create table mt1 (col1 int);
create table mt2 (col1 int);
update mt1,mt2 set mt1.col1 = (select max(col1) from mt1) where mt1.col1 = mt2.col1;
--- error ER_UPDATE_TABLE_USED
+# -- error ER_UPDATE_TABLE_USED
delete mt1 from mt1,mt2 where mt1.col1 < (select max(col1) from mt1) and mt1.col1 = mt2.col1;
drop table mt1,mt2;
@@ -865,7 +875,6 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
# eq-ref join
@@ -1166,7 +1175,6 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
# eq-ref join
@@ -1467,7 +1475,6 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
# eq-ref join
@@ -1768,7 +1775,6 @@ SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a2.c1=a1.c2;
--sorted_result
SELECT * FROM t1,t2 WHERE t2.c1=t1.c2;
---error ER_UPDATE_TABLE_USED
DELETE FROM t1,t2 using t1,t2 where t1.c1=(select c1 from t1);
# eq-ref join
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index fa338f0..ac47566 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -30,6 +30,8 @@
#include "sql_base.h"
#include "sql_const.h"
#include "sql_select.h"
+#include "sql_update.h"
+#include "sql_delete.h"
#include "filesort.h"
#include "opt_subselect.h"
#include "sql_test.h"
@@ -532,6 +534,48 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs,
return FALSE;
}
+/**
+ @brief Check whether an IN subquery must be excluded from conversion to SJ
+
+ @param thd global context the processed statement
+ @returns true if the IN subquery must be excluded from conversion to SJ
+
+ @note
+ Currently a top level IN subquery of an delete statement is not converted
+ to SJ if the statement contains ORDER BY ... LIMIT or contains RETURNING.
+
+ @todo
+ The disjunctive members
+ !((Sql_cmd_update *) cmd)->is_multitable()
+ !((Sql_cmd_delete *) cmd)->is_multitable()
+ will be removed when conversions of IN predicands to semi-joins are
+ fully supported for single-table UPDATE/DELETE statements.
+*/
+
+bool SELECT_LEX::is_sj_conversion_prohibited(THD *thd)
+{
+ DBUG_ASSERT(master_unit()->item->substype() == Item_subselect::IN_SUBS);
+
+ SELECT_LEX *outer_sl= outer_select();
+ if (outer_sl->outer_select())
+ return false;
+
+ Sql_cmd *cmd= thd->lex->m_sql_cmd;
+
+ switch (thd->lex->sql_command) {
+ case SQLCOM_UPDATE:
+ return
+ !((Sql_cmd_update *) cmd)->is_multitable() ||
+ ((Sql_cmd_update *) cmd)->processing_as_multitable_update_prohibited(thd);
+ case SQLCOM_DELETE:
+ return
+ !((Sql_cmd_delete *) cmd)->is_multitable() ||
+ ((Sql_cmd_delete *) cmd)->processing_as_multitable_delete_prohibited(thd);
+ default:
+ return false;
+ }
+}
+
/*
Check if we need JOIN::prepare()-phase subquery rewrites and if yes, do them
@@ -675,9 +719,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
3. Subquery does not have GROUP BY or ORDER BY
4. Subquery does not use aggregate functions or HAVING
5. Subquery predicate is at the AND-top-level of ON/WHERE clause
- 6. We are not in a subquery of a single table UPDATE/DELETE that
- doesn't have a JOIN (TODO: We should handle this at some
- point by switching to multi-table UPDATE/DELETE)
+ 6. We are not in a subquery of a single-table UPDATE/DELETE that
+ does not allow conversion to multi-table UPDATE/DELETE
7. We're not in a table-less subquery like "SELECT 1"
8. No execution method was already chosen (by a prepared statement)
9. Parent select is not a table-less select
@@ -692,9 +735,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
!select_lex->group_list.elements && !join->order && // 3
!join->having && !select_lex->with_sum_func && // 4
in_subs->emb_on_expr_nest && // 5
- select_lex->outer_select()->join && // 6
- (!thd->lex->m_sql_cmd ||
- thd->lex->m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI) &&
+ !select_lex->is_sj_conversion_prohibited(thd) && // 6
parent_unit->first_select()->leaf_tables.elements && // 7
!in_subs->has_strategy() && // 8
select_lex->outer_select()->table_list.first && // 9
@@ -754,7 +795,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
*/
if (in_subs && !in_subs->has_strategy())
{
- if (is_materialization_applicable(thd, in_subs, select_lex))
+ if (!select_lex->is_sj_conversion_prohibited(thd) &&
+ is_materialization_applicable(thd, in_subs, select_lex))
{
in_subs->add_strategy(SUBS_MATERIALIZATION);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 2b41b78..447573b 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -47,6 +47,8 @@
#include "sql_prepare.h"
#include "sql_statistics.h"
#include "sql_cte.h"
+#include "sql_update.h"
+#include "sql_delete.h"
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
@@ -1164,7 +1166,7 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
/*
If we found entry of this table or table of SELECT which already
processed in derived table or top select of multi-update/multi-delete
- (exclude_from_table_unique_test) or prelocking placeholder.
+ (exclude_from_table_unique_test) or prelocking placeholder.
*/
DBUG_PRINT("info",
("found same copy of table or table which we should skip"));
@@ -1175,16 +1177,43 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
We come here for queries of type:
INSERT INTO t1 (SELECT tmp.a FROM (select * FROM t1) as tmp);
- Try to fix by materializing the derived table
+ Try to fix by materializing the derived table if one can't do without it.
*/
TABLE_LIST *derived= res->belong_to_derived;
if (derived->is_merged_derived() && !derived->derived->is_excluded())
{
- DBUG_PRINT("info",
+ bool materialize= true;
+ if (thd->lex->sql_command == SQLCOM_UPDATE)
+ {
+ Sql_cmd_update *cmd= (Sql_cmd_update *) (thd->lex->m_sql_cmd);
+ if (cmd->is_multitable())
+ materialize= false;
+ else if (!cmd->processing_as_multitable_update_prohibited(thd))
+ {
+ cmd->set_as_multitable();
+ materialize= false;
+ }
+ }
+ else if (thd->lex->sql_command == SQLCOM_DELETE)
+ {
+ Sql_cmd_delete *cmd= (Sql_cmd_delete *) (thd->lex->m_sql_cmd);
+ if (cmd->is_multitable())
+ materialize= false;
+ if (!cmd->processing_as_multitable_delete_prohibited(thd))
+ {
+ cmd->set_as_multitable();
+ materialize= false;
+ }
+ }
+ if (materialize)
+ {
+ DBUG_PRINT("info",
("convert merged to materialization to resolve the conflict"));
- derived->change_refs_to_fields();
- derived->set_materialized_derived();
- goto retry;
+ derived->change_refs_to_fields();
+ derived->set_materialized_derived();
+ // derived->field_translation= 0;
+ goto retry;
+ }
}
}
DBUG_RETURN(res);
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 2ff4331..2db662c 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -1109,6 +1109,8 @@ multi_delete::~multi_delete()
table_being_deleted= table_being_deleted->next_local)
{
TABLE *table= table_being_deleted->table;
+ if (!table)
+ continue;
table->no_keyread=0;
table->no_cache= 0;
}
@@ -1440,6 +1442,34 @@ bool multi_delete::send_eof()
}
+/**
+ @brief Check whether processing to multi-table delete is prohibited
+
+ @param thd global context the processed statement
+ @returns true if processing as multitable is prohibited, false otherwise
+
+ @todo
+ Introduce handler level flag for storage engines that would prohibit
+ such conversion for any single-table delete.
+*/
+
+bool Sql_cmd_delete::processing_as_multitable_delete_prohibited(THD *thd)
+{
+ SELECT_LEX *const select_lex = thd->lex->first_select_lex();
+ return
+ ((select_lex->order_list.elements &&
+ select_lex->limit_params.select_limit) ||
+ thd->lex->has_returning());
+}
+
+
+/**
+ @brief Perform precheck of table privileges for delete statements
+
+ @param thd global context the processed statement
+ @returns false on success, true on error
+*/
+
bool Sql_cmd_delete::precheck(THD *thd)
{
if (!multitable)
@@ -1543,6 +1573,20 @@ bool Sql_cmd_delete::prepare_inner(THD *thd)
DBUG_RETURN(true);
}
+ if (!multitable)
+ {
+ TABLE_LIST *update_source_table= 0;
+ if (((update_source_table=unique_table(thd, table_list,
+ table_list->next_global, 0)) ||
+ table_list->is_multitable()))
+ {
+ DBUG_ASSERT(update_source_table || table_list->view != 0);
+ if (!table_list->is_multitable() &&
+ !processing_as_multitable_delete_prohibited(thd))
+ multitable= true;
+ }
+ }
+
if (table_list->has_period())
{
if (table_list->is_view_or_derived())
@@ -1627,25 +1671,6 @@ bool Sql_cmd_delete::prepare_inner(THD *thd)
}
}
- for (target_tbl= (TABLE_LIST*) aux_tables;
- target_tbl;
- target_tbl= target_tbl->next_local)
- {
- /*
- Check that table from which we delete is not used somewhere
- inside subqueries/view.
- */
- {
- TABLE_LIST *duplicate;
- if ((duplicate= unique_table(thd, target_tbl->correspondent_table,
- lex->query_tables, 0)))
- {
- update_non_unique_table_error(target_tbl->correspondent_table,
- "DELETE", duplicate);
- DBUG_RETURN(TRUE);
- }
- }
- }
/*
Reset the exclude flag to false so it doesn't interfare
with further calls to unique_table
diff --git a/sql/sql_delete.h b/sql/sql_delete.h
index e1d5044..ffb8173 100644
--- a/sql/sql_delete.h
+++ b/sql/sql_delete.h
@@ -44,11 +44,12 @@ class Sql_cmd_delete final : public Sql_cmd_dml
{
public:
Sql_cmd_delete(bool multitable_arg)
- : multitable(multitable_arg), save_protocol(NULL) {}
+ : orig_multitable(multitable_arg), save_protocol(NULL)
+ { multitable= orig_multitable; }
enum_sql_command sql_command_code() const override
{
- return multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE;
+ return orig_multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE;
}
DML_prelocking_strategy *get_dml_prelocking_strategy()
@@ -56,6 +57,12 @@ class Sql_cmd_delete final : public Sql_cmd_dml
return &dml_prelocking_strategy;
}
+ bool processing_as_multitable_delete_prohibited(THD *thd);
+
+ bool is_multitable() { return multitable; }
+
+ void set_as_multitable() { multitable= true; }
+
protected:
/**
@brief Perform precheck of table privileges for delete statements
@@ -85,6 +92,9 @@ class Sql_cmd_delete final : public Sql_cmd_dml
*/
bool multitable;
+ /* Original value of the 'multitable' flag set by constructor */
+ const bool orig_multitable;
+
/* The prelocking strategy used when opening the used tables */
DML_prelocking_strategy dml_prelocking_strategy;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 78c067a..4059c0e 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -37,6 +37,8 @@
#include "sql_partition.h"
#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part
#include "event_parse_data.h"
+#include "sql_update.h"
+#include "sql_delete.h"
void LEX::parse_error(uint err_number)
{
@@ -4037,9 +4039,8 @@ bool LEX::can_use_merged()
SYNOPSIS
LEX::can_not_use_merged()
- @param no_update_or_delete Set to 1 if we can't use merge with multiple-table
- updates, like when used from
- TALE_LIST::init_derived()
+ @param forced_no_merge_for_update_delete Set to 1 if we can't use merge with
+ multiple-table updates/deletes
DESCRIPTION
Temporary table algorithm will be used on all SELECT levels for queries
@@ -4050,7 +4051,7 @@ bool LEX::can_use_merged()
TRUE - VIEWs with MERGE algorithms can be used
*/
-bool LEX::can_not_use_merged(bool no_update_or_delete)
+bool LEX::can_not_use_merged(bool forced_no_merge_for_update_delete)
{
switch (sql_command) {
case SQLCOM_CREATE_VIEW:
@@ -4064,18 +4065,29 @@ bool LEX::can_not_use_merged(bool no_update_or_delete)
return TRUE;
case SQLCOM_UPDATE_MULTI:
- case SQLCOM_DELETE_MULTI:
- if (no_update_or_delete)
+ if (forced_no_merge_for_update_delete)
return TRUE;
/* Fall through */
case SQLCOM_UPDATE:
- if (no_update_or_delete && m_sql_cmd &&
- (m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI ||
+ if (forced_no_merge_for_update_delete &&
+ (((Sql_cmd_update *) m_sql_cmd)->is_multitable() ||
query_tables->is_multitable()))
return TRUE;
+ return FALSE;
+
+ case SQLCOM_DELETE_MULTI:
+ if (forced_no_merge_for_update_delete)
+ return TRUE;
/* Fall through */
+ case SQLCOM_DELETE:
+ if (forced_no_merge_for_update_delete &&
+ (((Sql_cmd_delete *) m_sql_cmd)->is_multitable() ||
+ query_tables->is_multitable()))
+ return TRUE;
+ return FALSE;
+
default:
return FALSE;
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index e733b4f..402e3bd 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1642,6 +1642,7 @@ class st_select_lex: public st_select_lex_node
void lex_start(LEX *plex);
bool is_unit_nest() { return (nest_flags & UNIT_NEST_FL); }
void mark_as_unit_nest() { nest_flags= UNIT_NEST_FL; }
+ bool is_sj_conversion_prohibited(THD *thd);
};
typedef class st_select_lex SELECT_LEX;
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 6b14c4f..7769777 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -2807,6 +2807,23 @@ bool multi_update::send_eof()
/**
+ @brief Check whether conversion to multi-table update is prohibited
+
+ @param thd global context the processed statement
+ @returns true if conversion is prohibited, false otherwise
+
+ @todo
+ Introduce handler level flag for storage engines that would prohibit
+ such conversion for any single-table update.
+*/
+
+bool Sql_cmd_update::processing_as_multitable_update_prohibited(THD *thd)
+{
+ return false;
+}
+
+
+/**
@brief Perform precheck of table privileges for update statements
@param thd global context the processed statement
@@ -2889,7 +2906,9 @@ bool Sql_cmd_update::prepare_inner(THD *thd)
"updating and querying the same temporal periods table");
DBUG_RETURN(TRUE);
}
- multitable= true;
+ if (!table_list->is_multitable() &&
+ !processing_as_multitable_update_prohibited(thd))
+ multitable= true;
}
}
diff --git a/sql/sql_update.h b/sql/sql_update.h
index d0fc7cb..4aff77a 100644
--- a/sql/sql_update.h
+++ b/sql/sql_update.h
@@ -46,12 +46,12 @@ class Sql_cmd_update final : public Sql_cmd_dml
{
public:
Sql_cmd_update(bool multitable_arg)
- : multitable(multitable_arg)
- { }
+ : orig_multitable(multitable_arg)
+ { multitable= orig_multitable; }
enum_sql_command sql_command_code() const override
{
- return multitable ? SQLCOM_UPDATE_MULTI : SQLCOM_UPDATE;
+ return orig_multitable ? SQLCOM_UPDATE_MULTI : SQLCOM_UPDATE;
}
DML_prelocking_strategy *get_dml_prelocking_strategy()
@@ -59,6 +59,12 @@ class Sql_cmd_update final : public Sql_cmd_dml
return &multiupdate_prelocking_strategy;
}
+ bool processing_as_multitable_update_prohibited(THD *thd);
+
+ bool is_multitable() { return multitable; }
+
+ void set_as_multitable() { multitable= true; }
+
protected:
/**
@brief Perform precheck of table privileges for update statements
@@ -89,13 +95,15 @@ class Sql_cmd_update final : public Sql_cmd_dml
*/
bool multitable;
+ /* Original value of the 'multitable' flag set by constructor */
+ const bool orig_multitable;
+
/* The prelocking strategy used when opening the used tables */
Multiupdate_prelocking_strategy multiupdate_prelocking_strategy;
public:
/* The list of the updating expressions used in the set clause */
List<Item> *update_value_list;
-
};
#endif /* SQL_UPDATE_INCLUDED */
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index a587a37..13dc602 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -13370,8 +13370,21 @@ delete_single_table:
YYPS->m_lock_type,
YYPS->m_mdl_type,
NULL,
+ 0)))
+ MYSQL_YYABORT;
+ Select->table_list.save_and_clear(&Lex->auxiliary_table_list);
+ Lex->table_count= 1;
+ Lex->query_tables= 0;
+ Lex->query_tables_last= &Lex->query_tables;
+ if (unlikely(!Select->
+ add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING,
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type,
+ NULL,
$3)))
MYSQL_YYABORT;
+ Lex->auxiliary_table_list.first->correspondent_table=
+ Lex->query_tables;
YYPS->m_lock_type= TL_READ_DEFAULT;
YYPS->m_mdl_type= MDL_SHARED_READ;
}
diff --git a/sql/table.cc b/sql/table.cc
index f0d5149..44eb9f6 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -7059,7 +7059,8 @@ void Field_iterator_table_ref::set_field_iterator()
table_ref->alias.str));
}
/* This is a merge view, so use field_translation. */
- else if (table_ref->field_translation)
+ else if (table_ref->field_translation &&
+ !table_ref->is_materialized_derived())
{
DBUG_ASSERT(table_ref->is_merged_derived());
field_it= &view_field_it;
@@ -7069,7 +7070,7 @@ void Field_iterator_table_ref::set_field_iterator()
/* This is a base table or stored view. */
else
{
- DBUG_ASSERT(table_ref->table || table_ref->view);
+ DBUG_ASSERT(table_ref->table || table_ref->is_materialized_derived());
field_it= &table_field_it;
DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_table",
table_ref->alias.str));
@@ -9518,13 +9519,16 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
!derived_table_optimization_done(this))
{
/* A subquery might be forced to be materialized due to a side-effect. */
+ bool forced_no_merge_for_update_delete=
+ belong_to_view ? belong_to_view->updating :
+ !unit->outer_select()->outer_select();
if (!is_materialized_derived() && first_select->is_mergeable() &&
(unit->outer_select() && !unit->outer_select()->with_rownum) &&
(!thd->lex->with_rownum ||
(!first_select->group_list.elements &&
!first_select->order_list.elements)) &&
optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE) &&
- !thd->lex->can_not_use_merged(1) &&
+ !thd->lex->can_not_use_merged(forced_no_merge_for_update_delete) &&
!is_recursive_with_table())
set_merged_derived();
else
2
1

[Commits] 90016d4: MDEV-28965 Assertion failure when preparing UPDATE with derived table in WHERE
by IgorBabaev 02 Jul '22
by IgorBabaev 02 Jul '22
02 Jul '22
revision-id: 90016d4de943f9557ea3905bd0c165421f658ddb (mariadb-10.6.1-477-g90016d4)
parent(s): 2db18fdb3d68d906fbd188ec570a64502ba55849
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-07-01 16:25:24 -0700
message:
MDEV-28965 Assertion failure when preparing UPDATE with derived table in WHERE
This is a preliminary patch to test the fix.
---
mysql-test/main/derived_cond_pushdown.result | 27 ++--
mysql-test/main/derived_cond_pushdown.test | 2 +-
mysql-test/main/multi_update.result | 2 -
mysql-test/main/multi_update.test | 2 -
mysql-test/main/subselect.result | 1 -
mysql-test/main/subselect.test | 1 -
mysql-test/main/subselect_no_exists_to_in.result | 1 -
mysql-test/main/subselect_no_mat.result | 1 -
mysql-test/main/subselect_no_opts.result | 1 -
mysql-test/main/subselect_no_scache.result | 1 -
mysql-test/main/subselect_no_semijoin.result | 1 -
mysql-test/main/update.result | 180 +++++++++++++++++++++++
mysql-test/main/update.test | 112 ++++++++++++++
sql/opt_subselect.cc | 56 ++++++-
sql/sql_base.cc | 41 +++++-
sql/sql_delete.cc | 49 +++---
sql/sql_delete.h | 14 +-
sql/sql_lex.cc | 28 +++-
sql/sql_lex.h | 1 +
sql/sql_update.cc | 21 ++-
sql/sql_update.h | 16 +-
sql/sql_yacc.yy | 13 ++
sql/table.cc | 10 +-
23 files changed, 508 insertions(+), 73 deletions(-)
diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result
index 9bbd32a..a7d7e87 100644
--- a/mysql-test/main/derived_cond_pushdown.result
+++ b/mysql-test/main/derived_cond_pushdown.result
@@ -11908,7 +11908,7 @@ DROP TABLE t1;
#
CREATE TABLE t1 (f1 text, f2 int);
INSERT INTO t1 VALUES ('x',1), ('y',2);
-CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 ) AS t;
+CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 GROUP BY f2) AS t;
UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2;
EXPLAIN FORMAT=JSON UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2;
EXPLAIN
@@ -11939,17 +11939,22 @@ EXPLAIN
"materialized": {
"query_block": {
"select_id": 3,
- "nested_loop": [
- {
- "table": {
- "table_name": "t1",
- "access_type": "ALL",
- "rows": 2,
- "filtered": 100,
- "attached_condition": "t1.f2 < 2"
- }
+ "filesort": {
+ "sort_key": "t1.f2",
+ "temporary_table": {
+ "nested_loop": [
+ {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "rows": 2,
+ "filtered": 100,
+ "attached_condition": "t1.f2 < 2"
+ }
+ }
+ ]
}
- ]
+ }
}
}
}
diff --git a/mysql-test/main/derived_cond_pushdown.test b/mysql-test/main/derived_cond_pushdown.test
index 39e8221..e88fae7 100644
--- a/mysql-test/main/derived_cond_pushdown.test
+++ b/mysql-test/main/derived_cond_pushdown.test
@@ -2089,7 +2089,7 @@ DROP TABLE t1;
CREATE TABLE t1 (f1 text, f2 int);
INSERT INTO t1 VALUES ('x',1), ('y',2);
-CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 ) AS t;
+CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 GROUP BY f2) AS t;
let $q1 =
UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2;
eval $q1;
diff --git a/mysql-test/main/multi_update.result b/mysql-test/main/multi_update.result
index 674dc79..ae661fa 100644
--- a/mysql-test/main/multi_update.result
+++ b/mysql-test/main/multi_update.result
@@ -441,12 +441,10 @@ create table t1 (col1 int);
create table t2 (col1 int);
update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1;
delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1;
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
drop table t1,t2;
create table t1(a int);
create table t2(a int);
delete from t1,t2 using t1,t2 where t1.a=(select a from t1);
-ERROR HY000: Table 't1' is specified twice, both as a target for 'DELETE' and as a separate source for data
drop table t1, t2;
create table t1 (a int, b int);
insert into t1 values (1, 2), (2, 3), (3, 4);
diff --git a/mysql-test/main/multi_update.test b/mysql-test/main/multi_update.test
index 5f4b5fc..839cebf 100644
--- a/mysql-test/main/multi_update.test
+++ b/mysql-test/main/multi_update.test
@@ -390,7 +390,6 @@ drop table t1, t2, t3;
create table t1 (col1 int);
create table t2 (col1 int);
update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1;
--- error ER_UPDATE_TABLE_USED
delete t1 from t1,t2 where t1.col1 < (select max(col1) from t1) and t1.col1 = t2.col1;
drop table t1,t2;
@@ -399,7 +398,6 @@ drop table t1,t2;
#
create table t1(a int);
create table t2(a int);
---error ER_UPDATE_TABLE_USED
delete from t1,t2 using t1,t2 where t1.a=(select a from t1);
drop table t1, t2;
# End of 4.1 tests
diff --git a/mysql-test/main/subselect.result b/mysql-test/main/subselect.result
index 22a814c..5e8265c 100644
--- a/mysql-test/main/subselect.result
+++ b/mysql-test/main/subselect.result
@@ -634,7 +634,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect.test b/mysql-test/main/subselect.test
index 19c30bd..7e29660 100644
--- a/mysql-test/main/subselect.test
+++ b/mysql-test/main/subselect.test
@@ -366,7 +366,6 @@ insert into t12 values (33, 10),(22, 11),(2, 12);
insert into t2 values (1, 21),(2, 12),(3, 23);
select * from t11;
select * from t12;
--- error ER_UPDATE_TABLE_USED
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-- error ER_SUBQUERY_NO_1_ROW
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
diff --git a/mysql-test/main/subselect_no_exists_to_in.result b/mysql-test/main/subselect_no_exists_to_in.result
index def116c..3c18950 100644
--- a/mysql-test/main/subselect_no_exists_to_in.result
+++ b/mysql-test/main/subselect_no_exists_to_in.result
@@ -638,7 +638,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_mat.result b/mysql-test/main/subselect_no_mat.result
index 7eb3734..c1b0c67 100644
--- a/mysql-test/main/subselect_no_mat.result
+++ b/mysql-test/main/subselect_no_mat.result
@@ -641,7 +641,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_opts.result b/mysql-test/main/subselect_no_opts.result
index f2981c0..13af2f4 100644
--- a/mysql-test/main/subselect_no_opts.result
+++ b/mysql-test/main/subselect_no_opts.result
@@ -637,7 +637,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_scache.result b/mysql-test/main/subselect_no_scache.result
index 17bec03..38d5665 100644
--- a/mysql-test/main/subselect_no_scache.result
+++ b/mysql-test/main/subselect_no_scache.result
@@ -640,7 +640,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/subselect_no_semijoin.result b/mysql-test/main/subselect_no_semijoin.result
index cb3620f..cab4e6f 100644
--- a/mysql-test/main/subselect_no_semijoin.result
+++ b/mysql-test/main/subselect_no_semijoin.result
@@ -637,7 +637,6 @@ a b
22 11
2 12
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t12 where t11.a = t12.a);
-ERROR HY000: Table 't12' is specified twice, both as a target for 'DELETE' and as a separate source for data
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2);
ERROR 21000: Subquery returns more than 1 row
delete t11.*, t12.* from t11,t12 where t11.a = t12.a and t11.b = (select b from t2 where t11.a = t2.a);
diff --git a/mysql-test/main/update.result b/mysql-test/main/update.result
index 15efd7e..bcbfd8e 100644
--- a/mysql-test/main/update.result
+++ b/mysql-test/main/update.result
@@ -734,3 +734,183 @@ UPDATE t1,t2 SET t1.i1 = -39 WHERE t2.d1 <> t1.i1 AND t2.d1 = t1.d2;
ERROR 22007: Incorrect datetime value: '19' for column `test`.`t1`.`i1` at row 1
DROP TABLE t1,t2;
# End of MariaDB 10.2 tests
+#
+# MDEV-28965: single-table update/delete whose WHERE condition
+# contains subquery from mergeable derived table
+# that uses the updated/deleted table
+#
+create table t1 (pk int, a int);
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain update t1 set a = 10
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 Using where
+update t1 set a = 10
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 10
+prepare stmt from "update t1 set a = 10
+where a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+4 10
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 10
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+explain update t1 set a = 10
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where; Using filesort
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 3 Using where
+update t1 set a = 10
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+select * from t1;
+pk a
+4 9
+3 10
+1 10
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+prepare stmt from "update t1 set a = 10
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2";
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 10
+1 10
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+4 9
+3 10
+1 10
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 4 Using where
+delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 7);
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+prepare stmt from "delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 7)";
+execute stmt;
+select * from t1;
+pk a
+1 3
+2 7
+3 1
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+explain delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where
+2 SUBQUERY <derived3> ALL NULL NULL NULL NULL 3 Using where
+3 DERIVED t1 ALL NULL NULL NULL NULL 3 Using where
+delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a;
+pk a
+4 9
+select * from t1;
+pk a
+3 1
+1 3
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+prepare stmt from "delete from t1
+where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a";
+execute stmt;
+pk a
+4 9
+select * from t1;
+pk a
+3 1
+1 3
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+pk a
+4 9
+select * from t1;
+pk a
+3 1
+1 3
+deallocate prepare stmt;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+explain delete from t1
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 4 Using where; Using filesort
+2 SUBQUERY <derived3> ALL NULL NULL NULL NULL 4 Using where
+3 DERIVED t1 ALL NULL NULL NULL NULL 4 Using where
+delete from t1
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+select * from t1;
+pk a
+2 7
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+prepare stmt from "delete from t1
+where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2";
+execute stmt;
+select * from t1;
+pk a
+2 7
+4 9
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+execute stmt;
+select * from t1;
+pk a
+2 7
+4 9
+deallocate prepare stmt;
+drop table t1;
+# End of MariaDB 10.10 tests
diff --git a/mysql-test/main/update.test b/mysql-test/main/update.test
index 8470910..1df917a 100644
--- a/mysql-test/main/update.test
+++ b/mysql-test/main/update.test
@@ -673,3 +673,115 @@ UPDATE t1,t2 SET t1.i1 = -39 WHERE t2.d1 <> t1.i1 AND t2.d1 = t1.d2;
DROP TABLE t1,t2;
--echo # End of MariaDB 10.2 tests
+
+--echo #
+--echo # MDEV-28965: single-table update/delete whose WHERE condition
+--echo # contains subquery from mergeable derived table
+--echo # that uses the updated/deleted table
+--echo #
+
+create table t1 (pk int, a int);
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q1=
+update t1 set a = 10
+ where a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q1;
+eval $q1;
+select * from t1;
+eval prepare stmt from "$q1";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+
+let $q2=
+update t1 set a = 10
+ where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+eval explain $q2;
+eval $q2;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+eval prepare stmt from "$q2";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q1=
+delete from t1
+ where a = ( select * from (select a from t1) dt where dt.a > 7);
+
+eval explain $q1;
+eval $q1;
+select * from t1;
+eval prepare stmt from "$q1";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+
+let $q2=
+delete from t1
+ where a = ( select * from (select a from t1) dt where dt.a > 5)
+returning pk, a;
+
+eval explain $q2;
+eval $q2;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+eval prepare stmt from "$q2";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 value (4,9), (3,1), (1,3);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+
+let $q3=
+delete from t1
+ where a <> ( select * from (select a from t1) dt where dt.a > 7)
+order by a limit 2;
+eval explain $q3;
+eval $q3;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+eval prepare stmt from "$q3";
+execute stmt;
+select * from t1;
+delete from t1;
+insert into t1 values (1,3), (2, 7), (3,1), (4,9);
+execute stmt;
+select * from t1;
+deallocate prepare stmt;
+
+drop table t1;
+
+--echo # End of MariaDB 10.10 tests
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index fa338f0..ac47566 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -30,6 +30,8 @@
#include "sql_base.h"
#include "sql_const.h"
#include "sql_select.h"
+#include "sql_update.h"
+#include "sql_delete.h"
#include "filesort.h"
#include "opt_subselect.h"
#include "sql_test.h"
@@ -532,6 +534,48 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs,
return FALSE;
}
+/**
+ @brief Check whether an IN subquery must be excluded from conversion to SJ
+
+ @param thd global context the processed statement
+ @returns true if the IN subquery must be excluded from conversion to SJ
+
+ @note
+ Currently a top level IN subquery of an delete statement is not converted
+ to SJ if the statement contains ORDER BY ... LIMIT or contains RETURNING.
+
+ @todo
+ The disjunctive members
+ !((Sql_cmd_update *) cmd)->is_multitable()
+ !((Sql_cmd_delete *) cmd)->is_multitable()
+ will be removed when conversions of IN predicands to semi-joins are
+ fully supported for single-table UPDATE/DELETE statements.
+*/
+
+bool SELECT_LEX::is_sj_conversion_prohibited(THD *thd)
+{
+ DBUG_ASSERT(master_unit()->item->substype() == Item_subselect::IN_SUBS);
+
+ SELECT_LEX *outer_sl= outer_select();
+ if (outer_sl->outer_select())
+ return false;
+
+ Sql_cmd *cmd= thd->lex->m_sql_cmd;
+
+ switch (thd->lex->sql_command) {
+ case SQLCOM_UPDATE:
+ return
+ !((Sql_cmd_update *) cmd)->is_multitable() ||
+ ((Sql_cmd_update *) cmd)->processing_as_multitable_update_prohibited(thd);
+ case SQLCOM_DELETE:
+ return
+ !((Sql_cmd_delete *) cmd)->is_multitable() ||
+ ((Sql_cmd_delete *) cmd)->processing_as_multitable_delete_prohibited(thd);
+ default:
+ return false;
+ }
+}
+
/*
Check if we need JOIN::prepare()-phase subquery rewrites and if yes, do them
@@ -675,9 +719,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
3. Subquery does not have GROUP BY or ORDER BY
4. Subquery does not use aggregate functions or HAVING
5. Subquery predicate is at the AND-top-level of ON/WHERE clause
- 6. We are not in a subquery of a single table UPDATE/DELETE that
- doesn't have a JOIN (TODO: We should handle this at some
- point by switching to multi-table UPDATE/DELETE)
+ 6. We are not in a subquery of a single-table UPDATE/DELETE that
+ does not allow conversion to multi-table UPDATE/DELETE
7. We're not in a table-less subquery like "SELECT 1"
8. No execution method was already chosen (by a prepared statement)
9. Parent select is not a table-less select
@@ -692,9 +735,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
!select_lex->group_list.elements && !join->order && // 3
!join->having && !select_lex->with_sum_func && // 4
in_subs->emb_on_expr_nest && // 5
- select_lex->outer_select()->join && // 6
- (!thd->lex->m_sql_cmd ||
- thd->lex->m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI) &&
+ !select_lex->is_sj_conversion_prohibited(thd) && // 6
parent_unit->first_select()->leaf_tables.elements && // 7
!in_subs->has_strategy() && // 8
select_lex->outer_select()->table_list.first && // 9
@@ -754,7 +795,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
*/
if (in_subs && !in_subs->has_strategy())
{
- if (is_materialization_applicable(thd, in_subs, select_lex))
+ if (!select_lex->is_sj_conversion_prohibited(thd) &&
+ is_materialization_applicable(thd, in_subs, select_lex))
{
in_subs->add_strategy(SUBS_MATERIALIZATION);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 2b41b78..447573b 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -47,6 +47,8 @@
#include "sql_prepare.h"
#include "sql_statistics.h"
#include "sql_cte.h"
+#include "sql_update.h"
+#include "sql_delete.h"
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
@@ -1164,7 +1166,7 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
/*
If we found entry of this table or table of SELECT which already
processed in derived table or top select of multi-update/multi-delete
- (exclude_from_table_unique_test) or prelocking placeholder.
+ (exclude_from_table_unique_test) or prelocking placeholder.
*/
DBUG_PRINT("info",
("found same copy of table or table which we should skip"));
@@ -1175,16 +1177,43 @@ TABLE_LIST* find_dup_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
We come here for queries of type:
INSERT INTO t1 (SELECT tmp.a FROM (select * FROM t1) as tmp);
- Try to fix by materializing the derived table
+ Try to fix by materializing the derived table if one can't do without it.
*/
TABLE_LIST *derived= res->belong_to_derived;
if (derived->is_merged_derived() && !derived->derived->is_excluded())
{
- DBUG_PRINT("info",
+ bool materialize= true;
+ if (thd->lex->sql_command == SQLCOM_UPDATE)
+ {
+ Sql_cmd_update *cmd= (Sql_cmd_update *) (thd->lex->m_sql_cmd);
+ if (cmd->is_multitable())
+ materialize= false;
+ else if (!cmd->processing_as_multitable_update_prohibited(thd))
+ {
+ cmd->set_as_multitable();
+ materialize= false;
+ }
+ }
+ else if (thd->lex->sql_command == SQLCOM_DELETE)
+ {
+ Sql_cmd_delete *cmd= (Sql_cmd_delete *) (thd->lex->m_sql_cmd);
+ if (cmd->is_multitable())
+ materialize= false;
+ if (!cmd->processing_as_multitable_delete_prohibited(thd))
+ {
+ cmd->set_as_multitable();
+ materialize= false;
+ }
+ }
+ if (materialize)
+ {
+ DBUG_PRINT("info",
("convert merged to materialization to resolve the conflict"));
- derived->change_refs_to_fields();
- derived->set_materialized_derived();
- goto retry;
+ derived->change_refs_to_fields();
+ derived->set_materialized_derived();
+ // derived->field_translation= 0;
+ goto retry;
+ }
}
}
DBUG_RETURN(res);
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 2ff4331..22b76da 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -1109,6 +1109,8 @@ multi_delete::~multi_delete()
table_being_deleted= table_being_deleted->next_local)
{
TABLE *table= table_being_deleted->table;
+ if (!table)
+ continue;
table->no_keyread=0;
table->no_cache= 0;
}
@@ -1440,6 +1442,34 @@ bool multi_delete::send_eof()
}
+/**
+ @brief Check whether processing to multi-table delete is prohibited
+
+ @param thd global context the processed statement
+ @returns true if processing as multitable is prohibited, false otherwise
+
+ @todo
+ Introduce handler level flag for storage engines that would prohibit
+ such conversion for any single-table delete.
+*/
+
+bool Sql_cmd_delete::processing_as_multitable_delete_prohibited(THD *thd)
+{
+ SELECT_LEX *const select_lex = thd->lex->first_select_lex();
+ return
+ ((select_lex->order_list.elements &&
+ select_lex->limit_params.select_limit) ||
+ thd->lex->has_returning());
+}
+
+
+/**
+ @brief Perform precheck of table privileges for delete statements
+
+ @param thd global context the processed statement
+ @returns false on success, true on error
+*/
+
bool Sql_cmd_delete::precheck(THD *thd)
{
if (!multitable)
@@ -1627,25 +1657,6 @@ bool Sql_cmd_delete::prepare_inner(THD *thd)
}
}
- for (target_tbl= (TABLE_LIST*) aux_tables;
- target_tbl;
- target_tbl= target_tbl->next_local)
- {
- /*
- Check that table from which we delete is not used somewhere
- inside subqueries/view.
- */
- {
- TABLE_LIST *duplicate;
- if ((duplicate= unique_table(thd, target_tbl->correspondent_table,
- lex->query_tables, 0)))
- {
- update_non_unique_table_error(target_tbl->correspondent_table,
- "DELETE", duplicate);
- DBUG_RETURN(TRUE);
- }
- }
- }
/*
Reset the exclude flag to false so it doesn't interfare
with further calls to unique_table
diff --git a/sql/sql_delete.h b/sql/sql_delete.h
index e1d5044..ffb8173 100644
--- a/sql/sql_delete.h
+++ b/sql/sql_delete.h
@@ -44,11 +44,12 @@ class Sql_cmd_delete final : public Sql_cmd_dml
{
public:
Sql_cmd_delete(bool multitable_arg)
- : multitable(multitable_arg), save_protocol(NULL) {}
+ : orig_multitable(multitable_arg), save_protocol(NULL)
+ { multitable= orig_multitable; }
enum_sql_command sql_command_code() const override
{
- return multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE;
+ return orig_multitable ? SQLCOM_DELETE_MULTI : SQLCOM_DELETE;
}
DML_prelocking_strategy *get_dml_prelocking_strategy()
@@ -56,6 +57,12 @@ class Sql_cmd_delete final : public Sql_cmd_dml
return &dml_prelocking_strategy;
}
+ bool processing_as_multitable_delete_prohibited(THD *thd);
+
+ bool is_multitable() { return multitable; }
+
+ void set_as_multitable() { multitable= true; }
+
protected:
/**
@brief Perform precheck of table privileges for delete statements
@@ -85,6 +92,9 @@ class Sql_cmd_delete final : public Sql_cmd_dml
*/
bool multitable;
+ /* Original value of the 'multitable' flag set by constructor */
+ const bool orig_multitable;
+
/* The prelocking strategy used when opening the used tables */
DML_prelocking_strategy dml_prelocking_strategy;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 78c067a..4059c0e 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -37,6 +37,8 @@
#include "sql_partition.h"
#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part
#include "event_parse_data.h"
+#include "sql_update.h"
+#include "sql_delete.h"
void LEX::parse_error(uint err_number)
{
@@ -4037,9 +4039,8 @@ bool LEX::can_use_merged()
SYNOPSIS
LEX::can_not_use_merged()
- @param no_update_or_delete Set to 1 if we can't use merge with multiple-table
- updates, like when used from
- TALE_LIST::init_derived()
+ @param forced_no_merge_for_update_delete Set to 1 if we can't use merge with
+ multiple-table updates/deletes
DESCRIPTION
Temporary table algorithm will be used on all SELECT levels for queries
@@ -4050,7 +4051,7 @@ bool LEX::can_use_merged()
TRUE - VIEWs with MERGE algorithms can be used
*/
-bool LEX::can_not_use_merged(bool no_update_or_delete)
+bool LEX::can_not_use_merged(bool forced_no_merge_for_update_delete)
{
switch (sql_command) {
case SQLCOM_CREATE_VIEW:
@@ -4064,18 +4065,29 @@ bool LEX::can_not_use_merged(bool no_update_or_delete)
return TRUE;
case SQLCOM_UPDATE_MULTI:
- case SQLCOM_DELETE_MULTI:
- if (no_update_or_delete)
+ if (forced_no_merge_for_update_delete)
return TRUE;
/* Fall through */
case SQLCOM_UPDATE:
- if (no_update_or_delete && m_sql_cmd &&
- (m_sql_cmd->sql_command_code() == SQLCOM_UPDATE_MULTI ||
+ if (forced_no_merge_for_update_delete &&
+ (((Sql_cmd_update *) m_sql_cmd)->is_multitable() ||
query_tables->is_multitable()))
return TRUE;
+ return FALSE;
+
+ case SQLCOM_DELETE_MULTI:
+ if (forced_no_merge_for_update_delete)
+ return TRUE;
/* Fall through */
+ case SQLCOM_DELETE:
+ if (forced_no_merge_for_update_delete &&
+ (((Sql_cmd_delete *) m_sql_cmd)->is_multitable() ||
+ query_tables->is_multitable()))
+ return TRUE;
+ return FALSE;
+
default:
return FALSE;
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index e733b4f..402e3bd 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1642,6 +1642,7 @@ class st_select_lex: public st_select_lex_node
void lex_start(LEX *plex);
bool is_unit_nest() { return (nest_flags & UNIT_NEST_FL); }
void mark_as_unit_nest() { nest_flags= UNIT_NEST_FL; }
+ bool is_sj_conversion_prohibited(THD *thd);
};
typedef class st_select_lex SELECT_LEX;
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 6b14c4f..7769777 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -2807,6 +2807,23 @@ bool multi_update::send_eof()
/**
+ @brief Check whether conversion to multi-table update is prohibited
+
+ @param thd global context the processed statement
+ @returns true if conversion is prohibited, false otherwise
+
+ @todo
+ Introduce handler level flag for storage engines that would prohibit
+ such conversion for any single-table update.
+*/
+
+bool Sql_cmd_update::processing_as_multitable_update_prohibited(THD *thd)
+{
+ return false;
+}
+
+
+/**
@brief Perform precheck of table privileges for update statements
@param thd global context the processed statement
@@ -2889,7 +2906,9 @@ bool Sql_cmd_update::prepare_inner(THD *thd)
"updating and querying the same temporal periods table");
DBUG_RETURN(TRUE);
}
- multitable= true;
+ if (!table_list->is_multitable() &&
+ !processing_as_multitable_update_prohibited(thd))
+ multitable= true;
}
}
diff --git a/sql/sql_update.h b/sql/sql_update.h
index d0fc7cb..4aff77a 100644
--- a/sql/sql_update.h
+++ b/sql/sql_update.h
@@ -46,12 +46,12 @@ class Sql_cmd_update final : public Sql_cmd_dml
{
public:
Sql_cmd_update(bool multitable_arg)
- : multitable(multitable_arg)
- { }
+ : orig_multitable(multitable_arg)
+ { multitable= orig_multitable; }
enum_sql_command sql_command_code() const override
{
- return multitable ? SQLCOM_UPDATE_MULTI : SQLCOM_UPDATE;
+ return orig_multitable ? SQLCOM_UPDATE_MULTI : SQLCOM_UPDATE;
}
DML_prelocking_strategy *get_dml_prelocking_strategy()
@@ -59,6 +59,12 @@ class Sql_cmd_update final : public Sql_cmd_dml
return &multiupdate_prelocking_strategy;
}
+ bool processing_as_multitable_update_prohibited(THD *thd);
+
+ bool is_multitable() { return multitable; }
+
+ void set_as_multitable() { multitable= true; }
+
protected:
/**
@brief Perform precheck of table privileges for update statements
@@ -89,13 +95,15 @@ class Sql_cmd_update final : public Sql_cmd_dml
*/
bool multitable;
+ /* Original value of the 'multitable' flag set by constructor */
+ const bool orig_multitable;
+
/* The prelocking strategy used when opening the used tables */
Multiupdate_prelocking_strategy multiupdate_prelocking_strategy;
public:
/* The list of the updating expressions used in the set clause */
List<Item> *update_value_list;
-
};
#endif /* SQL_UPDATE_INCLUDED */
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index a587a37..13dc602 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -13370,8 +13370,21 @@ delete_single_table:
YYPS->m_lock_type,
YYPS->m_mdl_type,
NULL,
+ 0)))
+ MYSQL_YYABORT;
+ Select->table_list.save_and_clear(&Lex->auxiliary_table_list);
+ Lex->table_count= 1;
+ Lex->query_tables= 0;
+ Lex->query_tables_last= &Lex->query_tables;
+ if (unlikely(!Select->
+ add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING,
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type,
+ NULL,
$3)))
MYSQL_YYABORT;
+ Lex->auxiliary_table_list.first->correspondent_table=
+ Lex->query_tables;
YYPS->m_lock_type= TL_READ_DEFAULT;
YYPS->m_mdl_type= MDL_SHARED_READ;
}
diff --git a/sql/table.cc b/sql/table.cc
index f0d5149..44eb9f6 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -7059,7 +7059,8 @@ void Field_iterator_table_ref::set_field_iterator()
table_ref->alias.str));
}
/* This is a merge view, so use field_translation. */
- else if (table_ref->field_translation)
+ else if (table_ref->field_translation &&
+ !table_ref->is_materialized_derived())
{
DBUG_ASSERT(table_ref->is_merged_derived());
field_it= &view_field_it;
@@ -7069,7 +7070,7 @@ void Field_iterator_table_ref::set_field_iterator()
/* This is a base table or stored view. */
else
{
- DBUG_ASSERT(table_ref->table || table_ref->view);
+ DBUG_ASSERT(table_ref->table || table_ref->is_materialized_derived());
field_it= &table_field_it;
DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_table",
table_ref->alias.str));
@@ -9518,13 +9519,16 @@ bool TABLE_LIST::init_derived(THD *thd, bool init_view)
!derived_table_optimization_done(this))
{
/* A subquery might be forced to be materialized due to a side-effect. */
+ bool forced_no_merge_for_update_delete=
+ belong_to_view ? belong_to_view->updating :
+ !unit->outer_select()->outer_select();
if (!is_materialized_derived() && first_select->is_mergeable() &&
(unit->outer_select() && !unit->outer_select()->with_rownum) &&
(!thd->lex->with_rownum ||
(!first_select->group_list.elements &&
!first_select->order_list.elements)) &&
optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_MERGE) &&
- !thd->lex->can_not_use_merged(1) &&
+ !thd->lex->can_not_use_merged(forced_no_merge_for_update_delete) &&
!is_recursive_with_table())
set_merged_derived();
else
1
0
revision-id: c58811a5b05a26428c8bdb8dfd984f97caa8f4c1 (mariadb-10.4.10-7-gc58811a)
parent(s): 0308de94ee806c21b6776ecab73396da75282596
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-06-28 17:40:08 -0700
message:
An attempt to fix MDEV-21633
---
mysql-test/main/group_min_max.result | 8 +-
mysql-test/main/innodb_ext_key.result | 4 +-
mysql-test/main/opt_trace.result | 188 +--------
mysql-test/main/opt_trace_index_merge.result | 5 -
.../main/opt_trace_index_merge_innodb.result | 14 -
mysql-test/main/opt_tvc.result | 4 +-
mysql-test/main/rowid_filter_innodb.result | 118 +++---
mysql-test/main/selectivity.result | 2 +-
mysql-test/main/selectivity.test | 72 ++++
mysql-test/main/subselect.result | 4 +-
mysql-test/main/subselect_no_exists_to_in.result | 4 +-
mysql-test/main/subselect_no_mat.result | 4 +-
mysql-test/main/subselect_no_opts.result | 4 +-
mysql-test/main/subselect_no_scache.result | 4 +-
mysql-test/main/subselect_no_semijoin.result | 2 +-
mysql-test/main/subselect_sj2.result | 4 +-
mysql-test/main/subselect_sj2_jcl6.result | 4 +-
mysql-test/main/subselect_sj2_mat.result | 4 +-
sql/field.h | 2 +
sql/multi_range_read.cc | 3 +
sql/opt_range.cc | 315 ++++++++-------
sql/opt_range.h | 3 +-
sql/opt_range_mrr.cc | 1 +
sql/sql_select.cc | 438 +++++++++------------
sql/sql_statistics.cc | 2 +-
sql/structs.h | 1 +
sql/table.h | 1 +
27 files changed, 527 insertions(+), 688 deletions(-)
diff --git a/mysql-test/main/group_min_max.result b/mysql-test/main/group_min_max.result
index a28cc41..b9b4a9b 100644
--- a/mysql-test/main/group_min_max.result
+++ b/mysql-test/main/group_min_max.result
@@ -2078,19 +2078,19 @@ id select_type table type possible_keys key key_len ref rows Extra
explain extended select a1,a2,min(b),max(b) from t1
where (a1 = 'b' or a1 = 'd' or a1 = 'a' or a1 = 'c') and (a2 > 'a') and (c > 'a111') group by a1,a2;
id select_type table type possible_keys key key_len ref rows filtered Extra
-1 SIMPLE t1 range idx_t1_0,idx_t1_1,idx_t1_2 idx_t1_1 130 NULL 77 99.22 Using where; Using index
+1 SIMPLE t1 range idx_t1_0,idx_t1_1,idx_t1_2 idx_t1_1 130 NULL 77 85.71 Using where; Using index
Warnings:
Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2`,min(`test`.`t1`.`b`) AS `min(b)`,max(`test`.`t1`.`b`) AS `max(b)` from `test`.`t1` where (`test`.`t1`.`a1` = 'b' or `test`.`t1`.`a1` = 'd' or `test`.`t1`.`a1` = 'a' or `test`.`t1`.`a1` = 'c') and `test`.`t1`.`a2` > 'a' and `test`.`t1`.`c` > 'a111' group by `test`.`t1`.`a1`,`test`.`t1`.`a2`
explain extended select a1,a2,b,min(c),max(c) from t1
where (a1 = 'b' or a1 = 'd' or a1 = 'a' or a1 = 'c') and (a2 > 'a') and (d > 'xy2') group by a1,a2,b;
id select_type table type possible_keys key key_len ref rows filtered Extra
-1 SIMPLE t1 ALL idx_t1_0,idx_t1_1,idx_t1_2 NULL NULL NULL 128 45.12 Using where; Using temporary; Using filesort
+1 SIMPLE t1 ALL idx_t1_0,idx_t1_1,idx_t1_2 NULL NULL NULL 128 74.41 Using where; Using temporary; Using filesort
Warnings:
Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2`,`test`.`t1`.`b` AS `b`,min(`test`.`t1`.`c`) AS `min(c)`,max(`test`.`t1`.`c`) AS `max(c)` from `test`.`t1` where (`test`.`t1`.`a1` = 'b' or `test`.`t1`.`a1` = 'd' or `test`.`t1`.`a1` = 'a' or `test`.`t1`.`a1` = 'c') and `test`.`t1`.`a2` > 'a' and `test`.`t1`.`d` > 'xy2' group by `test`.`t1`.`a1`,`test`.`t1`.`a2`,`test`.`t1`.`b`
explain extended select a1,a2,b,c from t1
where (a1 = 'b' or a1 = 'd' or a1 = 'a' or a1 = 'c') and (a2 > 'a') and (d > 'xy2') group by a1,a2,b,c;
id select_type table type possible_keys key key_len ref rows filtered Extra
-1 SIMPLE t1 ALL idx_t1_0,idx_t1_1,idx_t1_2 NULL NULL NULL 128 45.12 Using where; Using temporary; Using filesort
+1 SIMPLE t1 ALL idx_t1_0,idx_t1_1,idx_t1_2 NULL NULL NULL 128 74.41 Using where; Using temporary; Using filesort
Warnings:
Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c` from `test`.`t1` where (`test`.`t1`.`a1` = 'b' or `test`.`t1`.`a1` = 'd' or `test`.`t1`.`a1` = 'a' or `test`.`t1`.`a1` = 'c') and `test`.`t1`.`a2` > 'a' and `test`.`t1`.`d` > 'xy2' group by `test`.`t1`.`a1`,`test`.`t1`.`a2`,`test`.`t1`.`b`,`test`.`t1`.`c`
explain select a1,a2,b,max(c),min(c) from t2 where (a2 = 'a') and (b = 'b') or (b < 'b') group by a1;
@@ -2098,7 +2098,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t2 index NULL idx_t2_1 163 NULL 164 Using where; Using index
explain extended select a1,a2,b from t1 where (a1 = 'b' or a1 = 'd' or a1 = 'a' or a1 = 'c') and (a2 > 'a') and (c > 'a111') group by a1,a2,b;
id select_type table type possible_keys key key_len ref rows filtered Extra
-1 SIMPLE t1 range idx_t1_0,idx_t1_1,idx_t1_2 idx_t1_1 130 NULL 77 99.22 Using where; Using index
+1 SIMPLE t1 range idx_t1_0,idx_t1_1,idx_t1_2 idx_t1_1 130 NULL 77 85.71 Using where; Using index
Warnings:
Note 1003 select `test`.`t1`.`a1` AS `a1`,`test`.`t1`.`a2` AS `a2`,`test`.`t1`.`b` AS `b` from `test`.`t1` where (`test`.`t1`.`a1` = 'b' or `test`.`t1`.`a1` = 'd' or `test`.`t1`.`a1` = 'a' or `test`.`t1`.`a1` = 'c') and `test`.`t1`.`a2` > 'a' and `test`.`t1`.`c` > 'a111' group by `test`.`t1`.`a1`,`test`.`t1`.`a2`,`test`.`t1`.`b`
explain select a1,a2,min(b),c from t2 where (a2 = 'a') and (c = 'a111') group by a1;
diff --git a/mysql-test/main/innodb_ext_key.result b/mysql-test/main/innodb_ext_key.result
index e0fa557..a484a98 100644
--- a/mysql-test/main/innodb_ext_key.result
+++ b/mysql-test/main/innodb_ext_key.result
@@ -1186,7 +1186,7 @@ EXPLAIN
"key_length": "3070",
"used_key_parts": ["f2", "pk1"],
"rows": 1,
- "filtered": 50,
+ "filtered": 100,
"index_condition": "t1.pk1 <= 5 and t1.pk2 <= 5 and t1.f2 = 'abc'",
"attached_condition": "t1.f1 <= '3'"
}
@@ -1216,7 +1216,7 @@ EXPLAIN
"key_length": "3011",
"used_key_parts": ["pk1", "f2", "pk2"],
"rows": 1,
- "filtered": 50,
+ "filtered": 100,
"index_condition": "t1.f2 <= 5 and t1.pk2 <= 5 and t1.pk1 = 'abc'",
"attached_condition": "t1.f1 <= '3'"
}
diff --git a/mysql-test/main/opt_trace.result b/mysql-test/main/opt_trace.result
index 72cee2a..07bff70 100644
--- a/mysql-test/main/opt_trace.result
+++ b/mysql-test/main/opt_trace.result
@@ -104,16 +104,6 @@ select * from v1 {
{
"rows_estimation": [
{
- "selectivity_for_indexes": [],
- "selectivity_for_columns": [
- {
- "column_name": "a",
- "selectivity_from_histogram": 0.5
- }
- ],
- "cond_selectivity": 0.5
- },
- {
"table": "t1",
"table_scan": {
"rows": 2,
@@ -250,16 +240,6 @@ select * from (select * from t1 where t1.a=1)q {
{
"rows_estimation": [
{
- "selectivity_for_indexes": [],
- "selectivity_for_columns": [
- {
- "column_name": "a",
- "selectivity_from_histogram": 0.5
- }
- ],
- "cond_selectivity": 0.5
- },
- {
"table": "t1",
"table_scan": {
"rows": 2,
@@ -401,16 +381,6 @@ select * from v2 {
{
"rows_estimation": [
{
- "selectivity_for_indexes": [],
- "selectivity_for_columns": [
- {
- "column_name": "a",
- "selectivity_from_histogram": 0.5
- }
- ],
- "cond_selectivity": 0.5
- },
- {
"table": "t1",
"table_scan": {
"rows": 2,
@@ -1420,20 +1390,6 @@ EXPLAIN SELECT MIN(d) FROM t1 where b=2 and c=3 group by a {
"analyzing_index_merge_union": []
}
}
- },
- {
- "selectivity_for_indexes": [],
- "selectivity_for_columns": [
- {
- "column_name": "b",
- "selectivity_from_histogram": 0.2891
- },
- {
- "column_name": "c",
- "selectivity_from_histogram": 0.2891
- }
- ],
- "cond_selectivity": 0.0836
}
]
},
@@ -1631,11 +1587,6 @@ EXPLAIN SELECT id,MIN(a),MAX(a) FROM t1 WHERE a>=20010104e0 GROUP BY id {
"analyzing_index_merge_union": []
}
}
- },
- {
- "selectivity_for_indexes": [],
- "selectivity_for_columns": [],
- "cond_selectivity": 1
}
]
},
@@ -1822,11 +1773,6 @@ EXPLAIN SELECT * FROM t1 WHERE a = 20010104e0 GROUP BY id {
"analyzing_index_merge_union": []
}
}
- },
- {
- "selectivity_for_indexes": [],
- "selectivity_for_columns": [],
- "cond_selectivity": 1
}
]
},
@@ -1933,7 +1879,7 @@ test.t1 analyze status OK
set optimizer_trace='enabled=on';
explain select * from t1 where a=1 and b=2 order by c limit 1;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a_c,a_b a_c 5 NULL 180 Using where
+1 SIMPLE t1 ref a_c,a_b a_b 10 const,const 21 Using where; Using filesort
select * from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES
explain select * from t1 where a=1 and b=2 order by c limit 1 {
@@ -2075,25 +2021,6 @@ explain select * from t1 where a=1 and b=2 order by c limit 1 {
"chosen": true
}
}
- },
- {
- "selectivity_for_indexes": [
- {
- "index_name": "a_b",
- "selectivity_from_index": 0.021
- }
- ],
- "selectivity_for_columns": [
- {
- "column_name": "a",
- "selectivity_from_histogram": 0.1797
- },
- {
- "column_name": "b",
- "selectivity_from_histogram": 0.0156
- }
- ],
- "cond_selectivity": 0.021
}
]
},
@@ -2166,19 +2093,19 @@ explain select * from t1 where a=1 and b=2 order by c limit 1 {
{
"index": "a_a",
"can_resolve_order": true,
- "updated_limit": 47,
- "index_scan_time": 47,
+ "updated_limit": 500,
+ "index_scan_time": 500,
"usable": false,
"cause": "cost"
},
{
"index": "a_c",
"can_resolve_order": true,
- "updated_limit": 47,
- "range_scan_time": 4.324,
- "index_scan_time": 4.324,
- "records": 180,
- "chosen": true
+ "updated_limit": 500,
+ "range_scan_time": 46,
+ "index_scan_time": 46,
+ "usable": false,
+ "cause": "cost"
},
{
"index": "a_b",
@@ -2187,66 +2114,6 @@ explain select * from t1 where a=1 and b=2 order by c limit 1 {
}
]
}
- },
- {
- "table": "t1",
- "range_analysis": {
- "table_scan": {
- "rows": 1000,
- "cost": 2e308
- },
- "potential_range_indexes": [
- {
- "index": "a_a",
- "usable": false,
- "cause": "not applicable"
- },
- {
- "index": "a_c",
- "usable": true,
- "key_parts": ["a", "c"]
- },
- {
- "index": "a_b",
- "usable": false,
- "cause": "not applicable"
- }
- ],
- "setup_range_conditions": [],
- "group_index_range": {
- "chosen": false,
- "cause": "no group by or distinct"
- },
- "analyzing_range_alternatives": {
- "range_scan_alternatives": [
- {
- "index": "a_c",
- "ranges": ["(1) <= (a) <= (1)"],
- "rowid_ordered": false,
- "using_mrr": false,
- "index_only": false,
- "rows": 180,
- "cost": 231.72,
- "chosen": true
- }
- ],
- "analyzing_roworder_intersect": {
- "cause": "too few roworder scans"
- },
- "analyzing_index_merge_union": []
- },
- "chosen_range_access_summary": {
- "range_access_plan": {
- "type": "range_scan",
- "index": "a_c",
- "rows": 180,
- "ranges": ["(1) <= (a) <= (1)"]
- },
- "rows_for_plan": 180,
- "cost_for_plan": 231.72,
- "chosen": true
- }
- }
}
]
}
@@ -3283,25 +3150,6 @@ explain select * from t1 where pk = 2 and a=5 and b=1 {
"chosen": true
}
}
- },
- {
- "selectivity_for_indexes": [
- {
- "index_name": "pk_a_b",
- "selectivity_from_index": 0.1
- }
- ],
- "selectivity_for_columns": [
- {
- "column_name": "a",
- "selectivity_from_histogram": 0.1
- },
- {
- "column_name": "b",
- "selectivity_from_histogram": 0.1
- }
- ],
- "cond_selectivity": 0.1
}
]
},
@@ -3860,16 +3708,6 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 {
}
},
{
- "selectivity_for_indexes": [
- {
- "index_name": "a",
- "selectivity_from_index": 0.3
- }
- ],
- "selectivity_for_columns": [],
- "cond_selectivity": 0.3
- },
- {
"table": "t1",
"range_analysis": {
"table_scan": {
@@ -3923,16 +3761,6 @@ explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3 {
"chosen": true
}
}
- },
- {
- "selectivity_for_indexes": [
- {
- "index_name": "a",
- "selectivity_from_index": 0.3
- }
- ],
- "selectivity_for_columns": [],
- "cond_selectivity": 0.3
}
]
},
diff --git a/mysql-test/main/opt_trace_index_merge.result b/mysql-test/main/opt_trace_index_merge.result
index 7f1bd51..ed1a5a7 100644
--- a/mysql-test/main/opt_trace_index_merge.result
+++ b/mysql-test/main/opt_trace_index_merge.result
@@ -191,11 +191,6 @@ explain select * from t1 where a=1 or b=1 {
"chosen": true
}
}
- },
- {
- "selectivity_for_indexes": [],
- "selectivity_for_columns": [],
- "cond_selectivity": 0.002
}
]
},
diff --git a/mysql-test/main/opt_trace_index_merge_innodb.result b/mysql-test/main/opt_trace_index_merge_innodb.result
index afcf0b0..952d828 100644
--- a/mysql-test/main/opt_trace_index_merge_innodb.result
+++ b/mysql-test/main/opt_trace_index_merge_innodb.result
@@ -171,20 +171,6 @@ explain select * from t1 where pk1 != 0 and key1 = 1 {
"chosen": true
}
}
- },
- {
- "selectivity_for_indexes": [
- {
- "index_name": "PRIMARY",
- "selectivity_from_index": 1
- },
- {
- "index_name": "key1",
- "selectivity_from_index": 0.001
- }
- ],
- "selectivity_for_columns": [],
- "cond_selectivity": 0.001
}
]
},
diff --git a/mysql-test/main/opt_tvc.result b/mysql-test/main/opt_tvc.result
index 5329a9f..7886a99 100644
--- a/mysql-test/main/opt_tvc.result
+++ b/mysql-test/main/opt_tvc.result
@@ -444,7 +444,7 @@ where b in (3,5)
group by b
) as dr_table;
id select_type table type possible_keys key key_len ref rows filtered Extra
-1 PRIMARY <derived2> ALL NULL NULL NULL NULL 12 100.00
+1 PRIMARY <derived2> ALL NULL NULL NULL NULL 6 100.00
2 DERIVED t1 ALL NULL NULL NULL NULL 6 100.00 Using temporary; Using filesort
2 DERIVED <subquery3> eq_ref distinct_key distinct_key 4 func 1 100.00
3 MATERIALIZED <derived4> ALL NULL NULL NULL NULL 2 100.00
@@ -464,7 +464,7 @@ as tvc_0
group by b
) as dr_table;
id select_type table type possible_keys key key_len ref rows filtered Extra
-1 PRIMARY <derived2> ALL NULL NULL NULL NULL 12 100.00
+1 PRIMARY <derived2> ALL NULL NULL NULL NULL 6 100.00
2 DERIVED t1 ALL NULL NULL NULL NULL 6 100.00 Using temporary; Using filesort
2 DERIVED <subquery3> eq_ref distinct_key distinct_key 4 func 1 100.00
3 MATERIALIZED <derived4> ALL NULL NULL NULL NULL 2 100.00
diff --git a/mysql-test/main/rowid_filter_innodb.result b/mysql-test/main/rowid_filter_innodb.result
index 390c783..9d7775f 100644
--- a/mysql-test/main/rowid_filter_innodb.result
+++ b/mysql-test/main/rowid_filter_innodb.result
@@ -561,8 +561,8 @@ WHERE l_shipdate BETWEEN '1997-01-01' AND '1997-06-30' AND
l_quantity > 45 AND
o_totalprice between 180000 and 230000;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE orders range PRIMARY,i_o_totalprice i_o_totalprice 9 NULL 144 Using where; Using index
-1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity,i_l_quantity PRIMARY 4 dbt3_s001.orders.o_orderkey 4 Using where
+1 SIMPLE lineitem range|filter PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity,i_l_quantity i_l_shipdate|i_l_quantity 4|9 NULL 510 (10%) Using index condition; Using where; Using rowid filter
+1 SIMPLE orders eq_ref PRIMARY,i_o_totalprice PRIMARY 4 dbt3_s001.lineitem.l_orderkey 1 Using where
set statement optimizer_switch='rowid_filter=on' for EXPLAIN FORMAT=JSON SELECT o_orderkey, l_linenumber, l_shipdate, l_quantity, o_totalprice
FROM orders JOIN lineitem ON o_orderkey=l_orderkey
WHERE l_shipdate BETWEEN '1997-01-01' AND '1997-06-30' AND
@@ -573,20 +573,8 @@ EXPLAIN
"query_block": {
"select_id": 1,
"table": {
- "table_name": "orders",
- "access_type": "range",
- "possible_keys": ["PRIMARY", "i_o_totalprice"],
- "key": "i_o_totalprice",
- "key_length": "9",
- "used_key_parts": ["o_totalprice"],
- "rows": 144,
- "filtered": 100,
- "attached_condition": "orders.o_totalprice between 180000 and 230000",
- "using_index": true
- },
- "table": {
"table_name": "lineitem",
- "access_type": "ref",
+ "access_type": "range",
"possible_keys": [
"PRIMARY",
"i_l_shipdate",
@@ -594,13 +582,33 @@ EXPLAIN
"i_l_orderkey_quantity",
"i_l_quantity"
],
+ "key": "i_l_shipdate",
+ "key_length": "4",
+ "used_key_parts": ["l_shipDATE"],
+ "rowid_filter": {
+ "range": {
+ "key": "i_l_quantity",
+ "used_key_parts": ["l_quantity"]
+ },
+ "rows": 605,
+ "selectivity_pct": 10.075
+ },
+ "rows": 510,
+ "filtered": 10.075,
+ "index_condition": "lineitem.l_shipDATE between '1997-01-01' and '1997-06-30'",
+ "attached_condition": "lineitem.l_quantity > 45"
+ },
+ "table": {
+ "table_name": "orders",
+ "access_type": "eq_ref",
+ "possible_keys": ["PRIMARY", "i_o_totalprice"],
"key": "PRIMARY",
"key_length": "4",
- "used_key_parts": ["l_orderkey"],
- "ref": ["dbt3_s001.orders.o_orderkey"],
- "rows": 4,
- "filtered": 0.8557,
- "attached_condition": "lineitem.l_shipDATE between '1997-01-01' and '1997-06-30' and lineitem.l_quantity > 45"
+ "used_key_parts": ["o_orderkey"],
+ "ref": ["dbt3_s001.lineitem.l_orderkey"],
+ "rows": 1,
+ "filtered": 9.6,
+ "attached_condition": "orders.o_totalprice between 180000 and 230000"
}
}
}
@@ -610,8 +618,8 @@ WHERE l_shipdate BETWEEN '1997-01-01' AND '1997-06-30' AND
l_quantity > 45 AND
o_totalprice between 180000 and 230000;
id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra
-1 SIMPLE orders range PRIMARY,i_o_totalprice i_o_totalprice 9 NULL 144 144.00 100.00 100.00 Using where; Using index
-1 SIMPLE lineitem ref PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity,i_l_quantity PRIMARY 4 dbt3_s001.orders.o_orderkey 4 6.62 0.86 1.68 Using where
+1 SIMPLE lineitem range|filter PRIMARY,i_l_shipdate,i_l_orderkey,i_l_orderkey_quantity,i_l_quantity i_l_shipdate|i_l_quantity 4|9 NULL 510 (10%) 60.00 (11%) 10.07 100.00 Using index condition; Using where; Using rowid filter
+1 SIMPLE orders eq_ref PRIMARY,i_o_totalprice PRIMARY 4 dbt3_s001.lineitem.l_orderkey 1 1.00 9.60 26.67 Using where
set statement optimizer_switch='rowid_filter=on' for ANALYZE FORMAT=JSON SELECT o_orderkey, l_linenumber, l_shipdate, l_quantity, o_totalprice
FROM orders JOIN lineitem ON o_orderkey=l_orderkey
WHERE l_shipdate BETWEEN '1997-01-01' AND '1997-06-30' AND
@@ -624,24 +632,8 @@ ANALYZE
"r_loops": 1,
"r_total_time_ms": "REPLACED",
"table": {
- "table_name": "orders",
- "access_type": "range",
- "possible_keys": ["PRIMARY", "i_o_totalprice"],
- "key": "i_o_totalprice",
- "key_length": "9",
- "used_key_parts": ["o_totalprice"],
- "r_loops": 1,
- "rows": 144,
- "r_rows": 144,
- "r_total_time_ms": "REPLACED",
- "filtered": 100,
- "r_filtered": 100,
- "attached_condition": "orders.o_totalprice between 180000 and 230000",
- "using_index": true
- },
- "table": {
"table_name": "lineitem",
- "access_type": "ref",
+ "access_type": "range",
"possible_keys": [
"PRIMARY",
"i_l_shipdate",
@@ -649,17 +641,45 @@ ANALYZE
"i_l_orderkey_quantity",
"i_l_quantity"
],
+ "key": "i_l_shipdate",
+ "key_length": "4",
+ "used_key_parts": ["l_shipDATE"],
+ "rowid_filter": {
+ "range": {
+ "key": "i_l_quantity",
+ "used_key_parts": ["l_quantity"]
+ },
+ "rows": 605,
+ "selectivity_pct": 10.075,
+ "r_rows": 605,
+ "r_selectivity_pct": 11.765,
+ "r_buffer_size": "REPLACED",
+ "r_filling_time_ms": "REPLACED"
+ },
+ "r_loops": 1,
+ "rows": 510,
+ "r_rows": 60,
+ "r_total_time_ms": "REPLACED",
+ "filtered": 10.075,
+ "r_filtered": 100,
+ "index_condition": "lineitem.l_shipDATE between '1997-01-01' and '1997-06-30'",
+ "attached_condition": "lineitem.l_quantity > 45"
+ },
+ "table": {
+ "table_name": "orders",
+ "access_type": "eq_ref",
+ "possible_keys": ["PRIMARY", "i_o_totalprice"],
"key": "PRIMARY",
"key_length": "4",
- "used_key_parts": ["l_orderkey"],
- "ref": ["dbt3_s001.orders.o_orderkey"],
- "r_loops": 144,
- "rows": 4,
- "r_rows": 6.625,
+ "used_key_parts": ["o_orderkey"],
+ "ref": ["dbt3_s001.lineitem.l_orderkey"],
+ "r_loops": 60,
+ "rows": 1,
+ "r_rows": 1,
"r_total_time_ms": "REPLACED",
- "filtered": 0.8557,
- "r_filtered": 1.6771,
- "attached_condition": "lineitem.l_shipDATE between '1997-01-01' and '1997-06-30' and lineitem.l_quantity > 45"
+ "filtered": 9.6,
+ "r_filtered": 26.667,
+ "attached_condition": "orders.o_totalprice between 180000 and 230000"
}
}
}
@@ -2105,7 +2125,7 @@ EXPLAIN
}
},
"rows": 1,
- "filtered": 1.5873,
+ "filtered": 100,
"attached_condition": "t1.f1 is null and t1.f2 is null and (t1.f2 between 'a' and 'z' or t1.f1 = 'a')"
}
}
@@ -2132,7 +2152,7 @@ EXPLAIN
}
},
"rows": 1,
- "filtered": 1.5873,
+ "filtered": 100,
"attached_condition": "t1.f1 is null and t1.f2 is null and (t1.f2 between 'a' and 'z' or t1.f1 = 'a')"
}
}
diff --git a/mysql-test/main/selectivity.result b/mysql-test/main/selectivity.result
index c548748..1ee927a 100644
--- a/mysql-test/main/selectivity.result
+++ b/mysql-test/main/selectivity.result
@@ -1790,7 +1790,7 @@ set optimizer_use_condition_selectivity=2;
explain extended select t1.b,t2.a,t3.a,t3.b from t1,t2,t3
where t1.c = t2.a AND t1.d = t3.a and t1.a = 50 and t1.b <= 100;
id select_type table type possible_keys key key_len ref rows filtered Extra
-1 SIMPLE t1 range a a 10 NULL 9 9.00 Using index condition; Using where
+1 SIMPLE t1 range a a 10 NULL 9 10.00 Using index condition; Using where
1 SIMPLE t3 eq_ref PRIMARY PRIMARY 4 test.t1.d 1 100.00
1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.c 1 100.00 Using index
Warnings:
diff --git a/mysql-test/main/selectivity.test b/mysql-test/main/selectivity.test
index 3cf4f32..48e4a21 100644
--- a/mysql-test/main/selectivity.test
+++ b/mysql-test/main/selectivity.test
@@ -1235,6 +1235,78 @@ set optimizer_use_condition_selectivity= @@optimizer_use_condition_selectivity;
drop table t1,t2,t3;
+
+--echo #
+--echo # MDEV-20743: selectivity of equality conditions
+--echo #
+
+create table ta (a int);
+insert into ta select seq from seq_1_to_1000 order by rand(3);
+alter table ta add pk int primary key auto_increment;
+create table tb (b int);
+insert into tb select seq % 10 from seq_1_to_1000 order by rand(1009);
+alter table tb add pk int primary key auto_increment;
+create table tc (c int);
+insert into tc select seq % 10 from seq_1_to_1000 order by rand(2003);
+alter table tc add pk int primary key auto_increment;
+create table td (d int);
+insert into td select seq % 15 from seq_1_to_1000 order by rand(3019);
+alter table td add pk int primary key auto_increment;
+create table t1 (pk int primary key, a int, b int, c int, d int);
+insert into t1
+select ta.pk, a, b, c, d from ta, tb, tc, td where
+ ta.pk=tb.pk and ta.pk=tc.pk and ta.pk=td.pk;
+
+create table te (e int);
+insert into te select seq from seq_1_to_500 order by rand(4001);
+alter table te add pk int primary key auto_increment;
+create table tf (f int);
+insert into tf select seq % 10 from seq_1_to_500 order by rand(4507);
+alter table tf add pk int primary key auto_increment;
+create table tg (g int);
+insert into tg select seq % 20 from seq_1_to_500 order by rand(5003);
+alter table tg add pk int primary key auto_increment;
+create table t2 (pk int primary key, e int, f int, g int);
+insert into t2
+select te.pk, e, f, g from te, tf, tg where
+ te.pk=tf.pk and te.pk=tg.pk;
+
+select count(distinct a) from t1;
+select count(distinct b) from t1;
+select count(distinct c) from t1;
+select count(distinct d) from t1;
+select count(distinct e) from t2;
+select count(distinct f) from t2;
+select count(distinct g) from t2;
+
+set @@use_stat_tables='preferably';
+set optimizer_use_condition_selectivity=3;
+
+analyze table t1 persistent for all;
+
+select * from t1 where t1.b=t1.c;
+explain extended
+select * from t1 where t1.b=t1.c;
+
+select * from t1 where t1.b=t1.d;
+explain extended
+select * from t1 where t1.b=t1.d;
+
+select * from t1 where t1.b=t1.c and t1.c=t1.d;
+explain extended
+select * from t1 where t1.b=t1.c and t1.c=t1.d;
+
+create index idx on t1(b);
+
+select * from t1,t2 where t1.b=t1.c and t1.c=t1.d and t2.f=t2.g and t2.g=t1.b;
+explain extended
+select * from t1,t2 where t1.b=t1.c and t1.c=t1.d and t2.f=t2.g and t2.g=t1.b;
+
+set optimizer_use_condition_selectivity= @@optimizer_use_condition_selectivity;
+set use_stat_tables=@save_use_stat_tables;
+
+drop table ta,tb,tc,td,t1;
+
--echo # End of 10.1 tests
#
diff --git a/mysql-test/main/subselect.result b/mysql-test/main/subselect.result
index 349e7dc..224847f 100644
--- a/mysql-test/main/subselect.result
+++ b/mysql-test/main/subselect.result
@@ -1886,13 +1886,13 @@ id text
explain extended select * from t1 where id not in (select id from t1 where id < 8);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 12 100.00 Using where
-2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index; Using where
+2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 58.33 Using index; Using where
Warnings:
Note 1003 /* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where !<expr_cache><`test`.`t1`.`id`>(<in_optimizer>(`test`.`t1`.`id`,<exists>(<primary_index_lookup>(<cache>(`test`.`t1`.`id`) in t1 on PRIMARY where `test`.`t1`.`id` < 8 and <cache>(`test`.`t1`.`id`) = `test`.`t1`.`id`))))
explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY tt ALL NULL NULL NULL NULL 12 100.00 Using where
-2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 100.00 Using where; Using index
+2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 58.33 Using where; Using index
Warnings:
Note 1276 Field or reference 'test.tt.id' of SELECT #2 was resolved in SELECT #1
Note 1003 /* select#1 */ select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where !<in_optimizer>(1,<expr_cache><`test`.`tt`.`id`>(exists(/* select#2 */ select `test`.`t1`.`id` from `test`.`t1` where `test`.`t1`.`id` < 8 and `test`.`t1`.`id` = `test`.`tt`.`id` having `test`.`t1`.`id` is not null limit 1)))
diff --git a/mysql-test/main/subselect_no_exists_to_in.result b/mysql-test/main/subselect_no_exists_to_in.result
index 84c415d..b3ba535 100644
--- a/mysql-test/main/subselect_no_exists_to_in.result
+++ b/mysql-test/main/subselect_no_exists_to_in.result
@@ -1890,13 +1890,13 @@ id text
explain extended select * from t1 where id not in (select id from t1 where id < 8);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 12 100.00 Using where
-2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index; Using where
+2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 58.33 Using index; Using where
Warnings:
Note 1003 /* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where !<expr_cache><`test`.`t1`.`id`>(<in_optimizer>(`test`.`t1`.`id`,<exists>(<primary_index_lookup>(<cache>(`test`.`t1`.`id`) in t1 on PRIMARY where `test`.`t1`.`id` < 8 and <cache>(`test`.`t1`.`id`) = `test`.`t1`.`id`))))
explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY tt ALL NULL NULL NULL NULL 12 100.00 Using where
-2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 100.00 Using where; Using index
+2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 58.33 Using where; Using index
Warnings:
Note 1276 Field or reference 'test.tt.id' of SELECT #2 was resolved in SELECT #1
Note 1003 /* select#1 */ select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where !<expr_cache><`test`.`tt`.`id`>(exists(/* select#2 */ select `test`.`t1`.`id` from `test`.`t1` where `test`.`t1`.`id` < 8 and `test`.`t1`.`id` = `test`.`tt`.`id` having `test`.`t1`.`id` is not null limit 1))
diff --git a/mysql-test/main/subselect_no_mat.result b/mysql-test/main/subselect_no_mat.result
index 93035e2..6c874bc 100644
--- a/mysql-test/main/subselect_no_mat.result
+++ b/mysql-test/main/subselect_no_mat.result
@@ -1893,13 +1893,13 @@ id text
explain extended select * from t1 where id not in (select id from t1 where id < 8);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 12 100.00 Using where
-2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index; Using where
+2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 58.33 Using index; Using where
Warnings:
Note 1003 /* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where !<expr_cache><`test`.`t1`.`id`>(<in_optimizer>(`test`.`t1`.`id`,<exists>(<primary_index_lookup>(<cache>(`test`.`t1`.`id`) in t1 on PRIMARY where `test`.`t1`.`id` < 8 and <cache>(`test`.`t1`.`id`) = `test`.`t1`.`id`))))
explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY tt ALL NULL NULL NULL NULL 12 100.00 Using where
-2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 100.00 Using where; Using index
+2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 58.33 Using where; Using index
Warnings:
Note 1276 Field or reference 'test.tt.id' of SELECT #2 was resolved in SELECT #1
Note 1003 /* select#1 */ select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where !<in_optimizer>(1,<expr_cache><`test`.`tt`.`id`>(exists(/* select#2 */ select `test`.`t1`.`id` from `test`.`t1` where `test`.`t1`.`id` < 8 and `test`.`t1`.`id` = `test`.`tt`.`id` having `test`.`t1`.`id` is not null limit 1)))
diff --git a/mysql-test/main/subselect_no_opts.result b/mysql-test/main/subselect_no_opts.result
index 09f664d..dbc4eb5 100644
--- a/mysql-test/main/subselect_no_opts.result
+++ b/mysql-test/main/subselect_no_opts.result
@@ -1889,13 +1889,13 @@ id text
explain extended select * from t1 where id not in (select id from t1 where id < 8);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 12 100.00 Using where
-2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index; Using where
+2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 58.33 Using index; Using where
Warnings:
Note 1003 /* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where !<in_optimizer>(`test`.`t1`.`id`,<exists>(<primary_index_lookup>(<cache>(`test`.`t1`.`id`) in t1 on PRIMARY where `test`.`t1`.`id` < 8 and <cache>(`test`.`t1`.`id`) = `test`.`t1`.`id`)))
explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY tt ALL NULL NULL NULL NULL 12 100.00 Using where
-2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 100.00 Using where; Using index
+2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 58.33 Using where; Using index
Warnings:
Note 1276 Field or reference 'test.tt.id' of SELECT #2 was resolved in SELECT #1
Note 1003 /* select#1 */ select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where !<in_optimizer>(1,exists(/* select#2 */ select `test`.`t1`.`id` from `test`.`t1` where `test`.`t1`.`id` < 8 and `test`.`t1`.`id` = `test`.`tt`.`id` having `test`.`t1`.`id` is not null limit 1))
diff --git a/mysql-test/main/subselect_no_scache.result b/mysql-test/main/subselect_no_scache.result
index 765bb15..7e37b88 100644
--- a/mysql-test/main/subselect_no_scache.result
+++ b/mysql-test/main/subselect_no_scache.result
@@ -1892,13 +1892,13 @@ id text
explain extended select * from t1 where id not in (select id from t1 where id < 8);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 12 100.00 Using where
-2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 100.00 Using index; Using where
+2 DEPENDENT SUBQUERY t1 unique_subquery PRIMARY PRIMARY 4 func 1 58.33 Using index; Using where
Warnings:
Note 1003 /* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `text` from `test`.`t1` where !<in_optimizer>(`test`.`t1`.`id`,<exists>(<primary_index_lookup>(<cache>(`test`.`t1`.`id`) in t1 on PRIMARY where `test`.`t1`.`id` < 8 and <cache>(`test`.`t1`.`id`) = `test`.`t1`.`id`)))
explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY tt ALL NULL NULL NULL NULL 12 100.00 Using where
-2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 100.00 Using where; Using index
+2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 58.33 Using where; Using index
Warnings:
Note 1276 Field or reference 'test.tt.id' of SELECT #2 was resolved in SELECT #1
Note 1003 /* select#1 */ select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where !<in_optimizer>(1,exists(/* select#2 */ select `test`.`t1`.`id` from `test`.`t1` where `test`.`t1`.`id` < 8 and `test`.`t1`.`id` = `test`.`tt`.`id` having `test`.`t1`.`id` is not null limit 1))
diff --git a/mysql-test/main/subselect_no_semijoin.result b/mysql-test/main/subselect_no_semijoin.result
index 97d2f3b..77e6306 100644
--- a/mysql-test/main/subselect_no_semijoin.result
+++ b/mysql-test/main/subselect_no_semijoin.result
@@ -1895,7 +1895,7 @@ Note 1003 /* select#1 */ select `test`.`t1`.`id` AS `id`,`test`.`t1`.`text` AS `
explain extended select * from t1 as tt where not exists (select id from t1 where id < 8 and (id = tt.id or id is null) having id is not null);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY tt ALL NULL NULL NULL NULL 12 100.00 Using where
-2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 100.00 Using where; Using index
+2 DEPENDENT SUBQUERY t1 eq_ref PRIMARY PRIMARY 4 test.tt.id 1 58.33 Using where; Using index
Warnings:
Note 1276 Field or reference 'test.tt.id' of SELECT #2 was resolved in SELECT #1
Note 1003 /* select#1 */ select `test`.`tt`.`id` AS `id`,`test`.`tt`.`text` AS `text` from `test`.`t1` `tt` where !<in_optimizer>(1,<expr_cache><`test`.`tt`.`id`>(exists(/* select#2 */ select `test`.`t1`.`id` from `test`.`t1` where `test`.`t1`.`id` < 8 and `test`.`t1`.`id` = `test`.`tt`.`id` having `test`.`t1`.`id` is not null limit 1)))
diff --git a/mysql-test/main/subselect_sj2.result b/mysql-test/main/subselect_sj2.result
index 8045a81..31e94cc 100644
--- a/mysql-test/main/subselect_sj2.result
+++ b/mysql-test/main/subselect_sj2.result
@@ -1032,8 +1032,8 @@ alias2.b = alias1.a AND
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL a NULL NULL NULL 38
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 8 func,func 1
-2 MATERIALIZED alias1 ALL a NULL NULL NULL 19 Using where
-2 MATERIALIZED alias2 ref a a 4 test.alias1.a 1 Using where
+2 MATERIALIZED alias2 ALL a NULL NULL NULL 19 Using where
+2 MATERIALIZED alias1 ref a a 4 test.alias2.a 1 Using where
SELECT * FROM t2
WHERE (a, a) IN (SELECT alias2.b, alias2.a FROM t1 AS alias1, t1 AS alias2
WHERE
diff --git a/mysql-test/main/subselect_sj2_jcl6.result b/mysql-test/main/subselect_sj2_jcl6.result
index 22310ab..3a8f4a6 100644
--- a/mysql-test/main/subselect_sj2_jcl6.result
+++ b/mysql-test/main/subselect_sj2_jcl6.result
@@ -1045,8 +1045,8 @@ alias2.b = alias1.a AND
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL a NULL NULL NULL 38
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 8 func,func 1
-2 MATERIALIZED alias1 ALL a NULL NULL NULL 19 Using where
-2 MATERIALIZED alias2 ref a a 4 test.alias1.a 1 Using where; Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan
+2 MATERIALIZED alias2 ALL a NULL NULL NULL 19 Using where
+2 MATERIALIZED alias1 ref a a 4 test.alias2.a 1 Using where; Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan
SELECT * FROM t2
WHERE (a, a) IN (SELECT alias2.b, alias2.a FROM t1 AS alias1, t1 AS alias2
WHERE
diff --git a/mysql-test/main/subselect_sj2_mat.result b/mysql-test/main/subselect_sj2_mat.result
index 7ccaed5..bbdd1bd 100644
--- a/mysql-test/main/subselect_sj2_mat.result
+++ b/mysql-test/main/subselect_sj2_mat.result
@@ -1034,8 +1034,8 @@ alias2.b = alias1.a AND
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t2 ALL a NULL NULL NULL 38
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 8 func,func 1
-2 MATERIALIZED alias1 ALL a NULL NULL NULL 19 Using where
-2 MATERIALIZED alias2 ref a a 4 test.alias1.a 1 Using where
+2 MATERIALIZED alias2 ALL a NULL NULL NULL 19 Using where
+2 MATERIALIZED alias1 ref a a 4 test.alias2.a 1 Using where
SELECT * FROM t2
WHERE (a, a) IN (SELECT alias2.b, alias2.a FROM t1 AS alias1, t1 AS alias2
WHERE
diff --git a/sql/field.h b/sql/field.h
index f07eab3..0abd582 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -718,6 +718,8 @@ class Field: public Value_source
*/
double cond_selectivity;
+ double ndv;
+
/*
The next field in the class of equal fields at the top AND level
of the WHERE clause
diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc
index 8c6b0de..78ead1f0 100644
--- a/sql/multi_range_read.cc
+++ b/sql/multi_range_read.cc
@@ -198,6 +198,9 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
}
total_rows += rows;
total_touched_blocks+= new_touched_blocks;
+ KEY_PART_INFO *kp= table->key_info[keyno]->key_part +
+ table->curr_key_tree_part;
+ kp->quick_cnt+= rows;
}
if (total_rows != HA_POS_ERROR)
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index c47da28..3202e34 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -3218,17 +3218,6 @@ double records_in_column_ranges(PARAM *param, uint idx,
on the rows of 'table' in the processed query.
The calculated selectivity is assigned to the field table->cond_selectivity.
- Selectivity is calculated as a product of selectivities imposed by:
-
- 1. possible range accesses. (if multiple range accesses use the same
- restrictions on the same field, we make adjustments for that)
- 2. Sargable conditions on fields for which we have column statistics (if
- a field is used in a possible range access, we assume that selectivity
- is already provided by the range access' estimates)
- 3. Reading a few records from the table pages and checking the condition
- selectivity (this is used for conditions like "column LIKE '%val%'"
- where approaches #1 and #2 do not provide selectivity data).
-
NOTE
Currently the selectivities of range conditions over different columns are
considered independent.
@@ -3238,7 +3227,8 @@ double records_in_column_ranges(PARAM *param, uint idx,
TRUE otherwise
*/
-bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
+bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table,
+ COND_EQUAL *cond_equal, Item **cond)
{
uint keynr;
uint max_quick_key_parts= 0;
@@ -3265,131 +3255,17 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
if (table->pos_in_table_list->schema_table)
DBUG_RETURN(FALSE);
- MY_BITMAP handled_columns;
- my_bitmap_map* buf;
- if (!(buf= (my_bitmap_map*)thd->alloc(table->s->column_bitmap_size)))
- DBUG_RETURN(TRUE);
- my_bitmap_init(&handled_columns, buf, table->s->fields, FALSE);
-
- /*
- Calculate the selectivity of the range conditions supported by indexes.
-
- First, take into account possible range accesses.
- range access estimates are the most precise, we prefer them to any other
- estimate sources.
- */
-
- Json_writer_object trace_wrapper(thd);
- Json_writer_array selectivity_for_indexes(thd, "selectivity_for_indexes");
-
- for (keynr= 0; keynr < table->s->keys; keynr++)
- {
- if (table->quick_keys.is_set(keynr))
- set_if_bigger(max_quick_key_parts, table->quick_key_parts[keynr]);
- }
-
- /*
- Walk through all indexes, indexes where range access uses more keyparts
- go first.
- */
- for (uint quick_key_parts= max_quick_key_parts;
- quick_key_parts; quick_key_parts--)
- {
- for (keynr= 0; keynr < table->s->keys; keynr++)
- {
- if (table->quick_keys.is_set(keynr) &&
- table->quick_key_parts[keynr] == quick_key_parts)
- {
- uint i;
- uint used_key_parts= table->quick_key_parts[keynr];
- double quick_cond_selectivity= table->quick_rows[keynr] /
- table_records;
- KEY *key_info= table->key_info + keynr;
- KEY_PART_INFO* key_part= key_info->key_part;
- /*
- Suppose, there are range conditions on two keys
- KEY1 (col1, col2)
- KEY2 (col3, col2)
-
- we don't want to count selectivity of condition on col2 twice.
-
- First, find the longest key prefix that's made of columns whose
- selectivity wasn't already accounted for.
- */
- for (i= 0; i < used_key_parts; i++, key_part++)
- {
- if (bitmap_is_set(&handled_columns, key_part->fieldnr-1))
- break;
- bitmap_set_bit(&handled_columns, key_part->fieldnr-1);
- }
- if (i)
- {
- double UNINIT_VAR(selectivity_mult);
-
- /*
- There is at least 1-column prefix of columns whose selectivity has
- not yet been accounted for.
- */
- table->cond_selectivity*= quick_cond_selectivity;
- Json_writer_object selectivity_for_index(thd);
- selectivity_for_index.add("index_name", key_info->name)
- .add("selectivity_from_index",
- quick_cond_selectivity);
- if (i != used_key_parts)
- {
- /*
- Range access got us estimate for #used_key_parts.
- We need estimate for #(i-1) key parts.
- */
- double f1= key_info->actual_rec_per_key(i-1);
- double f2= key_info->actual_rec_per_key(i);
- if (f1 > 0 && f2 > 0)
- selectivity_mult= f1 / f2;
- else
- {
- /*
- No statistics available, assume the selectivity is proportional
- to the number of key parts.
- (i=0 means 1 keypart, i=1 means 2 keyparts, so use i+1)
- */
- selectivity_mult= ((double)(i+1)) / i;
- }
- table->cond_selectivity*= selectivity_mult;
- selectivity_for_index.add("selectivity_multiplier",
- selectivity_mult);
- }
- /*
- We need to set selectivity for fields supported by indexes.
- For single-component indexes and for some first components
- of other indexes we do it here. For the remaining fields
- we do it later in this function, in the same way as for the
- fields not used in any indexes.
- */
- if (i == 1)
- {
- uint fieldnr= key_info->key_part[0].fieldnr;
- table->field[fieldnr-1]->cond_selectivity= quick_cond_selectivity;
- if (i != used_key_parts)
- table->field[fieldnr-1]->cond_selectivity*= selectivity_mult;
- bitmap_clear_bit(used_fields, fieldnr-1);
- }
- }
- }
- }
- }
- selectivity_for_indexes.end();
-
- /*
- Second step: calculate the selectivity of the range conditions not
- supported by any index and selectivity of the range condition
- over the fields whose selectivity has not been set yet.
- */
- Json_writer_array selectivity_for_columns(thd, "selectivity_for_columns");
+ if (thd->variables.use_stat_tables == 0 || !table->stats_is_read)
+ bitmap_clear_all(used_fields);
if (thd->variables.optimizer_use_condition_selectivity > 2 &&
- !bitmap_is_clear_all(used_fields) &&
- thd->variables.use_stat_tables > 0 && table->stats_is_read)
+ !bitmap_is_clear_all(used_fields))
{
+ /*
+ Calculate the selectivity of the range conditions not supported
+ by any index
+ */
+
PARAM param;
MEM_ROOT alloc;
SEL_TREE *tree;
@@ -3402,7 +3278,6 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
param.mem_root= &alloc;
param.old_root= thd->mem_root;
param.table= table;
- param.remove_false_where_parts= true;
if (create_key_parts_for_pseudo_indexes(¶m, used_fields))
goto free_alloc;
@@ -3412,6 +3287,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
param.using_real_indexes= FALSE;
param.real_keynr[0]= 0;
param.alloced_sel_args= 0;
+ param.remove_false_where_parts= true;
thd->no_errors=1;
@@ -3443,14 +3319,10 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
SEL_ARG *key= tree->keys[idx];
if (key)
{
- Json_writer_object selectivity_for_column(thd);
- selectivity_for_column.add("column_name", key->field->field_name);
if (key->type == SEL_ARG::IMPOSSIBLE)
{
rows= 0;
table->reginfo.impossible_range= 1;
- selectivity_for_column.add("selectivity_from_histogram", rows);
- selectivity_for_column.add("cause", "impossible range");
goto free_alloc;
}
else
@@ -3459,8 +3331,6 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
if (rows != DBL_MAX)
{
key->field->cond_selectivity= rows/table_records;
- selectivity_for_column.add("selectivity_from_histogram",
- key->field->cond_selectivity);
}
}
}
@@ -3469,12 +3339,9 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
for (Field **field_ptr= table->field; *field_ptr; field_ptr++)
{
Field *table_field= *field_ptr;
- if (bitmap_is_set(used_fields, table_field->field_index) &&
+ if (bitmap_is_set(table->read_set, table_field->field_index) &&
table_field->cond_selectivity < 1.0)
- {
- if (!bitmap_is_set(&handled_columns, table_field->field_index))
- table->cond_selectivity*= table_field->cond_selectivity;
- }
+ table->cond_selectivity*= table_field->cond_selectivity;
}
free_alloc:
@@ -3483,7 +3350,104 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
free_root(&alloc, MYF(0));
}
- selectivity_for_columns.end();
+
+ for (keynr= 0; keynr < table->s->keys; keynr++)
+ {
+ if (table->quick_keys.is_set(keynr))
+ set_if_bigger(max_quick_key_parts, table->quick_key_parts[keynr]);
+ }
+
+ bool is_sel_added;
+ do
+ {
+ is_sel_added= false;
+ for (uint quick_key_parts= 1;
+ quick_key_parts <= max_quick_key_parts;
+ quick_key_parts++)
+ {
+ for (keynr= 0; keynr < table->s->keys; keynr++)
+ {
+ uint used_key_parts= table->quick_key_parts[keynr];
+ if (!(table->quick_keys.is_set(keynr) &&
+ used_key_parts == quick_key_parts))
+ continue;
+ KEY *key_info= table->key_info + keynr;
+ KEY_PART_INFO* key_part= key_info->key_part;
+ if (bitmap_is_set(used_fields, key_part->fieldnr-1))
+ continue;
+ Field *first_kp_field= table->field[key_part->fieldnr-1];
+ double quick_cond_selectivity= table->quick_rows[keynr] /
+ table_records;
+ double selectivity_mult= 1.0;
+ uint i= 1;
+ for (key_part++; i < used_key_parts; i++, key_part++)
+ {
+ if (!bitmap_is_set(used_fields, key_part->fieldnr-1))
+ break;
+ selectivity_mult/=
+ table->field[key_part->fieldnr-1]->cond_selectivity;
+ }
+ if (i != used_key_parts)
+ continue;
+ first_kp_field->cond_selectivity= quick_cond_selectivity *
+ selectivity_mult;
+ set_if_smaller(first_kp_field->cond_selectivity, 1.0);
+ table->cond_selectivity*= first_kp_field->cond_selectivity;
+ bitmap_set_bit(used_fields, first_kp_field->field_index);
+ is_sel_added= true;
+ }
+ }
+ } while (is_sel_added);
+
+ for (uint quick_key_parts= 1;
+ quick_key_parts <= max_quick_key_parts;
+ quick_key_parts++)
+ {
+ for (keynr= 0; keynr < table->s->keys; keynr++)
+ {
+ uint used_key_parts= table->quick_key_parts[keynr];
+ if (!(table->quick_keys.is_set(keynr) &&
+ used_key_parts == quick_key_parts))
+ continue;
+ KEY *key_info= table->key_info + keynr;
+ KEY_PART_INFO* key_part= key_info->key_part;
+ if (bitmap_is_set(used_fields, key_part->fieldnr-1))
+ continue;
+ Field *first_kp_field= table->field[key_part->fieldnr-1];
+ double quick_cond_selectivity= table->quick_rows[keynr] /
+ table_records;
+ key_part+= used_key_parts - 1;
+ double selectivity_mult= 1.0;
+ for (uint i= used_key_parts - 1; i; i--, key_part--)
+ {
+ if (!bitmap_is_set(used_fields, key_part->fieldnr-1))
+ {
+ double field_selectivity;
+ double f1= key_info->actual_rec_per_key(i-1);
+ double f2= key_info->actual_rec_per_key(i);
+ if (f1 > 0 && f2 > 0)
+ field_selectivity= f2 / f1;
+ else
+ {
+ /*
+ No statistics available, assume the field selectivity
+ is equal to 1
+ */
+ field_selectivity= 1;
+ }
+ table->cond_selectivity*=
+ table->field[i]->cond_selectivity= field_selectivity;
+ bitmap_set_bit(used_fields, table->field[i]->field_index);
+ }
+ selectivity_mult/= table->field[key_part->fieldnr-1]->cond_selectivity;
+ }
+ first_kp_field->cond_selectivity= quick_cond_selectivity *
+ selectivity_mult;
+ set_if_smaller(first_kp_field->cond_selectivity, 1.0);
+ table->cond_selectivity*= first_kp_field->cond_selectivity;
+ bitmap_set_bit(used_fields, first_kp_field->field_index);
+ }
+ }
if (quick && (quick->get_type() == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
quick->get_type() == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE))
@@ -3491,8 +3455,6 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
table->cond_selectivity*= (quick->records/table_records);
}
- bitmap_union(used_fields, &handled_columns);
-
/* Check if we can improve selectivity estimates by using sampling */
ulong check_rows=
MY_MIN(thd->variables.optimizer_selectivity_sampling_limit,
@@ -3552,13 +3514,64 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
}
}
}
-
}
/* This list and its elements put to mem_root so should not be freed */
table->cond_selectivity_sampling_explain= &dt->list;
}
}
- trace_wrapper.add("cond_selectivity", table->cond_selectivity);
+
+ /*
+ Take into account selectivity of the equalities between columns
+ of the table s and discount selectivities of range conditions
+ induced by these equalities
+ */
+
+ if (cond_equal && cond_equal->current_level.elements)
+ {
+ Item_equal *item_equal;
+ List_iterator_fast<Item_equal> it(cond_equal->current_level);
+ table_map table_bit= table->map;
+ while ((item_equal= it++))
+ {
+ if (!(item_equal->used_tables() & table_bit))
+ continue;
+ uint eq_cnt= 0;
+ double max_ndv= 0.0;
+ Item_equal_fields_iterator fi(*item_equal);
+ bool found= false;
+ while (fi++)
+ {
+ Field *fld= fi.get_curr_field();
+ if (fld->table->map != table_bit)
+ continue;
+ set_if_bigger(max_ndv, fld->ndv);
+ if (!found)
+ found= true;
+ else
+ {
+ /*
+ The field fld is equal to some other field of the table whose range
+ condition selectivity has been already taken into accout. Discount
+ selectivit of the range condition for the field fld.
+ */
+ table->cond_selectivity/= fld->cond_selectivity;
+ eq_cnt++;
+ }
+ }
+ if (eq_cnt)
+ {
+ /*
+ There are equality predicates over fields of the table s. Their
+ selectivity has to be taken into account.
+ */
+ for ( ; eq_cnt; eq_cnt--)
+ table->cond_selectivity/= max_ndv;
+ }
+ }
+ }
+
+ set_if_smaller(table->cond_selectivity,1.0);
+
DBUG_RETURN(FALSE);
}
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 73def7b..d49c6a3 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -1729,7 +1729,8 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables,
SORT_INFO* filesort,
bool allow_null_cond, int *error);
-bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond);
+bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table,
+ COND_EQUAL *cond_equl, Item **cond);
bool eq_ranges_exceeds_limit(RANGE_SEQ_IF *seq, void *seq_init_param,
uint limit);
diff --git a/sql/opt_range_mrr.cc b/sql/opt_range_mrr.cc
index 4afa06a..75c12ef 100644
--- a/sql/opt_range_mrr.cc
+++ b/sql/opt_range_mrr.cc
@@ -340,6 +340,7 @@ bool sel_arg_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range)
}
seq->param->range_count++;
seq->param->max_key_parts= MY_MAX(seq->param->max_key_parts, max_key_parts);
+ seq->param->table->curr_key_tree_part= key_tree->part;
return 0;
}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 896135d..e1c27cb 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -5337,6 +5337,16 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
s->table->cond_selectivity= 1.0;
+ /* Calculate expected number of distinct values for fields */
+ double table_records= (double)table->stat_records();
+ for (Field **field_ptr= s->table->field; *field_ptr; field_ptr++)
+ {
+ Field *table_field= *field_ptr;
+ if (bitmap_is_set(s->table->read_set, table_field->field_index))
+ table_field->ndv= table_records /
+ get_column_avg_frequency(table_field);
+ }
+
/*
Perform range analysis if there are keys it could use (1).
Don't do range analysis for materialized subqueries (2).
@@ -5384,7 +5394,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
if (!sargable_cond)
sargable_cond= get_sargable_cond(join, s->table);
if (join->thd->variables.optimizer_use_condition_selectivity > 1)
- calculate_cond_selectivity_for_table(join->thd, s->table,
+ calculate_cond_selectivity_for_table(join->thd, s->table, join->cond_equal,
sargable_cond);
if (s->table->reginfo.impossible_range)
{
@@ -5433,7 +5443,14 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
add_table_scan_values_to_trace(thd, s);
}
else
+ {
+ Item **sargable_cond= NULL;
+ sargable_cond= get_sargable_cond(join, s->table);
+ if (join->thd->variables.optimizer_use_condition_selectivity > 1)
+ calculate_cond_selectivity_for_table(join->thd, s->table, join->cond_equal,
+ sargable_cond);
add_table_scan_values_to_trace(thd, s);
+ }
}
}
@@ -5494,13 +5511,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
if (!(join->select_options & SELECT_DESCRIBE) &&
unit->derived && unit->derived->is_materialized_derived())
{
- /*
- Calculate estimated number of rows for materialized derived
- table/view.
- */
- for (i= 0; i < join->table_count ; i++)
- if (double rr= join->best_positions[i].records_read)
- records= COST_MULT(records, rr);
+ records= join->join_record_count;
ha_rows rows= records > HA_ROWS_MAX ? HA_ROWS_MAX : (ha_rows) records;
set_if_smaller(rows, unit->select_limit_cnt);
join->select_lex->increase_derived_records(rows);
@@ -5872,7 +5883,9 @@ add_key_field(JOIN *join,
if (is_const)
{
stat[0].const_keys.merge(possible_keys);
- bitmap_set_bit(&field->table->cond_set, field->field_index);
+ if (possible_keys.is_clear_all() &&
+ join->thd->variables.optimizer_use_condition_selectivity > 2)
+ bitmap_set_bit(&field->table->cond_set, field->field_index);
}
else if (!eq_func)
{
@@ -7109,7 +7122,7 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
inline
double matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint,
- uint use_cond_selectivity)
+ uint use_cond_selectivity)
{
ha_rows records;
double dbl_records;
@@ -7117,9 +7130,14 @@ double matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint,
if (use_cond_selectivity > 1)
{
TABLE *table= s->table;
- double sel= table->cond_selectivity;
- double table_records= (double)table->stat_records();
- dbl_records= table_records * sel;
+ if (s->quick)
+ dbl_records= table->quick_condition_rows;
+ else
+ {
+ double sel= table->cond_selectivity;
+ double table_records= (double)table->stat_records();
+ dbl_records= table_records * sel;
+ }
return dbl_records;
}
@@ -8862,115 +8880,88 @@ double JOIN::get_examined_rows()
*/
static
-double table_multi_eq_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
- table_map rem_tables, uint keyparts,
- uint16 *ref_keyuse_steps)
-{
+double table_multi_eq_cond_selectivity(JOIN *join, JOIN_TAB *s,
+ table_map rem_tables)
+{
double sel= 1.0;
+#if 1
+#else
COND_EQUAL *cond_equal= join->cond_equal;
-
if (!cond_equal || !cond_equal->current_level.elements)
return sel;
- if (!s->keyuse)
- return sel;
-
Item_equal *item_equal;
List_iterator_fast<Item_equal> it(cond_equal->current_level);
TABLE *table= s->table;
table_map table_bit= table->map;
- POSITION *pos= &join->positions[idx];
-
+
while ((item_equal= it++))
{
- /*
- Check whether we need to take into account the selectivity of
- multiple equality item_equal. If this is the case multiply
- the current value of sel by this selectivity
- */
table_map used_tables= item_equal->used_tables();
if (!(used_tables & table_bit))
+ {
+ /* Colums of table s are not used in this item_equal */
continue;
+ }
if (item_equal->get_const())
+ {
+ /*
+ Selectivity of the equalities from item_equal have been already
+ taken into account as selectivity of range condititions
+ */
continue;
-
- bool adjust_sel= FALSE;
+ }
+
+ double max_ndv= 0.0;
+ bool used_for_ref= false;
+ uint n= 0;
Item_equal_fields_iterator fi(*item_equal);
- while((fi++) && !adjust_sel)
+ while (fi++)
{
Field *fld= fi.get_curr_field();
if (fld->table->map != table_bit)
continue;
- if (pos->key == 0)
- adjust_sel= TRUE;
+ if (bitmap_is_set(&table->tmp_set, fld->field_index))
+ used_for_ref= true;
else
{
- uint i;
- KEYUSE *keyuse= pos->key;
- uint key= keyuse->key;
- for (i= 0; i < keyparts; i++)
- {
- if (i > 0)
- keyuse+= ref_keyuse_steps[i-1];
- uint fldno;
- if (is_hash_join_key_no(key))
- fldno= keyuse->keypart;
- else
- fldno= table->key_info[key].key_part[i].fieldnr - 1;
- if (fld->field_index == fldno)
- break;
- }
- keyuse= pos->key;
-
- if (i == keyparts)
- {
- /*
- Field fld is included in multiple equality item_equal
- and is not a part of the ref key.
- The selectivity of the multiple equality must be taken
- into account unless one of the ref arguments is
- equal to fld.
- */
- adjust_sel= TRUE;
- for (uint j= 0; j < keyparts && adjust_sel; j++)
- {
- if (j > 0)
- keyuse+= ref_keyuse_steps[j-1];
- Item *ref_item= keyuse->val;
- if (ref_item->real_item()->type() == Item::FIELD_ITEM)
- {
- Item_field *field_item= (Item_field *) (ref_item->real_item());
- if (item_equal->contains(field_item->field))
- adjust_sel= FALSE;
- }
- }
- }
+ set_if_bigger(max_ndv, fld->ndv);
+ n++;
}
}
- if (adjust_sel)
+ /*
+ n == number of fields of the multiple equality belonging to s that
+ are not accessed by ref in the currently evaluated partial plan
+ */
+ if (!used_for_ref)
+ {
+ /* no field of the multiple equality belonging to s is accessed by ref */
+ n--;
+ }
+ else
{
- /*
- If ref == 0 and there are no fields in the multiple equality
- item_equal that belong to the tables joined prior to s
- then the selectivity of multiple equality will be set to 1.0.
- */
- double eq_fld_sel= 1.0;
fi.rewind();
- while ((fi++))
+ bool found= false;
+ while(fi++)
{
- double curr_eq_fld_sel;
- Field *fld= fi.get_curr_field();
- if (!(fld->table->map & ~(table_bit | rem_tables)))
+ Field *fld= fi.get_curr_field();
+ if (fld->table->map & (rem_tables | table_bit))
continue;
- curr_eq_fld_sel= get_column_avg_frequency(fld) /
- fld->table->stat_records();
- if (curr_eq_fld_sel < 1.0)
- set_if_bigger(eq_fld_sel, curr_eq_fld_sel);
+ found= true;
+ set_if_bigger(max_ndv, fld->ndv);
}
- sel*= eq_fld_sel;
+ if (!found)
+ n--;
}
- }
- return sel;
+ if (max_ndv != 0.0) // max_ndv may be 0.0 if the table is empty
+ {
+ for ( ; n; n--)
+ sel/= max_ndv;
+ }
+ }
+#endif
+
+ return sel;
}
@@ -8983,15 +8974,6 @@ double table_multi_eq_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
@param rem_tables The bitmap of tables to be joined later
@detail
- Get selectivity of conditions that can be applied when joining this table
- with previous tables.
-
- For quick selects and full table scans, selectivity of COND(this_table)
- is accounted for in matching_candidates_in_table(). Here, we only count
- selectivity of COND(this_table, previous_tables).
-
- For other access methods, we need to calculate selectivity of the whole
- condition, "COND(this_table) AND COND(this_table, previous_tables)".
@retval
selectivity of the conditions imposed on the rows of s
@@ -9001,105 +8983,34 @@ static
double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
table_map rem_tables)
{
- uint16 ref_keyuse_steps[MAX_REF_PARTS - 1];
- Field *field;
TABLE *table= s->table;
- MY_BITMAP *read_set= table->read_set;
double sel= s->table->cond_selectivity;
- POSITION *pos= &join->positions[idx];
+ double table_records= table->stat_records();
uint keyparts= 0;
uint found_part_ref_or_null= 0;
+ POSITION *pos= &join->positions[idx];
- if (pos->key != 0)
+ /* Discount the selectivity of the access method used to join table s */
+ if (s->quick && s->quick->index != MAX_KEY && pos->key == 0)
{
/*
- A ref access or hash join is used for this table. ref access is created
- from
-
- tbl.keypart1=expr1 AND tbl.keypart2=expr2 AND ...
-
- and it will only return rows for which this condition is satisified.
- Suppose, certain expr{i} is a constant. Since ref access only returns
- rows that satisfy
-
- tbl.keypart{i}=const (*)
-
- then selectivity of this equality should not be counted in return value
- of this function. This function uses the value of
-
- table->cond_selectivity=selectivity(COND(tbl)) (**)
-
- as a starting point. This value includes selectivity of equality (*). We
- should somehow discount it.
-
- Looking at calculate_cond_selectivity_for_table(), one can see that that
- the value is not necessarily a direct multiplicand in
- table->cond_selectivity
-
- There are three possible ways to discount
- 1. There is a potential range access on t.keypart{i}=const.
- (an important special case: the used ref access has a const prefix for
- which a range estimate is available)
-
- 2. The field has a histogram. field[x]->cond_selectivity has the data.
-
- 3. Use index stats on this index:
- rec_per_key[key_part+1]/rec_per_key[key_part]
-
- (TODO: more details about the "t.key=othertable.col" case)
- */
- KEYUSE *keyuse= pos->key;
- KEYUSE *prev_ref_keyuse= keyuse;
- uint key= keyuse->key;
- bool used_range_selectivity= false;
-
- /*
- Check if we have a prefix of key=const that matches a quick select.
+ A range access to the table s is used when joining s.
+ Discount the selectivity of the range from sel.
*/
- if (!is_hash_join_key_no(key))
+ if (table_records > 0)
{
- key_part_map quick_key_map= (key_part_map(1) << table->quick_key_parts[key]) - 1;
- if (table->quick_rows[key] &&
- !(quick_key_map & ~table->const_key_parts[key]))
- {
- /*
- Ok, there is an equality for each of the key parts used by the
- quick select. This means, quick select's estimate can be reused to
- discount the selectivity of a prefix of a ref access.
- */
- for (; quick_key_map & 1 ; quick_key_map>>= 1)
- {
- while (keyuse->table == table && keyuse->key == key &&
- keyuse->keypart == keyparts)
- {
- keyuse++;
- }
- keyparts++;
- }
- /*
- Here we discount selectivity of the constant range CR. To calculate
- this selectivity we use elements from the quick_rows[] array.
- If we have indexes i1,...,ik with the same prefix compatible
- with CR any of the estimate quick_rows[i1], ... quick_rows[ik] could
- be used for this calculation but here we don't know which one was
- actually used. So sel could be greater than 1 and we have to cap it.
- However if sel becomes greater than 2 then with high probability
- something went wrong.
- */
- sel /= (double)table->quick_rows[key] / (double) table->stat_records();
- // MDEV-20595 FIXME: DBUG_ASSERT(sel > 0 && sel <= 2.0);
- set_if_smaller(sel, 1.0);
- used_range_selectivity= true;
- }
+ if (sel < 1.0)
+ sel/= s->quick->records/table_records;
+ set_if_smaller(sel, 1.0);
}
-
- /*
- Go through the "keypart{N}=..." equalities and find those that were
- already taken into account in table->cond_selectivity.
- */
- keyuse= pos->key;
- keyparts=0;
- while (keyuse->table == table && keyuse->key == key)
+ }
+ else if (pos->key != 0)
+ {
+ /* A ref access or hash join is used to join table */
+ bitmap_clear_all(&table->tmp_set);
+ KEYUSE *keyuse= pos->key;
+ uint key= keyuse->key;
+ do
{
if (!(keyuse->used_tables & (rem_tables | table->map)))
{
@@ -9116,97 +9027,102 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
!((keyuse->val->used_tables()) & ~pos->ref_depend_map) &&
!(found_part_ref_or_null & keyuse->optimize))
{
- /* Found a KEYUSE object that will be used by ref access */
- keyparts++;
found_part_ref_or_null|= keyuse->optimize & ~KEY_OPTIMIZE_EQ;
}
+ keyparts++;
}
- if (keyparts > keyuse->keypart)
+ if (keyparts - 1 == keyuse->keypart)
{
- /* Ok this is the keyuse that will be used for ref access */
- if (!used_range_selectivity && keyuse->val->const_item())
- {
- uint fldno;
- if (is_hash_join_key_no(key))
- fldno= keyuse->keypart;
- else
- fldno= table->key_info[key].key_part[keyparts-1].fieldnr - 1;
-
- if (table->field[fldno]->cond_selectivity > 0)
- {
- sel /= table->field[fldno]->cond_selectivity;
- // MDEV-20595 FIXME: DBUG_ASSERT(sel > 0 && sel <= 2.0);
- set_if_smaller(sel, 1.0);
+ uint fldno;
+ if (is_hash_join_key_no(key))
+ fldno= keyuse->keypart;
+ else
+ fldno= table->key_info[key].key_part[keyparts-1].fieldnr - 1;
+ if (keyuse->val->const_item())
+ {
+ DBUG_ASSERT(!is_hash_join_key_no(key));
+ if (table->field[fldno]->cond_selectivity < 1.0)
+ {
+ /*
+ The value of the field fld with number fldno of the table s
+ is calculated using a constant item: fld=c.
+ We need to discount the selectivity of this conditition.
+ Unfortunately this selectivity is not equal to the precomputed
+ table->field[fldno]->cond_selectivity unless fld=c is the only
+ condition imposed on fld. So we have to calculate the
+ selectivity of fld=c somehow and discount it from sel.
+ Currently we use rec_per_key values of index with number key
+ to calculate this selectivity.
+ */
+ double discount_sel;
+ KEY *key_info= table->key_info + key;
+ uint keypart= keyuse->keypart;
+ if (keypart == 0)
+ {
+ double rec_per_key= key_info->actual_rec_per_key(0);
+ if (rec_per_key == 0)
+ rec_per_key= 1;
+ discount_sel= rec_per_key / table_records;
+ }
+ else
+ {
+ discount_sel= key_info->actual_rec_per_key(keypart) /
+ key_info->actual_rec_per_key(keypart - 1);
+ }
+ sel/= discount_sel;
}
- /*
- TODO: we could do better here:
- 1. cond_selectivity might be =1 (the default) because quick
- select on some index prevented us from analyzing
- histogram for this column.
- 2. we could get an estimate through this?
- rec_per_key[key_part-1] / rec_per_key[key_part]
- */
}
- if (keyparts > 1)
+ else if (keyuse->val->real_item()->type() == Item::FIELD_ITEM)
{
- ref_keyuse_steps[keyparts-2]= (uint16)(keyuse - prev_ref_keyuse);
- prev_ref_keyuse= keyuse;
+ Field *start_field= table->field[fldno];
+ bitmap_set_bit(&table->tmp_set, fldno);
+ if (start_field->next_equal_field)
+ {
+ Field *next_field;
+ bool need_discount= false;
+ Field *value_field=
+ ((Item_field *) (keyuse->val->real_item()))->field;
+ for (next_field= start_field->next_equal_field;
+ next_field != start_field;
+ next_field= next_field->next_equal_field)
+ {
+ if (next_field == value_field)
+ {
+ need_discount= true;
+ break;
+ }
+ }
+ /*
+ To join the table s ref access is used that employs
+ the equality
+ */
+ if (need_discount)
+ {
+ next_field= start_field;
+ do
+ {
+ if (next_field->table == start_field->table)
+ sel/= next_field->cond_selectivity;
+ next_field= next_field->next_equal_field;
+ } while (next_field != start_field);
+ }
+ }
}
}
}
}
keyuse++;
- }
+ } while (keyuse->table == table && keyuse->key == key);
}
- else
+ else
{
- /*
- The table is accessed with full table scan, or quick select.
- Selectivity of COND(table) is already accounted for in
- matching_candidates_in_table().
- */
sel= 1;
}
- /*
- If the field f from the table is equal to a field from one the
- earlier joined tables then the selectivity of the range conditions
- over the field f must be discounted.
-
- We need to discount selectivity only if we're using ref-based
- access method (and have sel!=1).
- If we use ALL/range/index_merge, then sel==1, and no need to discount.
- */
- if (pos->key != NULL)
- {
- for (Field **f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
- {
- if (!bitmap_is_set(read_set, field->field_index) ||
- !field->next_equal_field)
- continue;
- for (Field *next_field= field->next_equal_field;
- next_field != field;
- next_field= next_field->next_equal_field)
- {
- if (!(next_field->table->map & rem_tables) && next_field->table != table)
- {
- if (field->cond_selectivity > 0)
- {
- sel/= field->cond_selectivity;
- // MDEV-20595 FIXME: DBUG_ASSERT(sel > 0 && sel <= 2.0);
- set_if_smaller(sel, 1.0);
- }
- break;
- }
- }
- }
- }
-
- sel*= table_multi_eq_cond_selectivity(join, idx, s, rem_tables,
- keyparts, ref_keyuse_steps);
+ sel*= table_multi_eq_cond_selectivity(join, s, rem_tables);
- DBUG_ASSERT(0.0 < sel && sel <= 1.0);
+ DBUG_ASSERT(0.0 < sel && sel <= 1.0 + DBL_EPSILON);
return sel;
}
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index a94fb11..ebf5d2d 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -3824,7 +3824,7 @@ double get_column_avg_frequency(Field * field)
Column_statistics *col_stats= field->read_stats;
- if (!col_stats)
+ if (!col_stats || col_stats->get_avg_frequency() == 0.0)
res= (double)table->stat_records();
else
res= col_stats->get_avg_frequency();
diff --git a/sql/structs.h b/sql/structs.h
index 0c00aee..6064b66 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -72,6 +72,7 @@ typedef struct st_key_part_info { /* Info about a key part */
NOT necessarily the original Field */
uint offset; /* Offset in record (from 0) */
uint null_offset; /* Offset to null_bit in record */
+ uint quick_cnt;
/* Length of key part in bytes, excluding NULL flag and length bytes */
uint16 length;
/*
diff --git a/sql/table.h b/sql/table.h
index 8297774..a7f48f9 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1244,6 +1244,7 @@ struct TABLE
*/
ha_rows quick_rows[MAX_KEY];
uint quick_key_parts[MAX_KEY];
+ uint16 curr_quick_cnt;
double quick_costs[MAX_KEY];
/*
1
0

[Commits] 85145b9: MDEV-28846 Poor performance when rowid filter contains no elements
by IgorBabaev 24 Jun '22
by IgorBabaev 24 Jun '22
24 Jun '22
revision-id: 85145b9e605057f5053f9dd929468e25e9f823e5 (mariadb-10.4.23-83-g85145b9)
parent(s): 6e7c6fcfd1f1ac131c423c1ba084d61abad10e8b
author: Igor Babaev
committer: Igor Babaev
timestamp: 2022-06-23 23:53:19 -0700
message:
MDEV-28846 Poor performance when rowid filter contains no elements
This is a preliminary patch, no test cases are added.
---
sql/rowid_filter.cc | 6 ++++--
sql/rowid_filter.h | 10 +++++++++-
sql/sql_analyze_stmt.h | 12 ++++++++++--
sql/sql_select.cc | 2 ++
4 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/sql/rowid_filter.cc b/sql/rowid_filter.cc
index 75f2ab7..bc72259 100644
--- a/sql/rowid_filter.cc
+++ b/sql/rowid_filter.cc
@@ -575,9 +575,10 @@ bool Range_rowid_filter::fill()
if (quick->init() || quick->reset())
rc= 1;
- while (!rc)
+ for ( ; ; )
{
- rc= quick->get_next();
+ if ((rc= quick->get_next()))
+ break;
if (thd->killed)
rc= 1;
if (!rc)
@@ -598,6 +599,7 @@ bool Range_rowid_filter::fill()
file->pushed_idx_cond_keyno= pushed_idx_cond_keyno_save;
file->in_range_check_pushed_down= in_range_check_pushed_down_save;
tracker->report_container_buff_size(table->file->ref_length);
+ tracker->report_time_fill_container_ms();
if (rc != HA_ERR_END_OF_FILE)
return 1;
diff --git a/sql/rowid_filter.h b/sql/rowid_filter.h
index 467b688..c02d128 100644
--- a/sql/rowid_filter.h
+++ b/sql/rowid_filter.h
@@ -192,6 +192,8 @@ class Rowid_filter_container : public Sql_alloc
*/
virtual bool check(void *ctxt, char *elem) = 0;
+ virtual bool is_empty() = 0;
+
virtual ~Rowid_filter_container() {}
};
@@ -231,6 +233,8 @@ class Rowid_filter : public Sql_alloc
virtual ~Rowid_filter() {}
+ bool is_empty() { return container->is_empty(); }
+
Rowid_filter_container *get_container() { return container; }
void set_tracker(Rowid_filter_tracker *track_arg) { tracker= track_arg; }
@@ -300,7 +304,7 @@ class Refpos_container_sorted_array : public Sql_alloc
public:
- Refpos_container_sorted_array(uint max_elems, uint elem_sz)
+ Refpos_container_sorted_array(uint max_elems, uint elem_sz)
: max_elements(max_elems), elem_size(elem_sz), array(0) {}
~Refpos_container_sorted_array()
@@ -339,6 +343,8 @@ class Refpos_container_sorted_array : public Sql_alloc
my_qsort2(array->front(), array->elements()/elem_size,
elem_size, (qsort2_cmp) cmp, cmp_arg);
}
+
+ bool is_empty() { return elements() == 0; }
};
@@ -368,6 +374,8 @@ class Rowid_filter_sorted_array: public Rowid_filter_container
bool add(void *ctxt, char *elem) { return refpos_container.add(elem); }
bool check(void *ctxt, char *elem);
+
+ bool is_empty() { return refpos_container.is_empty(); }
};
/**
diff --git a/sql/sql_analyze_stmt.h b/sql/sql_analyze_stmt.h
index eec5282..17ebb9c 100644
--- a/sql/sql_analyze_stmt.h
+++ b/sql/sql_analyze_stmt.h
@@ -306,6 +306,9 @@ class Rowid_filter_tracker : public Sql_alloc
/* Size of the rowid filter container buffer */
size_t container_buff_size;
+ /* Time spent to fill the rowid filter container */
+ double time_fill_container_ms;
+
/* Count of elements that were used to fill the rowid filter container */
uint container_elements;
@@ -314,7 +317,7 @@ class Rowid_filter_tracker : public Sql_alloc
uint n_positive_checks;
public:
Rowid_filter_tracker(bool do_timing) :
- time_tracker(do_timing), container_buff_size(0),
+ time_tracker(do_timing), container_buff_size(0), time_fill_container_ms(0),
container_elements(0), n_checks(0), n_positive_checks(0)
{}
@@ -339,9 +342,14 @@ class Rowid_filter_tracker : public Sql_alloc
return &time_tracker;
}
+ void report_time_fill_container_ms()
+ {
+ time_fill_container_ms= time_tracker.get_time_ms();
+ }
+
double get_time_fill_container_ms()
{
- return time_tracker.get_time_ms();
+ return time_fill_container_ms;
}
void increment_checked_elements_count(bool was_checked)
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 735e00d..7c2f081 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -20599,6 +20599,8 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
DBUG_RETURN(NESTED_LOOP_ERROR);
join_tab->build_range_rowid_filter_if_needed();
+ if (join_tab->rowid_filter && join_tab->rowid_filter->is_empty())
+ rc= NESTED_LOOP_NO_MORE_ROWS;
join->return_tab= join_tab;
1
0