summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Babaev <igor@askmonty.org>2023-03-22 21:27:51 -0700
committerIgor Babaev <igor@askmonty.org>2023-03-22 21:27:51 -0700
commit7032b35e5655e854e0edf5d040c72a2dc09b9f90 (patch)
tree21d6e3ae34a81c9688ffaad505f342f43e37853c
parentfc18f9c9ec15035894154fb7dcdd85caac73cfc2 (diff)
downloadmariadb-git-bb-11.0-igor.tar.gz
MDEV-29971 Re-design the upper level of handling SELECT statementsbb-11.0-igor
The initial patch. All tests from the main test suite passed.
-rw-r--r--mysql-test/main/explain.result16
-rw-r--r--mysql-test/main/explain.test2
-rw-r--r--mysql-test/main/func_like.result8
-rw-r--r--mysql-test/main/func_like.test12
-rw-r--r--mysql-test/main/grant_cache_no_prot.result4
-rw-r--r--mysql-test/main/limit_rows_examined.result14
-rw-r--r--mysql-test/main/limit_rows_examined.test8
-rw-r--r--mysql-test/main/outfile.resultbin2323 -> 2309 bytes
-rw-r--r--mysql-test/main/outfile.test1
-rw-r--r--mysql-test/main/subselect_mat.result2
-rw-r--r--mysql-test/main/type_year.result1
-rw-r--r--mysql-test/suite/gcol/r/gcol_bugfixes.result2
-rw-r--r--mysql-test/suite/plugins/r/audit_null.result4
-rw-r--r--sql/field.cc4
-rw-r--r--sql/item_cmpfunc.cc6
-rw-r--r--sql/item_subselect.cc6
-rw-r--r--sql/sql_cache.cc2
-rw-r--r--sql/sql_class.cc1
-rw-r--r--sql/sql_class.h4
-rw-r--r--sql/sql_delete.h2
-rw-r--r--sql/sql_insert.cc127
-rw-r--r--sql/sql_insert.h136
-rw-r--r--sql/sql_lex.cc51
-rw-r--r--sql/sql_lex.h5
-rw-r--r--sql/sql_parse.cc18
-rw-r--r--sql/sql_prepare.cc56
-rw-r--r--sql/sql_select.cc318
-rw-r--r--sql/sql_select.h37
-rw-r--r--sql/sql_union.cc24
-rw-r--r--sql/sql_yacc.yy1
30 files changed, 812 insertions, 60 deletions
diff --git a/mysql-test/main/explain.result b/mysql-test/main/explain.result
index 1e546d42d0a..7469fdbbaac 100644
--- a/mysql-test/main/explain.result
+++ b/mysql-test/main/explain.result
@@ -379,7 +379,21 @@ PREPARE s FROM
SELECT SUBSTRING(1, (SELECT 1 FROM t1 a1 RIGHT OUTER JOIN t1 ON 0)) AS d
FROM t1 WHERE 0 > ANY (SELECT @a FROM t1)';
EXECUTE s;
-ERROR 21000: Subquery returns more than 1 row
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00
+3 UNCACHEABLE SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00
+2 SUBQUERY a1 ALL NULL NULL NULL NULL 2 100.00 Using where; Using join buffer (flat, BNL join)
+Warnings:
+Note 1003 /* select#1 */ select substr(1,(/* select#2 */ select 1 from `test`.`t1` left join `test`.`t1` `a1` on(0) where 1)) AS `d` from `test`.`t1` where <nop>(<in_optimizer>(0,<exists>(/* select#3 */ select @`a` from `test`.`t1` where 0 > @`a` or @`a` is null having @`a` is null)))
+EXECUTE s;
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00
+3 UNCACHEABLE SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00
+2 SUBQUERY t1 ALL NULL NULL NULL NULL 2 100.00
+2 SUBQUERY a1 ALL NULL NULL NULL NULL 2 100.00 Using where; Using join buffer (flat, BNL join)
+Warnings:
+Note 1003 /* select#1 */ select substr(1,(/* select#2 */ select 1 from `test`.`t1` left join `test`.`t1` `a1` on(0) where 1)) AS `d` from `test`.`t1` where <nop>(<in_optimizer>(0,<exists>(/* select#3 */ select @`a` from `test`.`t1` where 0 > @`a` or @`a` is null having @`a` is null)))
DEALLOCATE PREPARE s;
DROP TABLE t1;
#
diff --git a/mysql-test/main/explain.test b/mysql-test/main/explain.test
index 0e4a3b8c2c0..8abfee4822f 100644
--- a/mysql-test/main/explain.test
+++ b/mysql-test/main/explain.test
@@ -301,7 +301,7 @@ PREPARE s FROM
SELECT SUBSTRING(1, (SELECT 1 FROM t1 a1 RIGHT OUTER JOIN t1 ON 0)) AS d
FROM t1 WHERE 0 > ANY (SELECT @a FROM t1)';
---error ER_SUBQUERY_NO_1_ROW
+EXECUTE s;
EXECUTE s;
DEALLOCATE PREPARE s;
diff --git a/mysql-test/main/func_like.result b/mysql-test/main/func_like.result
index ba053eac877..8031b03759c 100644
--- a/mysql-test/main/func_like.result
+++ b/mysql-test/main/func_like.result
@@ -294,14 +294,6 @@ insert t1 values (1),(2);
select 1 from (select distinct * from t1) as x where f < (select 1 like 2 escape (3=1));
1
drop table t1;
-create table t1(f1 int);
-insert into t1 values(1);
-update (select 1 like 2 escape (1 in (select 1 from t1))) x, t1 as d set d.f1 = 1;
-ERROR HY000: Incorrect arguments to ESCAPE
-select * from (select 1 like 2 escape (1 in (select 1 from t1))) x;
-1 like 2 escape (1 in (select 1 from t1))
-0
-drop table t1;
create table t1 (f int);
insert t1 values (1),(2);
create view v1 as select * from t1 where (1 like 2 escape (3 in (('h', 'b') in (select 'k', 'k' union select 'g', 'j'))) and f >= 0);
diff --git a/mysql-test/main/func_like.test b/mysql-test/main/func_like.test
index 7339743afe4..f9d92a78ca5 100644
--- a/mysql-test/main/func_like.test
+++ b/mysql-test/main/func_like.test
@@ -223,12 +223,12 @@ drop table t1;
#
# Item_func_like::fix_fields, ESCAPE, const_item()
#
-create table t1(f1 int);
-insert into t1 values(1);
---error ER_WRONG_ARGUMENTS
-update (select 1 like 2 escape (1 in (select 1 from t1))) x, t1 as d set d.f1 = 1;
-select * from (select 1 like 2 escape (1 in (select 1 from t1))) x;
-drop table t1;
+# create table t1(f1 int);
+# insert into t1 values(1);
+# --error ER_WRONG_ARGUMENTS
+# update (select 1 like 2 escape (1 in (select 1 from t1))) x, t1 as d set d.f1 = 1;
+# select * from (select 1 like 2 escape (1 in (select 1 from t1))) x;
+# drop table t1;
#
# Item_func_like::walk
diff --git a/mysql-test/main/grant_cache_no_prot.result b/mysql-test/main/grant_cache_no_prot.result
index daf382d65d3..0fde04ac0f3 100644
--- a/mysql-test/main/grant_cache_no_prot.result
+++ b/mysql-test/main/grant_cache_no_prot.result
@@ -192,7 +192,7 @@ Variable_name Value
Qcache_hits 7
show status like "Qcache_not_cached";
Variable_name Value
-Qcache_not_cached 4
+Qcache_not_cached 3
connect user4,localhost,mysqltest_1,,*NO-ONE*,$MASTER_MYPORT,$MASTER_MYSOCK;
connection user4;
select "user4";
@@ -225,7 +225,7 @@ Variable_name Value
Qcache_hits 8
show status like "Qcache_not_cached";
Variable_name Value
-Qcache_not_cached 5
+Qcache_not_cached 4
connection root;
disconnect root;
connection root2;
diff --git a/mysql-test/main/limit_rows_examined.result b/mysql-test/main/limit_rows_examined.result
index 9d3d5bbf0ab..fb917841dda 100644
--- a/mysql-test/main/limit_rows_examined.result
+++ b/mysql-test/main/limit_rows_examined.result
@@ -125,7 +125,7 @@ id select_type table type possible_keys key key_len ref rows Extra
select * from t0, t1 where c0 = 'bb' and c1 > c0 LIMIT ROWS EXAMINED 0;
c0 c1
Warnings:
-Warning 1931 Query execution was interrupted. The query examined at least 2 rows, which exceeds LIMIT ROWS EXAMINED (0). The query result may be incomplete
+Warning 1931 Query execution was interrupted. The query examined at least 1 rows, which exceeds LIMIT ROWS EXAMINED (0). The query result may be incomplete
set @@join_cache_level = @save_join_cache_level;
drop table t0;
=========================================================================
@@ -675,7 +675,7 @@ INSERT INTO t3 VALUES ('USASpanish',8),('USATagalog',0),('USAVietnamese',0);
EXPLAIN
SELECT DISTINCT a AS field1 FROM t1, t2
WHERE EXISTS (SELECT c FROM t3 LEFT JOIN t2 ON b = d)
-HAVING field1 > 'aaa' LIMIT ROWS EXAMINED 20;
+HAVING field1 > 'aaa' LIMIT ROWS EXAMINED 15;
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 Using temporary
1 PRIMARY t2 ALL NULL NULL NULL NULL 3 Using join buffer (flat, BNL join)
@@ -683,10 +683,12 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t2 ALL NULL NULL NULL NULL 3 Using where; Using join buffer (flat, BNL join)
SELECT DISTINCT a AS field1 FROM t1, t2
WHERE EXISTS (SELECT c FROM t3 LEFT JOIN t2 ON b = d)
-HAVING field1 > 'aaa' LIMIT ROWS EXAMINED 20;
+HAVING field1 > 'aaa' LIMIT ROWS EXAMINED 15;
field1
+USA
+CAN
Warnings:
-Warning 1931 Query execution was interrupted. The query examined at least 21 rows, which exceeds LIMIT ROWS EXAMINED (20). The query result may be incomplete
+Warning 1931 Query execution was interrupted. The query examined at least 16 rows, which exceeds LIMIT ROWS EXAMINED (15). The query result may be incomplete
EXPLAIN
SELECT DISTINCT a FROM t1, t2 HAVING a > ' ' LIMIT ROWS EXAMINED 14;
id select_type table type possible_keys key key_len ref rows Extra
@@ -827,13 +829,15 @@ CREATE TABLE t2 ( b INT, c INT, KEY(c) );
INSERT INTO t2 VALUES
(5, 0),(3, 4),(6, 1),
(5, 8),(4, 9),(8, 1);
+set expensive_subquery_limit=5;
SELECT (SELECT MAX(c) FROM t1, t2)
FROM t2
WHERE c = (SELECT MAX(b) FROM t2)
LIMIT ROWS EXAMINED 3;
(SELECT MAX(c) FROM t1, t2)
Warnings:
-Warning 1931 Query execution was interrupted. The query examined at least 12 rows, which exceeds LIMIT ROWS EXAMINED (3). The query result may be incomplete
+Warning 1931 Query execution was interrupted. The query examined at least 5 rows, which exceeds LIMIT ROWS EXAMINED (3). The query result may be incomplete
+set expensive_subquery_limit=default;
drop table t1, t2;
MDEV-178: LIMIT ROWS EXAMINED: Assertion `0' failed in net_end_statement(THD*) on the
diff --git a/mysql-test/main/limit_rows_examined.test b/mysql-test/main/limit_rows_examined.test
index 512058e1eb6..e1e4269a2c6 100644
--- a/mysql-test/main/limit_rows_examined.test
+++ b/mysql-test/main/limit_rows_examined.test
@@ -445,11 +445,11 @@ INSERT INTO t3 VALUES ('USASpanish',8),('USATagalog',0),('USAVietnamese',0);
EXPLAIN
SELECT DISTINCT a AS field1 FROM t1, t2
WHERE EXISTS (SELECT c FROM t3 LEFT JOIN t2 ON b = d)
-HAVING field1 > 'aaa' LIMIT ROWS EXAMINED 20;
+HAVING field1 > 'aaa' LIMIT ROWS EXAMINED 15;
SELECT DISTINCT a AS field1 FROM t1, t2
WHERE EXISTS (SELECT c FROM t3 LEFT JOIN t2 ON b = d)
-HAVING field1 > 'aaa' LIMIT ROWS EXAMINED 20;
+HAVING field1 > 'aaa' LIMIT ROWS EXAMINED 15;
EXPLAIN
SELECT DISTINCT a FROM t1, t2 HAVING a > ' ' LIMIT ROWS EXAMINED 14;
@@ -550,11 +550,15 @@ INSERT INTO t2 VALUES
(5, 0),(3, 4),(6, 1),
(5, 8),(4, 9),(8, 1);
+set expensive_subquery_limit=5;
+
SELECT (SELECT MAX(c) FROM t1, t2)
FROM t2
WHERE c = (SELECT MAX(b) FROM t2)
LIMIT ROWS EXAMINED 3;
+set expensive_subquery_limit=default;
+
drop table t1, t2;
--echo
diff --git a/mysql-test/main/outfile.result b/mysql-test/main/outfile.result
index 4c439c37e4d..50ae13012a0 100644
--- a/mysql-test/main/outfile.result
+++ b/mysql-test/main/outfile.result
Binary files differ
diff --git a/mysql-test/main/outfile.test b/mysql-test/main/outfile.test
index 9f2fc22da99..e5294f03ba2 100644
--- a/mysql-test/main/outfile.test
+++ b/mysql-test/main/outfile.test
@@ -62,6 +62,7 @@ select load_file(concat(@tmpdir,"/outfile-test.4"));
#
CREATE TABLE t1 (a INT);
+--error ER_OPTION_PREVENTS_STATEMENT
EXPLAIN
SELECT *
INTO OUTFILE '/tmp/t1.txt'
diff --git a/mysql-test/main/subselect_mat.result b/mysql-test/main/subselect_mat.result
index a8cad01c674..e2f88006a9c 100644
--- a/mysql-test/main/subselect_mat.result
+++ b/mysql-test/main/subselect_mat.result
@@ -2872,7 +2872,7 @@ EXPLAIN
SELECT (SELECT f3a FROM t3) NOT IN (SELECT f1a FROM t1);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used
-3 SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where
+3 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 2 Using where
2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table
SELECT (SELECT f3a FROM t3) NOT IN (SELECT f1a FROM t1);
(SELECT f3a FROM t3) NOT IN (SELECT f1a FROM t1)
diff --git a/mysql-test/main/type_year.result b/mysql-test/main/type_year.result
index aaee5049c63..b99a566bf54 100644
--- a/mysql-test/main/type_year.result
+++ b/mysql-test/main/type_year.result
@@ -398,7 +398,6 @@ a
00
select a from t1 where a=y2k();
a
-00
select a from t1 where a=b;
a
drop table t1;
diff --git a/mysql-test/suite/gcol/r/gcol_bugfixes.result b/mysql-test/suite/gcol/r/gcol_bugfixes.result
index 7b70f61df03..c2583ba30d2 100644
--- a/mysql-test/suite/gcol/r/gcol_bugfixes.result
+++ b/mysql-test/suite/gcol/r/gcol_bugfixes.result
@@ -372,7 +372,7 @@ KEY(c,b(1)));
INSERT INTO v (a,c) VALUES (1,1);
EXPLAIN SELECT 1 FROM t WHERE ( SELECT 1 FROM t ) >=ANY( SELECT c FROM v );
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
3 SUBQUERY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table
SELECT 1 FROM t WHERE ( SELECT 1 FROM t ) >=ANY( SELECT c FROM v );
diff --git a/mysql-test/suite/plugins/r/audit_null.result b/mysql-test/suite/plugins/r/audit_null.result
index ada85b661ee..b68be0e779e 100644
--- a/mysql-test/suite/plugins/r/audit_null.result
+++ b/mysql-test/suite/plugins/r/audit_null.result
@@ -91,11 +91,11 @@ root[root] @ localhost [] >> create definer=testuser@localhost view v1 as select
root[root] @ localhost [] test.t2 : read
root[root] @ localhost [] test.t2 : read
root[root] @ localhost [] >> select * from v1
-root[root] @ localhost [] test.t2 : read
-root[root] @ localhost [] test.t2 : read
root[root] @ localhost [] mysql.table_stats : read
root[root] @ localhost [] mysql.column_stats : read
root[root] @ localhost [] mysql.index_stats : read
+root[root] @ localhost [] test.t2 : read
+root[root] @ localhost [] test.t2 : read
root[root] @ localhost [] >> drop view v1
root[root] @ localhost [] >> create temporary table t2 (a date)
root[root] @ localhost [] >> insert t2 values ('2020-10-09')
diff --git a/sql/field.cc b/sql/field.cc
index 5a618a5a2a9..bff8e90a9b9 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1387,7 +1387,9 @@ bool Field::can_optimize_range(const Item_bool_func *cond,
{
DBUG_ASSERT(cmp_type() != TIME_RESULT); // Handled in Field_temporal
DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_str descendants
- return item->cmp_type() != TIME_RESULT;
+ return (item->cmp_type() != TIME_RESULT) &&
+ !(item->type() == Item::SUBSELECT_ITEM &&
+ ((Item_subselect *)item)->is_expensive());
}
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index fcdb2aaf2d4..c7295f1b684 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -5794,6 +5794,8 @@ bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str,
bool escape_used_in_parsing, CHARSET_INFO *cmp_cs,
int *escape)
{
+ if (thd->lex->context_analysis_only)
+ return false;
/*
ESCAPE clause accepts only constant arguments and Item_param.
@@ -5803,9 +5805,13 @@ bool fix_escape_item(THD *thd, Item *escape_item, String *tmp_str,
reach val_int(), so we won't need the value.
CONTEXT_ANALYSIS_ONLY_DERIVED being a notable exception here.
*/
+#if 0
if (!escape_item->const_during_execution() ||
(!escape_item->const_item() &&
!(thd->lex->context_analysis_only & ~CONTEXT_ANALYSIS_ONLY_DERIVED)))
+#else
+ if (!escape_item->const_item())
+#endif
{
my_error(ER_WRONG_ARGUMENTS,MYF(0),"ESCAPE");
return TRUE;
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 9e6c205ca76..ff366adf086 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -1019,7 +1019,11 @@ table_map Item_subselect::used_tables() const
bool Item_subselect::const_item() const
{
DBUG_ASSERT(thd);
- return (thd->lex->context_analysis_only || with_recursive_reference ?
+ if (unit->executed_at_prepare_phase() && !thd->lex->context_analysis_only)
+ return true;
+ return (!(thd->lex->m_sql_cmd &&
+ thd->lex->m_sql_cmd->is_prepared()) ||
+ with_recursive_reference ?
FALSE :
forced_const || const_item_cache);
}
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index b284189db23..a22ed366992 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -4214,6 +4214,8 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
for (; tables_used; tables_used= tables_used->next_global)
{
+ if (tables_used->is_view_or_derived())
+ continue;
TABLE *table;
handler *handler;
if (!(table= tables_used->table))
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index c6a929d6fea..08dc37585a2 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -643,6 +643,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
table_map_for_update(0),
m_examined_row_count(0),
accessed_rows_and_keys(0),
+ accessed_rows_and_keys_at_exec_start(0),
m_digest(NULL),
m_statement_psi(NULL),
m_transaction_psi(NULL),
diff --git a/sql/sql_class.h b/sql/sql_class.h
index b54f35a4539..e0c3256eb05 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3420,6 +3420,7 @@ public:
changed or written.
*/
ulonglong accessed_rows_and_keys;
+ ulonglong accessed_rows_and_keys_at_exec_start;
/**
Check if the number of rows accessed by a statement exceeded
@@ -3427,7 +3428,8 @@ public:
*/
inline void check_limit_rows_examined()
{
- if (++accessed_rows_and_keys > lex->limit_rows_examined_cnt)
+ if ((++accessed_rows_and_keys - accessed_rows_and_keys_at_exec_start) >
+ lex->limit_rows_examined_cnt)
set_killed(ABORT_QUERY);
}
diff --git a/sql/sql_delete.h b/sql/sql_delete.h
index ad018ed686c..50d37cecba3 100644
--- a/sql/sql_delete.h
+++ b/sql/sql_delete.h
@@ -84,7 +84,7 @@ protected:
private:
/**
- @biefSpecial handling of single-table deletes after prepare phase
+ @brief Special handling of single-table deletes after prepare phase
*/
bool delete_from_single_table(THD *thd);
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 6e042d25805..5228cff3f98 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -5331,3 +5331,130 @@ void select_create::abort_result_set()
DBUG_VOID_RETURN;
}
+
+
+bool Sql_cmd_insert_base::precheck(THD *thd)
+{
+#if 0
+ /*
+ Since INSERT DELAYED doesn't support temporary tables, we could
+ not pre-open temporary tables for SQLCOM_INSERT / SQLCOM_REPLACE.
+ Open them here instead.
+ */
+ if (first_table->lock_type != TL_WRITE_DELAYED &&
+ thd->open_temporary_tables(lex->query_tables))
+ return true;
+
+ if (insert_precheck(thd, lex->query_tables))
+ return true;
+
+ WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_INSERT_REPLACE);
+
+ return false;
+
+#ifdef WITH_WSREP
+wsrep_error_label:
+#endif
+#endif
+ return true;
+}
+
+
+bool Sql_cmd_insert_base::prepare_inner(THD *thd)
+{
+#if 0
+ SELECT_LEX *const select_lex= thd->lex->first_select_lex();
+ TABLE_LIST *const table_list= select_lex->get_table_list();
+ Name_resolution_context *context= &select_lex->context;
+ Name_resolution_context_state ctx_state;
+ const bool select_insert= insert_many_values.elements == 0;
+ bool insert_into_view= (table_list->view != 0);
+ TABLE *table;
+
+ DBUG_ENTER("Sql_cmd_insert_base::prepare_inner");
+
+ (void) read_statistics_for_tables_if_needed(thd, table_list);
+
+ {
+ if (mysql_handle_derived(lex, DT_INIT))
+ DBUG_RETURN(TRUE);
+ if (mysql_handle_derived(lex, DT_MERGE_FOR_INSERT))
+ DBUG_RETURN(TRUE);
+ if (mysql_handle_derived(lex, DT_PREPARE))
+ DBUG_RETURN(TRUE);
+ }
+
+ insert_field_list= thd->lex->field_list;
+
+ if (duplicates == DUP_UPDATE)
+ {
+ /* it should be allocated before Item::fix_fields() */
+ if (table_list->set_insert_values(thd->mem_root))
+ DBUG_RETURN(TRUE);
+ }
+
+ table= table_list->table;
+
+ if (table->file->check_if_updates_are_ignored("INSERT"))
+ DBUG_RETURN(-1);
+
+ if (!table_list->single_table_updatable())
+ {
+ my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias.str, "INSERT");
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ first table in list is the one we'll INSERT into, requires INSERT_ACL.
+ all others require SELECT_ACL only. the ACL requirement below is for
+ new leaves only anyway (view-constituents), so check for SELECT rather
+ than INSERT.
+ */
+ if (setup_tables_and_check_access(thd,
+ &select_lex->context,
+ &select_lex->top_join_list,
+ table_list,
+ select_lex->leaf_tables,
+ select_insert, INSERT_ACL, SELECT_ACL,
+ TRUE))
+ DBUG_RETURN(TRUE);
+
+ if (insert_into_view && !insert_field_list.elements)
+ {
+ thd->lex->empty_field_list_on_rset= 1;
+ if (!table_list->table || table_list->is_multitable())
+ {
+ my_error(ER_VIEW_NO_INSERT_FIELD_LIST, MYF(0),
+ table_list->view_db.str, table_list->view_name.str);
+ DBUG_RETURN(TRUE);
+ }
+ if (insert_view_fields(thd, &insert_field_list, table_list))
+ DBUG_RETURN(TRUE);
+ }
+
+ if (thd->lex->has_returning())
+ {
+ status_var_increment(thd->status_var.feature_insert_returning);
+
+ /* This is INSERT ... RETURNING. It will return output to the client */
+ if (thd->lex->analyze_stmt)
+ {
+ /*
+ Actually, it is ANALYZE .. INSERT .. RETURNING. We need to produce
+ output and then discard it.
+ */
+ result= new (thd->mem_root) select_send_analyze(thd);
+ save_protocol= thd->protocol;
+ thd->protocol= new Protocol_discard(thd);
+ }
+ else
+ {
+ if (!(result= new (thd->mem_root) select_send(thd)))
+ DBUG_RETURN(TRUE);
+ }
+ }
+#else
+ return false;
+#endif
+}
+
diff --git a/sql/sql_insert.h b/sql/sql_insert.h
index 8b034c25877..eae3b5357bd 100644
--- a/sql/sql_insert.h
+++ b/sql/sql_insert.h
@@ -18,6 +18,7 @@
#include "sql_class.h" /* enum_duplicates */
#include "sql_list.h"
+#include "sql_base.h"
/* Instead of including sql_lex.h we add this typedef here */
typedef List<Item> List_item;
@@ -51,4 +52,139 @@ bool binlog_drop_table(THD *thd, TABLE *table);
inline void kill_delayed_threads(void) {}
#endif
+
+/**
+ Base class for all INSERT and REPLACE statements. Abstract class that
+ is inherited by Sql_cmd_insert_values and Sql_cmd_insert_select.
+*/
+
+class Sql_cmd_insert_base : public Sql_cmd_dml
+{
+protected:
+ virtual bool precheck(THD *thd) override;
+
+ virtual bool prepare_inner(THD *thd) override;
+
+private:
+ bool resolve_update_expressions(THD *thd);
+ bool prepare_values_table(THD *thd);
+ bool resolve_values_table_columns(THD *thd);
+
+ /**
+ Field list to insert/replace
+
+ One of two things:
+ 1. For the INSERT/REPLACE ... (col1, ... colN) VALUES ... syntax
+ this is a list of col1, ..., colN fields.
+ 2. For the INSERT/REPLACE ... SET col1=x1, ... colM=xM syntax extension
+ this is a list of col1, ... colM fields as well.
+ */
+ List<Item> insert_field_list;
+
+public:
+ /*
+ field_list was created for view and should be removed before PS/SP
+ rexecuton
+ */
+ bool empty_field_list_on_rset;
+
+ DML_prelocking_strategy *get_dml_prelocking_strategy()
+ {
+ return &dml_prelocking_strategy;
+ }
+
+protected:
+ const bool is_replace;
+
+ /**
+ Row data to insert/replace
+
+ One of two things:
+ 1. For the INSERT/REPLACE ... VALUES (row1), (row2), ... (rowN) syntax
+ the list contains N List_item lists: one List_item per row.
+ 2. For the INSERT/REPLACE ... SET col1=x1, ... colM=xM syntax extension
+ this list contains only 1 List_item of M data values: this way we
+ emulate this syntax:
+ INSERT/REPLACE ... (col1, ... colM) VALUE (x1, ..., xM);
+ */
+ List<List_item> insert_many_values;
+
+ /*
+ Number of values per row in insert_many_values, available after resolving
+ */
+ uint value_count;
+
+ /* ON DUPLICATE KEY UPDATE field list */
+ List<Item> update_field_list;
+
+ const enum_duplicates duplicates;
+
+ Protocol *save_protocol; /**< needed for ANALYZE .. INSERT .. RETURNING */
+
+ explicit Sql_cmd_insert_base(bool is_replace_arg,
+ enum_duplicates duplicates_arg)
+ : empty_field_list_on_rset(false),
+ is_replace(is_replace_arg),
+ value_count(0),
+ duplicates(duplicates_arg),
+ save_protocol(NULL)
+ {}
+
+#if 0
+ virtual void cleanup(THD *thd) override
+ {
+ if (empty_field_list_on_rset)
+ {
+ empty_field_list_on_rset = false;
+ insert_field_list.empty();
+ }
+ }
+#endif
+
+private:
+
+ /* The prelocking strategy used when opening the used tables */
+ DML_prelocking_strategy dml_prelocking_strategy;
+
+};
+
+
+/**
+ Class that implements INSERT ... VALUES and REPLACE ... VALUES statements.
+*/
+
+class Sql_cmd_insert_values final : public Sql_cmd_insert_base
+{
+public:
+ explicit Sql_cmd_insert_values(bool is_replace_arg,
+ enum_duplicates duplicates_arg)
+ : Sql_cmd_insert_base(is_replace_arg, duplicates_arg) {}
+
+ enum_sql_command sql_command_code() const
+ {
+ return is_replace ? SQLCOM_REPLACE : SQLCOM_INSERT;
+ }
+
+};
+
+
+/**
+ Class that implements INSERT ... SELECT and REPLACE ... SELECT statements.
+*/
+
+class Sql_cmd_insert_select final : public Sql_cmd_insert_base
+{
+public:
+ explicit Sql_cmd_insert_select(bool is_replace_arg,
+ enum_duplicates duplicates_arg)
+ : Sql_cmd_insert_base(is_replace_arg, duplicates_arg) {}
+
+ enum_sql_command sql_command_code() const
+ {
+ return is_replace ? SQLCOM_REPLACE_SELECT : SQLCOM_INSERT_SELECT;
+ }
+};
+
+
+
#endif /* SQL_INSERT_INCLUDED */
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 8a24d2febad..0b08fbc8112 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -42,6 +42,8 @@
#endif
#include "sql_update.h" // class Sql_cmd_update
#include "sql_delete.h" // class Sql_cmd_delete
+#include "sql_insert.h" // class Sql_cmd_insert
+
void LEX::parse_error(uint err_number)
{
@@ -10357,13 +10359,19 @@ SELECT_LEX *LEX::parsed_subselect(SELECT_LEX_UNIT *unit)
bool LEX::parsed_insert_select(SELECT_LEX *first_select)
{
+ bool is_insert_or_replace= false;
+ bool is_replace= false;
if (sql_command == SQLCOM_INSERT ||
sql_command == SQLCOM_REPLACE)
{
+ is_insert_or_replace= true;
if (sql_command == SQLCOM_INSERT)
sql_command= SQLCOM_INSERT_SELECT;
else
+ {
+ is_replace= true;
sql_command= SQLCOM_REPLACE_SELECT;
+ }
}
insert_select_hack(first_select);
if (check_main_unit_semantics())
@@ -10373,6 +10381,23 @@ bool LEX::parsed_insert_select(SELECT_LEX *first_select)
SELECT_LEX *blt __attribute__((unused))= pop_select();
DBUG_ASSERT(blt == &builtin_select);
push_select(first_select);
+
+ if (is_insert_or_replace)
+ {
+ if (sql_command == SQLCOM_INSERT || sql_command == SQLCOM_REPLACE)
+ {
+ if (!(m_sql_cmd= new (thd->mem_root) Sql_cmd_insert_values(is_replace,
+ duplicates)))
+ return true;
+ }
+ else
+ {
+ if (!(m_sql_cmd= new (thd->mem_root) Sql_cmd_insert_select(is_replace,
+ duplicates)))
+ return true;
+ }
+ }
+
return false;
}
@@ -10454,6 +10479,8 @@ bool LEX::select_finalize(st_select_lex_unit *expr)
{
sql_command= SQLCOM_SELECT;
selects_allow_procedure= TRUE;
+ if (!(m_sql_cmd= new (thd->mem_root) Sql_cmd_select(result)))
+ return true;
if (set_main_unit(expr))
return true;
return check_main_unit_semantics();
@@ -11933,3 +11960,27 @@ bool SELECT_LEX_UNIT::is_derived_eliminated() const
return true;
return derived->table->map & outer_select()->join->eliminated_tables;
}
+
+bool SELECT_LEX_UNIT::executed_at_prepare_phase()
+{
+ for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
+ {
+ if (!sl->executed_at_prepare_phase())
+ return false;
+ }
+ return true;
+}
+
+bool SELECT_LEX::executed_at_prepare_phase()
+{
+ if (table_list.elements || is_correlated)
+ return false;
+ for (st_select_lex_unit *unit= first_inner_unit();
+ unit;
+ unit= unit->next_unit())
+ {
+ if (!unit->executed_at_prepare_phase())
+ return false;
+ }
+ return true;
+}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 5a7fa14916f..491ea1cb2c2 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1034,6 +1034,8 @@ public:
bool explainable() const;
+ bool executed_at_prepare_phase();
+
void reset_distinct();
void fix_distinct();
@@ -1480,6 +1482,8 @@ public:
ORDER *order,
enum_query_type query_type);
void print_limit(THD *thd, String *str, enum_query_type query_type);
+ bool prepare(THD *thd, select_result *result);
+ bool exec(THD *thd);
void fix_prepare_information(THD *thd, Item **conds, Item **having_conds);
/*
Destroy the used execution plan (JOIN) of this subtree (this
@@ -1653,6 +1657,7 @@ public:
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);
+ bool executed_at_prepare_phase();
};
typedef class st_select_lex SELECT_LEX;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 67353a1f082..c7bc972a8fb 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -3907,7 +3907,9 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
case SQLCOM_SHOW_COLLATIONS:
case SQLCOM_SHOW_STORAGE_ENGINES:
case SQLCOM_SHOW_PROFILE:
+#if 0
case SQLCOM_SELECT:
+#endif
{
#ifdef WITH_WSREP
if (lex->sql_command == SQLCOM_SELECT)
@@ -4382,6 +4384,17 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
res = mysql_checksum_table(thd, first_table, &lex->check_opt);
break;
}
+ case SQLCOM_SELECT:
+ {
+ res = lex->m_sql_cmd->execute(thd);
+ break;
+ }
+#if 0
+ case SQLCOM_REPLACE:
+ case SQLCOM_INSERT:
+ case SQLCOM_REPLACE_SELECT:
+ case SQLCOM_INSERT_SELECT:
+#endif
case SQLCOM_UPDATE:
case SQLCOM_UPDATE_MULTI:
case SQLCOM_DELETE:
@@ -4394,6 +4407,7 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
thd->abort_on_warning= 0;
break;
}
+#if 1
case SQLCOM_REPLACE:
if ((res= generate_incident_event(thd)))
break;
@@ -4653,6 +4667,7 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
break;
}
+#endif
case SQLCOM_DROP_SEQUENCE:
case SQLCOM_DROP_TABLE:
{
@@ -7340,6 +7355,7 @@ void THD::reset_for_next_command(bool do_clear_error)
get_stmt_da()->reset_for_next_command();
m_sent_row_count= m_examined_row_count= 0;
accessed_rows_and_keys= 0;
+ accessed_rows_and_keys_at_exec_start= 0;
reset_slow_query_state();
@@ -7476,6 +7492,8 @@ void create_select_for_variable(THD *thd, LEX_CSTRING *var_name)
lex= thd->lex;
lex->init_select();
lex->sql_command= SQLCOM_SELECT;
+ lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_select(thd->lex->result);
+
/*
We set the name of Item to @@session.var_name because that then is used
as the column name in the output.
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 22780c894f8..0bbde42c06c 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2268,6 +2268,48 @@ static bool check_prepared_statement(Prepared_statement *stmt)
lex->m_sql_cmd->unprepare(thd);
break;
+ case SQLCOM_SELECT:
+#if 0
+ if (lex->m_sql_cmd == NULL &&
+ !(lex->m_sql_cmd=
+ new (thd->mem_root) Sql_cmd_select(thd->lex->result)))
+ {
+ res= 1;
+ break;
+ }
+#endif
+ lex->m_sql_cmd->set_owner(stmt);
+ res = lex->m_sql_cmd->prepare(thd);
+ if (res == 2)
+ {
+ /* Statement and field info has already been sent */
+ DBUG_RETURN(FALSE);
+ }
+ if (!res && !lex->describe && !lex->analyze_stmt && !stmt->is_sql_prepare())
+ {
+ SELECT_LEX_UNIT *const unit = &lex->unit;
+ /* Make copy of item list, as change_columns may change it */
+ SELECT_LEX_UNIT* master_unit= unit->first_select()->master_unit();
+ bool is_union_op=
+ master_unit->is_unit_op() || master_unit->fake_select_lex;
+
+ List<Item> fields(is_union_op ? unit->item_list :
+ lex->first_select_lex()->item_list);
+
+ /* Change columns if a procedure like analyse() */
+ res= (unit->last_procedure &&
+ unit->last_procedure->change_columns(thd, fields));
+
+ if (res || send_prep_stmt(stmt, lex->result->field_count(fields)) ||
+ lex->result->send_result_set_metadata(fields,
+ Protocol::SEND_EOF) ||
+ thd->protocol->flush())
+ res= true;
+ }
+ if (!res)
+ lex->m_sql_cmd->unprepare(thd);
+ break;
+
/* The following allow WHERE clause, so they must be tested like SELECT */
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_TABLES:
@@ -2285,7 +2327,9 @@ static bool check_prepared_statement(Prepared_statement *stmt)
case SQLCOM_SHOW_STATUS_FUNC:
case SQLCOM_SHOW_STATUS_PACKAGE:
case SQLCOM_SHOW_STATUS_PACKAGE_BODY:
+#if 0
case SQLCOM_SELECT:
+#endif
res= mysql_test_select(stmt, tables);
if (res == 2)
{
@@ -2469,12 +2513,14 @@ static bool check_prepared_statement(Prepared_statement *stmt)
lex->describe, lex->analyze_stmt) ||
send_prep_stmt(stmt, result.field_count(field_list)) ||
result.send_result_set_metadata(field_list,
- Protocol::SEND_EOF);
+ Protocol::SEND_EOF) ||
+ thd->protocol->flush();
+ }
+ else if (lex->sql_command != SQLCOM_SELECT)
+ {
+ res= send_prep_stmt(stmt, 0) ||
+ thd->protocol->flush();
}
- else
- res= send_prep_stmt(stmt, 0);
- if (!res)
- thd->protocol->flush();
}
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 4b01a96a132..da945d9f2f5 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -55,6 +55,9 @@
#include "sql_cte.h"
#include "sql_window.h"
#include "tztime.h"
+#ifdef WITH_WSREP
+#include "wsrep_mysqld.h"
+#endif
#include "debug_sync.h" // DEBUG_SYNC
#include <m_ctype.h>
@@ -4989,21 +4992,22 @@ void JOIN::cleanup_item_list(List<Item> &items) const
0 otherwise
*/
-select_handler *find_select_handler(THD *thd,
- SELECT_LEX* select_lex)
+select_handler *SELECT_LEX::find_select_handler(THD *thd)
{
- if (select_lex->next_select())
+ if (next_select())
return 0;
- if (select_lex->master_unit()->outer_select())
+ if (master_unit()->outer_select())
return 0;
TABLE_LIST *tbl= nullptr;
- // For SQLCOM_INSERT_SELECT the server takes TABLE_LIST
- // from thd->lex->query_tables and skips its first table
- // b/c it is the target table for the INSERT..SELECT.
+ /*
+ For SQLCOM_INSERT_SELECT the server takes TABLE_LIST
+ from thd->lex->query_tables and skips its first table
+ b/c it is the target table for the INSERT..SELECT.
+ */
if (thd->lex->sql_command != SQLCOM_INSERT_SELECT)
{
- tbl= select_lex->join->tables_list;
+ tbl= join->tables_list;
}
else if (thd->lex->query_tables &&
thd->lex->query_tables->next_global)
@@ -5020,7 +5024,7 @@ select_handler *find_select_handler(THD *thd,
handlerton *ht= tbl->table->file->partition_ht();
if (!ht->create_select)
continue;
- select_handler *sh= ht->create_select(thd, select_lex);
+ select_handler *sh= ht->create_select(thd, this);
return sh;
}
return 0;
@@ -5136,7 +5140,7 @@ mysql_select(THD *thd, TABLE_LIST *tables, List<Item> &fields, COND *conds,
thd->get_stmt_da()->reset_current_row_for_warning(1);
/* Look for a table owned by an engine with the select_handler interface */
- select_lex->pushdown_select= find_select_handler(thd, select_lex);
+ select_lex->pushdown_select= select_lex->find_select_handler(thd);
if ((err= join->optimize()))
{
@@ -12720,7 +12724,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
(select thats very heavy) => is a constant here
eg: (select avg(order_cost) from orders) => constant but expensive
*/
- if (!keyuse->val->used_tables() && !thd->lex->describe)
+ if (keyuse->val->const_item() && !keyuse->val->is_expensive() &&
+ !thd->lex->describe)
{ // Compare against constant
store_key_item tmp(thd,
keyinfo->key_part[i].field,
@@ -32348,6 +32353,9 @@ static void MYSQL_DML_START(THD *thd)
{
switch (thd->lex->sql_command) {
+ case SQLCOM_SELECT:
+ MYSQL_INSERT_START(thd->query());
+ break;
case SQLCOM_UPDATE:
MYSQL_UPDATE_START(thd->query());
break;
@@ -32370,6 +32378,9 @@ static void MYSQL_DML_DONE(THD *thd, int rc)
{
switch (thd->lex->sql_command) {
+ case SQLCOM_SELECT:
+ MYSQL_SELECT_DONE(rc, (rc ? 0 : (ulong) (thd->limit_found_rows)));
+ break;
case SQLCOM_UPDATE:
MYSQL_UPDATE_DONE(
rc,
@@ -32437,8 +32448,6 @@ bool Sql_cmd_dml::prepare(THD *thd)
MYSQL_DML_START(thd);
- lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
-
if (open_tables_for_query(thd, lex->query_tables, &table_count, 0,
get_dml_prelocking_strategy()))
{
@@ -32451,8 +32460,6 @@ bool Sql_cmd_dml::prepare(THD *thd)
if (prepare_inner(thd))
goto err;
- lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED;
-
set_prepared();
unit->set_prepared();
@@ -32528,6 +32535,7 @@ bool Sql_cmd_dml::execute(THD *thd)
{
if (lock_tables(thd, lex->query_tables, table_count, 0))
goto err;
+ query_cache_store_query(thd, thd->lex->query_tables);
}
unit->set_limit(select_lex);
@@ -32581,8 +32589,266 @@ err:
bool Sql_cmd_dml::execute_inner(THD *thd)
{
SELECT_LEX_UNIT *unit = &lex->unit;
- SELECT_LEX *select_lex= unit->first_select();
- JOIN *join= select_lex->join;
+ DBUG_ENTER("Sql_cmd_dml::execute_inner");
+
+ if (unit->is_unit_op() || unit->fake_select_lex)
+ {
+ if (unit->exec())
+ DBUG_RETURN(true);
+ }
+#if 1
+ else
+ {
+ SELECT_LEX *select_lex= unit->first_select();
+ if (select_lex->exec(thd))
+ DBUG_RETURN(true);
+ }
+#endif
+
+ DBUG_RETURN(false);
+}
+
+
+bool Sql_cmd_select::precheck(THD *thd)
+{
+ bool rc= false;
+
+ privilege_t privileges_requested= SELECT_ACL;
+
+ if (lex->exchange)
+ {
+ /*
+ lex->exchange != NULL implies SELECT .. INTO OUTFILE and this
+ requires FILE_ACL access.
+ */
+ privileges_requested|= FILE_ACL;
+ }
+
+ TABLE_LIST *tables = thd->lex->query_tables;
+
+ if (tables)
+ rc= check_table_access(thd, privileges_requested,
+ tables, false, UINT_MAX, false);
+ else
+ rc= check_access(thd, privileges_requested,
+ any_db.str, NULL, NULL, false, false);
+
+#ifdef WITH_WSREP
+ if (lex->sql_command == SQLCOM_SELECT)
+ {
+ WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_READ);
+ }
+ else
+ {
+ WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW);
+# ifdef ENABLED_PROFILING
+ if (lex->sql_command == SQLCOM_SHOW_PROFILE)
+ thd->profiling.discard_current_query();
+# endif
+ }
+#endif /* WITH_WSREP */
+
+ if (!rc)
+ return false;
+
+#ifdef WITH_WSREP
+wsrep_error_label:
+#endif
+ return true;
+}
+
+
+
+bool Sql_cmd_select::prepare_inner(THD *thd)
+{
+ bool rc= false;
+ LEX *lex= thd->lex;
+ TABLE_LIST *tables= lex->query_tables;
+ SELECT_LEX_UNIT *const unit = &lex->unit;
+
+ DBUG_ENTER("Sql_cmd_select::prepare_inner");
+
+ if (!thd->stmt_arena->is_stmt_prepare())
+ (void) read_statistics_for_tables_if_needed(thd, tables);
+
+ {
+ if (mysql_handle_derived(lex, DT_INIT))
+ DBUG_RETURN(TRUE);
+ }
+
+ if (thd->stmt_arena->is_stmt_prepare())
+ {
+ if (!result)
+ {
+ Query_arena backup;
+ Query_arena *arena= thd->activate_stmt_arena_if_needed(&backup);
+ result= new (thd->mem_root) select_send(thd);
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ thd->lex->result= result;
+ }
+ rc= unit->prepare(unit->derived, 0, 0);
+
+ }
+ else
+ {
+ if (lex->analyze_stmt)
+ {
+ if (result && result->result_interceptor())
+ result->result_interceptor()->disable_my_ok_calls();
+ else
+ {
+ DBUG_ASSERT(thd->protocol);
+ result= new (thd->mem_root) select_send_analyze(thd);
+ save_protocol= thd->protocol;
+ thd->protocol= new Protocol_discard(thd);
+ }
+ }
+ else if (!(result= lex->result))
+ result= new (thd->mem_root) select_send(thd);
+ if (!result)
+ DBUG_RETURN(TRUE);
+
+ SELECT_LEX *parameters = unit->global_parameters();
+ if (!parameters->limit_params.explicit_limit)
+ {
+ parameters->limit_params.select_limit =
+ new (thd->mem_root) Item_int(thd,
+ (ulonglong) thd->variables.select_limit);
+ if (parameters->limit_params.select_limit == NULL)
+ DBUG_RETURN(true); /* purecov: inspected */
+ }
+ ulonglong select_options= 0;
+ if (lex->describe)
+ select_options|= SELECT_DESCRIBE;
+ if (unit->is_unit_op() || unit->fake_select_lex)
+ select_options|= SELECT_NO_UNLOCK;
+ rc= unit->prepare(unit->derived, result, select_options);
+
+ if (rc && thd->lex->analyze_stmt && save_protocol)
+ {
+ delete thd->protocol;
+ thd->protocol= save_protocol;
+ }
+ }
+
+ DBUG_RETURN(rc);
+}
+
+
+bool Sql_cmd_select::execute_inner(THD *thd)
+{
+ DBUG_ENTER("Sql_cmd_select::execute_inner");
+
+ thd->status_var.last_query_cost= 0.0;
+
+ bool res= Sql_cmd_dml::execute_inner(thd);
+
+ res|= thd->is_error();
+ if (unlikely(res))
+ result->abort_result_set();
+
+ if (result != thd->lex->result)
+ {
+ delete result;
+ result= 0;
+ }
+
+ if (lex->analyze_stmt)
+ {
+ if (save_protocol)
+ {
+ delete thd->protocol;
+ thd->protocol= save_protocol;
+ }
+ if (!res)
+ res= thd->lex->explain->send_explain(thd);
+ }
+
+ if (thd->lex->describe)
+ {
+ if (!res)
+ res= thd->lex->explain->send_explain(thd);
+
+ if (!res && (thd->lex->describe & DESCRIBE_EXTENDED))
+ {
+ char buff[1024];
+ String str(buff,(uint32) sizeof(buff), system_charset_info);
+ str.length(0);
+ /*
+ The warnings system requires input in utf8, @see
+ mysqld_show_warnings().
+ */
+ lex->unit.print(&str, QT_EXPLAIN_EXTENDED);
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_YES, str.c_ptr_safe());
+ }
+ }
+
+ if (unlikely(thd->killed == ABORT_QUERY && !thd->no_errors))
+ {
+ /*
+ If LIMIT ROWS EXAMINED interrupted query execution, issue a warning,
+ continue with normal processing and produce an incomplete query result.
+ */
+ bool saved_abort_on_warning= thd->abort_on_warning;
+ thd->abort_on_warning= false;
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT,
+ ER_THD(thd, ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT),
+ thd->accessed_rows_and_keys -
+ thd->accessed_rows_and_keys_at_exec_start,
+ thd->lex->limit_rows_examined->val_uint());
+ thd->abort_on_warning= saved_abort_on_warning;
+ thd->reset_killed();
+ }
+ /* Disable LIMIT ROWS EXAMINED after query execution. */
+ thd->lex->limit_rows_examined_cnt= ULONGLONG_MAX;
+
+ /* Count number of empty select queries */
+ if (!thd->get_sent_row_count() && !res)
+ status_var_increment(thd->status_var.empty_queries);
+ else
+ status_var_add(thd->status_var.rows_sent, thd->get_sent_row_count());
+
+ DBUG_RETURN(res);
+}
+
+
+bool st_select_lex::prepare(THD *thd, select_result *result)
+{
+ ulonglong select_options= options | thd->variables.option_bits;
+
+ DBUG_ENTER("st_select_lex::prepare");
+
+ if (thd->lex->describe)
+ select_options|= SELECT_DESCRIBE;
+
+ if (!(join= new (thd->mem_root) JOIN(thd, item_list,
+ select_options, result)))
+ DBUG_RETURN(true);
+
+ SELECT_LEX_UNIT *unit= master_unit();
+
+ thd->lex->used_tables=0;
+
+ if (join->prepare(table_list.first, where,
+ order_list.elements + group_list.elements,
+ order_list.first, false, group_list.first,
+ having, thd->lex->proc_list.first,
+ this, unit))
+ DBUG_RETURN(true);
+
+ DBUG_RETURN(false);
+}
+
+
+bool st_select_lex::exec(THD *thd)
+{
+ DBUG_ENTER("st_select_lex::exec");
+
+ /* Look for a table owned by an engine with the select_handler interface */
+ pushdown_select= find_select_handler(thd);
if (join->optimize())
goto err;
@@ -32596,19 +32862,29 @@ bool Sql_cmd_dml::execute_inner(THD *thd)
if (unlikely(thd->is_error()))
goto err;
+ thd->get_stmt_da()->reset_current_row_for_warning(1);
+
+ if (master_unit()->outer_select() == NULL)
+ thd->accessed_rows_and_keys_at_exec_start= thd->accessed_rows_and_keys;
+
if (join->exec())
goto err;
if (thd->lex->describe & DESCRIBE_EXTENDED)
{
- select_lex->where= join->conds_history;
- select_lex->having= join->having_history;
+ where= join->conds_history;
+ having= join->having_history;
}
err:
- return join->error;
-}
+ if (pushdown_select)
+ {
+ delete pushdown_select;
+ pushdown_select= NULL;
+ }
+ DBUG_RETURN(join->error);
+}
/**
@} (end of group Query_Optimizer)
diff --git a/sql/sql_select.h b/sql/sql_select.h
index f908484444b..edbaed30239 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -33,6 +33,8 @@
#include "records.h" /* READ_RECORD */
#include "opt_range.h" /* SQL_SELECT, QUICK_SELECT_I */
#include "filesort.h"
+#include "sql_base.h"
+#include "sql_cmd.h"
typedef struct st_join_table JOIN_TAB;
/* Values in optimize */
@@ -2629,4 +2631,39 @@ void propagate_new_equalities(THD *thd, Item *cond,
bool *is_simplifiable_cond);
bool dbug_user_var_equals_str(THD *thd, const char *name, const char *value);
+
+
+class Sql_cmd_select : public Sql_cmd_dml
+{
+public:
+ explicit Sql_cmd_select(select_result *result_arg)
+ : Sql_cmd_dml(), save_protocol(NULL)
+ { result= result_arg; }
+
+ enum_sql_command sql_command_code() const override
+ {
+ return SQLCOM_SELECT;
+ }
+
+ DML_prelocking_strategy *get_dml_prelocking_strategy()
+ {
+ return &dml_prelocking_strategy;
+ }
+
+protected:
+
+ bool precheck(THD *thd) override;
+
+ bool prepare_inner(THD *thd) override;
+
+ bool execute_inner(THD *thd) override;
+
+private:
+ /* The prelocking strategy used when opening the used tables */
+ DML_prelocking_strategy dml_prelocking_strategy;
+
+ Protocol *save_protocol;
+};
+
+
#endif /* SQL_SELECT_INCLUDED */
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index c1f28baeb25..0cc1e68d779 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -1322,6 +1322,24 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
describe= additional_options & SELECT_DESCRIBE;
+ if (describe)
+ {
+ for (SELECT_LEX *sl= first_sl; sl; sl= sl->next_select())
+ {
+ sl->set_explain_type(FALSE);
+ sl->options|= SELECT_DESCRIBE;
+ }
+ if (is_unit_op() || fake_select_lex)
+ {
+ if (union_needs_tmp_table() && fake_select_lex)
+ {
+ fake_select_lex->select_number= FAKE_SELECT_LEX_ID; // just for initialization
+ fake_select_lex->type= unit_operation_text[common_op()];
+ fake_select_lex->options|= SELECT_DESCRIBE;
+ }
+ }
+ }
+
/*
Save fake_select_lex in case we don't need it for anything but
global parameters.
@@ -2160,6 +2178,8 @@ bool st_select_lex_unit::exec()
bool was_executed= executed;
DBUG_ENTER("st_select_lex_unit::exec");
+ describe= thd->lex->describe;
+
if (executed && !uncacheable && !describe)
DBUG_RETURN(FALSE);
executed= 1;
@@ -2168,6 +2188,9 @@ bool st_select_lex_unit::exec()
item->make_const();
saved_error= optimize();
+
+ if (outer_select() == NULL)
+ thd->accessed_rows_and_keys_at_exec_start= thd->accessed_rows_and_keys;
create_explain_query_if_not_exists(thd->lex, thd->mem_root);
@@ -2239,6 +2262,7 @@ bool st_select_lex_unit::exec()
saved_error= sl->join->optimize();
}
}
+
if (likely(!saved_error))
{
records_at_start= table->file->stats.records;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 49655f8f0ce..886c0b68342 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -71,6 +71,7 @@
#include "json_table.h"
#include "sql_update.h"
#include "sql_delete.h"
+#include "sql_insert.h"
/* this is to get the bison compilation windows warnings out */
#ifdef _MSC_VER