diff options
author | Oleksandr Byelkin <sanja@mariadb.com> | 2017-03-13 11:04:46 +0100 |
---|---|---|
committer | Oleksandr Byelkin <sanja@mariadb.com> | 2017-03-13 11:04:46 +0100 |
commit | 4c31280c5bfdfc3a1fec177de563c61559efe9c0 (patch) | |
tree | fcc58f429a711e36df2bf47a29b8d90050d5bfe0 /sql | |
parent | 1e47dece126cfab481d81bda3eedf71c0e12119c (diff) | |
download | mariadb-git-10.3-MDEV-10141.tar.gz |
MDEV-10141 & MDEV-10140 : Postreview changes10.3-MDEV-10141
Diffstat (limited to 'sql')
-rw-r--r-- | sql/sql_class.cc | 2 | ||||
-rw-r--r-- | sql/sql_class.h | 5 | ||||
-rw-r--r-- | sql/sql_explain.cc | 41 | ||||
-rw-r--r-- | sql/sql_explain.h | 5 | ||||
-rw-r--r-- | sql/sql_lex.cc | 86 | ||||
-rw-r--r-- | sql/sql_lex.h | 2 | ||||
-rw-r--r-- | sql/sql_select.cc | 2 | ||||
-rw-r--r-- | sql/sql_union.cc | 130 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 6 |
9 files changed, 206 insertions, 73 deletions
diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 6cf13447ccc..19846da8e8c 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3918,7 +3918,7 @@ void select_materialize_with_stats::cleanup() /** - Override select_union::send_data to analyze each row for NULLs and to + Override select_unit::send_data to analyze each row for NULLs and to update null_statistics before sending data to the client. @return TRUE if fatal error when sending data to the client diff --git a/sql/sql_class.h b/sql/sql_class.h index eb9b8fe5c6e..d5afa0a89dd 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -4866,7 +4866,7 @@ public: class select_unit :public select_result_interceptor { uint curr_step, prev_step, curr_sel; - enum step_type {union_step, intersect_step, except_step} step; + enum sub_select_type step; public: Item_int *intersect_mark; TMP_TABLE_PARAM tmp_table_param; @@ -4877,7 +4877,7 @@ public: select_unit(THD *thd_arg): select_result_interceptor(thd_arg), curr_step(0), prev_step(0), curr_sel(UINT_MAX), - step(union_step), intersect_mark(0), write_err(0), table(0), + step(UNION_TYPE), intersect_mark(0), write_err(0), table(0), records(0) { tmp_table_param.init(); } int prepare(List<Item> &list, SELECT_LEX_UNIT *u); @@ -4903,6 +4903,7 @@ public: bool keep_row_order, uint hidden); TMP_TABLE_PARAM *get_tmp_table_param() { return &tmp_table_param; } + void change_select(); }; class select_union_recursive :public select_unit diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 857c292630c..693235c79e6 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -29,6 +29,11 @@ const char * STR_DELETING_ALL_ROWS= "Deleting all rows"; const char * STR_IMPOSSIBLE_WHERE= "Impossible WHERE"; const char * STR_NO_ROWS_AFTER_PRUNING= "No matching rows after partition pruning"; +const char *unit_operation_text[4]= +{ + "UNIT RESULT","UNION RESULT","INTERSECT RESULT","EXCEPT RESULT" +}; + static void write_item(Json_writer *writer, Item *item); static void append_item_to_str(String *out, Item *item); @@ -418,8 +423,28 @@ int print_explain_row(select_result_sink *result, uint Explain_union::make_union_table_name(char *buf) { uint childno= 0; - uint len= 6, lastop= 0; - memcpy(buf, STRING_WITH_LEN("<union")); + uint len, lastop= 0; + switch (operation) + { + case OP_MIX: + len= 5; + memcpy(buf, STRING_WITH_LEN("<unit")); + break; + case OP_UNION: + len= 6; + memcpy(buf, STRING_WITH_LEN("<union")); + break; + case OP_INTERSECT: + len= 10; + memcpy(buf, STRING_WITH_LEN("<intersect")); + break; + case OP_EXCEPT: + len= 7; + memcpy(buf, STRING_WITH_LEN("<except")); + break; + default: + DBUG_ASSERT(0); + } for (; childno < union_members.elements() && len + lastop + 5 < NAME_LEN; childno++) @@ -462,7 +487,7 @@ int Explain_union::print_explain(Explain_query *query, if (!using_tmp) return 0; - /* Print a line with "UNION RESULT" */ + /* Print a line with "UNIT RESULT" */ List<Item> item_list; Item *item_null= new (mem_root) Item_null(thd); @@ -816,20 +841,24 @@ int Explain_basic_join::print_explain(Explain_query *query, void Explain_select::add_linkage(Json_writer *writer) { + const char *operation= NULL; switch (linkage) { case UNION_TYPE: - writer->add_member("operation").add_str("UNION"); + operation= "UNION"; break; case INTERSECT_TYPE: - writer->add_member("operation").add_str("INTERSECT"); + operation= "INTERSECT"; break; case EXCEPT_TYPE: - writer->add_member("operation").add_str("EXCEPT"); + operation= "EXCEPT"; break; default: + // It is the first or the only SELECT => no operation break; } + if (operation) + writer->add_member("operation").add_str(operation); } void Explain_select::print_explain_json(Explain_query *query, diff --git a/sql/sql_explain.h b/sql/sql_explain.h index 02357acd873..6e1b55e2b00 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -320,7 +320,9 @@ public: ///////////////////////////////////////////////////////////////////////////// -/* +extern const char *unit_operation_text[4]; + +/* Explain structure for a UNION. A UNION may or may not have "Using filesort". @@ -336,6 +338,7 @@ public: {} enum explain_node_type get_type() { return EXPLAIN_UNION; } + unit_common_op operation; int get_select_id() { diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 608eccbaff6..cc6e33263b8 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4413,7 +4413,7 @@ void st_select_lex::set_explain_type(bool on_the_fly) { type= is_uncacheable ? "UNCACHEABLE UNION": "UNION"; if (this == master_unit()->fake_select_lex) - type= "UNION RESULT"; + type= unit_operation_text[master_unit()->common_op()]; /* join below may be =NULL when this functions is called at an early stage. It will be later called again and we will set the correct @@ -4484,6 +4484,7 @@ void SELECT_LEX::increase_derived_records(ha_rows records) // in worse case none of record will be removed break; default: + // usual UNION result->records+= records; break; } @@ -4740,6 +4741,42 @@ void LEX::restore_set_statement_var() DBUG_VOID_RETURN; } +unit_common_op st_select_lex_unit::common_op() +{ + SELECT_LEX *first= first_select(); + bool first_op= TRUE; + unit_common_op operation= OP_MIX; // if no op + for (SELECT_LEX *sl= first; sl; sl= sl->next_select()) + { + if (sl != first) + { + unit_common_op op; + switch (sl->linkage) + { + case INTERSECT_TYPE: + op= OP_INTERSECT; + break; + case EXCEPT_TYPE: + op= OP_EXCEPT; + break; + default: + op= OP_UNION; + break; + } + if (first_op) + { + operation= op; + first_op= TRUE; + } + else + { + if (operation != op) + operation= OP_MIX; + } + } + } + return operation; +} /* Save explain structures of a UNION. The only variable member is whether the union has "Using filesort". @@ -4776,11 +4813,10 @@ int st_select_lex_unit::save_union_explain(Explain_query *output) Note: Non-merged semi-joins cannot be made out of UNIONs currently, so we dont ever set EXPLAIN_NODE_NON_MERGED_SJ. */ - for (SELECT_LEX *sl= first; sl; sl= sl->next_select()) eu->add_select(sl->select_number); - eu->fake_select_type= "UNION RESULT"; + eu->fake_select_type= unit_operation_text[eu->operation= common_op()]; eu->using_filesort= MY_TEST(global_parameters()->order_list.first); eu->using_tmp= union_needs_tmp_table(); @@ -4836,6 +4872,12 @@ bool LEX::is_partition_management() const } +/** + Exclude last added SELECT_LEX (current) in the UNIT and return pointer in it + (previous become currect) + + @return detached SELECT_LEX or NULL in case of error +*/ SELECT_LEX *LEX::exclude_last_select() { @@ -4857,6 +4899,26 @@ SELECT_LEX *LEX::exclude_last_select() DBUG_RETURN(exclude); } + +/** + Put given (new) SELECT_LEX level below after currect (last) SELECT + + LAST SELECT -> DUMMY SELECT + | + V + NEW UNIT + | + V + NEW SELECT + + SELECT (*LAST*) ... FROM (SELECT (*NEW*) ... ) + + @param nselect Select to put one level below + + @retval TRUE Error + @retval FALSE OK +*/ + bool LEX::add_unit_in_brackets(SELECT_LEX *nselect) { DBUG_ENTER("LEX::add_unit_in_brackets"); @@ -4872,7 +4934,7 @@ bool LEX::add_unit_in_brackets(SELECT_LEX *nselect) Name_resolution_context *context= &dummy_select->context; context->init(); - /* add SELECT list*/ + /* add SELECT list*/ Item *item= new (thd->mem_root) Item_field(thd, context, NULL, NULL, "*"); if (item == NULL) @@ -4895,8 +4957,11 @@ bool LEX::add_unit_in_brackets(SELECT_LEX *nselect) char buff[10]; LEX_STRING alias; alias.length= my_snprintf(buff, sizeof(buff), - "__%d", dummy_select->select_number); - alias.str= thd->strdup(buff); + "__%u", dummy_select->select_number); + alias.str= thd->strmake(buff, alias.length); + if (!alias.str) + DBUG_RETURN(TRUE); + TABLE_LIST *table_list; if (!(table_list= dummy_select->add_table_to_list(thd, ti, &alias, 0, TL_READ, @@ -4912,6 +4977,15 @@ bool LEX::add_unit_in_brackets(SELECT_LEX *nselect) DBUG_RETURN(rc); } + +/** + Checks if we need finish "automatic brackets" mode + + INTERSECT has higher priority then UNION and EXCEPT, so when it is need we + automatically create lower layer for INTERSECT (automatic brackets) and + here we check if we should return back one level up during parsing procedure. +*/ + void LEX::check_automatic_up(enum sub_select_type type) { if (type != INTERSECT_TYPE && diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 3525e36aa5b..e2c6f4aaaaf 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -40,6 +40,7 @@ enum sub_select_type UNION_TYPE, INTERSECT_TYPE, EXCEPT_TYPE, GLOBAL_OPTIONS_TYPE, DERIVED_TABLE_TYPE, OLAP_TYPE }; +enum unit_common_op {OP_MIX, OP_UNION, OP_INTERSECT, OP_EXCEPT}; /* These may not be declared yet */ class Table_ident; class sql_exchange; @@ -738,6 +739,7 @@ public: select_unit *get_union_result() { return union_result; } int save_union_explain(Explain_query *output); int save_union_explain_part2(Explain_query *output); + unit_common_op common_op(); }; typedef class st_select_lex_unit SELECT_LEX_UNIT; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 814cf20979e..0bd787de1cd 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -24694,7 +24694,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) if (unit->union_needs_tmp_table() && unit->fake_select_lex) { unit->fake_select_lex->select_number= FAKE_SELECT_LEX_ID; // just for initialization - unit->fake_select_lex->type= "UNION RESULT"; + unit->fake_select_lex->type= unit_operation_text[unit->common_op()]; unit->fake_select_lex->options|= SELECT_DESCRIBE; } if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE))) diff --git a/sql/sql_union.cc b/sql/sql_union.cc index dfe6f159703..3d9949a2e07 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -54,7 +54,38 @@ int select_unit::prepare(List<Item> &list, SELECT_LEX_UNIT *u) return 0; } +/** + This called by SELECT_LEX_UNIT::exec when select chenged +*/ + +void select_unit::change_select() +{ + uint current_select_number= thd->lex->current_select->select_number; + DBUG_ENTER("select_unit::change_select"); + DBUG_PRINT("enter", ("select in unit change: %u -> %u", + curr_sel, current_select_number)); + DBUG_ASSERT(curr_sel != current_select_number); + curr_sel= current_select_number; + /* New SELECT processing starts */ + DBUG_ASSERT(table->file->inited == 0); + switch (thd->lex->current_select->linkage) + { + case INTERSECT_TYPE: + case EXCEPT_TYPE: + step= thd->lex->current_select->linkage; + break; + default: + step= UNION_TYPE; + break; + } + if (step == INTERSECT_TYPE) + { + intersect_mark->value= prev_step= curr_step; + curr_step= current_select_number; + } + DBUG_VOID_RETURN; +} /** Fill temporary tables for UNION/EXCEPT/INTERSECT @@ -82,31 +113,6 @@ int select_unit::send_data(List<Item> &values) { int rc; int not_reported_error= 0; - if (curr_sel != thd->lex->current_select->select_number) - { - curr_sel= thd->lex->current_select->select_number; - /* New SELECT processing starts */ - DBUG_ASSERT(table->file->inited == 0); - switch (thd->lex->current_select->linkage) - { - case INTERSECT_TYPE: - step= intersect_step; - break; - case EXCEPT_TYPE: - step= except_step; - break; - default: - step= union_step; - break; - } - - if (step == intersect_step) - { - prev_step= curr_step; - intersect_mark->value= prev_step; - curr_step= thd->lex->current_select->select_number; - } - } if (unit->offset_limit_cnt) { // using limit offset,count unit->offset_limit_cnt--; @@ -117,8 +123,12 @@ int select_unit::send_data(List<Item> &values) if (table->no_rows_with_nulls) table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT; if (intersect_mark) - values.push_front(intersect_mark); - fill_record(thd, table, table->field, values, TRUE, FALSE); + { + fill_record(thd, table, table->field + 1, values, TRUE, FALSE); + table->field[0]->store((ulonglong) curr_step, 1); + } + else + fill_record(thd, table, table->field, values, TRUE, FALSE); if (thd->is_error()) { rc= 1; @@ -134,9 +144,10 @@ int select_unit::send_data(List<Item> &values) } } + // select_unit::change_select() change step & Co correctly for each SELECT switch (step) { - case union_step: + case UNION_TYPE: { if ((write_err= table->file->ha_write_tmp_row(table->record[0]))) { @@ -169,7 +180,7 @@ int select_unit::send_data(List<Item> &values) } break; } - case except_step: + case EXCEPT_TYPE: { int find_res; /* @@ -185,10 +196,13 @@ int select_unit::send_data(List<Item> &values) goto end; } else - DBUG_ASSERT(find_res == 1); + { + if ((rc= not_reported_error= (find_res != 1))) + goto end; + } break; } - case intersect_step: + case INTERSECT_TYPE: { int find_res; /* @@ -200,10 +214,7 @@ int select_unit::send_data(List<Item> &values) DBUG_ASSERT(!table->triggers); if (table->field[0]->val_int() != prev_step) { - table->status|= STATUS_DELETED; - not_reported_error= - table->file->ha_delete_tmp_row(table->record[0]); - rc= MY_TEST(not_reported_error); + rc= 0; goto end; } store_record(table, record[1]); @@ -215,9 +226,14 @@ int select_unit::send_data(List<Item> &values) goto end; } else - DBUG_ASSERT(find_res == 1); + { + if ((rc= not_reported_error= (find_res != 1))) + goto end; + } break; } + default: + DBUG_ASSERT(0); } rc= 0; @@ -227,14 +243,12 @@ end: DBUG_ASSERT(rc); table->file->print_error(not_reported_error, MYF(0)); } - if (intersect_mark) - values.pop(); return rc; } bool select_unit::send_eof() { - if (step != intersect_step || + if (step != INTERSECT_TYPE || (thd->lex->current_select->next_select() && thd->lex->current_select->next_select()->linkage == INTERSECT_TYPE)) { @@ -250,7 +264,7 @@ bool select_unit::send_eof() It is last select in the sequence of INTERSECTs so we should filter out all records except marked with actual counter. - TODO: as optimisation for simple case this could be moved to + TODO: as optimization for simple case this could be moved to 'fake_select' WHERE condition */ handler *file= table->file; @@ -262,15 +276,18 @@ bool select_unit::send_eof() do { error= file->ha_rnd_next(table->record[0]); - if (error == HA_ERR_RECORD_DELETED) - { - error= 0; - continue; - } if (error) { if (error == HA_ERR_END_OF_FILE) + { + error= 0; + break; + } + if (unlikely(error == HA_ERR_RECORD_DELETED)) + { error= 0; + continue; + } break; } if (table->field[0]->val_int() != curr_step) @@ -943,6 +960,12 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, hidden= 1; if (!intersect_mark) { + /* + For intersect we add a hidden column first that contains + the current select number of the time when the row was + added to the temporary table + */ + Query_arena *arena, backup_arena; arena= thd->activate_stmt_arena_if_needed(&backup_arena); @@ -959,14 +982,16 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, types.push_front(union_result->intersect_mark= intersect_mark); union_result->intersect_mark->name= (char *)"___"; } - if (union_result->create_result_table(thd, &types, - MY_TEST(union_distinct), - create_options, "", false, - instantiate_tmp_table, false, - hidden)) - goto err; + bool error= + union_result->create_result_table(thd, &types, + MY_TEST(union_distinct), + create_options, "", false, + instantiate_tmp_table, false, + hidden); if (intersect_mark) types.pop(); + if (error) + goto err; } if (fake_select_lex && !fake_select_lex->first_cond_optimization) { @@ -993,6 +1018,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, arena= thd->activate_stmt_arena_if_needed(&backup_arena); saved_error= table->fill_item_list(&item_list); + // Item_list is inherited from 'types', so there could be the counter if (intersect_mark) item_list.pop(); // remove intersect counter @@ -1182,6 +1208,8 @@ bool st_select_lex_unit::exec() { ha_rows records_at_start= 0; thd->lex->current_select= sl; + if (union_result) + union_result->change_select(); if (fake_select_lex) { if (sl != &thd->lex->select_lex) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 4ca96364834..e0739ea848e 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -701,7 +701,7 @@ bool LEX::add_select_to_union_list(bool is_union_distinct, current_select != current_select->master_unit()->first_select())) { /* - This and previous SELECTs should go one level down becaous of + This and previous SELECTs should go one level down because of priority */ SELECT_LEX *prev= exclude_last_select(); @@ -11063,10 +11063,6 @@ table_primary_derived: lex->pop_context(); lex->nest_level--; } - /*else if (($3->select_lex && - $3->select_lex->master_unit()->is_unit_op() && - ($3->select_lex->master_unit()->first_select() == - $3->select_lex || !$3->lifted)) || $5)*/ else if ($5 != NULL) { /* |