[Commits] eebe2090c84: Merge 10.3 -> 10.4
revision-id: eebe2090c848b5cedc5235473d80dbd2c25d2943 (mariadb-10.4.20-21-geebe2090c84) parent(s): a1e2ca057dda4dc434f057ce9391aa7afd9b5583 4a6e2d343745c11086c05f0041a8267591bb073c author: Sergei Petrunia committer: Sergei Petrunia timestamp: 2021-06-30 18:41:46 +0300 message: Merge 10.3 -> 10.4 .gitignore | 3 + cmake/cpack_rpm.cmake | 6 +- mysql-test/main/cte_nonrecursive.result | 55 ++++++++ mysql-test/main/cte_nonrecursive.test | 29 ++++ mysql-test/main/derived_cond_pushdown.result | 147 +++++++++++++++++++++ mysql-test/main/derived_cond_pushdown.test | 70 ++++++++++ mysql-test/main/gis-json.result | 10 ++ mysql-test/main/gis-json.test | 7 + mysql-test/main/information_schema.result | 2 + .../main/information_schema_all_engines.result | 12 +- mysql-test/suite/funcs_1/r/is_columns_is.result | 4 + .../suite/funcs_1/r/is_columns_is_embedded.result | 4 + mysql-test/suite/funcs_1/r/is_tables_is.result | 92 +++++++++++++ .../suite/funcs_1/r/is_tables_is_embedded.result | 92 +++++++++++++ .../galera/r/galera_sst_rsync_logbasename.result | 1 + mysql-test/suite/galera/r/lp1376747-4.result | 2 +- mysql-test/suite/galera/t/lp1376747-4.test | 2 +- sql/handler.h | 2 + sql/item_create.cc | 5 +- sql/lex.h | 7 +- sql/spatial.cc | 6 + sql/sql_derived.cc | 63 ++++++++- sql/sql_derived.h | 5 + sql/sql_lex.cc | 13 +- sql/sql_show.cc | 83 ++++++++++++ storage/innobase/os/os0file.cc | 38 +++++- 26 files changed, 741 insertions(+), 19 deletions(-) diff --cc .gitignore index c10f08b20d7,c26767717c6..b3cbd1d430d --- a/.gitignore +++ b/.gitignore @@@ -554,73 -521,4 +554,76 @@@ compile_commands.jso # Visual Studio Code workspace .vscode/ +# Clion && other JetBrains ides +.idea + + .cache/clangd ++ ++ +client/mariadb +client/mariadb-admin +client/mariadb-binlog +client/mariadb-check +client/mariadb-dump +client/mariadb-import +client/mariadb-plugin +client/mariadb-show +client/mariadb-slap +client/mariadb-test +client/mariadb-upgrade +extra/mariabackup/mariadb-backup +extra/mariadb-waitpid +extra/mariadbd-safe-helper +libmysqld/examples/mariadb-client-test-embedded +libmysqld/examples/mariadb-embedded +libmysqld/examples/mariadb-test-embedded +man/mariadb-access.1 +man/mariadb-admin.1 +man/mariadb-backup.1 +man/mariadb-binlog.1 +man/mariadb-check.1 +man/mariadb-client-test-embedded.1 +man/mariadb-client-test.1 +man/mariadb-convert-table-format.1 +man/mariadb-dump.1 +man/mariadb-dumpslow.1 +man/mariadb-embedded.1 +man/mariadb-find-rows.1 +man/mariadb-fix-extensions.1 +man/mariadb-hotcopy.1 +man/mariadb-import.1 +man/mariadb-install-db.1 +man/mariadb-ldb.1 +man/mariadb-plugin.1 +man/mariadb-secure-installation.1 +man/mariadb-setpermission.1 +man/mariadb-show.1 +man/mariadb-slap.1 +man/mariadb-test-embedded.1 +man/mariadb-test.1 +man/mariadb-tzinfo-to-sql.1 +man/mariadb-upgrade.1 +man/mariadb-waitpid.1 +man/mariadb.1 +man/mariadbd-multi.1 +man/mariadbd-safe-helper.1 +man/mariadbd-safe.1 +man/mariadbd.8 +scripts/mariadb-access +scripts/mariadb-convert-table-format +scripts/mariadb-dumpslow +scripts/mariadb-find-rows +scripts/mariadb-fix-extensions +scripts/mariadb-hotcopy +scripts/mariadb-install-db +scripts/mariadb-secure-installation +scripts/mariadb-setpermission +scripts/mariadbd-multi +scripts/mariadbd-safe +sql/mariadb-tzinfo-to-sql +sql/mariadbd +storage/rocksdb/mariadb-ldb +tests/mariadb-client-test +versioninfo_dll.rc +versioninfo_exe.rc +win/packaging/ca/symlinks.cc diff --cc mysql-test/main/information_schema.result index 3534c4bd337,0559c42350d..9ba19cca7e1 --- a/mysql-test/main/information_schema.result +++ b/mysql-test/main/information_schema.result @@@ -65,9 -65,9 +65,10 @@@ GEOMETRY_COLUMN GLOBAL_STATUS GLOBAL_VARIABLES INDEX_STATISTICS + KEYWORDS KEY_CACHES KEY_COLUMN_USAGE +OPTIMIZER_TRACE PARAMETERS PARTITIONS PLUGINS diff --cc mysql-test/main/information_schema_all_engines.result index 9ba4d20c76d,41d6ab3b2f4..1269972d127 --- a/mysql-test/main/information_schema_all_engines.result +++ b/mysql-test/main/information_schema_all_engines.result @@@ -41,9 -41,9 +41,10 @@@ INNODB_SYS_VIRTUA INNODB_TABLESPACES_ENCRYPTION INNODB_TABLESPACES_SCRUBBING INNODB_TRX + KEYWORDS KEY_CACHES KEY_COLUMN_USAGE +OPTIMIZER_TRACE PARAMETERS PARTITIONS PLUGINS @@@ -122,9 -123,9 +124,10 @@@ INNODB_SYS_VIRTUAL TABLE_I INNODB_TABLESPACES_ENCRYPTION SPACE INNODB_TABLESPACES_SCRUBBING SPACE INNODB_TRX trx_id + KEYWORDS WORD KEY_CACHES KEY_CACHE_NAME KEY_COLUMN_USAGE CONSTRAINT_SCHEMA +OPTIMIZER_TRACE QUERY PARAMETERS SPECIFIC_SCHEMA PARTITIONS TABLE_SCHEMA PLUGINS PLUGIN_NAME @@@ -203,9 -205,9 +207,10 @@@ INNODB_SYS_VIRTUAL TABLE_I INNODB_TABLESPACES_ENCRYPTION SPACE INNODB_TABLESPACES_SCRUBBING SPACE INNODB_TRX trx_id + KEYWORDS WORD KEY_CACHES KEY_CACHE_NAME KEY_COLUMN_USAGE CONSTRAINT_SCHEMA +OPTIMIZER_TRACE QUERY PARAMETERS SPECIFIC_SCHEMA PARTITIONS TABLE_SCHEMA PLUGINS PLUGIN_NAME @@@ -360,9 -362,9 +366,10 @@@ Database: information_schem | INNODB_TABLESPACES_ENCRYPTION | | INNODB_TABLESPACES_SCRUBBING | | INNODB_TRX | + | KEYWORDS | | KEY_CACHES | | KEY_COLUMN_USAGE | +| OPTIMIZER_TRACE | | PARAMETERS | | PARTITIONS | | PLUGINS | @@@ -431,9 -434,9 +439,10 @@@ Database: INFORMATION_SCHEM | INNODB_TABLESPACES_ENCRYPTION | | INNODB_TABLESPACES_SCRUBBING | | INNODB_TRX | + | KEYWORDS | | KEY_CACHES | | KEY_COLUMN_USAGE | +| OPTIMIZER_TRACE | | PARAMETERS | | PARTITIONS | | PLUGINS | @@@ -465,5 -469,5 +475,5 @@@ Wildcard: inf_rmation_schem | information_schema | SELECT table_schema, count(*) FROM information_schema.TABLES WHERE table_schema IN ('mysql', 'INFORMATION_SCHEMA', 'test', 'mysqltest') GROUP BY TABLE_SCHEMA; table_schema count(*) - information_schema 66 -information_schema 67 ++information_schema 68 mysql 31 diff --cc sql/sql_derived.cc index fc01dcdc750,93dc62828ac..a4e0fd6b683 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@@ -25,16 -25,13 +25,16 @@@ #include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" #include "unireg.h" --#include "sql_derived.h" #include "sql_select.h" +#include "derived_handler.h" #include "sql_base.h" #include "sql_view.h" // check_duplicate_names #include "sql_acl.h" // SELECT_ACL #include "sql_class.h" ++#include "sql_derived.h" #include "sql_cte.h" +#include "my_json_writer.h" +#include "opt_trace.h" typedef bool (*dt_processor)(THD *thd, LEX *lex, TABLE_LIST *derived); @@@ -1359,63 -1264,89 +1359,124 @@@ bool mysql_derived_reinit(THD *thd, LE } + /* + @brief + Given condition cond and transformer+argument, try transforming as many + conjuncts as possible. + + @detail + The motivation of this function is to convert the condition that's being + pushed into a WHERE clause with derived_field_transformer_for_where or + with derived_grouping_field_transformer_for_where. + The transformer may fail for some sub-condition, in this case we want to + convert the most restrictive part of the condition that can be pushed. + + This function only does it for top-level AND: conjuncts that could not be + converted are dropped. + + @return + Converted condition, or NULL if nothing could be converted + */ + -static + Item *transform_condition_or_part(THD *thd, + Item *cond, + Item_transformer transformer, + uchar *arg) + { + if (cond->type() != Item::COND_ITEM || + ((Item_cond*) cond)->functype() != Item_func::COND_AND_FUNC) + { + Item *new_item= cond->transform(thd, transformer, arg); + // Indicate that the condition is not pushable + if (!new_item) + cond->clear_extraction_flag(); + return new_item; + } + + List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + Item *item; + while ((item=li++)) + { + Item *new_item= item->transform(thd, transformer, arg); + if (!new_item) + { + // Indicate that the condition is not pushable + item->clear_extraction_flag(); + li.remove(); + } + else + li.replace(new_item); + } + + switch (((Item_cond*) cond)->argument_list()->elements) + { + case 0: + return NULL; + case 1: + return ((Item_cond*) cond)->argument_list()->head(); + default: + return cond; + } + } + + /** @brief - Extract the condition depended on derived table/view and pushed it there + Extract condition that can be pushed into a derived table/view - @param thd The thread handle - @param cond The condition from which to extract the pushed condition - @param derived The reference to the derived table/view + @param thd the thread handle + @param cond current condition + @param derived the reference to the derived table/view @details - This functiom builds the most restrictive condition depending only on - the derived table/view that can be extracted from the condition cond. - The built condition is pushed into the having clauses of the - selects contained in the query specifying the derived table/view. - The function also checks for each select whether any condition depending - only on grouping fields can be extracted from the pushed condition. - If so, it pushes the condition over grouping fields into the where - clause of the select. - - @retval - true if an error is reported - false otherwise + This function builds the most restrictive condition depending only on + the derived table/view (directly or indirectly through equality) that + can be extracted from the given condition cond and pushes it into the + derived table/view. + + Example of the transformation: + + SELECT * + FROM t1, + ( + SELECT x,MAX(y) AS max_y + FROM t2 + GROUP BY x + ) AS d_tab + WHERE d_tab.x>1 AND d_tab.max_y<30; + + => + + SELECT * + FROM t1, + ( + SELECT x,z,MAX(y) AS max_y + FROM t2 + WHERE x>1 + HAVING max_y<30 + GROUP BY x + ) AS d_tab + WHERE d_tab.x>1 AND d_tab.max_y<30; + + In details: + 1. Check what pushable formula can be extracted from cond + 2. Build a clone PC of the formula that can be extracted + (the clone is built only if the extracted formula is a AND subformula + of cond or conjunction of such subformulas) + Do for every select specifying derived table/view: + 3. If there is no HAVING clause prepare PC to be conjuncted with + WHERE clause of the select. Otherwise do 4-7. + 4. Check what formula PC_where can be extracted from PC to be pushed + into the WHERE clause of the select + 5. Build PC_where and if PC_where is a conjunct(s) of PC remove it from PC + getting PC_having + 6. Prepare PC_where to be conjuncted with the WHERE clause of the select + 7. Prepare PC_having to be conjuncted with the HAVING clause of the select + @note + This method is similar to pushdown_cond_for_in_subquery() + + @retval TRUE if an error occurs + @retval FALSE otherwise */ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) diff --cc sql/sql_derived.h index 403277d65c9,093191e62a7..6100b4b4d7e --- a/sql/sql_derived.h +++ b/sql/sql_derived.h @@@ -22,7 -22,8 +22,12 @@@ struct LEX bool mysql_handle_derived(LEX *lex, uint phases); bool mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases); -bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived); + ++Item *transform_condition_or_part(THD *thd, ++ Item *cond, ++ Item_transformer transformer, ++ uchar *arg); + bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived); #endif /* SQL_DERIVED_INCLUDED */ diff --cc sql/sql_lex.cc index 93a4fcbe277,2a337b0f842..1ec5d0b0550 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@@ -31,11 -31,8 +31,12 @@@ #include "sql_select.h" #include "sql_cte.h" #include "sql_signal.h" ++#include "sql_derived.h" +#include "sql_truncate.h" // Sql_cmd_truncate_table +#include "sql_admin.h" // Sql_cmd_analyze/Check..._table #include "sql_partition.h" - +#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part +#include "event_parse_data.h" void LEX::parse_error(uint err_number) { @@@ -9000,1522 -8388,16 +9001,1524 @@@ void st_select_lex::register_unit(SELEC } -bool LEX::tvc_finalize_derived() +void st_select_lex::add_statistics(SELECT_LEX_UNIT *unit) { - derived_tables|= DERIVED_SUBQUERY; - if (unlikely(!expr_allows_subselect || sql_command == (int)SQLCOM_PURGE)) + for (; + unit; + unit= unit->next_unit()) + for(SELECT_LEX *child= unit->first_select(); + child; + child= child->next_select()) + { + /* + A subselect can add fields to an outer select. + Reserve space for them. + */ + select_n_where_fields+= child->select_n_where_fields; + /* + Aggregate functions in having clause may add fields + to an outer select. Count them also. + */ + select_n_having_items+= child->select_n_having_items; + } +} + + +bool LEX::main_select_push(bool service) +{ + DBUG_ENTER("LEX::main_select_push"); + current_select_number= ++thd->lex->stmt_lex->current_select_number; + builtin_select.select_number= current_select_number; + builtin_select.is_service_select= service; + if (push_select(&builtin_select)) + DBUG_RETURN(TRUE); + DBUG_RETURN(FALSE); +} + +void Lex_select_lock::set_to(SELECT_LEX *sel) +{ + if (defined_lock) { - thd->parse_error(); + if (sel->master_unit() && + sel == sel->master_unit()->fake_select_lex) + sel->master_unit()->set_lock_to_the_last_select(*this); + else + { + sel->parent_lex->safe_to_cache_query= 0; + if (update_lock) + { + sel->lock_type= TL_WRITE; + sel->set_lock_for_tables(TL_WRITE, false); + } + else + { + sel->lock_type= TL_READ_WITH_SHARED_LOCKS; + sel->set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS, false); + } + } + } +} + +bool Lex_order_limit_lock::set_to(SELECT_LEX *sel) +{ + /*TODO: lock */ + //if (lock.defined_lock && sel == sel->master_unit()->fake_select_lex) + // return TRUE; + if (lock.defined_timeout) + { + THD *thd= sel->parent_lex->thd; + if (set_statement_var_if_exists(thd, + C_STRING_WITH_LEN("lock_wait_timeout"), + lock.timeout) || + set_statement_var_if_exists(thd, + C_STRING_WITH_LEN("innodb_lock_wait_timeout"), + lock.timeout)) + return TRUE; + } + lock.set_to(sel); + sel->explicit_limit= limit.explicit_limit; + sel->select_limit= limit.select_limit; + sel->offset_limit= limit.offset_limit; + if (order_list) + { + if (sel->get_linkage() != GLOBAL_OPTIONS_TYPE && + sel->olap != UNSPECIFIED_OLAP_TYPE && + (sel->get_linkage() != UNION_TYPE || sel->braces)) + { + my_error(ER_WRONG_USAGE, MYF(0), + "CUBE/ROLLUP", "ORDER BY"); + return TRUE; + } + sel->order_list= *(order_list); + } + sel->is_set_query_expr_tail= true; + return FALSE; +} + + +static void change_item_list_context(List<Item> *list, + Name_resolution_context *context) +{ + List_iterator_fast<Item> it (*list); + Item *item; + while((item= it++)) + { + item->walk(&Item::change_context_processor, FALSE, (void *)context); + } +} + + +bool LEX::insert_select_hack(SELECT_LEX *sel) +{ + DBUG_ENTER("LEX::insert_select_hack"); + + DBUG_ASSERT(first_select_lex() == &builtin_select); + DBUG_ASSERT(sel != NULL); + + DBUG_ASSERT(builtin_select.first_inner_unit() == NULL); + + if (builtin_select.link_prev) + { + if ((*builtin_select.link_prev= builtin_select.link_next)) + ((st_select_lex *)builtin_select.link_next)->link_prev= + builtin_select.link_prev; + builtin_select.link_prev= NULL; // indicator of removal + } + + if (set_main_unit(sel->master_unit())) return true; + + DBUG_ASSERT(builtin_select.table_list.elements == 1); + TABLE_LIST *insert_table= builtin_select.table_list.first; + + if (!(insert_table->next_local= sel->table_list.first)) + { + sel->table_list.next= &insert_table->next_local; + } + sel->table_list.first= insert_table; + sel->table_list.elements++; + insert_table->select_lex= sel; + + sel->context.first_name_resolution_table= insert_table; + builtin_select.context= sel->context; + change_item_list_context(&field_list, &sel->context); + + if (sel->tvc && !sel->next_select() && + (sql_command == SQLCOM_INSERT_SELECT || + sql_command == SQLCOM_REPLACE_SELECT)) + { + DBUG_PRINT("info", ("'Usual' INSERT detected")); + many_values= sel->tvc->lists_of_values; + sel->options= sel->tvc->select_options; + sel->tvc= NULL; + if (sql_command == SQLCOM_INSERT_SELECT) + sql_command= SQLCOM_INSERT; + else + sql_command= SQLCOM_REPLACE; + } + + + for (SELECT_LEX *sel= all_selects_list; + sel; + sel= sel->next_select_in_list()) + { + if (sel->select_number != 1) + sel->select_number--; + }; + + DBUG_RETURN(FALSE); +} + + +/** + Create an Item_singlerow_subselect for a query expression. +*/ + +Item *LEX::create_item_query_expression(THD *thd, + st_select_lex_unit *unit) +{ + if (clause_that_disallows_subselect) + { + my_error(ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), + clause_that_disallows_subselect); + return NULL; + } + + // Add the subtree of subquery to the current SELECT_LEX + SELECT_LEX *curr_sel= select_stack_head(); + DBUG_ASSERT(current_select == curr_sel || + (curr_sel == NULL && current_select == &builtin_select)); + if (!curr_sel) + { + curr_sel= &builtin_select; + curr_sel->register_unit(unit, &curr_sel->context); + curr_sel->add_statistics(unit); + } + + return new (thd->mem_root) + Item_singlerow_subselect(thd, unit->first_select()); +} + + +SELECT_LEX_UNIT *LEX::parsed_select_expr_start(SELECT_LEX *s1, SELECT_LEX *s2, + enum sub_select_type unit_type, + bool distinct) +{ + SELECT_LEX_UNIT *res; + SELECT_LEX *sel1; + SELECT_LEX *sel2; + if (!s1->next_select()) + sel1= s1; + else + { + sel1= wrap_unit_into_derived(s1->master_unit()); + if (!sel1) + return NULL; + } + if (!s2->next_select()) + sel2= s2; + else + { + sel2= wrap_unit_into_derived(s2->master_unit()); + if (!sel2) + return NULL; + } + sel1->link_neighbour(sel2); + sel2->set_linkage_and_distinct(unit_type, distinct); + sel2->first_nested= sel1->first_nested= sel1; + res= create_unit(sel1); + if (res == NULL) + return NULL; + res->pre_last_parse= sel1; + push_select(res->fake_select_lex); + return res; +} + + +SELECT_LEX_UNIT *LEX::parsed_select_expr_cont(SELECT_LEX_UNIT *unit, + SELECT_LEX *s2, + enum sub_select_type unit_type, + bool distinct, bool oracle) +{ + DBUG_ASSERT(!s2->next_select()); + SELECT_LEX *sel1= s2; + SELECT_LEX *last= unit->pre_last_parse->next_select(); + + int cmp= oracle? 0 : cmp_unit_op(unit_type, last->get_linkage()); + if (cmp == 0) + { + sel1->first_nested= last->first_nested; + } + else if (cmp > 0) + { + last->first_nested= unit->pre_last_parse; + sel1->first_nested= last; + } + else /* cmp < 0 */ + { + SELECT_LEX *first_in_nest= last->first_nested; + if (first_in_nest->first_nested != first_in_nest) + { + /* There is a priority jump starting from first_in_nest */ + if ((last= create_priority_nest(first_in_nest)) == NULL) + return NULL; + unit->fix_distinct(); + } + sel1->first_nested= last->first_nested; + } + last->link_neighbour(sel1); + sel1->set_master_unit(unit); + sel1->set_linkage_and_distinct(unit_type, distinct); + unit->pre_last_parse= last; + return unit; +} + + +/** + Add primary expression as the next term in a given query expression body + pruducing a new query expression body +*/ + +SELECT_LEX_UNIT * +LEX::add_primary_to_query_expression_body(SELECT_LEX_UNIT *unit, + SELECT_LEX *sel, + enum sub_select_type unit_type, + bool distinct, + bool oracle) +{ + SELECT_LEX *sel2= sel; + if (sel->master_unit() && sel->master_unit()->first_select()->next_select()) + { + sel2= wrap_unit_into_derived(sel->master_unit()); + if (!sel2) + return NULL; + } + SELECT_LEX *sel1= unit->first_select(); + if (!sel1->next_select()) + unit= parsed_select_expr_start(sel1, sel2, unit_type, distinct); + else + unit= parsed_select_expr_cont(unit, sel2, unit_type, distinct, oracle); + return unit; +} + + +/** + Add query primary to a parenthesized query primary + pruducing a new query expression body +*/ + +SELECT_LEX_UNIT * +LEX::add_primary_to_query_expression_body_ext_parens( + SELECT_LEX_UNIT *unit, + SELECT_LEX *sel, + enum sub_select_type unit_type, + bool distinct) +{ + SELECT_LEX *sel1= unit->first_select(); + if (unit->first_select()->next_select()) + { + sel1= wrap_unit_into_derived(unit); + if (!sel1) + return NULL; + if (!create_unit(sel1)) + return NULL; + } + SELECT_LEX *sel2= sel; + if (sel->master_unit() && sel->master_unit()->first_select()->next_select()) + { + sel2= wrap_unit_into_derived(sel->master_unit()); + if (!sel2) + return NULL; + } + unit= parsed_select_expr_start(sel1, sel2, unit_type, distinct); + return unit; +} + + +/** + Process multi-operand query expression body +*/ + +bool LEX::parsed_multi_operand_query_expression_body(SELECT_LEX_UNIT *unit) +{ + SELECT_LEX *first_in_nest= + unit->pre_last_parse->next_select()->first_nested; + if (first_in_nest->first_nested != first_in_nest) + { + /* There is a priority jump starting from first_in_nest */ + if (create_priority_nest(first_in_nest) == NULL) + return true; + unit->fix_distinct(); } - current_select->linkage= DERIVED_TABLE_TYPE; - return tvc_finalize(); + return false; +} + + +/** + Add non-empty tail to a query expression body +*/ + +SELECT_LEX_UNIT *LEX::add_tail_to_query_expression_body(SELECT_LEX_UNIT *unit, + Lex_order_limit_lock *l) +{ + DBUG_ASSERT(l != NULL); + pop_select(); + SELECT_LEX *sel= unit->first_select()->next_select() ? unit->fake_select_lex : + unit->first_select(); + l->set_to(sel); + return unit; +} + + +/** + Add non-empty tail to a parenthesized query primary +*/ + +SELECT_LEX_UNIT * +LEX::add_tail_to_query_expression_body_ext_parens(SELECT_LEX_UNIT *unit, + Lex_order_limit_lock *l) +{ + SELECT_LEX *sel= unit->first_select()->next_select() ? unit->fake_select_lex : + unit->first_select(); + + DBUG_ASSERT(l != NULL); + + pop_select(); + if (sel->is_set_query_expr_tail) + { + if (!l->order_list && !sel->explicit_limit) + l->order_list= &sel->order_list; + else + { + if (!unit) + return NULL; + sel= wrap_unit_into_derived(unit); + if (!sel) + return NULL; + if (!create_unit(sel)) + return NULL; + } + } + l->set_to(sel); + return sel->master_unit(); +} + + +/** + Process subselect parsing +*/ + +SELECT_LEX *LEX::parsed_subselect(SELECT_LEX_UNIT *unit) +{ + if (clause_that_disallows_subselect) + { + my_error(ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), + clause_that_disallows_subselect); + return NULL; + } + + // Add the subtree of subquery to the current SELECT_LEX + SELECT_LEX *curr_sel= select_stack_head(); + DBUG_ASSERT(current_select == curr_sel || + (curr_sel == NULL && current_select == &builtin_select)); + if (curr_sel) + { + curr_sel->register_unit(unit, context_stack.head()); + curr_sel->add_statistics(unit); + } + + return unit->first_select(); +} + + +/** + Process INSERT-like select +*/ + +bool LEX::parsed_insert_select(SELECT_LEX *first_select) +{ + if (sql_command == SQLCOM_INSERT || + sql_command == SQLCOM_REPLACE) + { + if (sql_command == SQLCOM_INSERT) + sql_command= SQLCOM_INSERT_SELECT; + else + sql_command= SQLCOM_REPLACE_SELECT; + } + insert_select_hack(first_select); + if (check_main_unit_semantics()) + return true; + + // fix "main" select + SELECT_LEX *blt __attribute__((unused))= pop_select(); + DBUG_ASSERT(blt == &builtin_select); + push_select(first_select); + return false; +} + + +bool LEX::parsed_TVC_start() +{ + SELECT_LEX *sel; + save_values_list_state(); + many_values.empty(); + insert_list= 0; + if (!(sel= alloc_select(TRUE)) || + push_select(sel)) + return true; + sel->init_select(); + sel->braces= FALSE; // just initialisation + return false; +} + + +SELECT_LEX *LEX::parsed_TVC_end() +{ + SELECT_LEX *res= pop_select(); // above TVC select + if (!(res->tvc= + new (thd->mem_root) table_value_constr(many_values, + res, + res->options))) + return NULL; + restore_values_list_state(); + return res; +} + + + +TABLE_LIST *LEX::parsed_derived_table(SELECT_LEX_UNIT *unit, + int for_system_time, + LEX_CSTRING *alias) +{ + TABLE_LIST *res; + derived_tables|= DERIVED_SUBQUERY; + unit->first_select()->set_linkage(DERIVED_TABLE_TYPE); + + // Add the subtree of subquery to the current SELECT_LEX + SELECT_LEX *curr_sel= select_stack_head(); + DBUG_ASSERT(current_select == curr_sel || + (curr_sel == NULL && current_select == &builtin_select)); + + Table_ident *ti= new (thd->mem_root) Table_ident(unit); + if (ti == NULL) + return NULL; + if (!(res= curr_sel->add_table_to_list(thd, ti, alias, 0, + TL_READ, MDL_SHARED_READ))) + return NULL; + if (for_system_time) + { + res->vers_conditions= vers_conditions; + } + return res; +} + +bool LEX::parsed_create_view(SELECT_LEX_UNIT *unit, int check) +{ + SQL_I_List<TABLE_LIST> *save= &first_select_lex()->table_list; + if (set_main_unit(unit)) + return true; + if (check_main_unit_semantics()) + return true; + first_select_lex()->table_list.push_front(save); + current_select= first_select_lex(); + size_t len= thd->m_parser_state->m_lip.get_cpp_ptr() - + create_view->select.str; + void *create_view_select= thd->memdup(create_view->select.str, len); + create_view->select.length= len; + create_view->select.str= (char *) create_view_select; + size_t not_used; + trim_whitespace(thd->charset(), + &create_view->select, ¬_used); + create_view->check= check; + parsing_options.allows_variable= TRUE; + return false; +} + +bool LEX::select_finalize(st_select_lex_unit *expr) +{ + sql_command= SQLCOM_SELECT; + selects_allow_into= TRUE; + selects_allow_procedure= TRUE; + if (set_main_unit(expr)) + return true; + return check_main_unit_semantics(); +} + + +bool LEX::select_finalize(st_select_lex_unit *expr, Lex_select_lock l) +{ + return expr->set_lock_to_the_last_select(l) || + select_finalize(expr); +} + + +/* + "IN" and "EXISTS" subselect can appear in two statement types: + + 1. Statements that can have table columns, such as SELECT, DELETE, UPDATE + 2. Statements that cannot have table columns, e.g: + RETURN ((1) IN (SELECT * FROM t1)) + IF ((1) IN (SELECT * FROM t1)) + + Statements of the first type call master_select_push() in the beginning. + In such case everything is properly linked. + + Statements of the second type do not call mastr_select_push(). + Here we catch the second case and relink thd->lex->builtin_select and + select_lex to properly point to each other. + + QQ: Shouldn't subselects of other type also call relink_hack()? + QQ: Can we do it at constructor time instead? +*/ + +void LEX::relink_hack(st_select_lex *select_lex) +{ + if (!select_stack_top) // Statements of the second type + { + if (!select_lex->get_master()->get_master()) + ((st_select_lex *) select_lex->get_master())-> + set_master(&builtin_select); + if (!builtin_select.get_slave()) + builtin_select.set_slave(select_lex->get_master()); + } +} + + +bool SELECT_LEX_UNIT::set_lock_to_the_last_select(Lex_select_lock l) +{ + if (l.defined_lock) + { + SELECT_LEX *sel= first_select(); + while (sel->next_select()) + sel= sel->next_select(); + if (sel->braces) + { + my_error(ER_WRONG_USAGE, MYF(0), "lock options", + "SELECT in brackets"); + return TRUE; + } + l.set_to(sel); + } + return FALSE; +} + +/** + Generate unique name for generated derived table for this SELECT +*/ + +bool SELECT_LEX::make_unique_derived_name(THD *thd, LEX_CSTRING *alias) +{ + // uint32 digits + two underscores + trailing '\0' + char buff[MAX_INT_WIDTH + 2 + 1]; + alias->length= my_snprintf(buff, sizeof(buff), "__%u", select_number); + alias->str= thd->strmake(buff, alias->length); + return !alias->str; +} + + +/* + Make a new sp_instr_stmt and set its m_query to a concatenation + of two strings. +*/ +bool LEX::new_sp_instr_stmt(THD *thd, + const LEX_CSTRING &prefix, + const LEX_CSTRING &suffix) +{ + LEX_STRING qbuff; + sp_instr_stmt *i; + + if (!(i= new (thd->mem_root) sp_instr_stmt(sphead->instructions(), + spcont, this))) + return true; + + qbuff.length= prefix.length + suffix.length; + if (!(qbuff.str= (char*) alloc_root(thd->mem_root, qbuff.length + 1))) + return true; + if (prefix.length) + memcpy(qbuff.str, prefix.str, prefix.length); + strmake(qbuff.str + prefix.length, suffix.str, suffix.length); + i->m_query= qbuff; + return sphead->add_instr(i); +} + + +bool LEX::sp_proc_stmt_statement_finalize_buf(THD *thd, const LEX_CSTRING &qbuf) +{ + sphead->m_flags|= sp_get_flags_for_command(this); + /* "USE db" doesn't work in a procedure */ + if (unlikely(sql_command == SQLCOM_CHANGE_DB)) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "USE"); + return true; + } + /* + Don't add an instruction for SET statements, since all + instructions for them were already added during processing + of "set" rule. + */ + DBUG_ASSERT(sql_command != SQLCOM_SET_OPTION || var_list.is_empty()); + if (sql_command != SQLCOM_SET_OPTION) + return new_sp_instr_stmt(thd, empty_clex_str, qbuf); + return false; +} + + +bool LEX::sp_proc_stmt_statement_finalize(THD *thd, bool no_lookahead) +{ + // Extract the query statement from the tokenizer + Lex_input_stream *lip= &thd->m_parser_state->m_lip; + Lex_cstring qbuf(sphead->m_tmp_query, no_lookahead ? lip->get_ptr() : + lip->get_tok_start()); + return LEX::sp_proc_stmt_statement_finalize_buf(thd, qbuf); +} + + +/** + @brief + Extract the condition that can be pushed into WHERE clause + + @param thd the thread handle + @param cond the condition from which to extract a pushed condition + @param remaining_cond IN/OUT the condition that will remain of cond after + the extraction + @param transformer the transformer callback function to be + applied to the fields of the condition so it + can be pushed` + @param arg parameter to be passed to the transformer + + @details + This function builds the most restrictive condition depending only on + the fields used in the GROUP BY of this SELECT. These fields were + collected before in grouping_tmp_fields list of this SELECT. + + First this method checks if this SELECT doesn't have any aggregation + functions and has no GROUP BY clause. If so cond can be entirely pushed + into WHERE. + + Otherwise the method checks if there is a condition depending only on + grouping fields that can be extracted from cond. + + The condition that can be pushed into WHERE should be transformed. + It is done by transformer. + + The extracted condition is saved in cond_pushed_into_where of this select. + cond can remain un empty after the extraction of the condition that can be + pushed into WHERE. It is saved in remaining_cond. + + @note + This method is called for pushdown conditions into materialized + derived tables/views optimization. + Item::derived_field_transformer_for_where is passed as the actual + callback function. + Also it is called for pushdown into materialized IN subqueries. + Item::in_subq_field_transformer_for_where is passed as the actual + callback function. +*/ + +void st_select_lex::pushdown_cond_into_where_clause(THD *thd, Item *cond, + Item **remaining_cond, + Item_transformer transformer, + uchar *arg) +{ + if (!cond_pushdown_is_allowed()) + return; + thd->lex->current_select= this; + if (have_window_funcs()) + { + Item *cond_over_partition_fields; + check_cond_extraction_for_grouping_fields(thd, cond); + cond_over_partition_fields= + build_cond_for_grouping_fields(thd, cond, true); + if (cond_over_partition_fields) + cond_over_partition_fields= cond_over_partition_fields->transform(thd, + &Item::grouping_field_transformer_for_where, + (uchar*) this); + if (cond_over_partition_fields) + { + cond_over_partition_fields->walk( + &Item::cleanup_excluding_const_fields_processor, 0, 0); + cond_pushed_into_where= cond_over_partition_fields; + } + + return; + } + + if (!join->group_list && !with_sum_func) + { - cond= - cond->transform(thd, transformer, arg); ++ cond= transform_condition_or_part(thd, cond, transformer, arg); + if (cond) + { + cond->walk( + &Item::cleanup_excluding_const_fields_processor, 0, 0); + cond_pushed_into_where= cond; + } + + return; + } + + /* + Figure out what can be extracted from cond and pushed into + the WHERE clause of this select. + */ + Item *cond_over_grouping_fields; + check_cond_extraction_for_grouping_fields(thd, cond); + cond_over_grouping_fields= + build_cond_for_grouping_fields(thd, cond, true); + + /* + Transform references to the columns of condition that can be pushed + into WHERE so it can be pushed. + */ + if (cond_over_grouping_fields) - cond_over_grouping_fields= cond_over_grouping_fields->transform(thd, - &Item::grouping_field_transformer_for_where, - (uchar*) this); ++ { ++ cond_over_grouping_fields= ++ transform_condition_or_part(thd, cond_over_grouping_fields, ++ &Item::grouping_field_transformer_for_where, ++ (uchar*) this); ++ } + + if (cond_over_grouping_fields) + { + + /* + Remove top conjuncts in cond that has been pushed into the WHERE + clause of this select + */ + cond= remove_pushed_top_conjuncts(thd, cond); + + cond_over_grouping_fields->walk( + &Item::cleanup_excluding_const_fields_processor, 0, 0); + cond_pushed_into_where= cond_over_grouping_fields; + } + + *remaining_cond= cond; +} + + +/** + @brief + Mark OR-conditions as non-pushable to avoid repeatable pushdown + + @param cond the processed condition + + @details + Consider pushdown into the materialized derived table/view. + Consider OR condition that can be pushed into HAVING and some + parts of this OR condition that can be pushed into WHERE. + + On example: + + SELECT * + FROM t1, + ( + SELECT a,MAX(c) AS m_c + GROUP BY a + ) AS dt + WHERE ((dt.m_c>10) AND (dt.a>2)) OR ((dt.m_c<7) and (dt.a<3)) AND + (t1.a=v1.a); + + + Here ((dt.m_c>10) AND (dt.a>2)) OR ((dt.m_c<7) and (dt.a<3)) or1 + can be pushed down into the HAVING of the materialized + derived table dt. + + (dt.a>2) OR (dt.a<3) part of or1 depends only on grouping fields + of dt and can be pushed into WHERE. + + As a result: + + SELECT * + FROM t1, + ( + SELECT a,MAX(c) AS m_c + WHERE (dt.a>2) OR (dt.a<3) + GROUP BY a + HAVING ((dt.m_c>10) AND (dt.a>2)) OR ((dt.m_c<7) and (dt.a<3)) + ) AS dt + WHERE ((dt.m_c>10) AND (dt.a>2)) OR ((dt.m_c<7) and (dt.a<3)) AND + (t1.a=v1.a); + + + Here (dt.a>2) OR (dt.a<3) also remains in HAVING of dt. + When SELECT that defines df is processed HAVING pushdown optimization + is made. In HAVING pushdown optimization it will extract + (dt.a>2) OR (dt.a<3) condition from or1 again and push it into WHERE. + This will cause duplicate conditions in WHERE of dt. + + To avoid repeatable pushdown such OR conditions as or1 describen + above are marked with NO_EXTRACTION_FL. + + @note + This method is called for pushdown into materialized + derived tables/views/IN subqueries optimization. +*/ + +void mark_or_conds_to_avoid_pushdown(Item *cond) +{ + if (cond->type() == Item::COND_ITEM && + ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) + { + List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + Item *item; + while ((item=li++)) + { + if (item->type() == Item::COND_ITEM && + ((Item_cond*) item)->functype() == Item_func::COND_OR_FUNC) + item->set_extraction_flag(NO_EXTRACTION_FL); + } + } + else if (cond->type() == Item::COND_ITEM && + ((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC) + cond->set_extraction_flag(NO_EXTRACTION_FL); +} + +/** + @brief + Get condition that can be pushed from HAVING into WHERE + + @param thd the thread handle + @param cond the condition from which to extract the condition + + @details + The method collects in attach_to_conds list conditions from cond + that can be pushed from HAVING into WHERE. + + Conditions that can be pushed were marked with FULL_EXTRACTION_FL in + check_cond_extraction_for_grouping_fields() method. + Conditions that can't be pushed were marked with NO_EXTRACTION_FL. + Conditions which parts can be pushed weren't marked. + + There are two types of conditions that can be pushed: + 1. Condition that can be simply moved from HAVING + (if cond is marked with FULL_EXTRACTION_FL or + cond is an AND condition and some of its parts are marked with + FULL_EXTRACTION_FL) + In this case condition is transformed and pushed into attach_to_conds + list. + 2. Part of some other condition c1 that can't be entirely pushed + (if с1 isn't marked with any flag). + + For example: + + SELECT t1.a,MAX(t1.b),t1.c + FROM t1 + GROUP BY t1.a + HAVING ((t1.a > 5) AND (t1.c < 3)) OR (t1.a = 3); + + Here (t1.a > 5) OR (t1.a = 3) from HAVING can be pushed into WHERE. + + In this case build_pushable_cond() is called for c1. + This method builds a clone of the c1 part that can be pushed. + + Transformation mentioned above is made with multiple_equality_transformer + transformer. It transforms all multiple equalities in the extracted + condition into the set of equalities. + + @note + Conditions that can be pushed are collected in attach_to_conds in this way: + 1. if cond is an AND condition its parts that can be pushed into WHERE + are added to attach_to_conds list separately. + 2. in all other cases conditions are pushed into the list entirely. + + @retval + true - if an error occurs + false - otherwise +*/ + +bool +st_select_lex::build_pushable_cond_for_having_pushdown(THD *thd, Item *cond) +{ + List<Item> equalities; + + /* Condition can't be pushed */ + if (cond->get_extraction_flag() == NO_EXTRACTION_FL) + return false; + + /** + Condition can be pushed entirely. + Transform its multiple equalities and add to attach_to_conds list. + */ + if (cond->get_extraction_flag() == FULL_EXTRACTION_FL) + { + Item *result= cond->transform(thd, + &Item::multiple_equality_transformer, + (uchar *)this); + if (!result) + return true; + if (result->type() == Item::COND_ITEM && + ((Item_cond*) result)->functype() == Item_func::COND_AND_FUNC) + { + List_iterator<Item> li(*((Item_cond*) result)->argument_list()); + Item *item; + while ((item= li++)) + { + if (attach_to_conds.push_back(item, thd->mem_root)) + return true; + } + } + else + { + if (attach_to_conds.push_back(result, thd->mem_root)) + return true; + } + return false; + } + + /** + There is no flag set for this condition. It means that some + part of this condition can be pushed. + */ + if (cond->type() != Item::COND_ITEM) + return false; + + if (((Item_cond *)cond)->functype() != Item_cond::COND_AND_FUNC) + { + /* + cond is not a conjunctive formula and it cannot be pushed into WHERE. + Try to extract a formula that can be pushed. + */ + Item *fix= cond->build_pushable_cond(thd, 0, 0); + if (!fix) + return false; + if (attach_to_conds.push_back(fix, thd->mem_root)) + return true; + } + else + { + List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + Item *item; + while ((item=li++)) + { + if (item->get_extraction_flag() == NO_EXTRACTION_FL) + continue; + else if (item->get_extraction_flag() == FULL_EXTRACTION_FL) + { + Item *result= item->transform(thd, + &Item::multiple_equality_transformer, + (uchar *)item); + if (!result) + return true; + if (result->type() == Item::COND_ITEM && + ((Item_cond*) result)->functype() == Item_func::COND_AND_FUNC) + { + List_iterator<Item> li(*((Item_cond*) result)->argument_list()); + Item *item; + while ((item=li++)) + { + if (attach_to_conds.push_back(item, thd->mem_root)) + return true; + } + } + else + { + if (attach_to_conds.push_back(result, thd->mem_root)) + return true; + } + } + else + { + Item *fix= item->build_pushable_cond(thd, 0, 0); + if (!fix) + continue; + if (attach_to_conds.push_back(fix, thd->mem_root)) + return true; + } + } + } + return false; +} + + +/** + Check if item is equal to some field in Field_pair 'field_pair' + from 'pair_list' and return found 'field_pair' if it exists. +*/ + +Field_pair *get_corresponding_field_pair(Item *item, + List<Field_pair> pair_list) +{ + DBUG_ASSERT(item->type() == Item::FIELD_ITEM || + (item->type() == Item::REF_ITEM && + ((((Item_ref *) item)->ref_type() == Item_ref::VIEW_REF) || + (((Item_ref *) item)->ref_type() == Item_ref::REF)))); + + List_iterator<Field_pair> it(pair_list); + Field_pair *field_pair; + Item_field *field_item= (Item_field *) (item->real_item()); + while ((field_pair= it++)) + { + if (field_item->field == field_pair->field) + return field_pair; + } + return NULL; +} + + +/** + @brief + Collect fields from multiple equalities which are equal to grouping + + @param thd the thread handle + + @details + This method checks if multiple equalities of the WHERE clause contain + fields from GROUP BY of this SELECT. If so all fields of such multiple + equalities are collected in grouping_tmp_fields list without repetitions. + + @retval + true - if an error occurs + false - otherwise +*/ + +bool st_select_lex::collect_fields_equal_to_grouping(THD *thd) +{ + if (!join->cond_equal || join->cond_equal->is_empty()) + return false; + + List_iterator_fast<Item_equal> li(join->cond_equal->current_level); + Item_equal *item_equal; + + while ((item_equal= li++)) + { + Item_equal_fields_iterator it(*item_equal); + Item *item; + while ((item= it++)) + { + if (get_corresponding_field_pair(item, grouping_tmp_fields)) + break; + } + if (!item) + break; + + it.rewind(); + while ((item= it++)) + { + if (get_corresponding_field_pair(item, grouping_tmp_fields)) + continue; + Field_pair *grouping_tmp_field= + new Field_pair(((Item_field *)item->real_item())->field, item); + if (grouping_tmp_fields.push_back(grouping_tmp_field, thd->mem_root)) + return true; + } + } + return false; +} + + +/** + @brief + Remove marked top conjuncts of HAVING for having pushdown + + @param thd the thread handle + @param cond the condition which subformulas are to be removed + + @details + This method removes from cond all subformulas that can be moved from HAVING + into WHERE. + + @retval + condition without removed subformulas + 0 if the whole 'cond' is removed +*/ + +Item *remove_pushed_top_conjuncts_for_having(THD *thd, Item *cond) +{ + /* Nothing to extract */ + if (cond->get_extraction_flag() == NO_EXTRACTION_FL) + { + cond->clear_extraction_flag(); + return cond; + } + /* cond can be pushed in WHERE entirely */ + if (cond->get_extraction_flag() == FULL_EXTRACTION_FL) + { + cond->clear_extraction_flag(); + return 0; + } + + /* Some parts of cond can be pushed */ + if (cond->type() == Item::COND_ITEM && + ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) + { + List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + Item *item; + while ((item=li++)) + { + if (item->get_extraction_flag() == NO_EXTRACTION_FL) + item->clear_extraction_flag(); + else if (item->get_extraction_flag() == FULL_EXTRACTION_FL) + { + if (item->type() == Item::FUNC_ITEM && + ((Item_func*) item)->functype() == Item_func::MULT_EQUAL_FUNC) + item->set_extraction_flag(DELETION_FL); + else + { + item->clear_extraction_flag(); + li.remove(); + } + } + } + switch (((Item_cond*) cond)->argument_list()->elements) + { + case 0: + return 0; + case 1: + return (((Item_cond*) cond)->argument_list()->head()); + default: + return cond; + } + } + return cond; +} + + +/** + @brief + Extract condition that can be pushed from HAVING into WHERE + + @param thd the thread handle + @param having the HAVING clause of this select + @param having_equal multiple equalities of HAVING + + @details + This method builds a set of conditions dependent only on + fields used in the GROUP BY of this select (directly or indirectly + through equalities). These conditions are extracted from the HAVING + clause of this select. + The method saves these conditions into attach_to_conds list and removes + from HAVING conditions that can be entirely pushed into WHERE. + + Example of the HAVING pushdown transformation: + + SELECT t1.a,MAX(t1.b) + FROM t1 + GROUP BY t1.a + HAVING (t1.a>2) AND (MAX(c)>12); + + => + + SELECT t1.a,MAX(t1.b) + FROM t1 + WHERE (t1.a>2) + GROUP BY t1.a + HAVING (MAX(c)>12); + + In this method (t1.a>2) is not attached to the WHERE clause. + It is pushed into the attach_to_conds list to be attached to + the WHERE clause later. + + In details: + 1. Collect fields used in the GROUP BY grouping_fields of this SELECT + 2. Collect fields equal to grouping_fields from the WHERE clause + of this SELECT and add them to the grouping_fields list. + 3. Extract the most restrictive condition from the HAVING clause of this + select that depends only on the grouping fields (directly or indirectly + through equality). + If the extracted condition is an AND condition it is transformed into a + list of all its conjuncts saved in attach_to_conds. Otherwise, + the condition is put into attach_to_conds as the only its element. + 4. Remove conditions from HAVING clause that can be entirely pushed + into WHERE. + Multiple equalities are not removed but marked with DELETION_FL flag. + They will be deleted later in substitite_for_best_equal_field() called + for the HAVING condition. + 5. Unwrap fields wrapped in Item_ref wrappers contained in the condition + of attach_to_conds so the condition could be pushed into WHERE. + + @note + This method is similar to st_select_lex::pushdown_cond_into_where_clause(). + + @retval TRUE if an error occurs + @retval FALSE otherwise +*/ + +Item *st_select_lex::pushdown_from_having_into_where(THD *thd, Item *having) +{ + if (!having || !group_list.first) + return having; + if (!cond_pushdown_is_allowed()) + return having; + + st_select_lex *save_curr_select= thd->lex->current_select; + thd->lex->current_select= this; + + /* + 1. Collect fields used in the GROUP BY grouping fields of this SELECT + 2. Collect fields equal to grouping_fields from the WHERE clause + of this SELECT and add them to the grouping fields list. + */ + if (collect_grouping_fields(thd) || + collect_fields_equal_to_grouping(thd)) + return having; + + /* + 3. Extract the most restrictive condition from the HAVING clause of this + select that depends only on the grouping fields (directly or indirectly + through equality). + If the extracted condition is an AND condition it is transformed into a + list of all its conjuncts saved in attach_to_conds. Otherwise, + the condition is put into attach_to_conds as the only its element. + */ + List_iterator_fast<Item> it(attach_to_conds); + Item *item; + check_cond_extraction_for_grouping_fields(thd, having); + if (build_pushable_cond_for_having_pushdown(thd, having)) + { + attach_to_conds.empty(); + goto exit; + } + if (!attach_to_conds.elements) + goto exit; + + /* + 4. Remove conditions from HAVING clause that can be entirely pushed + into WHERE. + Multiple equalities are not removed but marked with DELETION_FL flag. + They will be deleted later in substitite_for_best_equal_field() called + for the HAVING condition. + */ + having= remove_pushed_top_conjuncts_for_having(thd, having); + + /* + Change join->cond_equal which points to the multiple equalities of + the top level of HAVING. + Removal of AND conditions may leave only one conjunct in HAVING. + + Example 1: + SELECT * + FROM t1 + GROUP BY t1.a + (t1.a < 2) AND (t1.b = 2) + + (t1.a < 2) is pushed into WHERE. + join->cond_equal should point on (t1.b = 2) multiple equality now. + + Example 2: + SELECT * + FROM t1 + GROUP BY t1.a + (t1.a = 2) AND (t1.b < 2) + + (t1.a = 2) is pushed into WHERE. + join->cond_equal should be NULL now. + */ + if (having && + having->type() == Item::FUNC_ITEM && + ((Item_func*) having)->functype() == Item_func::MULT_EQUAL_FUNC) + join->having_equal= new (thd->mem_root) COND_EQUAL((Item_equal *)having, + thd->mem_root); + else if (!having || + having->type() != Item::COND_ITEM || + ((Item_cond *)having)->functype() != Item_cond::COND_AND_FUNC) + join->having_equal= 0; + + /* + 5. Unwrap fields wrapped in Item_ref wrappers contained in the condition + of attach_to_conds so the condition could be pushed into WHERE. + */ + it.rewind(); + while ((item=it++)) + { + item= item->transform(thd, + &Item::field_transformer_for_having_pushdown, + (uchar *)this); + + if (item->walk(&Item::cleanup_excluding_immutables_processor, 0, STOP_PTR) + || item->fix_fields(thd, NULL)) + { + attach_to_conds.empty(); + goto exit; + } + } +exit: + thd->lex->current_select= save_curr_select; + return having; +} + + +bool LEX::stmt_install_plugin(const DDL_options_st &opt, + const Lex_ident_sys_st &name, + const LEX_CSTRING &soname) +{ + create_info.init(); + if (add_create_options_with_check(opt)) + return true; + sql_command= SQLCOM_INSTALL_PLUGIN; + comment= name; + ident= soname; + return false; +} + + +void LEX::stmt_install_plugin(const LEX_CSTRING &soname) +{ + sql_command= SQLCOM_INSTALL_PLUGIN; + comment= null_clex_str; + ident= soname; +} + + +bool LEX::stmt_uninstall_plugin_by_name(const DDL_options_st &opt, + const Lex_ident_sys_st &name) +{ + check_opt.init(); + if (add_create_options_with_check(opt)) + return true; + sql_command= SQLCOM_UNINSTALL_PLUGIN; + comment= name; + ident= null_clex_str; + return false; +} + + +bool LEX::stmt_uninstall_plugin_by_soname(const DDL_options_st &opt, + const LEX_CSTRING &soname) +{ + check_opt.init(); + if (add_create_options_with_check(opt)) + return true; + sql_command= SQLCOM_UNINSTALL_PLUGIN; + comment= null_clex_str; + ident= soname; + return false; +} + + +bool LEX::stmt_prepare_validate(const char *stmt_type) +{ + if (unlikely(table_or_sp_used())) + { + my_error(ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), stmt_type); + return true; + } + return check_main_unit_semantics(); +} + + +bool LEX::stmt_prepare(const Lex_ident_sys_st &ident, Item *code) +{ + sql_command= SQLCOM_PREPARE; + if (stmt_prepare_validate("PREPARE..FROM")) + return true; + prepared_stmt.set(ident, code, NULL); + return false; +} + + +bool LEX::stmt_execute_immediate(Item *code, List<Item> *params) +{ + sql_command= SQLCOM_EXECUTE_IMMEDIATE; + if (stmt_prepare_validate("EXECUTE IMMEDIATE")) + return true; + static const Lex_ident_sys immediate(STRING_WITH_LEN("IMMEDIATE")); + prepared_stmt.set(immediate, code, params); + return false; +} + + +bool LEX::stmt_execute(const Lex_ident_sys_st &ident, List<Item> *params) +{ + sql_command= SQLCOM_EXECUTE; + prepared_stmt.set(ident, NULL, params); + return stmt_prepare_validate("EXECUTE..USING"); +} + + +void LEX::stmt_deallocate_prepare(const Lex_ident_sys_st &ident) +{ + sql_command= SQLCOM_DEALLOCATE_PREPARE; + prepared_stmt.set(ident, NULL, NULL); +} + + +bool LEX::stmt_alter_table_exchange_partition(Table_ident *table) +{ + DBUG_ASSERT(sql_command == SQLCOM_ALTER_TABLE); + first_select_lex()->db= table->db; + if (first_select_lex()->db.str == NULL && + copy_db_to(&first_select_lex()->db)) + return true; + name= table->table; + alter_info.partition_flags|= ALTER_PARTITION_EXCHANGE; + if (!first_select_lex()->add_table_to_list(thd, table, NULL, + TL_OPTION_UPDATING, + TL_READ_NO_INSERT, + MDL_SHARED_NO_WRITE)) + return true; + DBUG_ASSERT(!m_sql_cmd); + m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_table_exchange_partition(); + return m_sql_cmd == NULL; +} + + +void LEX::stmt_purge_to(const LEX_CSTRING &to) +{ + type= 0; + sql_command= SQLCOM_PURGE; + to_log= to.str; +} + + +bool LEX::stmt_purge_before(Item *item) +{ + type= 0; + sql_command= SQLCOM_PURGE_BEFORE; + value_list.empty(); + value_list.push_front(item, thd->mem_root); + return check_main_unit_semantics(); +} + + +bool LEX::stmt_create_udf_function(const DDL_options_st &options, + enum_sp_aggregate_type agg_type, + const Lex_ident_sys_st &name, + Item_result return_type, + const LEX_CSTRING &soname) +{ + if (stmt_create_function_start(options)) + return true; + + if (unlikely(is_native_function(thd, &name))) + { + my_error(ER_NATIVE_FCT_NAME_COLLISION, MYF(0), name.str); + return true; + } + sql_command= SQLCOM_CREATE_FUNCTION; + udf.name= name; + udf.returns= return_type; + udf.dl= soname.str; + udf.type= agg_type == GROUP_AGGREGATE ? UDFTYPE_AGGREGATE : + UDFTYPE_FUNCTION; + stmt_create_routine_finalize(); + return false; +} + + +bool LEX::stmt_create_stored_function_start(const DDL_options_st &options, + enum_sp_aggregate_type agg_type, + const sp_name *spname) +{ + if (stmt_create_function_start(options) || + unlikely(!make_sp_head_no_recursive(thd, spname, + &sp_handler_function, agg_type))) + return true; + return false; +} + + +Spvar_definition *LEX::row_field_name(THD *thd, const Lex_ident_sys_st &name) +{ + Spvar_definition *res; + if (unlikely(check_string_char_length(&name, 0, NAME_CHAR_LEN, + system_charset_info, 1))) + { + my_error(ER_TOO_LONG_IDENT, MYF(0), name.str); + return NULL; + } + if (unlikely(!(res= new (thd->mem_root) Spvar_definition()))) + return NULL; + init_last_field(res, &name, thd->variables.collation_database); + return res; } diff --cc sql/sql_show.cc index 7b5b2c8bf89,e7c42cbc8e4..1f8278528cf --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@@ -63,9 -63,20 +63,22 @@@ #include "ha_partition.h" #endif #include "transaction.h" +#include "opt_trace.h" +#include "my_cpu.h" + + #include "lex_symbol.h" + #define KEYWORD_SIZE 64 + + extern SYMBOL symbols[]; + extern size_t symbols_length; + + extern SYMBOL sql_functions[]; + extern size_t sql_functions_length; + + extern Native_func_registry func_array[]; + extern size_t func_array_length; + enum enum_i_s_events_fields { ISE_EVENT_CATALOG= 0,
participants (1)
-
psergey