diff options
author | Igor Babaev <igor@askmonty.org> | 2018-02-22 10:08:49 -0800 |
---|---|---|
committer | Igor Babaev <igor@askmonty.org> | 2018-02-22 10:26:56 -0800 |
commit | 907b236112d95681cb09be98197b97bbd2b525dd (patch) | |
tree | 79eee71c3ea4bb29a7f8e0168f31b922693a798f | |
parent | 988ec800edb3dd9238b6f3948157d21bdb0c083b (diff) | |
download | mariadb-git-907b236112d95681cb09be98197b97bbd2b525dd.tar.gz |
Fixed MDEV-14883 Usage of EXCEPT and INTERSECT in recursive CTE
is not supported
Allowed to use recursive references in derived tables.
As a result usage of recursive references in operands of
INTERSECT / EXCEPT is now supported.
-rw-r--r-- | mysql-test/r/cte_recursive.result | 233 | ||||
-rw-r--r-- | mysql-test/t/cte_recursive.test | 170 | ||||
-rw-r--r-- | sql/sql_cte.cc | 31 | ||||
-rw-r--r-- | sql/sql_cte.h | 4 | ||||
-rw-r--r-- | sql/sql_derived.cc | 14 | ||||
-rw-r--r-- | sql/sql_select.cc | 3 | ||||
-rw-r--r-- | sql/sql_union.cc | 24 | ||||
-rw-r--r-- | sql/table.cc | 1 | ||||
-rw-r--r-- | sql/table.h | 6 |
9 files changed, 464 insertions, 22 deletions
diff --git a/mysql-test/r/cte_recursive.result b/mysql-test/r/cte_recursive.result index dd0f1e91cb7..15d4fc1a01f 100644 --- a/mysql-test/r/cte_recursive.result +++ b/mysql-test/r/cte_recursive.result @@ -3105,3 +3105,236 @@ SELECT * FROM cte; 5 2 3 +# +# MDEV-14883: recursive references in operands of INTERSECT / EXCEPT +# +create table flights +(departure varchar(32), +arrival varchar(32), +carrier varchar(20), +flight_number char(7)); +insert into flights values +('Seattle', 'Frankfurt', 'Lufthansa', 'LH 491'), +('Seattle', 'Amsterdam', 'KLM', 'KL 6032'), +('Seattle', 'Chicago', 'American', 'AA 2573'), +('Seattle', 'Los Angeles', 'Alaska Air', 'AS 410'), +('Chicago', 'New York', 'American', 'AA 375'), +('Chicago', 'Montreal', 'Air Canada', 'AC 3053'), +('Los Angeles', 'New York', 'Delta', 'DL 1197'), +('New York', 'London', 'British Airways', 'BA 1511'), +('London', 'Moscow', 'British Airways', 'BA 233'), +('Moscow', 'Tokyo', 'Aeroflot', 'SU 264'), +('Moscow', 'Dubai', 'Emirates', 'EK 2421'), +('Dubai', 'Tokyo', 'Emirates', 'EK 318'), +('Dubai', 'Bangkok', 'Emirates', 'EK 2142'), +('Beijing', 'Bangkok', 'Air China', 'CA 757'), +('Beijing', 'Tokyo', 'Air China', 'CA 6653'), +('Moscow', 'Bangkok', 'Aeroflot', 'SU 270'), +('New York', 'Reykjavik', 'Icelandair', 'FL 416'), +('New York', 'Paris', 'Air France', 'AF 23'), +('Amsterdam', 'Moscow', 'KLM', 'KL 903'), +('Frankfurt', 'Dubai', 'Lufthansa', 'LH 630'), +('Frankfurt', 'Moscow', 'Lufthansa', 'LH 1444'), +('Reykjavik', 'London', 'British Airways', 'BA 2229'), +('Frankfurt', 'Beijing', 'Air China', 'CA 966'), +('Tokyo', 'Seattle', 'ANA', 'NH 178'), +('Los Angeles', 'Tokyo', 'ANA', 'NH 175'), +('Moscow', 'Los Angeles', 'Aeroflot', 'SU 106'), +('Montreal', 'Paris', 'Air Canada', 'AC 870'), +('London', 'Delhi', 'British Airways', 'BA 143'), +('Delhi', 'Bangkok', 'Air India', 'AI 306'), +('Delhi', 'Dubai', 'Air India', 'AI 995'), +('Dubai', 'Cairo', 'Emirates', 'EK 927'), +('Cairo', 'Paris', 'Air France', 'AF 503'), +('Amsterdam', 'New York', 'Delta', 'DL 47'), +('New York', 'Seattle', 'American', 'AA 45'), +('Paris', 'Chicago', 'Air France', 'AF 6734'); +create table distances +(city1 varchar(32), +city2 varchar(32), +dist int); +insert into distances values +('Seattle', 'Frankfurt', 5080), +('Seattle', 'Amsterdam', 4859), +('Seattle', 'Chicago', 1733), +('Seattle', 'Los Angeles', 960), +('Chicago', 'New York', 712), +('Chicago', 'Montreal', 746), +('Los Angeles', 'New York', 2446), +('New York', 'London', 3459), +('London', 'Moscow', 1554), +('Moscow', 'Tokyo', 4647), +('Moscow', 'Dubai', 2298), +('Dubai', 'Tokyo', 4929), +('Dubai', 'Bangkok', 3050), +('Beijing', 'Bangkok', 2046), +('Beijing', 'Tokyo', 1301), +('Moscow', 'Bangkok', 4390), +('New York', 'Reykjavik', 2613), +('New York', 'Paris', 3625), +('Amsterdam', 'Moscow', 1334), +('Frankfurt', 'Dubai', 3003), +('Frankfurt', 'Moscow', 1256), +('Reykjavik', 'London', 1173), +('Frankfurt', 'Beijing', 4836), +('Tokyo', 'Seattle', 4783), +('Los Angeles', 'Tokyo', 5479), +('Moscow', 'Los Angeles', 6071), +('Moscow', 'Reykjavik', 2052), +('Montreal', 'Paris', 3425), +('London', 'Delhi', 4159), +('London', 'Paris', 214), +('Delhi', 'Bangkok', 1810), +('Delhi', 'Dubai', 1369), +('Delhi', 'Beijing', 2350), +('Dubai', 'Cairo', 1501), +('Cairo', 'Paris', 1992), +('Amsterdam', 'New York', 3643), +('New York', 'Seattle', 2402), +('Paris', 'Chicago', 4136), +('Paris', 'Los Angeles', 5647); +with recursive destinations (city) as +( +select a.arrival from flights a where a.departure = 'Seattle' + union +select b.arrival from destinations r, flights b where r.city = b.departure +) +select * from destinations; +city +Frankfurt +Amsterdam +Chicago +Los Angeles +New York +Montreal +Moscow +Dubai +Beijing +Tokyo +London +Bangkok +Reykjavik +Paris +Seattle +Cairo +Delhi +with recursive destinations (city) as +( +select a.arrival from flights a, distances d +where a.departure = 'Seattle' and +a.departure = d.city1 and a.arrival = d.city2 and +d.dist < 4000 +union +select b.arrival from destinations r, flights b, distances d +where r.city = b.departure and +b.departure = d.city1 and b.arrival = d.city2 and +d.dist < 4000 +) +select * from destinations; +city +Chicago +Los Angeles +New York +Montreal +London +Reykjavik +Paris +Seattle +Moscow +Dubai +Bangkok +Cairo +set standard_compliant_cte=0; +with recursive legs_to_destinations +(departure, arrival, dist, leg_no, acc_mileage) as +( +select a.departure, a.arrival, d.dist, 1, d.dist +from flights a, distances d +where a.departure = 'Seattle' and +a.departure = d.city1 and a.arrival = d.city2 and +d.dist < 4000 +union all +select b.departure, b.arrival, d.dist, r.leg_no + 1, r.acc_mileage + d.dist +from legs_to_destinations r, flights b, distances d +where r.arrival = b.departure and +b.departure = d.city1 and b.arrival = d.city2 and +d.dist < 4000 and +b.arrival not in (select arrival from legs_to_destinations) +) +select * from legs_to_destinations; +departure arrival dist leg_no acc_mileage +Seattle Chicago 1733 1 1733 +Seattle Los Angeles 960 1 960 +Chicago New York 712 2 2445 +Chicago Montreal 746 2 2479 +Los Angeles New York 2446 2 3406 +New York London 3459 3 6865 +New York London 3459 3 5904 +New York Reykjavik 2613 3 6019 +New York Reykjavik 2613 3 5058 +New York Paris 3625 3 7031 +New York Paris 3625 3 6070 +Montreal Paris 3425 3 5904 +New York Seattle 2402 3 5808 +New York Seattle 2402 3 4847 +London Moscow 1554 4 7458 +London Moscow 1554 4 8419 +Moscow Dubai 2298 5 10717 +Moscow Dubai 2298 5 9756 +Dubai Bangkok 3050 6 12806 +Dubai Bangkok 3050 6 13767 +Dubai Cairo 1501 6 11257 +Dubai Cairo 1501 6 12218 +set standard_compliant_cte=default; +with recursive destinations (city) as +( +select a.arrival from flights a, distances d +where a.departure = 'Seattle' and +a.departure = d.city1 and a.arrival = d.city2 and +d.dist < 4000 +union +select b.arrival from destinations r, flights b +where r.city = b.departure +intersect +select city2 from destinations s, distances d +where s.city = d.city1 and d.dist < 4000 +) +select * from destinations; +city +Chicago +Los Angeles +New York +Montreal +London +Reykjavik +Paris +Seattle +Moscow +Dubai +Bangkok +Cairo +with recursive destinations (city) as +( +select a.arrival from flights a where a.departure = 'Seattle' + union +select * from +( +select b.arrival from destinations r, flights b +where r.city = b.departure +except +select arrival from flights +where arrival in +('New York', 'London', 'Moscow', 'Dubai', 'Cairo', 'Tokyo') +) t +) +select * from destinations; +city +Frankfurt +Amsterdam +Chicago +Los Angeles +Montreal +Beijing +Bangkok +Paris +drop table flights, distances; diff --git a/mysql-test/t/cte_recursive.test b/mysql-test/t/cte_recursive.test index c6391aabc41..7ed55a1daaa 100644 --- a/mysql-test/t/cte_recursive.test +++ b/mysql-test/t/cte_recursive.test @@ -2138,3 +2138,173 @@ WITH RECURSIVE cte AS UNION SELECT @c:=@c+1 FROM cte WHERE @c<3) SELECT * FROM cte; + +--echo # +--echo # MDEV-14883: recursive references in operands of INTERSECT / EXCEPT +--echo # + +create table flights +(departure varchar(32), + arrival varchar(32), + carrier varchar(20), + flight_number char(7)); + +insert into flights values +('Seattle', 'Frankfurt', 'Lufthansa', 'LH 491'), +('Seattle', 'Amsterdam', 'KLM', 'KL 6032'), +('Seattle', 'Chicago', 'American', 'AA 2573'), +('Seattle', 'Los Angeles', 'Alaska Air', 'AS 410'), +('Chicago', 'New York', 'American', 'AA 375'), +('Chicago', 'Montreal', 'Air Canada', 'AC 3053'), +('Los Angeles', 'New York', 'Delta', 'DL 1197'), +('New York', 'London', 'British Airways', 'BA 1511'), +('London', 'Moscow', 'British Airways', 'BA 233'), +('Moscow', 'Tokyo', 'Aeroflot', 'SU 264'), +('Moscow', 'Dubai', 'Emirates', 'EK 2421'), +('Dubai', 'Tokyo', 'Emirates', 'EK 318'), +('Dubai', 'Bangkok', 'Emirates', 'EK 2142'), +('Beijing', 'Bangkok', 'Air China', 'CA 757'), +('Beijing', 'Tokyo', 'Air China', 'CA 6653'), +('Moscow', 'Bangkok', 'Aeroflot', 'SU 270'), +('New York', 'Reykjavik', 'Icelandair', 'FL 416'), +('New York', 'Paris', 'Air France', 'AF 23'), +('Amsterdam', 'Moscow', 'KLM', 'KL 903'), +('Frankfurt', 'Dubai', 'Lufthansa', 'LH 630'), +('Frankfurt', 'Moscow', 'Lufthansa', 'LH 1444'), +('Reykjavik', 'London', 'British Airways', 'BA 2229'), +('Frankfurt', 'Beijing', 'Air China', 'CA 966'), +('Tokyo', 'Seattle', 'ANA', 'NH 178'), +('Los Angeles', 'Tokyo', 'ANA', 'NH 175'), +('Moscow', 'Los Angeles', 'Aeroflot', 'SU 106'), +('Montreal', 'Paris', 'Air Canada', 'AC 870'), +('London', 'Delhi', 'British Airways', 'BA 143'), +('Delhi', 'Bangkok', 'Air India', 'AI 306'), +('Delhi', 'Dubai', 'Air India', 'AI 995'), +('Dubai', 'Cairo', 'Emirates', 'EK 927'), +('Cairo', 'Paris', 'Air France', 'AF 503'), +('Amsterdam', 'New York', 'Delta', 'DL 47'), +('New York', 'Seattle', 'American', 'AA 45'), +('Paris', 'Chicago', 'Air France', 'AF 6734'); + +create table distances +(city1 varchar(32), + city2 varchar(32), + dist int); + +insert into distances values +('Seattle', 'Frankfurt', 5080), +('Seattle', 'Amsterdam', 4859), +('Seattle', 'Chicago', 1733), +('Seattle', 'Los Angeles', 960), +('Chicago', 'New York', 712), +('Chicago', 'Montreal', 746), +('Los Angeles', 'New York', 2446), +('New York', 'London', 3459), +('London', 'Moscow', 1554), +('Moscow', 'Tokyo', 4647), +('Moscow', 'Dubai', 2298), +('Dubai', 'Tokyo', 4929), +('Dubai', 'Bangkok', 3050), +('Beijing', 'Bangkok', 2046), +('Beijing', 'Tokyo', 1301), +('Moscow', 'Bangkok', 4390), +('New York', 'Reykjavik', 2613), +('New York', 'Paris', 3625), +('Amsterdam', 'Moscow', 1334), +('Frankfurt', 'Dubai', 3003), +('Frankfurt', 'Moscow', 1256), +('Reykjavik', 'London', 1173), +('Frankfurt', 'Beijing', 4836), +('Tokyo', 'Seattle', 4783), +('Los Angeles', 'Tokyo', 5479), +('Moscow', 'Los Angeles', 6071), +('Moscow', 'Reykjavik', 2052), +('Montreal', 'Paris', 3425), +('London', 'Delhi', 4159), +('London', 'Paris', 214), +('Delhi', 'Bangkok', 1810), +('Delhi', 'Dubai', 1369), +('Delhi', 'Beijing', 2350), +('Dubai', 'Cairo', 1501), +('Cairo', 'Paris', 1992), +('Amsterdam', 'New York', 3643), +('New York', 'Seattle', 2402), +('Paris', 'Chicago', 4136), +('Paris', 'Los Angeles', 5647); + +with recursive destinations (city) as +( + select a.arrival from flights a where a.departure = 'Seattle' + union + select b.arrival from destinations r, flights b where r.city = b.departure +) +select * from destinations; + +with recursive destinations (city) as +( + select a.arrival from flights a, distances d + where a.departure = 'Seattle' and + a.departure = d.city1 and a.arrival = d.city2 and + d.dist < 4000 + union + select b.arrival from destinations r, flights b, distances d + where r.city = b.departure and + b.departure = d.city1 and b.arrival = d.city2 and + d.dist < 4000 +) +select * from destinations; + +set standard_compliant_cte=0; + +with recursive legs_to_destinations + (departure, arrival, dist, leg_no, acc_mileage) as +( + select a.departure, a.arrival, d.dist, 1, d.dist + from flights a, distances d + where a.departure = 'Seattle' and + a.departure = d.city1 and a.arrival = d.city2 and + d.dist < 4000 + union all + select b.departure, b.arrival, d.dist, r.leg_no + 1, r.acc_mileage + d.dist + from legs_to_destinations r, flights b, distances d + where r.arrival = b.departure and + b.departure = d.city1 and b.arrival = d.city2 and + d.dist < 4000 and + b.arrival not in (select arrival from legs_to_destinations) +) +select * from legs_to_destinations; + +set standard_compliant_cte=default; + +with recursive destinations (city) as +( + select a.arrival from flights a, distances d + where a.departure = 'Seattle' and + a.departure = d.city1 and a.arrival = d.city2 and + d.dist < 4000 + union + select b.arrival from destinations r, flights b + where r.city = b.departure + intersect + select city2 from destinations s, distances d + where s.city = d.city1 and d.dist < 4000 +) +select * from destinations; + +with recursive destinations (city) as +( + select a.arrival from flights a where a.departure = 'Seattle' + union + select * from + ( + select b.arrival from destinations r, flights b + where r.city = b.departure + except + select arrival from flights + where arrival in + ('New York', 'London', 'Moscow', 'Dubai', 'Cairo', 'Tokyo') + ) t +) +select * from destinations; + +drop table flights, distances; diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index 05d12998026..61eaa535c00 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -1314,32 +1314,29 @@ bool With_element::check_unrestricted_recursive(st_select_lex *sel, bool st_select_lex::check_subqueries_with_recursive_references() { - st_select_lex_unit *sl_master= master_unit(); List_iterator<TABLE_LIST> ti(leaf_tables); TABLE_LIST *tbl; while ((tbl= ti++)) { - if (!(tbl->is_with_table_recursive_reference() && sl_master->item)) + if (!(tbl->is_with_table_recursive_reference())) continue; - With_element *with_elem= tbl->with; - bool check_embedding_materialized_derived= true; + With_element *rec_elem= tbl->with; + st_select_lex_unit *sl_master; for (st_select_lex *sl= this; sl; sl= sl_master->outer_select()) - { + { sl_master= sl->master_unit(); - if (with_elem->get_owner() == sl_master->with_clause) - check_embedding_materialized_derived= false; - if (check_embedding_materialized_derived && !sl_master->with_element && - sl_master->derived && sl_master->derived->is_materialized_derived()) + if (sl_master->with_element && + sl_master->with_element->get_owner() == rec_elem->get_owner()) + break; + sl->uncacheable|= UNCACHEABLE_DEPENDENT; + sl_master->uncacheable|= UNCACHEABLE_DEPENDENT; + if (sl_master->derived) + sl_master->derived->register_as_derived_with_rec_ref(rec_elem); + if (sl_master->item) { - my_error(ER_REF_TO_RECURSIVE_WITH_TABLE_IN_DERIVED, - MYF(0), with_elem->query_name->str); - return true; + Item_subselect *subq= (Item_subselect *) (sl_master->item); + subq->register_as_with_rec_ref(rec_elem); } - if (!sl_master->item) - continue; - Item_subselect *subq= (Item_subselect *) sl_master->item; - subq->with_recursive_reference= true; - subq->register_as_with_rec_ref(tbl->with); } } return false; diff --git a/sql/sql_cte.h b/sql/sql_cte.h index 13e9b83955c..16b473f0665 100644 --- a/sql/sql_cte.h +++ b/sql/sql_cte.h @@ -146,7 +146,9 @@ public: select_union_recursive *rec_result; /* List of Item_subselects containing recursive references to this CTE */ - SQL_I_List<Item_subselect> sq_with_rec_ref; + SQL_I_List<Item_subselect> sq_with_rec_ref; + /* List of derived tables containing recursive references to this CTE */ + SQL_I_List<TABLE_LIST> derived_with_rec_ref; With_element(LEX_CSTRING *name, List <LEX_CSTRING> list, diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index be5058b7c63..ab66384c6cb 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -1010,6 +1010,20 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived) } +void TABLE_LIST::register_as_derived_with_rec_ref(With_element *rec_elem) +{ + rec_elem->derived_with_rec_ref.link_in_list(this, &this->next_with_rec_ref); + is_derived_with_recursive_reference= true; + get_unit()->uncacheable|= UNCACHEABLE_DEPENDENT; +} + + +bool TABLE_LIST::is_nonrecursive_derived_with_rec_ref() +{ + return is_derived_with_recursive_reference; +} + + /** @brief Fill the recursive with table diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a58c34cc2fa..82f332b83df 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -12500,7 +12500,8 @@ bool JOIN_TAB::preread_init() derived, DT_CREATE | DT_FILL)) return TRUE; - if (!(derived->get_unit()->uncacheable & UNCACHEABLE_DEPENDENT)) + if (!(derived->get_unit()->uncacheable & UNCACHEABLE_DEPENDENT) || + derived->is_nonrecursive_derived_with_rec_ref()) preread_init_done= TRUE; if (select && select->quick) select->quick->replace_handler(table->file); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index a14c5dd4bd5..857c9a117f5 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -1365,7 +1365,15 @@ bool st_select_lex_unit::exec() DBUG_RETURN(saved_error); if (union_result) + { union_result->init(); + if (uncacheable & UNCACHEABLE_DEPENDENT && + union_result->table && union_result->table->is_created()) + { + union_result->table->file->ha_delete_all_rows(); + union_result->table->file->ha_enable_indexes(HA_KEY_SWITCH_ALL); + } + } if (uncacheable || !item || !item->assigned() || describe) { @@ -1663,6 +1671,20 @@ bool st_select_lex_unit::exec_recursive() for (st_select_lex *sl= start ; sl != end; sl= sl->next_select()) { + if (with_element->level) + { + for (TABLE_LIST *derived= with_element->derived_with_rec_ref.first; + derived; + derived= derived->next_with_rec_ref) + { + if (derived->is_materialized_derived()) + { + if (derived->table->is_created()) + derived->table->file->ha_delete_all_rows(); + derived->table->reginfo.join_tab->preread_init_done= false; + } + } + } thd->lex->current_select= sl; if (sl->tvc) sl->tvc->exec(sl); @@ -1706,7 +1728,7 @@ bool st_select_lex_unit::exec_recursive() if (!with_element->rec_result->first_rec_table_to_update) with_element->rec_result->first_rec_table_to_update= rec_table; if (with_element->level == 1 && rec_table->reginfo.join_tab) - rec_table->reginfo.join_tab->preread_init_done= true; + rec_table->reginfo.join_tab->preread_init_done= true; } for (Item_subselect *sq= with_element->sq_with_rec_ref.first; sq; diff --git a/sql/table.cc b/sql/table.cc index 549814a57bc..36e92e0e303 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -8371,7 +8371,6 @@ bool TABLE_LIST::is_with_table() return derived && derived->with_element; } - uint TABLE_SHARE::actual_n_key_parts(THD *thd) { return use_ext_keys && diff --git a/sql/table.h b/sql/table.h index 917439c243d..1977b7fc7e3 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2131,7 +2131,9 @@ inline void init_one_table(const LEX_CSTRING *db_arg, st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */ With_element *with; /* With element defining this table (if any) */ /* Bitmap of the defining with element */ - table_map with_internal_reference_map; + table_map with_internal_reference_map; + TABLE_LIST * next_with_rec_ref; + bool is_derived_with_recursive_reference; bool block_handle_derived; ST_SCHEMA_TABLE *schema_table; /* Information_schema table */ st_select_lex *schema_select_lex; @@ -2508,6 +2510,8 @@ inline void init_one_table(const LEX_CSTRING *db_arg, bool is_with_table(); bool is_recursive_with_table(); bool is_with_table_recursive_reference(); + void register_as_derived_with_rec_ref(With_element *rec_elem); + bool is_nonrecursive_derived_with_rec_ref(); bool fill_recursive(THD *thd); inline void set_view() |