revision-id: 6915cb7abe3ba6c237549e6a0803aba41787548e (mariadb-10.2.18-68-g6915cb7) parent(s): 4142589207649e3317adc8c0d371897b7cb53733 author: Igor Babaev committer: Igor Babaev timestamp: 2018-11-08 22:54:03 -0800 message: MDEV-17574 SIGSEGV or Assertion `producing_item != __null' in Item_direct_view_ref::derived_field_transformer_for_where upon updating a view The condition pushed into a materialized derived / view mast be adjusted for the new context: its column references must be substituted for references to the columns of the underlying tables if the condition is pushed into WHERE. The substitution is performed by the 'transform' method. If the materialized derived is used in a mergeable view then the references to the columns of the view are represented by Item_direct_view_ref objects. The transform method first processes the item wrapped in such an object and only after this it transforms the object itself. The transformation procedure of an Item_direct_view_ref object has to know whether the item it wraps has been substituted. If so the procedure does not have to do anything. In the code before this patch it was not possible for the transformation procedure used by an Item_direct_view_ref object to find out whether a substitution for the wrapped item had happened. --- mysql-test/r/derived_cond_pushdown.result | 93 +++++++++++++++++++++++++++++++ mysql-test/t/derived_cond_pushdown.test | 27 +++++++++ sql/item.cc | 32 ++++++++++- sql/item.h | 1 + 4 files changed, 150 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/derived_cond_pushdown.result b/mysql-test/r/derived_cond_pushdown.result index f5bc72e..14c8e4d 100644 --- a/mysql-test/r/derived_cond_pushdown.result +++ b/mysql-test/r/derived_cond_pushdown.result @@ -10387,4 +10387,97 @@ f 3 DROP VIEW v1; DROP TABLE t1; +# +# MDEV-17574: pushdown into derived from mergeable view +# used in multi-table UPDATE +# pushdown into materialized derived from mergeable view +# used in SELECT +# +CREATE TABLE t1 (f1 text, f2 int); +INSERT INTO t1 VALUES ('x',1), ('y',2); +CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 ) AS t; +UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2; +EXPLAIN FORMAT=JSON UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "<derived3>", + "access_type": "ALL", + "rows": 2, + "filtered": 100, + "attached_condition": "t.f2 < 2", + "materialized": { + "query_block": { + "select_id": 3, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 2, + "filtered": 100, + "attached_condition": "t1.f2 < 2" + } + } + } + }, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 2, + "filtered": 100, + "attached_condition": "t1.f2 = t.f2" + } + } +} +SELECT * FROM t1; +f1 f2 +z 1 +y 2 +CREATE VIEW v2 AS SELECT f2 FROM ( SELECT DISTINCT f2 FROM t1 ) AS t; +SELECT * FROM v2, t1 WHERE v2.f2 < 2 AND t1.f2 = v2.f2; +f2 f1 f2 +1 z 1 +EXPLAIN FORMAT=JSON SELECT * FROM v2, t1 WHERE v2.f2 < 2 AND t1.f2 = v2.f2; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "<derived3>", + "access_type": "ALL", + "rows": 2, + "filtered": 100, + "attached_condition": "t.f2 < 2", + "materialized": { + "query_block": { + "select_id": 3, + "temporary_table": { + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 2, + "filtered": 100, + "attached_condition": "t1.f2 < 2" + } + } + } + } + }, + "block-nl-join": { + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 2, + "filtered": 100 + }, + "buffer_type": "flat", + "buffer_size": "256Kb", + "join_type": "BNL", + "attached_condition": "t1.f2 = t.f2" + } + } +} +DROP VIEW v1,v2; +DROP TABLE t1; # End of 10.2 tests diff --git a/mysql-test/t/derived_cond_pushdown.test b/mysql-test/t/derived_cond_pushdown.test index 46aa898..25cb29e 100644 --- a/mysql-test/t/derived_cond_pushdown.test +++ b/mysql-test/t/derived_cond_pushdown.test @@ -2075,4 +2075,31 @@ SELECT * FROM t1; DROP VIEW v1; DROP TABLE t1; +--echo # +--echo # MDEV-17574: pushdown into derived from mergeable view +--echo # used in multi-table UPDATE +--echo # pushdown into materialized derived from mergeable view +--echo # used in SELECT +--echo # + +CREATE TABLE t1 (f1 text, f2 int); +INSERT INTO t1 VALUES ('x',1), ('y',2); + +CREATE VIEW v1 AS SELECT f2 FROM ( SELECT f2 FROM t1 ) AS t; +let $q1 = +UPDATE v1, t1 SET t1.f1 = 'z' WHERE v1.f2 < 2 AND t1.f2 = v1.f2; +eval $q1; +eval EXPLAIN FORMAT=JSON $q1; + +SELECT * FROM t1; + +CREATE VIEW v2 AS SELECT f2 FROM ( SELECT DISTINCT f2 FROM t1 ) AS t; +let $q2 = +SELECT * FROM v2, t1 WHERE v2.f2 < 2 AND t1.f2 = v2.f2; +eval $q2; +eval EXPLAIN FORMAT=JSON $q2; + +DROP VIEW v1,v2; +DROP TABLE t1; + --echo # End of 10.2 tests diff --git a/sql/item.cc b/sql/item.cc index 2adec33..9ac1ed3 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -7143,13 +7143,21 @@ Item *Item_field::derived_field_transformer_for_having(THD *thd, uchar *arg) return this; if (!item_equal && used_tables() != tab_map) return this; - return get_field_item_for_having(thd, this, sel); + Item *item= get_field_item_for_having(thd, this, sel); + if (item) + item->marker|= SUBSTITUTION_FL; + return item; } Item *Item_direct_view_ref::derived_field_transformer_for_having(THD *thd, uchar *arg) { + if ((*ref)->marker & SUBSTITUTION_FL) + { + this->marker|= SUBSTITUTION_FL; + return this; + } st_select_lex *sel= (st_select_lex *)arg; table_map tab_map= sel->master_unit()->derived->table->map; if ((item_equal && !(item_equal->used_tables() & tab_map)) || @@ -7200,13 +7208,20 @@ Item *Item_field::derived_field_transformer_for_where(THD *thd, uchar *arg) st_select_lex *sel= (st_select_lex *)arg; Item *producing_item= find_producing_item(this, sel); if (producing_item) - return producing_item->build_clone(thd, thd->mem_root); + { + Item *producing_clone= producing_item->build_clone(thd, thd->mem_root); + if (producing_clone) + producing_clone->marker|= SUBSTITUTION_FL; + return producing_clone; + } return this; } Item *Item_direct_view_ref::derived_field_transformer_for_where(THD *thd, uchar *arg) { + if ((*ref)->marker & SUBSTITUTION_FL) + return (*ref); if (item_equal) { st_select_lex *sel= (st_select_lex *)arg; @@ -7258,7 +7273,13 @@ Item *Item_field::derived_grouping_field_transformer_for_where(THD *thd, st_select_lex *sel= (st_select_lex *)arg; Grouping_tmp_field *gr_field= find_matching_grouping_field(this, sel); if (gr_field) - return gr_field->producing_item->build_clone(thd, thd->mem_root); + { + Item *producing_clone= + gr_field->producing_item->build_clone(thd, thd->mem_root); + if (producing_clone) + producing_clone->marker|= SUBSTITUTION_FL; + return producing_clone; + } return this; } @@ -7267,6 +7288,11 @@ Item * Item_direct_view_ref::derived_grouping_field_transformer_for_where(THD *thd, uchar *arg) { + if ((*ref)->marker & SUBSTITUTION_FL) + { + this->marker|= SUBSTITUTION_FL; + return this; + } if (!item_equal) return this; st_select_lex *sel= (st_select_lex *)arg; diff --git a/sql/item.h b/sql/item.h index 8d02d98..3a64ea1 100644 --- a/sql/item.h +++ b/sql/item.h @@ -147,6 +147,7 @@ bool mark_unsupported_function(const char *w1, const char *w2, #define NO_EXTRACTION_FL (1 << 6) #define FULL_EXTRACTION_FL (1 << 7) +#define SUBSTITUTION_FL (1 << 8) #define EXTRACTION_MASK (NO_EXTRACTION_FL | FULL_EXTRACTION_FL) class DTCollation {