[Commits] 5d2a36f: MDEV-29971 Re-design the upper level of handling SELECT statements
revision-id: 5d2a36f33b2d89c680804a4c088c25bf3e56321f (mariadb-11.0.1-36-g5d2a36f) parent(s): fc18f9c9ec15035894154fb7dcdd85caac73cfc2 author: Igor Babaev committer: Igor Babaev timestamp: 2023-03-16 16:20:05 -0700 message: MDEV-29971 Re-design the upper level of handling SELECT statements The initial patch. All tests from the main test suite passed. --- mysql-test/main/explain.result | 16 +- mysql-test/main/explain.test | 2 +- mysql-test/main/func_like.result | 8 - mysql-test/main/func_like.test | 12 +- mysql-test/main/grant_cache_no_prot.result | 4 +- mysql-test/main/limit_rows_examined.result | 14 +- mysql-test/main/limit_rows_examined.test | 8 +- mysql-test/main/outfile.result | Bin 2323 -> 2309 bytes mysql-test/main/outfile.test | 1 + mysql-test/main/subselect_mat.result | 2 +- mysql-test/main/type_year.result | 1 - sql/field.cc | 4 +- sql/item_cmpfunc.cc | 6 + sql/item_subselect.cc | 6 +- sql/sql_cache.cc | 2 + sql/sql_class.cc | 1 + sql/sql_class.h | 4 +- sql/sql_delete.h | 2 +- sql/sql_insert.cc | 127 ++++++++++++ sql/sql_insert.h | 136 ++++++++++++ sql/sql_lex.cc | 51 +++++ sql/sql_lex.h | 5 + sql/sql_parse.cc | 18 ++ sql/sql_prepare.cc | 56 ++++- sql/sql_select.cc | 318 +++++++++++++++++++++++++++-- sql/sql_select.h | 37 ++++ sql/sql_union.cc | 24 +++ sql/sql_yacc.yy | 1 + 28 files changed, 809 insertions(+), 57 deletions(-) diff --git a/mysql-test/main/explain.result b/mysql-test/main/explain.result index 1e546d4..7469fdb 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 0e4a3b8..8abfee4 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 ba053ea..8031b03 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 7339743..f9d92a7 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 daf382d..0fde04a 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 9d3d5bb..fb91784 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 512058e..e1e4269 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 4c439c3..50ae130 100644 Binary files a/mysql-test/main/outfile.result and b/mysql-test/main/outfile.result differ diff --git a/mysql-test/main/outfile.test b/mysql-test/main/outfile.test index 9f2fc22..e5294f0 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 a8cad01..e2f8800 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 aaee504..b99a566 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/sql/field.cc b/sql/field.cc index 5a618a5..bff8e90 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 fcdb2aa..c7295f1 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 9e6c205..ff366ad 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 b284189..a22ed36 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 c6a929d..08dc375 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 b54f35a..e0c3256 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3420,6 +3420,7 @@ class THD: public THD_count, /* this must be first */ 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 @@ class THD: public THD_count, /* this must be first */ */ 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 ad018ed..50d37ce 100644 --- a/sql/sql_delete.h +++ b/sql/sql_delete.h @@ -84,7 +84,7 @@ class Sql_cmd_delete final : public Sql_cmd_dml 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 6e042d2..5228cff 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 8b034c2..eae3b53 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 8a24d2f..0b08fbc 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 5a7fa14..491ea1c 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1034,6 +1034,8 @@ class st_select_lex_unit: public st_select_lex_node { bool explainable() const; + bool executed_at_prepare_phase(); + void reset_distinct(); void fix_distinct(); @@ -1480,6 +1482,8 @@ class st_select_lex: public st_select_lex_node 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 @@ class st_select_lex: public st_select_lex_node 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 67353a1..c7bc972 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 22780c8..0bbde42 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 4b01a96..da945d9 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 @@ bool Sql_cmd_dml::execute(THD *thd) 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 f908484..edbaed3 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 c1f28ba..0cc1e68 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 49655f8..886c0b6 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
participants (1)
-
IgorBabaev