lists.mariadb.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

commits

Thread Start a new thread
Threads by month
  • ----- 2025 -----
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
commits@lists.mariadb.org

  • 14605 discussions
[Commits] 0dd2d7d91d2: MDEV-20224: main.derived crashes with ASAN with error use-after-poison
by Varun 01 Aug '19

01 Aug '19
revision-id: 0dd2d7d91d23bcfc035de8a024af40dc36f543ff (mariadb-10.4.4-258-g0dd2d7d91d2) parent(s): 46553c250808f0cc21e91f7e13439add2903aea0 author: Varun Gupta committer: Varun Gupta timestamp: 2019-08-01 11:16:06 +0530 message: MDEV-20224: main.derived crashes with ASAN with error use-after-poison Make sure that the references in the GROUP BY clause that need to be considered for pushdown from having to where are of Item_field objects --- sql/sql_lex.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index feda4cd23fb..eceef9a7097 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -7893,6 +7893,7 @@ bool st_select_lex::collect_grouping_fields(THD *thd) Item *item= *ord->item; if (item->type() != Item::FIELD_ITEM && !(item->type() == Item::REF_ITEM && + ((Item_ref*)item)->real_type() == Item::FIELD_ITEM && ((((Item_ref *) item)->ref_type() == Item_ref::VIEW_REF) || (((Item_ref *) item)->ref_type() == Item_ref::REF)))) continue;
1 0
0 0
[Commits] c776ed4d6ec: MDEV-19848 Server crashes in check_vcol_forward_refs upon INSERT DELAYED into table with long blob key
by sachin.setiya@mariadb.com 29 Jul '19

29 Jul '19
revision-id: c776ed4d6ecf9a0393a8eb53cd6660ff3eb63758 (mariadb-10.4.5-153-gc776ed4d6ec) parent(s): 4ca016237f1a1813c9e1ce9e4227c056c53896bb author: Sachin committer: Sachin timestamp: 2019-07-30 03:42:21 +0530 message: MDEV-19848 Server crashes in check_vcol_forward_refs upon INSERT DELAYED into table with long blob key There are 2 issues 1st:- in make_new_field when we & into new field flag we forget LONG_UNIQUE_HASH_FIELD Flag. 2nd:- We are calling parse_vcol_defs on keyinfo , but they are not in right form. We should call setup_keyinfo_hash_all before calling parse_vcol_defs --- mysql-test/main/long_unique_bugs.result | 3 +++ mysql-test/main/long_unique_bugs.test | 8 ++++++++ sql/field.cc | 2 +- sql/sql_insert.cc | 1 + sql/table.cc | 20 ++++++++++++++++++++ sql/table.h | 2 ++ 6 files changed, 35 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/long_unique_bugs.result b/mysql-test/main/long_unique_bugs.result index 910e5e592e1..4e831bcff92 100644 --- a/mysql-test/main/long_unique_bugs.result +++ b/mysql-test/main/long_unique_bugs.result @@ -268,3 +268,6 @@ DROP TABLE t1, t2; create table t1(a int, unique(a) using hash); #BULK insert > 100 rows (MI_MIN_ROWS_TO_DISABLE_INDEXES) drop table t1; +CREATE TABLE t1 (a BLOB, UNIQUE(a)) ENGINE=MyISAM; +INSERT DELAYED t1 () VALUES (); +DROP TABLE t1; diff --git a/mysql-test/main/long_unique_bugs.test b/mysql-test/main/long_unique_bugs.test index da054e59f34..b9b10b3d6cd 100644 --- a/mysql-test/main/long_unique_bugs.test +++ b/mysql-test/main/long_unique_bugs.test @@ -334,3 +334,11 @@ while ($count) --eval $insert_stmt --enable_query_log drop table t1; + +# +# MDEV-19848 Server crashes in check_vcol_forward_refs upon INSERT DELAYED into table with long blob key +# +CREATE TABLE t1 (a BLOB, UNIQUE(a)) ENGINE=MyISAM; +INSERT DELAYED t1 () VALUES (); +# Cleanup +DROP TABLE t1; diff --git a/sql/field.cc b/sql/field.cc index 621f0136bea..caae3bca430 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -2326,7 +2326,7 @@ Field *Field::make_new_field(MEM_ROOT *root, TABLE *new_table, tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG | ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG | VERS_SYS_START_FLAG | VERS_SYS_END_FLAG | - VERS_UPDATE_UNVERSIONED_FLAG); + VERS_UPDATE_UNVERSIONED_FLAG | LONG_UNIQUE_HASH_FIELD); tmp->reset_fields(); tmp->invisible= VISIBLE; return tmp; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index f3a548d7265..a05bc934553 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2623,6 +2623,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) if (share->virtual_fields || share->default_expressions || share->default_fields) { + table->setup_keyinfo_hash_all(); bool error_reported= FALSE; if (unlikely(parse_vcol_defs(client_thd, client_thd->mem_root, copy, &error_reported))) diff --git a/sql/table.cc b/sql/table.cc index 48421c4051e..d859152f700 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -9039,6 +9039,26 @@ void re_setup_keyinfo_hash(KEY *key_info) key_info->ext_key_parts= 1; key_info->flags&= ~HA_NOSAME; } + +/* + call setup_keyinfo_hash for all keys in table + */ +void TABLE::setup_keyinfo_hash_all() +{ + for (uint i= 0; i < s->keys; i++) + if (key_info[i].algorithm == HA_KEY_ALG_LONG_HASH) + setup_keyinfo_hash(&key_info[i]); +} + +/* + call re_setup_keyinfo_hash for all keys in table + */ +void TABLE::re_setup_keyinfo_hash_all() +{ + for (uint i= 0; i < s->keys; i++) + if (key_info[i].algorithm == HA_KEY_ALG_LONG_HASH) + re_setup_keyinfo_hash(&key_info[i]); +} /** @brief clone of current handler. Creates a clone of handler used in update for diff --git a/sql/table.h b/sql/table.h index 42c017d63af..40ff7cdba29 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1596,6 +1596,8 @@ struct TABLE void vers_update_fields(); void vers_update_end(); void find_constraint_correlated_indexes(); + void setup_keyinfo_hash_all(); + void re_setup_keyinfo_hash_all(); void clone_handler_for_update(); void delete_update_handler();
1 0
0 0
[Commits] c5b7cc28b0c: MDEV-20129: Equality propagation for ORDER BY items do not work with expressions
by Varun 29 Jul '19

29 Jul '19
revision-id: c5b7cc28b0cbc15c9bf44fe5a74e8ef363773133 (mariadb-10.4.4-255-gc5b7cc28b0c) parent(s): ddce85907611e0533d6226de7f53e751cf173f6a author: Varun Gupta committer: Varun Gupta timestamp: 2019-07-30 00:29:11 +0530 message: MDEV-20129: Equality propagation for ORDER BY items do not work with expressions Introduced a function equality_propagation_for_order_items, that would propagate equalities to order by clause and will substitute them with the best field --- mysql-test/main/order_by.result | 110 ++++++++++++++++++++++++++++++++++++++++ mysql-test/main/order_by.test | 54 ++++++++++++++++++++ sql/item.cc | 50 ++++++++++++++++++ sql/item.h | 20 ++++++++ sql/item_func.cc | 11 ++++ sql/item_func.h | 5 ++ sql/sql_select.cc | 23 +++------ 7 files changed, 258 insertions(+), 15 deletions(-) diff --git a/mysql-test/main/order_by.result b/mysql-test/main/order_by.result index b059cc686cd..54190d2f608 100644 --- a/mysql-test/main/order_by.result +++ b/mysql-test/main/order_by.result @@ -3436,3 +3436,113 @@ Note 1003 select `test`.`t4`.`a` AS `a`,`test`.`t4`.`b` AS `b`,`test`.`t4`.`c` A set histogram_size=@tmp_h, histogram_type=@tmp_ht, use_stat_tables=@tmp_u, optimizer_use_condition_selectivity=@tmp_o; drop table t1,t2,t3,t4; +create table ten(a int); +insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table one_k(a int); +insert into one_k select A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C; +create table t1(a int, b int); +insert into t1 select A.a + B.a* 10, A.a + B.a* 10 from ten A, ten B; +create table t2(a int, b int, key(a)); +insert into t2 select A.a + B.a* 10, A.a+B.a*10 from ten A, ten B; +should have Using Filesort only +explain select * from t1,t2 where t1.a > 95 and t1.a=t2.a order by t2.a; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 Using where; Using filesort +1 SIMPLE t2 ref a a 5 test.t1.a 1 +should have Using Filesort only with expressions too +explain select * from t1,t2 where t1.a > 95 and t1.a=t2.a order by t2.a+t1.b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 Using where; Using filesort +1 SIMPLE t2 ref a a 5 test.t1.a 1 +drop table t1,t2,ten,one_k; +CREATE TABLE t1(a int, b int); +CREATE TABLE t2(a int, b int, index i(a)); +INSERT INTO t1 VALUES (1,1), (2,2); +INSERT INTO t2 VALUES (1,5), (1,6), (2,5), (2,6); +INSERT INTO t2 VALUES (1,5), (1,6), (2,5), (2,6); +INSERT INTO t2 VALUES (1,5), (1,6), (2,5), (2,6); +CREATE VIEW v1 AS +SELECT t1.a as c, t2.a as d FROM t1,t2 WHERE t1.a=t2.a; +CREATE VIEW v2 AS +SELECT t1.a+t2.a as c, abs(t2.a) as d FROM t1,t2 WHERE t1.a=t2.a; +should have Using Filesort only +explain SELECT c,d FROM v1 ORDER BY c; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where; Using filesort +1 SIMPLE t2 ref i i 5 test.t1.a 2 Using index +SELECT c,d FROM v1 ORDER BY c; +c d +1 1 +1 1 +1 1 +1 1 +1 1 +1 1 +2 2 +2 2 +2 2 +2 2 +2 2 +2 2 +views use Item_direct_view_ref, so this shows equalities are propagated +should have Using Filesort only +explain SELECT c,d FROM v1 ORDER BY d; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where; Using filesort +1 SIMPLE t2 ref i i 5 test.t1.a 2 Using index +SELECT c,d FROM v1 ORDER BY d; +c d +1 1 +1 1 +1 1 +1 1 +1 1 +1 1 +2 2 +2 2 +2 2 +2 2 +2 2 +2 2 +views use Item_direct_view_ref that are expressions +should have Using Filesort only +explain SELECT c,d FROM v2 ORDER BY c; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where; Using filesort +1 SIMPLE t2 ref i i 5 test.t1.a 2 Using index +SELECT c,d FROM v2 ORDER BY c; +c d +2 1 +2 1 +2 1 +2 1 +2 1 +2 1 +4 2 +4 2 +4 2 +4 2 +4 2 +4 2 +views use Item_direct_view_ref that are expressions +should have Using Filesort only +explain SELECT c,d FROM v2 ORDER BY d; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where; Using filesort +1 SIMPLE t2 ref i i 5 test.t1.a 2 Using index +SELECT c,d FROM v2 ORDER BY d; +c d +2 1 +2 1 +2 1 +2 1 +2 1 +2 1 +4 2 +4 2 +4 2 +4 2 +4 2 +4 2 +DROP VIEW v1,v2; +DROP TABLE t1, t2; diff --git a/mysql-test/main/order_by.test b/mysql-test/main/order_by.test index 934c503302f..2f39bb88329 100644 --- a/mysql-test/main/order_by.test +++ b/mysql-test/main/order_by.test @@ -2276,3 +2276,57 @@ set histogram_size=@tmp_h, histogram_type=@tmp_ht, use_stat_tables=@tmp_u, optimizer_use_condition_selectivity=@tmp_o; drop table t1,t2,t3,t4; + +create table ten(a int); +insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table one_k(a int); +insert into one_k select A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C; +create table t1(a int, b int); +insert into t1 select A.a + B.a* 10, A.a + B.a* 10 from ten A, ten B; +create table t2(a int, b int, key(a)); +insert into t2 select A.a + B.a* 10, A.a+B.a*10 from ten A, ten B; + +--echo should have Using Filesort only +explain select * from t1,t2 where t1.a > 95 and t1.a=t2.a order by t2.a; + +--echo should have Using Filesort only with expressions too +explain select * from t1,t2 where t1.a > 95 and t1.a=t2.a order by t2.a+t1.b; +drop table t1,t2,ten,one_k; + +CREATE TABLE t1(a int, b int); +CREATE TABLE t2(a int, b int, index i(a)); +INSERT INTO t1 VALUES (1,1), (2,2); +INSERT INTO t2 VALUES (1,5), (1,6), (2,5), (2,6); +INSERT INTO t2 VALUES (1,5), (1,6), (2,5), (2,6); +INSERT INTO t2 VALUES (1,5), (1,6), (2,5), (2,6); +CREATE VIEW v1 AS + SELECT t1.a as c, t2.a as d FROM t1,t2 WHERE t1.a=t2.a; + +CREATE VIEW v2 AS + SELECT t1.a+t2.a as c, abs(t2.a) as d FROM t1,t2 WHERE t1.a=t2.a; + + +--echo should have Using Filesort only +let $query= SELECT c,d FROM v1 ORDER BY c; +eval explain $query; +eval $query; + +--echo views use Item_direct_view_ref, so this shows equalities are propagated +--echo should have Using Filesort only +let $query= SELECT c,d FROM v1 ORDER BY d; +eval explain $query; +eval $query; + +--echo views use Item_direct_view_ref that are expressions +--echo should have Using Filesort only +let $query= SELECT c,d FROM v2 ORDER BY c; +eval explain $query; +eval $query; + +--echo views use Item_direct_view_ref that are expressions +--echo should have Using Filesort only +let $query= SELECT c,d FROM v2 ORDER BY d; +eval explain $query; +eval $query; +DROP VIEW v1,v2; +DROP TABLE t1, t2; diff --git a/sql/item.cc b/sql/item.cc index 42bcb216935..53220453cd9 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6096,6 +6096,23 @@ Item *Item_field::propagate_equal_fields(THD *thd, } +Item *Item_field::equality_propagation_for_order_items(THD *thd, + const Context &ctx, + COND_EQUAL *arg) +{ + Item *item= propagate_equal_fields(thd, ctx, arg); + + DBUG_ASSERT(item); + Item_equal *item_eq; + if ((item_eq= item->get_item_equal())) + { + Item *first= item_eq->get_first(NO_PARTICULAR_TAB, NULL); + return first; + } + return item; +} + + /** Replace an Item_field for an equal Item_field that evaluated earlier (if any). @@ -7110,6 +7127,18 @@ Item* Item::propagate_equal_fields_and_change_item_tree(THD *thd, } +Item* Item::equality_propagation_and_change_item_tree(THD *thd, + const Context &ctx, + COND_EQUAL *cond, + Item **place) +{ + Item *item= equality_propagation_for_order_items(thd, ctx, cond); + if (item && item != this) + thd->change_item_tree(place, item); + return item; +} + + void Item_field::update_null_value() { /* @@ -9003,6 +9032,17 @@ Item *Item_direct_view_ref::propagate_equal_fields(THD *thd, } +Item *Item_direct_view_ref::equality_propagation_for_order_items(THD *thd, + const Context &ctx, + COND_EQUAL *arg) +{ + Item *item= real_item(); + Item *res= item->equality_propagation_for_order_items(thd, ctx, arg); + set_item_equal(res->get_item_equal()); + return res; +} + + Item *Item_ref::propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) { @@ -9016,6 +9056,16 @@ Item *Item_ref::propagate_equal_fields(THD *thd, const Context &ctx, } +Item *Item_ref::equality_propagation_for_order_items(THD *thd, + const Context &ctx, + COND_EQUAL *arg) +{ + Item *item= real_item(); + Item *res= item->equality_propagation_for_order_items(thd, ctx, arg); + return res; +} + + /** Replace an Item_direct_view_ref for an equal Item_field evaluated earlier (if any). diff --git a/sql/item.h b/sql/item.h index 14c29fe4e6a..b6ca0d81ad0 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2022,10 +2022,26 @@ class Item: public Value_source, return this; }; + /* + This function would propagate the equalities to the order item and + return the first item of the Item_equal if the field + has participated in an equality + */ + + virtual Item* equality_propagation_for_order_items(THD*, const Context &, COND_EQUAL *) + { + return this; + }; + + Item* propagate_equal_fields_and_change_item_tree(THD *thd, const Context &ctx, COND_EQUAL *cond, Item **place); + Item* equality_propagation_and_change_item_tree(THD *thd, + const Context &ctx, + COND_EQUAL *cond, + Item **place); /* arg points to REPLACE_EQUAL_FIELD_ARG object */ virtual Item *replace_equal_field(THD *thd, uchar *arg) { return this; } @@ -2448,6 +2464,7 @@ class Item_args } bool transform_args(THD *thd, Item_transformer transformer, uchar *arg); void propagate_equal_fields(THD *, const Item::Context &, COND_EQUAL *); + void equality_propagation_for_order_items(THD *, const Item::Context &, COND_EQUAL *); bool excl_dep_on_table(table_map tab_map) { for (uint i= 0; i < arg_count; i++) @@ -3392,6 +3409,7 @@ class Item_field :public Item_ident, void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; } Item_equal *find_item_equal(COND_EQUAL *cond_equal); Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *); + Item* equality_propagation_for_order_items(THD *, const Context &, COND_EQUAL *); Item *replace_equal_field(THD *thd, uchar *arg); uint32 max_display_length() const { return field->max_display_length(); } Item_field *field_for_view_update() { return this; } @@ -5120,6 +5138,7 @@ class Item_ref :public Item_ident, Field *create_tmp_field_ex(MEM_ROOT *root, TABLE *table, Tmp_field_src *src, const Tmp_field_param *param); Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *); + Item* equality_propagation_for_order_items(THD *, const Context &, COND_EQUAL *); table_map used_tables() const; void update_used_tables(); COND *build_equal_items(THD *thd, COND_EQUAL *inherited, @@ -5554,6 +5573,7 @@ class Item_direct_view_ref :public Item_direct_ref void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; } Item_equal *find_item_equal(COND_EQUAL *cond_equal); Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *); + Item* equality_propagation_for_order_items(THD *, const Context &, COND_EQUAL *); Item *replace_equal_field(THD *thd, uchar *arg); table_map used_tables() const; void update_used_tables(); diff --git a/sql/item_func.cc b/sql/item_func.cc index 03abe4f26fb..cdf710d9be0 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -561,6 +561,17 @@ void Item_args::propagate_equal_fields(THD *thd, } +void Item_args::equality_propagation_for_order_items(THD *thd, + const Item::Context &ctx, + COND_EQUAL *cond) +{ + uint i; + for (i= 0; i < arg_count; i++) + args[i]->equality_propagation_and_change_item_tree(thd, ctx, cond, + &args[i]); +} + + /** See comments in Item_cond::split_sum_func() */ diff --git a/sql/item_func.h b/sql/item_func.h index 00ed688e9e3..4e15fe2fd7e 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -326,6 +326,11 @@ class Item_func :public Item_func_or_sum, Item_args::propagate_equal_fields(thd, Context_identity(), cond); return this; } + Item* equality_propagation_for_order_items(THD *thd, const Context &ctx, COND_EQUAL *cond) + { + Item_args::equality_propagation_for_order_items(thd, Context_identity(), cond); + return this; + } bool has_rand_bit() { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8163f5b4cf0..39ca47991eb 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13987,7 +13987,6 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, */ if (optimizer_flag(join->thd, OPTIMIZER_SWITCH_ORDERBY_EQ_PROP) && first_is_base_table && !first_is_in_sjm_nest && - order->item[0]->real_item()->type() == Item::FIELD_ITEM && join->cond_equal) { table_map first_table_bit= @@ -14003,20 +14002,14 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, multiple equality the item belongs to and set item->item_equal accordingly. */ - Item *res= item->propagate_equal_fields(join->thd, - Value_source:: - Context_identity(), - join->cond_equal); - Item_equal *item_eq; - if ((item_eq= res->get_item_equal())) - { - Item *first= item_eq->get_first(NO_PARTICULAR_TAB, NULL); - if (first->const_item() || first->used_tables() == - first_table_bit) - { - can_subst_to_first_table= true; - } - } + Item *res= item->equality_propagation_for_order_items(join->thd, + Value_source:: + Context_identity(), + join->cond_equal); + res->update_used_tables(); + Item *real_item= res->real_item(); + if (real_item->const_item() || real_item->used_tables() == first_table_bit) + can_subst_to_first_table= TRUE; } if (!can_subst_to_first_table)
1 0
0 0
[Commits] 4ca016237f1: MDEV-20001 Potential dangerous regression: INSERT INTO >=100 rows fail for myisam table with HASH indexes
by sachin.setiya@mariadb.com 29 Jul '19

29 Jul '19
revision-id: 4ca016237f1a1813c9e1ce9e4227c056c53896bb (mariadb-10.4.5-152-g4ca016237f1) parent(s): 4a5cd4072894864c9f1f59675c2b8b8cd2ae40a4 author: Sachin committer: Sachin timestamp: 2019-07-29 19:33:05 +0530 message: MDEV-20001 Potential dangerous regression: INSERT INTO >=100 rows fail for myisam table with HASH indexes Dont deactivate the long unique keys on bulk insert. --- mysql-test/main/long_unique_bugs.result | 3 +++ mysql-test/main/long_unique_bugs.test | 17 +++++++++++++++++ storage/myisam/ha_myisam.cc | 11 ++++++++++- storage/myisam/mi_check.c | 5 +++-- storage/myisam/myisamdef.h | 2 +- 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/mysql-test/main/long_unique_bugs.result b/mysql-test/main/long_unique_bugs.result index 33496c4e20d..910e5e592e1 100644 --- a/mysql-test/main/long_unique_bugs.result +++ b/mysql-test/main/long_unique_bugs.result @@ -265,3 +265,6 @@ ERROR 40001: Deadlock found when trying to get lock; try restarting transaction disconnect con1; connection default; DROP TABLE t1, t2; +create table t1(a int, unique(a) using hash); +#BULK insert > 100 rows (MI_MIN_ROWS_TO_DISABLE_INDEXES) +drop table t1; diff --git a/mysql-test/main/long_unique_bugs.test b/mysql-test/main/long_unique_bugs.test index dc78f6c7067..da054e59f34 100644 --- a/mysql-test/main/long_unique_bugs.test +++ b/mysql-test/main/long_unique_bugs.test @@ -317,3 +317,20 @@ INSERT IGNORE INTO t1 VALUES (4, 1)/*4*/; --disconnect con1 --connection default DROP TABLE t1, t2; + +# +# MDEV-20001 Potential dangerous regression: INSERT INTO >=100 rows fail for myisam table with HASH indexes +# +create table t1(a int, unique(a) using hash); +--let $count=150 +--let insert_stmt= insert into t1 values(200) +while ($count) +{ + --let $insert_stmt=$insert_stmt,($count) + --dec $count +} +--disable_query_log +--echo #BULK insert > 100 rows (MI_MIN_ROWS_TO_DISABLE_INDEXES) +--eval $insert_stmt +--enable_query_log +drop table t1; diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index f478e01e441..c1169737911 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -1749,7 +1749,16 @@ void ha_myisam::start_bulk_insert(ha_rows rows, uint flags) else { my_bool all_keys= MY_TEST(flags & HA_CREATE_UNIQUE_INDEX_BY_SORT); - mi_disable_indexes_for_rebuild(file, rows, all_keys); + if (table->s->long_unique_table) + { + ulonglong hash_key_map= 0ULL; + for(uint i= 0; i < table->s->keys; i++) + if (table->key_info[i].algorithm == HA_KEY_ALG_LONG_HASH) + mi_set_key_active(hash_key_map, i); + mi_disable_indexes_for_rebuild(file, rows, all_keys, hash_key_map); + } + else + mi_disable_indexes_for_rebuild(file, rows, all_keys, 0ULL); } } else diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c index 869e86b7495..d3205b89d90 100644 --- a/storage/myisam/mi_check.c +++ b/storage/myisam/mi_check.c @@ -4694,7 +4694,7 @@ static my_bool mi_too_big_key_for_sort(MI_KEYDEF *key, ha_rows rows) */ void mi_disable_indexes_for_rebuild(MI_INFO *info, ha_rows rows, - my_bool all_keys) + my_bool all_keys, ulonglong hash_key_map) { MYISAM_SHARE *share=info->s; MI_KEYDEF *key=share->keyinfo; @@ -4706,7 +4706,8 @@ void mi_disable_indexes_for_rebuild(MI_INFO *info, ha_rows rows, { if (!(key->flag & (HA_SPATIAL | HA_AUTO_KEY)) && ! mi_too_big_key_for_sort(key,rows) && info->s->base.auto_key != i+1 && - (all_keys || !(key->flag & HA_NOSAME))) + (all_keys || !(key->flag & HA_NOSAME)) && + !mi_is_key_active(hash_key_map, i)) { mi_clear_key_active(share->state.key_map, i); info->update|= HA_STATE_CHANGED; diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h index 7fc8a8eba4f..a8794f76fbd 100644 --- a/storage/myisam/myisamdef.h +++ b/storage/myisam/myisamdef.h @@ -716,7 +716,7 @@ void mi_copy_status(void *to, void *from); my_bool mi_check_status(void *param); void mi_fix_status(MI_INFO *org_table, MI_INFO *new_table); void mi_disable_indexes_for_rebuild(MI_INFO *info, ha_rows rows, - my_bool all_keys); + my_bool all_keys, ulonglong hash_key_map); extern MI_INFO *test_if_reopen(char *filename); my_bool check_table_is_closed(const char *name, const char *where); int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share);
1 0
0 0
[Commits] 2de94d8de7a: Poor optimization of JOIN and ORDER BY LIMIT
by Varun 26 Jul '19

26 Jul '19
revision-id: 2de94d8de7a48a4a169dae8cd2a915dcd73de22f (mariadb-10.4.4-154-g2de94d8de7a) parent(s): c51f615bf575480b13ee542fdb3b7fe644e21098 author: Varun Gupta committer: Varun Gupta timestamp: 2019-07-26 23:43:04 +0530 message: Poor optimization of JOIN and ORDER BY LIMIT Iteration one --- mysql-test/main/order_by_limit.result | 200 +++++++++++ mysql-test/main/order_by_limit.test | 23 ++ mysql-test/main/sort_nest.result | 524 +++++++++++++++++++++++++++ mysql-test/main/sort_nest.test | 145 ++++++++ sql/item.cc | 41 ++- sql/item.h | 40 +++ sql/item_cmpfunc.cc | 16 + sql/item_cmpfunc.h | 1 + sql/item_func.h | 8 + sql/item_row.h | 5 + sql/opt_split.cc | 1 - sql/opt_subselect.cc | 30 ++ sql/opt_subselect.h | 1 + sql/opt_trace.cc | 20 ++ sql/opt_trace.h | 1 + sql/sql_class.h | 14 + sql/sql_const.h | 8 + sql/sql_select.cc | 660 +++++++++++++++++++++++++++++++--- sql/sql_select.h | 40 ++- 19 files changed, 1729 insertions(+), 49 deletions(-) diff --git a/mysql-test/main/order_by_limit.result b/mysql-test/main/order_by_limit.result new file mode 100644 index 00000000000..5199cc88519 --- /dev/null +++ b/mysql-test/main/order_by_limit.result @@ -0,0 +1,200 @@ +create table ten(a int); +insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table one_k(a int); +insert into one_k select A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C; +create table t1(a int, b int); +insert into t1 select A.a + B.a* 10, A.a + B.a* 10 from ten A, ten B,ten C; +create table t2(a int, b int); +insert into t2(a,b) values (1,1), (2,1); +insert into t2 select A.a + B.a* 10, -1 from ten A, ten B; +create table t3(a int, b int); +insert into t3 select A.a + B.a* 10 + C.a * 100, A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C; +set optimizer_trace=1; +the best plan would be having an order nest on (t2,t1) +explain +select * from t1,t2,t3 where t1.a=t2.b order by t1.b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using temporary; Using filesort +1 SIMPLE t1 ALL NULL NULL NULL NULL 1000 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using join buffer (incremental, BNL join) +select JSON_DETAILED(JSON_EXTRACT(trace, '$**.considered_execution_plans')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE; +JSON_DETAILED(JSON_EXTRACT(trace, '$**.considered_execution_plans')) +[ + + [ + + { + "plan_prefix": + [ + ], + "table": "t2", + "best_access_path": + { + "considered_access_paths": + [ + + { + "access_type": "scan", + "resulting_rows": 102, + "cost": 2.2241, + "chosen": true + } + ] + }, + "rest_of_plan": + [ + + { + "plan_prefix": + [ + "t2" + ], + "table": "t1", + "best_access_path": + { + "considered_access_paths": + [ + + { + "access_type": "scan", + "resulting_rows": 1000, + "cost": 4.1973, + "chosen": true + } + ] + }, + "rest_of_plan": + [ + + { + "plan_prefix": + [ + "t2", + "t1" + ], + "table": "t3", + "best_access_path": + { + "considered_access_paths": + [ + + { + "access_type": "scan", + "resulting_rows": 1000, + "cost": 33.578, + "chosen": true + } + ] + }, + "cardinality": 1.02e8, + "cost_of_plan": 1.22e8 + }, + + { + "plan_prefix": + [ + "t2", + "t1" + ], + "table": "t3", + "order_by_nest": + [ + "t2", + "t1" + ], + "best_access_path": + { + "considered_access_paths": + [ + + { + "access_type": "scan", + "resulting_rows": 1000, + "cost": 428121, + "chosen": true + } + ] + }, + "cost_of_plan": 2.1e7 + } + ] + }, + + { + "plan_prefix": + [ + "t2" + ], + "table": "t3", + "best_access_path": + { + "considered_access_paths": + [ + + { + "access_type": "scan", + "resulting_rows": 1000, + "cost": 4.1973, + "chosen": true + } + ] + }, + "pruned_by_heuristic": true + } + ] + }, + + { + "plan_prefix": + [ + ], + "table": "t1", + "best_access_path": + { + "considered_access_paths": + [ + + { + "access_type": "scan", + "resulting_rows": 1000, + "cost": 4.1973, + "chosen": true, + "use_tmp_table": true + } + ] + }, + "pruned_by_heuristic": true + }, + + { + "plan_prefix": + [ + ], + "table": "t3", + "best_access_path": + { + "considered_access_paths": + [ + + { + "access_type": "scan", + "resulting_rows": 1000, + "cost": 4.1973, + "chosen": true + } + ] + }, + "pruned_by_heuristic": true + } + ] +] +select JSON_DETAILED(JSON_EXTRACT(trace, '$**.order_nest')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE; +JSON_DETAILED(JSON_EXTRACT(trace, '$**.order_nest')) +[ + + [ + "t2", + "t1" + ] +] +drop table t1,t2,t3,ten,one_k; diff --git a/mysql-test/main/order_by_limit.test b/mysql-test/main/order_by_limit.test new file mode 100644 index 00000000000..b717e84a1a8 --- /dev/null +++ b/mysql-test/main/order_by_limit.test @@ -0,0 +1,23 @@ +create table ten(a int); +insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +create table one_k(a int); +insert into one_k select A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C; + +create table t1(a int, b int); +insert into t1 select A.a + B.a* 10, A.a + B.a* 10 from ten A, ten B,ten C; +create table t2(a int, b int); +insert into t2(a,b) values (1,1), (2,1); +insert into t2 select A.a + B.a* 10, -1 from ten A, ten B; +create table t3(a int, b int); +insert into t3 select A.a + B.a* 10 + C.a * 100, A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C; + +set optimizer_trace=1; + +--echo the best plan would be having an order nest on (t2,t1) + +explain +select * from t1,t2,t3 where t1.a=t2.b order by t1.b; +select JSON_DETAILED(JSON_EXTRACT(trace, '$**.considered_execution_plans')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE; +select JSON_DETAILED(JSON_EXTRACT(trace, '$**.order_nest')) from INFORMATION_SCHEMA.OPTIMIZER_TRACE; +drop table t1,t2,t3,ten,one_k; diff --git a/mysql-test/main/sort_nest.result b/mysql-test/main/sort_nest.result new file mode 100644 index 00000000000..f3e10597536 --- /dev/null +++ b/mysql-test/main/sort_nest.result @@ -0,0 +1,524 @@ +create table ten(a int); +insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table one_k(a int); +insert into one_k select A.a + B.a* 10 from ten A, ten B; +create table t1 (a int, b int); +insert into t1 select a,a from one_k; +create table t2 (a int, b int); +insert into t2 select a,a from one_k; +explain +select t1.b from t1,t2 where t1.a=t2.a order by t2.b limit 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 Using filesort +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 Using where +select t1.b from t1,t2 where t1.a=t2.a order by t2.b limit 2; +b +0 +1 +analyze +select t1.a,t2.a from t1,t2 where t1.a=t2.a order by t2.b limit 10; +id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 10.00 100.00 100.00 Using filesort +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 91.00 100.00 1.10 Using where +select t1.a,t2.a from t1,t2 where t1.a=t2.a order by t2.b limit 10; +a a +0 0 +1 1 +2 2 +3 3 +4 4 +5 5 +6 6 +7 7 +8 8 +9 9 +alter table t1 add key (a); +ref should be func +explain +select t1.b,t2.b from t1,t2 where t1.a=t2.a+1 order by t2.b limit 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 Using filesort +1 SIMPLE t1 ref a a 5 func 1 Using index condition +select t1.b,t2.b from t1,t2 where t1.a=t2.a+1 order by t2.b limit 2; +b b +1 0 +2 1 +ref should be order-nest.a +explain +select t1.b,t2.b from t1,t2 where t1.a=t2.a order by t2.b limit 2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 ALL NULL NULL NULL NULL 100 Using where; Using filesort +1 SIMPLE t1 ref a a 5 test.t2.a 1 +select t1.b,t2.b from t1,t2 where t1.a=t2.a order by t2.b limit 2; +b b +0 0 +1 1 +drop table t1,t2,ten,one_k; +create table ten(a int); +insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table one_k(a int); +insert into one_k select A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C; +create table t1(a int, b int); +insert into t1 select A.a + B.a* 10, A.a + B.a* 10 from ten A, ten B; +create table t2(a int, b int); +insert into t2(a,b) values (1,1), (2,2); +insert into t2 select A.a + B.a* 10, A.a+B.a*10 from ten A, ten B; +create table t3(a int, b int); +insert into t3 select A.a + B.a* 10 + C.a * 100, A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C; +create function f1(a int) returns int +begin +declare b int default 0; +return a+b; +end| +Covering 3 table joins +# {t1,t2} part of the nest +# t1.a > 95 would be attached to table t1 +# t1.b=t2.a would be attached to table t2; +explain select * from t1,t2,t3 where t1.a > 95 and t1.a=t2.a and t1.b = t3.a order by t2.b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 Using where +1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using where; Using join buffer (flat, BNL join) +1 SIMPLE <order-nest> ALL NULL NULL NULL NULL 10200 Using filesort +1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where +explain format=json select * from t1,t2,t3 where t1.a > 95 and t1.a=t2.a and t1.b = t3.a order by t2.b; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 100, + "filtered": 100, + "attached_condition": "t1.a > 95" + }, + "block-nl-join": { + "table": { + "table_name": "t2", + "access_type": "ALL", + "rows": 102, + "filtered": 100 + }, + "buffer_type": "flat", + "buffer_size": "1Kb", + "join_type": "BNL", + "attached_condition": "t2.a = t1.a" + }, + "read_sorted_file": { + "filesort": { + "sort_key": "`order-nest`.b", + "table": { + "table_name": "<order-nest>", + "access_type": "ALL", + "rows": 10200, + "filtered": 100 + } + } + }, + "table": { + "table_name": "t3", + "access_type": "ALL", + "rows": 1000, + "filtered": 100, + "attached_condition": "t3.a = `order-nest`.b" + } + } +} +select * from t1,t2,t3 where t1.a > 95 and t1.a=t2.a and t1.b = t3.a order by t2.b; +a b a b a b +96 96 96 96 96 96 +97 97 97 97 97 97 +98 98 98 98 98 98 +99 99 99 99 99 99 +# {t1,t2} part of the sort nest +# (t2.a < 2 or t1.b > 98) would be attached to table t2 +explain select * from t1,t2,t3 where (t3.a<2 and t2.a <2) or (t1.b > 98 and t3.b > 98) +order by t1.a, t2.b limit 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 +1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using where; Using join buffer (flat, BNL join) +1 SIMPLE <order-nest> ALL NULL NULL NULL NULL 10200 Using filesort +1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where +explain format=json select * from t1,t2,t3 where (t3.a<2 and t2.a <2) or (t1.b > 98 and t3.b > 98) +order by t1.a, t2.b limit 5; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 100, + "filtered": 100 + }, + "block-nl-join": { + "table": { + "table_name": "t2", + "access_type": "ALL", + "rows": 102, + "filtered": 100 + }, + "buffer_type": "flat", + "buffer_size": "1Kb", + "join_type": "BNL", + "attached_condition": "t2.a < 2 or t1.b > 98" + }, + "read_sorted_file": { + "filesort": { + "sort_key": "`order-nest`.a, `order-nest`.b", + "table": { + "table_name": "<order-nest>", + "access_type": "ALL", + "rows": 10200, + "filtered": 100 + } + } + }, + "table": { + "table_name": "t3", + "access_type": "ALL", + "rows": 1000, + "filtered": 100, + "attached_condition": "t3.a < 2 and `order-nest`.a < 2 or `order-nest`.b > 98 and t3.b > 98" + } + } +} +select * from t1,t2,t3 where (t3.a<2 and t2.a <2) or (t1.b > 98 and t3.b > 98) +order by t1.a, t2.b limit 5; +a b a b a b +0 0 1 1 0 0 +0 0 1 1 1 1 +1 1 1 1 0 0 +1 1 1 1 1 1 +2 2 1 1 0 0 +# {t1,t2} part of the nest +# t2.a < 2 or f1(t1.b) attached to table t2 +# t1.b=t2.a would be attached to table t2; +explain select * from t1,t2,t3 where (t3.a<2 and t2.a <2) or (f1(t1.b) > 98 and t3.b > 98) +order by t1.a,t2.b limit 5; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 +1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using join buffer (flat, BNL join) +1 SIMPLE <order-nest> ALL NULL NULL NULL NULL 10200 Using filesort +1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where +explain format=json select * from t1,t2,t3 where (t3.a<2 and t2.a <2) or (f1(t1.b) > 98 and t3.b > 98) +order by t1.a,t2.b limit 5; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 100, + "filtered": 100 + }, + "block-nl-join": { + "table": { + "table_name": "t2", + "access_type": "ALL", + "rows": 102, + "filtered": 100 + }, + "buffer_type": "flat", + "buffer_size": "1Kb", + "join_type": "BNL" + }, + "read_sorted_file": { + "filesort": { + "sort_key": "`order-nest`.a, `order-nest`.b", + "table": { + "table_name": "<order-nest>", + "access_type": "ALL", + "rows": 10200, + "filtered": 100 + } + } + }, + "table": { + "table_name": "t3", + "access_type": "ALL", + "rows": 1000, + "filtered": 100, + "attached_condition": "t3.a < 2 and `order-nest`.a < 2 or f1(`order-nest`.b) > 98 and t3.b > 98" + } + } +} +select * from t1,t2,t3 where (t3.a<2 and t2.a <2) or (f1(t1.b) > 98 and t3.b > 98) +order by t1.a,t2.b limit 5; +a b a b a b +0 0 0 0 0 0 +0 0 0 0 1 1 +0 0 1 1 0 0 +0 0 1 1 1 1 +0 0 1 1 0 0 +# +# Removing constant from the order by clause +# +explain select * from t1,t2 where t1.a > 95 and t1.a=t2.a order by t2.a limit 4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 Using where; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using where +explain format=json select * from t1,t2 where t1.a > 95 and t1.a=t2.a order by t2.a limit 4; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "read_sorted_file": { + "filesort": { + "sort_key": "t2.a", + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 100, + "filtered": 100, + "attached_condition": "t1.a > 95" + } + } + }, + "table": { + "table_name": "t2", + "access_type": "ALL", + "rows": 102, + "filtered": 100, + "attached_condition": "t2.a = t1.a" + } + } +} +select * from t1,t2 where t1.a > 95 and t1.a=t2.a order by t2.a limit 4; +a b a b +96 96 96 96 +97 97 97 97 +98 98 98 98 +99 99 99 99 +explain select * from t1,t2 where t1.a > 95 and t1.a=t2.a order by 1+2,t2.a limit 4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 Using where; Using filesort +1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using where +explain format=json select * from t1,t2 where t1.a > 95 and t1.a=t2.a order by 1+2,t2.a limit 4; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "read_sorted_file": { + "filesort": { + "sort_key": "t2.a", + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 100, + "filtered": 100, + "attached_condition": "t1.a > 95" + } + } + }, + "table": { + "table_name": "t2", + "access_type": "ALL", + "rows": 102, + "filtered": 100, + "attached_condition": "t2.a = t1.a" + } + } +} +select * from t1,t2 where t1.a > 95 and t1.a=t2.a order by 1+2,t2.a limit 4; +a b a b +96 96 96 96 +97 97 97 97 +98 98 98 98 +99 99 99 99 +# +# Equality propagation, both the queries should use a sort nest on {t1,t2} +# +explain select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.b=t3.b +order by t1.b desc, t2.a desc limit 3; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 +1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using join buffer (flat, BNL join) +1 SIMPLE <order-nest> ALL NULL NULL NULL NULL 10200 Using filesort +1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where +explain format=json select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.b=t3.b +order by t1.b desc, t2.a desc limit 3; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 100, + "filtered": 100 + }, + "block-nl-join": { + "table": { + "table_name": "t2", + "access_type": "ALL", + "rows": 102, + "filtered": 100 + }, + "buffer_type": "flat", + "buffer_size": "1Kb", + "join_type": "BNL" + }, + "read_sorted_file": { + "filesort": { + "sort_key": "`order-nest`.b desc, `order-nest`.a desc", + "table": { + "table_name": "<order-nest>", + "access_type": "ALL", + "rows": 10200, + "filtered": 100 + } + } + }, + "table": { + "table_name": "t3", + "access_type": "ALL", + "rows": 1000, + "filtered": 100, + "attached_condition": "t3.b = `order-nest`.b" + } + } +} +select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.b=t3.b +order by t1.b desc, t2.a desc limit 3; +b a b a +99 99 99 99 +99 98 99 99 +99 97 99 99 +explain select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.b=t3.b +order by t3.b desc, t2.a desc limit 3; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 +1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using join buffer (flat, BNL join) +1 SIMPLE <order-nest> ALL NULL NULL NULL NULL 10200 Using filesort +1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where +select * from INFORMATION_SCHEMA.OPTIMIZER_TRACE; +QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES +explain format=json select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.b=t3.b +order by t3.b desc, t2.a desc limit 3; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 100, + "filtered": 100 + }, + "block-nl-join": { + "table": { + "table_name": "t2", + "access_type": "ALL", + "rows": 102, + "filtered": 100 + }, + "buffer_type": "flat", + "buffer_size": "1Kb", + "join_type": "BNL" + }, + "read_sorted_file": { + "filesort": { + "sort_key": "`order-nest`.b desc, `order-nest`.a desc", + "table": { + "table_name": "<order-nest>", + "access_type": "ALL", + "rows": 10200, + "filtered": 100 + } + } + }, + "table": { + "table_name": "t3", + "access_type": "ALL", + "rows": 1000, + "filtered": 100, + "attached_condition": "t3.b = `order-nest`.b" + } + } +} +select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.b=t3.b +order by t3.b desc, t2.a desc limit 3; +b a b a +99 99 99 99 +99 98 99 99 +99 97 99 99 +# +# Equality propagation also for arguments of expressions, +# the plan should use a sort nest on {t1,t2} +# +explain select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.b=t3.b +order by t3.b+1 desc, t2.a desc limit 3; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 100 +1 SIMPLE t2 ALL NULL NULL NULL NULL 102 Using join buffer (flat, BNL join) +1 SIMPLE <order-nest> ALL NULL NULL NULL NULL 10200 Using filesort +1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 Using where +explain format=json select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.b=t3.b +order by t3.b+1 desc, t2.a desc limit 3; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 100, + "filtered": 100 + }, + "block-nl-join": { + "table": { + "table_name": "t2", + "access_type": "ALL", + "rows": 102, + "filtered": 100 + }, + "buffer_type": "flat", + "buffer_size": "1Kb", + "join_type": "BNL" + }, + "read_sorted_file": { + "filesort": { + "sort_key": "tmp_field desc, `order-nest`.a desc", + "table": { + "table_name": "<order-nest>", + "access_type": "ALL", + "rows": 10200, + "filtered": 100 + } + } + }, + "table": { + "table_name": "t3", + "access_type": "ALL", + "rows": 1000, + "filtered": 100, + "attached_condition": "t3.b = `order-nest`.b" + } + } +} +select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.b=t3.b +order by t3.b+1 desc, t2.a desc limit 3; +b a b a +99 99 99 99 +99 98 99 99 +99 97 99 99 +# +# Rows for the sort-nest should be the cardinality of the join of inner tables +# of the sort-nest +# +# Rows for sort nest would be 9894 here +alter table t1 add key(a); +explain extended select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.a > 5 and t1.b=t3.b +order by t1.b desc, t2.a desc limit 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL a NULL NULL NULL 100 97.00 Using where +1 SIMPLE t2 ALL NULL NULL NULL NULL 102 100.00 Using join buffer (flat, BNL join) +1 SIMPLE <order-nest> ALL NULL NULL NULL NULL 9894 100.00 Using filesort +1 SIMPLE t3 ALL NULL NULL NULL NULL 1000 100.00 Using where +Warnings: +Note 1003 select `test`.`t3`.`b` AS `b`,`order-nest`.`a` AS `a`,`order-nest`.`b` AS `b`,`order-nest`.`a` AS `a` from `test`.`t1` join `test`.`t2` join `test`.`t3` where `test`.`t3`.`b` = `order-nest`.`b` order by `order-nest`.`b` desc,`order-nest`.`a` desc limit 3 +alter table t1 drop key a; +drop table t1,t2,t3,ten,one_k; +drop function f1; diff --git a/mysql-test/main/sort_nest.test b/mysql-test/main/sort_nest.test new file mode 100644 index 00000000000..729a6cd9eaa --- /dev/null +++ b/mysql-test/main/sort_nest.test @@ -0,0 +1,145 @@ +create table ten(a int); +insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); +create table one_k(a int); +insert into one_k select A.a + B.a* 10 from ten A, ten B; +create table t1 (a int, b int); +insert into t1 select a,a from one_k; + +create table t2 (a int, b int); +insert into t2 select a,a from one_k; +explain +select t1.b from t1,t2 where t1.a=t2.a order by t2.b limit 2; +select t1.b from t1,t2 where t1.a=t2.a order by t2.b limit 2; + +analyze +select t1.a,t2.a from t1,t2 where t1.a=t2.a order by t2.b limit 10; +select t1.a,t2.a from t1,t2 where t1.a=t2.a order by t2.b limit 10; + +alter table t1 add key (a); + +--echo ref should be func +explain +select t1.b,t2.b from t1,t2 where t1.a=t2.a+1 order by t2.b limit 2; +select t1.b,t2.b from t1,t2 where t1.a=t2.a+1 order by t2.b limit 2; + +--echo ref should be order-nest.a +explain +select t1.b,t2.b from t1,t2 where t1.a=t2.a order by t2.b limit 2; +select t1.b,t2.b from t1,t2 where t1.a=t2.a order by t2.b limit 2; + +drop table t1,t2,ten,one_k; + +create table ten(a int); +insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); + +create table one_k(a int); +insert into one_k select A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C; + +create table t1(a int, b int); +insert into t1 select A.a + B.a* 10, A.a + B.a* 10 from ten A, ten B; +create table t2(a int, b int); +insert into t2(a,b) values (1,1), (2,2); +insert into t2 select A.a + B.a* 10, A.a+B.a*10 from ten A, ten B; +create table t3(a int, b int); +insert into t3 select A.a + B.a* 10 + C.a * 100, A.a + B.a* 10 + C.a * 100 from ten A, ten B, ten C; + +delimiter |; +create function f1(a int) returns int +begin + declare b int default 0; + return a+b; +end| +delimiter ;| + +--echo Covering 3 table joins + +--echo # {t1,t2} part of the nest +--echo # t1.a > 95 would be attached to table t1 +--echo # t1.b=t2.a would be attached to table t2; + +let $query= +select * from t1,t2,t3 where t1.a > 95 and t1.a=t2.a and t1.b = t3.a order by t2.b; +eval explain $query; +eval explain format=json $query; +eval $query; + +--echo # {t1,t2} part of the sort nest +--echo # (t2.a < 2 or t1.b > 98) would be attached to table t2 + +let $query= +select * from t1,t2,t3 where (t3.a<2 and t2.a <2) or (t1.b > 98 and t3.b > 98) +order by t1.a, t2.b limit 5; +eval explain $query; +eval explain format=json $query; +eval $query; + +--echo # {t1,t2} part of the nest +--echo # t2.a < 2 or f1(t1.b) attached to table t2 +--echo # t1.b=t2.a would be attached to table t2; + +let $query= +select * from t1,t2,t3 where (t3.a<2 and t2.a <2) or (f1(t1.b) > 98 and t3.b > 98) +order by t1.a,t2.b limit 5; +eval explain $query; +eval explain format=json $query; +eval $query; + +--echo # +--echo # Removing constant from the order by clause +--echo # + +let $query= +select * from t1,t2 where t1.a > 95 and t1.a=t2.a order by t2.a limit 4; +eval explain $query; +eval explain format=json $query; +eval $query; + +let $query= +select * from t1,t2 where t1.a > 95 and t1.a=t2.a order by 1+2,t2.a limit 4; +eval explain $query; +eval explain format=json $query; +eval $query; + + +--echo # +--echo # Equality propagation, both the queries should use a sort nest on {t1,t2} +--echo # + +let $query=select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.b=t3.b +order by t1.b desc, t2.a desc limit 3; +eval explain $query; +eval explain format=json $query; +eval $query; + +let $query=select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.b=t3.b +order by t3.b desc, t2.a desc limit 3; + +eval explain $query; +select * from INFORMATION_SCHEMA.OPTIMIZER_TRACE; +eval explain format=json $query; +eval $query; + +--echo # +--echo # Equality propagation also for arguments of expressions, +--echo # the plan should use a sort nest on {t1,t2} +--echo # + +let $query=select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.b=t3.b +order by t3.b+1 desc, t2.a desc limit 3; +eval explain $query; +eval explain format=json $query; +eval $query; + +--echo # +--echo # Rows for the sort-nest should be the cardinality of the join of inner tables +--echo # of the sort-nest +--echo # + +--echo # Rows for sort nest would be 9894 here +alter table t1 add key(a); +let $query=select t3.b, t2.a , t1.b , t1.a from t1,t2,t3 where t1.a > 5 and t1.b=t3.b +order by t1.b desc, t2.a desc limit 3; +eval explain extended $query; +alter table t1 drop key a; +drop table t1,t2,t3,ten,one_k; +drop function f1; diff --git a/sql/item.cc b/sql/item.cc index 84fd4dc6fb7..cf71a31be10 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6158,6 +6158,28 @@ Item *Item_field::replace_equal_field(THD *thd, uchar *arg) } +Item *Item_field::replace_with_nest_items(THD *thd, uchar *arg) +{ + REPLACE_NEST_FIELD_ARG* param= (REPLACE_NEST_FIELD_ARG*)arg; + JOIN *join= param->join; + SORT_NEST_INFO *sort_nest_info= join->sort_nest_info; + if (!(used_tables() & sort_nest_info->nest_tables_map)) + return this; + + List_iterator_fast<Item> li(sort_nest_info->nest_base_table_cols); + uint index= 0; + Item *item; + while((item= li++)) + { + Item *field_item= item->real_item(); + if (field->eq(((Item_field*)field_item)->field)) + return sort_nest_info->nest_temp_table_cols.elem(index); + index++; + } + return this; +} + + void Item::init_make_send_field(Send_field *tmp_field, const Type_handler *h) { @@ -9061,11 +9083,17 @@ Item *Item_direct_view_ref::replace_equal_field(THD *thd, uchar *arg) bool Item_field::excl_dep_on_table(table_map tab_map) { - return used_tables() == tab_map || + return !(used_tables() & ~tab_map) || (item_equal && (item_equal->used_tables() & tab_map)); } +bool Item_field::excl_dep_on_nest(table_map tab_map) +{ + return !(used_tables() & ~tab_map); +} + + bool Item_field::excl_dep_on_grouping_fields(st_select_lex *sel) { @@ -9089,6 +9117,17 @@ bool Item_direct_view_ref::excl_dep_on_table(table_map tab_map) } +bool Item_direct_view_ref::excl_dep_on_nest(table_map tab_map) +{ + table_map used= used_tables(); + if (used & OUTER_REF_TABLE_BIT) + return false; + if (!(used & ~tab_map)) + return true; + return (*ref)->excl_dep_on_nest(tab_map); +} + + bool Item_direct_view_ref::excl_dep_on_grouping_fields(st_select_lex *sel) { if (item_equal) diff --git a/sql/item.h b/sql/item.h index 3b889b08e7c..8c4d07ca5b8 100644 --- a/sql/item.h +++ b/sql/item.h @@ -445,6 +445,11 @@ typedef struct replace_equal_field_arg struct st_join_table *context_tab; } REPLACE_EQUAL_FIELD_ARG; +typedef struct replace_nest_field_arg +{ + JOIN *join; +} REPLACE_NEST_FIELD_ARG; + class Settable_routine_parameter { public: @@ -1887,6 +1892,12 @@ class Item: public Value_source, Not to be used for AND/OR formulas. */ virtual bool excl_dep_on_table(table_map tab_map) { return false; } + + /* + TRUE if the expression depends only on the table indicated by tab_map + Not to be used for AND/OR formulas. + */ + virtual bool excl_dep_on_nest(table_map tab_map) { return false; } /* TRUE if the expression depends only on grouping fields of sel or can be converted to such an expression using equalities. @@ -2109,6 +2120,8 @@ class Item: public Value_source, { return this; } virtual Item *multiple_equality_transformer(THD *thd, uchar *arg) { return this; } + virtual Item *replace_with_nest_items(THD *thd, uchar *arg) + { return this; } virtual bool expr_cache_is_needed(THD *) { return FALSE; } virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs); bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs) const @@ -2316,6 +2329,10 @@ class Item: public Value_source, { return excl_dep_on_table(*((table_map *)arg)); } + bool pushable_cond_checker_for_nest(uchar *arg) + { + return excl_dep_on_nest(*((table_map *)arg)); + } bool pushable_cond_checker_for_subquery(uchar *arg) { return excl_dep_on_in_subq_left_part((Item_in_subselect *)arg); @@ -2511,6 +2528,17 @@ class Item_args } return true; } + bool excl_dep_on_nest(table_map tab_map) + { + for (uint i= 0; i < arg_count; i++) + { + if (args[i]->const_item()) + continue; + if (!args[i]->excl_dep_on_nest(tab_map)) + return false; + } + return true; + } bool excl_dep_on_grouping_fields(st_select_lex *sel); bool eq(const Item_args *other, bool binary_cmp) const { @@ -3451,6 +3479,7 @@ class Item_field :public Item_ident, Item *in_subq_field_transformer_for_having(THD *thd, uchar *arg); virtual void print(String *str, enum_query_type query_type); bool excl_dep_on_table(table_map tab_map); + bool excl_dep_on_nest(table_map tab_map); bool excl_dep_on_grouping_fields(st_select_lex *sel); bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred); bool cleanup_excluding_fields_processor(void *arg) @@ -3471,6 +3500,7 @@ class Item_field :public Item_ident, return field->get_geometry_type(); } bool check_index_dependence(void *arg); + Item *replace_with_nest_items(THD *thd, uchar *arg); friend class Item_default_value; friend class Item_insert_value; friend class st_select_lex_unit; @@ -5306,6 +5336,15 @@ class Item_ref :public Item_ident, return false; return (used == tab_map) || (*ref)->excl_dep_on_table(tab_map); } + + bool excl_dep_on_nest(table_map tab_map) + { + table_map used= used_tables(); + if (used & OUTER_REF_TABLE_BIT) + return false; + return (!(used & ~tab_map) || (*ref)->excl_dep_on_nest(tab_map)); + } + bool excl_dep_on_grouping_fields(st_select_lex *sel) { return (*ref)->excl_dep_on_grouping_fields(sel); } bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred) @@ -5631,6 +5670,7 @@ class Item_direct_view_ref :public Item_direct_ref return 0; } bool excl_dep_on_table(table_map tab_map); + bool excl_dep_on_nest(table_map tab_map); bool excl_dep_on_grouping_fields(st_select_lex *sel); bool excl_dep_on_in_subq_left_part(Item_in_subselect *subq_pred); Item *derived_field_transformer_for_having(THD *thd, uchar *arg); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 02fc7719fbc..48434f66f64 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -5225,6 +5225,22 @@ bool Item_cond::excl_dep_on_table(table_map tab_map) return true; } +bool Item_cond::excl_dep_on_nest(table_map tab_map) +{ + if (used_tables() & OUTER_REF_TABLE_BIT) + return false; + if (!(used_tables() & ~tab_map)) + return true; + List_iterator_fast<Item> li(list); + Item *item; + while ((item= li++)) + { + if (!item->excl_dep_on_nest(tab_map)) + return false; + } + return true; +} + bool Item_cond::excl_dep_on_grouping_fields(st_select_lex *sel) { diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 2af7ebdf231..b992ee1b676 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -3011,6 +3011,7 @@ class Item_cond :public Item_bool_func bool eval_not_null_tables(void *opt_arg); Item *build_clone(THD *thd); bool excl_dep_on_table(table_map tab_map); + bool excl_dep_on_nest(table_map tab_map); bool excl_dep_on_grouping_fields(st_select_lex *sel); }; diff --git a/sql/item_func.h b/sql/item_func.h index 610adb4bb46..350fb937957 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -340,6 +340,14 @@ class Item_func :public Item_func_or_sum, Item_args::excl_dep_on_table(tab_map); } + bool excl_dep_on_nest(table_map tab_map) + { + if (used_tables() & OUTER_REF_TABLE_BIT) + return false; + return !(used_tables() & ~tab_map) || + Item_args::excl_dep_on_nest(tab_map); + } + bool excl_dep_on_grouping_fields(st_select_lex *sel) { if (has_rand_bit() || with_subquery()) diff --git a/sql/item_row.h b/sql/item_row.h index ea5a0f21d8b..4698dcd8150 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -140,6 +140,11 @@ class Item_row: public Item_fixed_hybrid, return Item_args::excl_dep_on_table(tab_map); } + bool excl_dep_on_nest(table_map tab_map) + { + return Item_args::excl_dep_on_nest(tab_map); + } + bool excl_dep_on_grouping_fields(st_select_lex *sel) { return Item_args::excl_dep_on_grouping_fields(sel); diff --git a/sql/opt_split.cc b/sql/opt_split.cc index cfac0c93544..0dc8006029b 100644 --- a/sql/opt_split.cc +++ b/sql/opt_split.cc @@ -651,7 +651,6 @@ add_ext_keyuses_for_splitting_field(Dynamic_array<KEYUSE_EXT> *ext_keyuses, @brief Cost of the post join operation used in specification of splittable table */ - static double spl_postjoin_oper_cost(THD *thd, double join_record_count, uint rec_len) { diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 02d221d13b0..ef1f6f2c6ff 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -4777,6 +4777,11 @@ int setup_semijoin_loosescan(JOIN *join) for (i= join->const_tables ; i < join->top_join_tab_count; ) { JOIN_TAB *tab=join->join_tab + i; + if (tab->is_sort_nest) + { + i++; + continue; + } switch (pos->sj_strategy) { case SJ_OPT_MATERIALIZE: case SJ_OPT_MATERIALIZE_SCAN: @@ -5517,6 +5522,31 @@ enum_nested_loop_state join_tab_execution_startup(JOIN_TAB *tab) sjm->materialized= TRUE; } } + /* + This should be the place to add the sub_select call for the + prefix of the join order + */ + + else if (tab->is_sort_nest) + { + enum_nested_loop_state rc; + JOIN *join= tab->join; + SORT_NEST_INFO *nest_info= join->sort_nest_info; + + if (!nest_info->materialized) + { + JOIN_TAB *join_tab= join->join_tab + join->const_tables; + JOIN_TAB *save_return_tab= join->return_tab; + if ((rc= sub_select(join, join_tab, FALSE)) < 0 || + (rc= sub_select(join, join_tab, TRUE)) < 0) + { + join->return_tab= save_return_tab; + DBUG_RETURN(rc); + } + join->return_tab= save_return_tab; + nest_info->materialized= TRUE; + } + } DBUG_RETURN(NESTED_LOOP_OK); } diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h index 65131f6bc89..dad63c5d1db 100644 --- a/sql/opt_subselect.h +++ b/sql/opt_subselect.h @@ -298,6 +298,7 @@ class Loose_scan_opt pos->loosescan_picker.loosescan_key= best_loose_scan_key; pos->loosescan_picker.loosescan_parts= best_max_loose_keypart + 1; pos->use_join_buffer= FALSE; + pos->sort_nest_operation_here= FALSE; pos->table= tab; pos->range_rowid_filter_info= tab->range_rowid_filter_info; // todo need ref_depend_map ? diff --git a/sql/opt_trace.cc b/sql/opt_trace.cc index befc7934a3a..ca75b697ec3 100644 --- a/sql/opt_trace.cc +++ b/sql/opt_trace.cc @@ -606,6 +606,13 @@ void Json_writer::add_table_name(const JOIN_TAB *tab) ctab->emb_sj_nest->sj_subq_pred->get_identifier()); add_str(table_name_buffer, len); } + else if (tab->is_sort_nest) + { + size_t len= my_snprintf(table_name_buffer, + sizeof(table_name_buffer)-1, + "<order-nest>"); + add_str(table_name_buffer, len); + } else { TABLE_LIST *real_table= tab->table->pos_in_table_list; @@ -630,6 +637,19 @@ void add_table_scan_values_to_trace(THD *thd, JOIN_TAB *tab) table_rec.add("rows", tab->found_records) .add("cost", tab->read_time); } + +void add_sort_nest_tables_to_trace(JOIN *join) +{ + JOIN_TAB *end_tab, *tab; + THD *thd= join->thd; + SORT_NEST_INFO *sort_nest_info= join->sort_nest_info; + end_tab= sort_nest_info->nest_tab; + Json_writer_object trace_wrapper(thd); + Json_writer_array sort_nest(thd, "sort_nest"); + for (tab= join->join_tab + join->const_tables; tab < end_tab; tab++) + sort_nest.add_table_name(tab); +} + /* Introduce enum_query_type flags parameter, maybe also allow EXPLAIN also use this function. diff --git a/sql/opt_trace.h b/sql/opt_trace.h index 52318bc6b7f..b2dd3dff924 100644 --- a/sql/opt_trace.h +++ b/sql/opt_trace.h @@ -105,6 +105,7 @@ void opt_trace_print_expanded_query(THD *thd, SELECT_LEX *select_lex, Json_writer_object *trace_object); void add_table_scan_values_to_trace(THD *thd, JOIN_TAB *tab); +void add_sort_nest_tables_to_trace(JOIN *join); /* Security related (need to add a proper comment here) diff --git a/sql/sql_class.h b/sql/sql_class.h index 63f964e96ce..73f97ba727b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -6037,6 +6037,20 @@ class SJ_MATERIALIZATION_INFO : public Sql_alloc Copy_field *copy_field; /* Needed for SJ_Materialization scan */ }; +class SORT_NEST_INFO : public Sql_alloc +{ +public: + TMP_TABLE_PARAM tmp_table_param; + List<Item> nest_base_table_cols; + List<Item> nest_temp_table_cols; + TABLE *table; + st_join_table *nest_tab; + uint n_tables; + bool materialized; /* TRUE <=> materialization already performed */ + table_map nest_tables_map; + Item *nest_cond; +}; + /* Structs used when sorting */ struct SORT_FIELD_ATTR diff --git a/sql/sql_const.h b/sql/sql_const.h index 7aa4249f5ad..96779e4dbeb 100644 --- a/sql/sql_const.h +++ b/sql/sql_const.h @@ -296,6 +296,14 @@ */ #define MAX_TIME_ZONE_NAME_LENGTH (NAME_LEN + 1) + +/** + Average record length is the number of bytes for the record, it is just a rough guess, needs + this to calculate cost of filling and reading the temp table + +*/ +#define AVG_REC_LEN 50 + #if defined(__WIN__) #define INTERRUPT_PRIOR -2 diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e97abf3d981..2346a600c6f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -120,8 +120,12 @@ static bool best_extension_by_limited_search(JOIN *join, uint idx, double record_count, double read_time, uint depth, uint prune_level, - uint use_cond_selectivity); + uint use_cond_selectivity, + table_map previous_tables, + bool nest_created, + double *cardinality); void trace_plan_prefix(JOIN *join, uint idx, table_map remaining_tables); +void trace_order_by_nest(JOIN *join, uint idx, table_map remaining_tables); static uint determine_search_depth(JOIN* join); C_MODE_START static int join_tab_cmp(const void *dummy, const void* ptr1, const void* ptr2); @@ -189,6 +193,8 @@ static enum_nested_loop_state end_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); static enum_nested_loop_state end_unique_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); +enum_nested_loop_state +end_nest_materialization(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); static int join_read_const_table(THD *thd, JOIN_TAB *tab, POSITION *pos); static int join_read_system(JOIN_TAB *tab); @@ -304,6 +310,11 @@ void set_postjoin_aggr_write_func(JOIN_TAB *tab); static Item **get_sargable_cond(JOIN *join, TABLE *table); +void substitute_base_to_nest_items(JOIN *join); +void substitute_base_to_nest_items2(JOIN *join, Item **cond); +void check_cond_extraction_for_nest(THD *thd, Item *cond, + Pushdown_checker checker, uchar* arg); + #ifndef DBUG_OFF /* @@ -2463,6 +2474,10 @@ int JOIN::optimize_stage2() if (setup_semijoin_loosescan(this)) DBUG_RETURN(1); + if (setup_sort_nest(this)) + DBUG_RETURN(1); + substitute_base_to_nest_items(this); + if (make_join_select(this, select, conds)) { zero_result_cause= @@ -2474,22 +2489,9 @@ int JOIN::optimize_stage2() error= -1; /* if goto err */ /* Optimize distinct away if possible */ - { - ORDER *org_order= order; - order=remove_const(this, order,conds,1, &simple_order); - if (unlikely(thd->is_error())) - { - error= 1; - DBUG_RETURN(1); - } + if (remove_const_from_order_by()) + DBUG_RETURN(TRUE); - /* - If we are using ORDER BY NULL or ORDER BY const_expression, - return result in any order (even if we are using a GROUP BY) - */ - if (!order && org_order) - skip_sort_order= 1; - } /* Check if we can optimize away GROUP BY/DISTINCT. We can do that if there are no aggregate functions, the @@ -2710,7 +2712,9 @@ int JOIN::optimize_stage2() Yet the current implementation of FORCE INDEX hints does not allow us to do it in a clean manner. */ - no_jbuf_after= 1 ? table_count : make_join_orderinfo(this); + no_jbuf_after= 1 ? (sort_nest_info ? const_tables + sort_nest_info->n_tables + : table_count) + : make_join_orderinfo(this); // Don't use join buffering when we use MATCH select_opts_for_readinfo= @@ -3583,7 +3587,9 @@ bool JOIN::make_aggr_tables_info() curr_tab->type != JT_EQ_REF) // Don't sort 1 row { // Sort either first non-const table or the last tmp table - JOIN_TAB *sort_tab= curr_tab; + JOIN_TAB *sort_tab= sort_nest_info ? + sort_nest_info->nest_tab : + curr_tab; if (add_sorting_to_table(sort_tab, order_arg)) DBUG_RETURN(true); @@ -4421,7 +4427,7 @@ JOIN::destroy() WITH_CONST_TABLES); tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { - if (tab->aggr) + if (tab->aggr || tab->is_sort_nest) { free_tmp_table(thd, tab->table); delete tab->tmp_table_param; @@ -4755,6 +4761,156 @@ static Item **get_sargable_cond(JOIN *join, TABLE *table) return retval; } +void substitute_base_to_nest_items(JOIN *join) +{ + if (!join->sort_nest_needed()) + return; + SORT_NEST_INFO *sort_nest_info= join->sort_nest_info; + REPLACE_NEST_FIELD_ARG arg= {join}; + + List_iterator<Item> it(join->fields_list); + Item *item, *new_item; + while ((item= it++)) + { + if ((new_item= item->transform(join->thd, + &Item::replace_with_nest_items, + (uchar *) &arg)) != item) + it.replace(new_item); + } + JOIN_TAB *end_tab= sort_nest_info->nest_tab; + uint i, j; + for (i= join->const_tables + sort_nest_info->n_tables, j=0; + i < join->top_join_tab_count; i++, j++) + { + JOIN_TAB *tab= end_tab + j; + if (tab->type == JT_REF || tab->type == JT_EQ_REF || + tab->type == JT_REF_OR_NULL) + { + for (uint keypart= 0; keypart < tab->ref.key_parts; keypart++) + { + item= tab->ref.items[keypart]->transform(join->thd, + &Item::replace_with_nest_items, + (uchar *) &arg); + if (item != tab->ref.items[keypart]) + { + tab->ref.items[keypart]= item; + Item *real_item= item->real_item(); + store_key *key_copy= tab->ref.key_copy[keypart]; + if (key_copy->type() == store_key::FIELD_STORE_KEY) + { + store_key_field *field_copy= ((store_key_field *)key_copy); + DBUG_ASSERT(real_item->type() == Item::FIELD_ITEM); + field_copy->change_source_field((Item_field *) real_item); + } + } + } + } + } + substitute_base_to_nest_items2(join, &join->conds); +} + +void substitute_base_to_nest_items2(JOIN *join, Item **cond) +{ + SORT_NEST_INFO *sort_nest_info= join->sort_nest_info; + Item *orig_cond= *cond; + if (!sort_nest_info) + return; + THD *thd= join->thd; + Item *extracted_cond; + SELECT_LEX* sl= join->select_lex; + + /* + check_cond_extraction_for_nest would set NO_EXTRACTION_FL for + all the items that cannot be added to the inner tables of the nest + */ + check_cond_extraction_for_nest(thd, orig_cond, + &Item::pushable_cond_checker_for_nest, + (uchar *)(&sort_nest_info->nest_tables_map)); + /* + build_cond_for_grouping_fields would create the entire + condition that would be added to the tables inside the nest. + This may clone some items too. + */ + extracted_cond= sl->build_cond_for_grouping_fields(thd, orig_cond, TRUE); + + if (extracted_cond) + { + if (extracted_cond->fix_fields_if_needed(thd, 0)) + return; + /* + Remove from the WHERE clause all the conditions that were added + to the inner tables of the sort nest + */ + orig_cond= remove_pushed_top_conjuncts(thd, orig_cond); + sort_nest_info->nest_cond= extracted_cond; + } + + REPLACE_NEST_FIELD_ARG arg= {join}; + if (orig_cond) + { + orig_cond= orig_cond->transform(join->thd, &Item::replace_with_nest_items, + (uchar *) &arg); + orig_cond->update_used_tables(); + } + *cond= orig_cond; +} + +/* + Add a transformer to this call so that we dont have both + check_cond_extraction_for_nest and check_cond_extraction_for_grouping_fields +*/ + +void +check_cond_extraction_for_nest(THD *thd, Item *cond, + Pushdown_checker checker, uchar* arg) +{ + if (cond->get_extraction_flag() == NO_EXTRACTION_FL) + return; + cond->clear_extraction_flag(); + if (cond->type() == Item::COND_ITEM) + { + Item_cond_and *and_cond= + (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) ? + ((Item_cond_and*) cond) : 0; + + List<Item> *arg_list= ((Item_cond*) cond)->argument_list(); + List_iterator<Item> li(*arg_list); + uint count= 0; // to count items not containing NO_EXTRACTION_FL + uint count_full= 0; // to count items with FULL_EXTRACTION_FL + Item *item; + while ((item=li++)) + { + check_cond_extraction_for_nest(thd, item, checker, arg); + if (item->get_extraction_flag() != NO_EXTRACTION_FL) + { + count++; + if (item->get_extraction_flag() == FULL_EXTRACTION_FL) + count_full++; + } + else if (!and_cond) + break; + } + if ((and_cond && count == 0) || item) + cond->set_extraction_flag(NO_EXTRACTION_FL); + if (count_full == arg_list->elements) + { + cond->set_extraction_flag(FULL_EXTRACTION_FL); + } + if (cond->get_extraction_flag() != 0) + { + li.rewind(); + while ((item=li++)) + item->clear_extraction_flag(); + } + } + else + { + int fl= (cond->*checker)(arg) ? + FULL_EXTRACTION_FL : NO_EXTRACTION_FL; + cond->set_extraction_flag(fl); + } +} + /** Calculate the best possible join and initialize the join structure. @@ -5210,6 +5366,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, join->sort_by_table= get_sort_by_table(join->order, join->group_list, join->select_lex->leaf_tables, join->const_table_map); + /* Update info on indexes that can be used for search lookups as reading const tables may has added new sargable predicates. @@ -5444,12 +5601,22 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, if (pull_out_semijoin_tables(join)) DBUG_RETURN(TRUE); - join->join_tab=stat; join->top_join_tab_count= table_count; - join->map2table=stat_ref; - join->table= table_vector; join->const_tables=const_count; join->found_const_table_map=found_const_table_map; + /* + Here a call is made to remove the constant from the order by clause, + this call would only remove the basic constants. This is done for + the ORDER BY LIMIT optimization. + */ + if (join->remove_const_from_order_by()) + DBUG_RETURN(TRUE); + + (void)propagate_equal_field_for_orderby(join, join->order); + + join->join_tab=stat; + join->map2table=stat_ref; + join->table= table_vector; if (join->const_tables != join->table_count) optimize_keyuse(join, keyuse_array); @@ -7075,6 +7242,7 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key) join->positions[idx].sj_strategy= SJ_OPT_NONE; join->positions[idx].use_join_buffer= FALSE; join->positions[idx].range_rowid_filter_info= 0; + join->positions[idx].sort_nest_operation_here= FALSE; /* Move the const table as down as possible in best_ref */ JOIN_TAB **pos=join->best_ref+idx+1; @@ -7922,6 +8090,7 @@ best_access_path(JOIN *join, pos->use_join_buffer= best_uses_jbuf; pos->spl_plan= spl_plan; pos->range_rowid_filter_info= best_filter; + pos->sort_nest_operation_here= FALSE; loose_scan_opt.save_to_position(s, loose_scan_pos); @@ -8135,6 +8304,7 @@ choose_plan(JOIN *join, table_map join_tables) use_cond_selectivity)) DBUG_RETURN(TRUE); } + trace_plan.end(); /* Store the cost of this query into a user variable @@ -8440,6 +8610,7 @@ optimize_straight_join(JOIN *join, table_map join_tables) pushdown_cond_selectivity= table_cond_selectivity(join, idx, s, join_tables); position->cond_selectivity= pushdown_cond_selectivity; + record_count= record_count*pushdown_cond_selectivity; ++idx; } @@ -8552,6 +8723,7 @@ greedy_search(JOIN *join, JOIN_TAB *best_table; // the next plan node to be added to the curr QEP // ==join->tables or # tables in the sj-mat nest we're optimizing uint n_tables __attribute__((unused)); + double cardinality= DBL_MAX; DBUG_ENTER("greedy_search"); /* number of tables that remain to be optimized */ @@ -8565,9 +8737,11 @@ greedy_search(JOIN *join, do { /* Find the extension of the current QEP with the lowest cost */ join->best_read= DBL_MAX; + table_map previous_tables= 0; if (best_extension_by_limited_search(join, remaining_tables, idx, record_count, read_time, search_depth, prune_level, - use_cond_selectivity)) + use_cond_selectivity, + previous_tables, FALSE, &cardinality)) DBUG_RETURN(TRUE); /* 'best_read < DBL_MAX' means that optimizer managed to find @@ -9166,6 +9340,19 @@ void trace_plan_prefix(JOIN *join, uint idx, table_map remaining_tables) } } +void trace_order_by_nest(JOIN *join, uint idx, table_map remaining_tables) +{ + THD *const thd= join->thd; + Json_writer_array plan_prefix(thd, "order_by_nest"); + for (uint i= 0; i < idx; i++) + { + TABLE *tr= join->positions[i].table->table; + if (tr->map & remaining_tables) + plan_prefix.add_table_name(join->positions[i].table); + } + +} + /** Find a good, possibly optimal, query execution plan (QEP) by a possibly exhaustive search. @@ -9293,7 +9480,10 @@ best_extension_by_limited_search(JOIN *join, double read_time, uint search_depth, uint prune_level, - uint use_cond_selectivity) + uint use_cond_selectivity, + table_map previous_tables, + bool nest_created, + double *cardinality) { DBUG_ENTER("best_extension_by_limited_search"); @@ -9319,7 +9509,16 @@ best_extension_by_limited_search(JOIN *join, JOIN_TAB *s; double best_record_count= DBL_MAX; double best_read_time= DBL_MAX; - bool disable_jbuf= join->thd->variables.join_cache_level == 0; + bool disable_jbuf= (join->thd->variables.join_cache_level == 0) || nest_created; + double fraction_output; + + if (nest_created) + { + fraction_output= join->select_limit < (*cardinality) ? + (join->select_limit/(*cardinality)) : 1.0; + } + else + fraction_output= 1.0; DBUG_EXECUTE("opt", print_plan(join, idx, record_count, read_time, read_time, "part_plan");); @@ -9348,6 +9547,8 @@ best_extension_by_limited_search(JOIN *join, { trace_plan_prefix(join, idx, remaining_tables); trace_one_table.add_table_name(s); + if (nest_created) + trace_order_by_nest(join, idx, previous_tables); } /* Find the best access method from 's' to the current partial plan */ @@ -9361,10 +9562,12 @@ best_extension_by_limited_search(JOIN *join, ? position->range_rowid_filter_info->get_cmp_gain(current_record_count) : 0; current_read_time=COST_ADD(read_time, - COST_ADD(position->read_time - - filter_cmp_gain, - current_record_count / - (double) TIME_FOR_COMPARE)); + COST_MULT( + COST_ADD(position->read_time - + filter_cmp_gain, + current_record_count / + (double) TIME_FOR_COMPARE), fraction_output)); + current_record_count= COST_MULT(current_record_count, fraction_output); advance_sj_state(join, remaining_tables, idx, &current_record_count, &current_read_time, &loose_scan_pos); @@ -9427,9 +9630,12 @@ best_extension_by_limited_search(JOIN *join, double partial_join_cardinality= current_record_count * pushdown_cond_selectivity; if ( (search_depth > 1) && (remaining_tables & ~real_table_bit) & allowed_tables ) - { /* Recursively expand the current partial plan */ + { + /* Recursively expand the current partial plan */ swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); Json_writer_array trace_rest(thd, "rest_of_plan"); + bool nest_allow= (join->cur_sj_inner_tables == 0 && + join->cur_embedding_map == 0); if (best_extension_by_limited_search(join, remaining_tables & ~real_table_bit, idx + 1, @@ -9437,24 +9643,58 @@ best_extension_by_limited_search(JOIN *join, current_read_time, search_depth - 1, prune_level, - use_cond_selectivity)) + use_cond_selectivity, + nest_created ? previous_tables : + previous_tables | real_table_bit, + nest_created, cardinality)) DBUG_RETURN(TRUE); + + if (!nest_created && !join->emb_sjm_nest && nest_allow && !join->need_order_nest() && + check_join_prefix_contains_ordering(join, s, previous_tables)) + { + join->positions[idx].sort_nest_operation_here= TRUE; + double cost= postjoin_oper_cost(thd, partial_join_cardinality, AVG_REC_LEN, idx); + current_read_time= COST_ADD(current_read_time, cost); + if (best_extension_by_limited_search(join, + remaining_tables & ~real_table_bit, + idx + 1, + partial_join_cardinality, + current_read_time, + search_depth - 1, + 0, + use_cond_selectivity, + previous_tables | real_table_bit, + TRUE, cardinality)) + DBUG_RETURN(TRUE); + join->positions[idx].sort_nest_operation_here= FALSE; + } swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); } else - { /* + { + /* 'join' is either the best partial QEP with 'search_depth' relations, or the best complete QEP so far, whichever is smaller. */ if (join->sort_by_table && join->sort_by_table != - join->positions[join->const_tables].table->table) + join->positions[join->const_tables].table->table + && !nest_created) + { /* We may have to make a temp table, note that this is only a heuristic since we cannot know for sure at this point. Hence it may be wrong. */ - current_read_time= COST_ADD(current_read_time, current_record_count); + double cost= postjoin_oper_cost(thd, partial_join_cardinality, AVG_REC_LEN, idx); + current_read_time= COST_ADD(current_read_time, cost); + } + if (!nest_created) + { + *cardinality= partial_join_cardinality; + trace_one_table.add("cardinality", partial_join_cardinality); + } + trace_one_table.add("cost_of_plan", current_read_time); if (current_read_time < join->best_read) { memcpy((uchar*) join->best_positions, (uchar*) join->positions, @@ -10145,7 +10385,7 @@ bool JOIN::get_best_combination() JOIN_TAB_RANGE *root_range; if (!(root_range= new (thd->mem_root) JOIN_TAB_RANGE)) DBUG_RETURN(TRUE); - root_range->start= join_tab; + root_range->start= join_tab; /* root_range->end will be set later */ join_tab_ranges.empty(); @@ -10220,7 +10460,7 @@ bool JOIN::get_best_combination() j->type=JT_ALL; if (best_positions[tablenr].use_join_buffer && tablenr != const_tables) - full_join= 1; + full_join= 1; } /*if (best_positions[tablenr].sj_strategy == SJ_OPT_LOOSE_SCAN) @@ -10253,12 +10493,45 @@ bool JOIN::get_best_combination() sjm_nest_root= NULL; sjm_nest_end= NULL; } + if (cur_pos->sort_nest_operation_here) + { + /* + Ok, we've entered an ORDERING nest + 1. Put into main join order a JOIN_TAB that represents a scan + in the temptable. + */ + JOIN_TAB *prev= j; + if ((prev - (join_tab + const_tables) > 0)) + { + j= j+1; + bzero((void*)j, sizeof(JOIN_TAB)); + + j->join= this; + j->table= NULL; //temporary way to tell SJM tables from others. + j->ref.key = -1; + j->on_expr_ref= (Item**) &null_ptr; + j->is_sort_nest= TRUE; + uint tables= prev - (join_tab + const_tables)+1; + j->records_read= calculate_record_count_for_sort_nest(this, tables); + j->records= (ha_rows) j->records_read; + j->cond_selectivity= 1.0; + } + SORT_NEST_INFO *sort_nest_info; + if (!(sort_nest_info= new SORT_NEST_INFO())) + return TRUE; + sort_nest_info->n_tables= prev - (join_tab + const_tables)+1; + sort_nest_info->nest_tab= j; + this->sort_nest_info= sort_nest_info; + DBUG_ASSERT(sort_nest_info->n_tables != 0); + } } root_range->end= j; used_tables= OUTER_REF_TABLE_BIT; // Outer row is already read for (j=join_tab, tablenr=0 ; tablenr < table_count ; tablenr++,j++) { + if (j->is_sort_nest) + j++; if (j->bush_children) j= j->bush_children->start; @@ -10952,6 +11225,8 @@ make_outerjoin_info(JOIN *join) tab; tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { + if (tab->is_sort_nest) + continue; TABLE *table= tab->table; TABLE_LIST *tbl= table->pos_in_table_list; TABLE_LIST *embedding= tbl->embedding; @@ -11127,10 +11402,20 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) JOIN_TAB *tab; table_map current_map; i= join->const_tables; + Item *saved_cond= cond; + SORT_NEST_INFO *sort_nest_info= join->sort_nest_info; + if (join->sort_nest_needed()) + cond= sort_nest_info->nest_cond; + for (tab= first_depth_first_tab(join); tab; tab= next_depth_first_tab(join, tab)) { bool is_hj; + if (tab->is_sort_nest) + { + cond= saved_cond; + continue; + } /* first_inner is the X in queries like: @@ -12136,6 +12421,35 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) } +enum_nested_loop_state +end_nest_materialization(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) +{ + int error; + THD *thd= join->thd; + SORT_NEST_INFO *nest_info= join->sort_nest_info; + DBUG_ENTER("end_sj_materialize"); + if (!end_of_records) + { + TABLE *table= nest_info->table; + fill_record(thd, table, table->field, + nest_info->nest_base_table_cols, TRUE, FALSE); + + if (unlikely(thd->is_error())) + DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ + if (unlikely((error= table->file->ha_write_tmp_row(table->record[0])))) + { + /* create_myisam_from_heap will generate error if needed */ + if (table->file->is_fatal_error(error, HA_CHECK_DUP) && + create_internal_tmp_table_from_heap(thd, table, + nest_info->tmp_table_param.start_recinfo, + &nest_info->tmp_table_param.recinfo, + error, 1, NULL)) + DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ + } + } + DBUG_RETURN(NESTED_LOOP_OK); +} + /* Check whether a join buffer can be used to join the specified table @@ -12367,7 +12681,7 @@ uint check_join_cache_usage(JOIN_TAB *tab, Don't use join buffering if we're dictated not to by no_jbuf_after (This is not meaningfully used currently) */ - if (table_index > no_jbuf_after) + if (table_index+1 > no_jbuf_after) goto no_join_cache; /* @@ -12559,7 +12873,7 @@ void check_join_cache_usage_for_tables(JOIN *join, ulonglong options, */ prev_tab= tab - 1; if (tab == join->join_tab + join->const_tables || - (tab->bush_root_tab && tab->bush_root_tab->bush_children->start == tab)) + (tab->bush_root_tab && tab->bush_root_tab->bush_children->start == tab) || tab->is_sort_nest) prev_tab= NULL; switch (tab->type) { @@ -12588,7 +12902,7 @@ void check_join_cache_usage_for_tables(JOIN *join, ulonglong options, default: tab->used_join_cache_level= 0; } - if (!tab->bush_children) + if (!tab->bush_children && !tab->is_sort_nest) idx++; } } @@ -12731,17 +13045,20 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) Later it should be improved. */ - if (tab->bush_root_tab && tab->bush_root_tab->bush_children->start == tab) + if ((tab->bush_root_tab && tab->bush_root_tab->bush_children->start == tab) || + tab->is_sort_nest) prev_tab= NULL; - DBUG_ASSERT(tab->bush_children || tab->table == join->best_positions[i].table->table); + DBUG_ASSERT(tab->bush_children || tab->table == join->best_positions[i].table->table + || tab->is_sort_nest); tab->partial_join_cardinality= join->best_positions[i].records_read * (prev_tab? prev_tab->partial_join_cardinality : 1); - if (!tab->bush_children) + if (!tab->bush_children && !tab->is_sort_nest) i++; } check_join_cache_usage_for_tables(join, options, no_jbuf_after); + SORT_NEST_INFO *sort_nest_info= join->sort_nest_info; JOIN_TAB *first_tab; for (tab= first_tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); @@ -12768,7 +13085,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) end_sj_materialize. */ if (!(tab->bush_root_tab && - tab->bush_root_tab->bush_children->end == tab + 1)) + tab->bush_root_tab->bush_children->end == tab + 1) && + !(sort_nest_info && tab+1 == sort_nest_info->nest_tab)) { tab->next_select=sub_select; /* normal select */ } @@ -12958,7 +13276,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) It could be that sort_by_tab==NULL, and the plan is to use filesort() on the first table. */ - if (join->order) + if (join->order && !join->sort_nest_info) { join->simple_order= 0; join->need_tmp= 1; @@ -13711,6 +14029,8 @@ static void update_depend_map(JOIN *join) join_tab; join_tab= next_linear_tab(join, join_tab, WITH_BUSH_ROOTS)) { + if (join_tab->is_sort_nest) + continue; TABLE_REF *ref= &join_tab->ref; table_map depend_map=0; Item **item=ref->items; @@ -14001,6 +14321,22 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, } +bool JOIN::remove_const_from_order_by() +{ + ORDER *org_order= order; + order=remove_const(this, order,conds,1, &simple_order); + if (unlikely(thd->is_error())) + return TRUE; + /* + If we are using ORDER BY NULL or ORDER BY const_expression, + return result in any order (even if we are using a GROUP BY) + */ + if (!order && org_order) + skip_sort_order= 1; + return FALSE; +} + + /** Filter out ORDER items those are equal to constants in WHERE @@ -14041,6 +14377,198 @@ ORDER *simple_remove_const(ORDER *order, COND *where) } +/* + This function basically tries to propgate all the multiple equalites + for the order by items, so that one can use them to generate QEP that would + also take into consideration equality propagation. + Example + select * from t1,t2 where t1.a=t2.a order by t1.a + + So the possible join orders would be: + + t1 join t2 then sort + t2 join t1 then sort + t1 sort(t1) join t2 + t2 sort(t2) join t1 => this is only possible when equality propagation is + performed +*/ +void propagate_equal_field_for_orderby(JOIN *join, ORDER *first_order) +{ + ORDER *order; + for (order= first_order; order; order= order->next) + { + if (optimizer_flag(join->thd, OPTIMIZER_SWITCH_ORDERBY_EQ_PROP) && + join->cond_equal) + { + Item *item= order->item[0]; + /* + TODO: equality substitution in the context of ORDER BY is + sometimes allowed when it is not allowed in the general case. + We make the below call for its side effect: it will locate the + multiple equality the item belongs to and set item->item_equal + accordingly. + */ + (void)item->propagate_equal_fields(join->thd, + Value_source:: + Context_identity(), + join->cond_equal); + } + } +} + +/* + This function checks if by considering the current join_tab + would we be able to achieve the ordering +*/ + +bool check_join_prefix_contains_ordering(JOIN *join, JOIN_TAB *tab, + table_map previous_tables) +{ + ORDER *order; + for (order= join->order; order; order= order->next) + { + Item *order_item= order->item[0]; + table_map order_tables=order_item->used_tables(); + if (!(order_tables & ~previous_tables) || + (order_item->excl_dep_on_table(previous_tables | tab->table->map))) + continue; + else + return FALSE; + } + return TRUE; +} + + +bool setup_sort_nest(JOIN *join) +{ + if (!join->sort_nest_needed()) + return FALSE; + + /* + The sort nest is only needed when there are more than one table + in the sort nest, else we can just sort with the first table if the + sort nest has only one table + */ + SORT_NEST_INFO* sort_nest_info= join->sort_nest_info; + THD *thd= join->thd; + Field_iterator_table field_iterator; + + JOIN_TAB *start_tab= join->join_tab+join->const_tables, *j, *tab; + tab= sort_nest_info->nest_tab; + sort_nest_info->nest_tables_map= 0; + + if (unlikely(thd->trace_started())) + add_sort_nest_tables_to_trace(join); + + /* This needs to be added to JOIN structure, looks the best option or we + can have a seperate struture NEST_INFO to hold it. + Final Implementation here should just walk over the where clause and collect + the field for which we should have a temp table field + */ + + for (j= start_tab; j < tab; j++) + { + TABLE *table= j->table; + field_iterator.set_table(table); + sort_nest_info->nest_tables_map|= table->map; + for (; !field_iterator.end_of_fields(); field_iterator.next()) + { + Field *field= field_iterator.field(); + if (!bitmap_is_set(table->read_set, field->field_index)) + continue; + Item *item; + if (!(item= field_iterator.create_item(thd))) + return TRUE; + sort_nest_info->nest_base_table_cols.push_back(item, thd->mem_root); + } + } + + uint non_order_fields= sort_nest_info->nest_base_table_cols.elements; + ORDER *order= join->order; + + /* + Order by items need to be in the temp table ,we can avoid the Field items in + the order by list but we need to fields inside the temp table for expressions + */ + for (order= join->order; order; order=order->next) + { + Item *item= order->item[0]; + Item *res= substitute_for_best_equal_field(thd, NO_PARTICULAR_TAB, item, + join->cond_equal, + join->map2table, true); + res->update_used_tables(); + sort_nest_info->nest_base_table_cols.push_back(res, thd->mem_root); + } + + DBUG_ASSERT(!tab->table); + + sort_nest_info->tmp_table_param.init(); + sort_nest_info->tmp_table_param.bit_fields_as_long= TRUE; + sort_nest_info->tmp_table_param.field_count= sort_nest_info->nest_base_table_cols.elements; + sort_nest_info->tmp_table_param.force_not_null_cols= FALSE; + + const LEX_CSTRING order_nest_name= { STRING_WITH_LEN("order-nest") }; + if (!(tab->table= create_tmp_table(thd, &sort_nest_info->tmp_table_param, + sort_nest_info->nest_base_table_cols, (ORDER*) 0, + FALSE /* distinct */, + 0, /*save_sum_fields*/ + thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS, + HA_POS_ERROR /*rows_limit */, + &order_nest_name))) + return TRUE; /* purecov: inspected */ + + tab->table->map= sort_nest_info->nest_tables_map; + sort_nest_info->table= tab->table; + tab->type= JT_ALL; + + /* + The list of temp table items created here, these are needed for the substitution + for items that would be evaluated in POST SORT NEST context + */ + field_iterator.set_table(tab->table); + for (; !field_iterator.end_of_fields(); field_iterator.next()) + { + Field *field= field_iterator.field(); + Item *item; + if (!(item= new (thd->mem_root)Item_temptable_field(thd, field))) + return TRUE; + sort_nest_info->nest_temp_table_cols.push_back(item, thd->mem_root); + } + + /* + Here we substitute order by items with the items of the temp table + */ + List_iterator_fast<Item> it(sort_nest_info->nest_temp_table_cols); + Item *item; + order= join->order; + uint i=0; + while ((item= it++)) + { + if (i++ < non_order_fields) + continue; + order->item[0]= item; + order= order->next; + } + tab->table->reginfo.join_tab= tab; + + /* + Create mapping between base table to temp table + Need a key-value structure + would like to have base_table_field ----> temp_table_item mapping + We can use a hash-set that we already have in the file sql-hset.h + */ + + /* + Setting up the scan on the temp table + */ + tab->read_first_record= join_init_read_record; + tab->read_record.read_record_func= rr_sequential; + tab[-1].next_select= end_nest_materialization; + sort_nest_info->materialized= FALSE; + + return FALSE; +} + static int return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables, List<Item> &fields, bool send_row, ulonglong select_options, @@ -19721,6 +20249,10 @@ do_select(JOIN *join, Procedure *procedure) JOIN_TAB *join_tab= join->join_tab + (join->tables_list ? join->const_tables : 0); + SORT_NEST_INFO *sort_nest_info= join->sort_nest_info; + join_tab= sort_nest_info ? sort_nest_info->nest_tab + : join_tab; + if (join->outer_ref_cond && !join->outer_ref_cond->val_int()) error= NESTED_LOOP_NO_MORE_ROWS; else @@ -25951,6 +26483,13 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta, ctab->emb_sj_nest->sj_subq_pred->get_identifier()); eta->table_name.copy(table_name_buffer, len, cs); } + else if (is_sort_nest) + { + size_t len= my_snprintf(table_name_buffer, + sizeof(table_name_buffer)-1, + "<order-nest>"); + eta->table_name.copy(table_name_buffer, len, cs); + } else { TABLE_LIST *real_table= table->pos_in_table_list; @@ -28550,6 +29089,39 @@ select_handler *SELECT_LEX::find_select_handler(THD *thd) return 0; } +double postjoin_oper_cost(THD *thd, double join_record_count, uint rec_len, uint idx) +{ + double cost= 0; + /* + For only one table in the order_nest, we don't need a fill the temp table, we can + just read the data into the filesort buffer and read the sorted data from the buffers. + */ + if (idx) + cost= get_tmp_table_write_cost(thd, join_record_count,rec_len) * + join_record_count; // cost to fill tmp table + + cost+= get_tmp_table_lookup_cost(thd, join_record_count,rec_len) * + join_record_count; // cost to perform post join operation used here + cost+= get_tmp_table_lookup_cost(thd, join_record_count, rec_len) + + (join_record_count == 0 ? 0 : + join_record_count * log2 (join_record_count)) * + SORT_INDEX_CMP_COST; // cost to perform sorting + return cost; +} + + +double calculate_record_count_for_sort_nest(JOIN *join, uint n_tables) +{ + double sort_nest_records=1, record_count; + JOIN_TAB *tab= join->join_tab + join->const_tables; + for (uint j= 0; j < n_tables ;j++, tab++) + { + record_count= tab->records_read * tab->cond_selectivity; + sort_nest_records= COST_MULT(sort_nest_records, record_count); + } + return sort_nest_records; +} + /** @} (end of group Query_Optimizer) diff --git a/sql/sql_select.h b/sql/sql_select.h index 40a9ed303f7..e0a9886b984 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -289,13 +289,14 @@ typedef struct st_join_table { /* TRUE <=> This join_tab is inside an SJM bush and is the last leaf tab here */ bool last_leaf_in_bush; - + /* ptr - this is a bush, and ptr points to description of child join_tab range NULL - this join tab has no bush children */ JOIN_TAB_RANGE *bush_children; + JOIN_TAB_RANGE *order_nest_children; /* Special content for EXPLAIN 'Extra' column or NULL if none */ enum explain_extra_tag info; @@ -524,6 +525,12 @@ typedef struct st_join_table { /* Becomes true just after the used range filter has been built / filled */ bool is_rowid_filter_built; + /* + Set to true if we consider creating a nest for a prefix of the JOIN order + that satisfies the ordering + */ + bool is_sort_nest; + void build_range_rowid_filter_if_needed(); void cleanup(); @@ -991,6 +998,9 @@ typedef struct st_position /* Cost info for the range filter used at this position */ Range_rowid_filter_cost_info *range_rowid_filter_info; + /* Flag to be set to TRUE if the join prefix satisfies the ORDER BY CLAUSE */ + bool sort_nest_operation_here; + } POSITION; typedef Bounds_checked_array<Item_null_result*> Item_null_array; @@ -1506,6 +1516,7 @@ class JOIN :public Sql_alloc the optimize_cond() call in JOIN::optimize_inner() method. */ bool is_orig_degenerated; + SORT_NEST_INFO *sort_nest_info; JOIN(THD *thd_arg, List<Item> &fields_arg, ulonglong select_options_arg, select_result *result_arg) @@ -1602,6 +1613,7 @@ class JOIN :public Sql_alloc sjm_lookup_tables= 0; sjm_scan_tables= 0; is_orig_degenerated= false; + sort_nest_info= NULL; } /* True if the plan guarantees that it will be returned zero or one row */ @@ -1609,6 +1621,11 @@ class JOIN :public Sql_alloc /* Number of tables actually joined at the top level */ uint exec_join_tab_cnt() { return tables_list ? top_join_tab_count : 0; } + /* TRUE if the sort-nest contains more than one table else FALSE */ + bool sort_nest_needed() { return sort_nest_info ? + (sort_nest_info->n_tables == 1 ? FALSE : TRUE): + FALSE; } + /* Number of tables in the join which also includes the temporary tables created for GROUP BY, DISTINCT , WINDOW FUNCTION etc. @@ -1721,7 +1738,8 @@ class JOIN :public Sql_alloc JOIN_TAB *get_sort_by_join_tab() { return (need_tmp || !sort_by_table || skip_sort_order || - ((group || tmp_table_param.sum_func_count) && !group_list)) ? + ((group || tmp_table_param.sum_func_count) && !group_list) || + sort_nest_info) ? NULL : join_tab+const_tables; } bool setup_subquery_caches(); @@ -1748,11 +1766,20 @@ class JOIN :public Sql_alloc bool test_if_need_tmp_table() { return ((const_tables != table_count && - ((select_distinct || !simple_order || !simple_group) || + ((select_distinct || (!simple_order && !sort_nest_info) || !simple_group) || (group_list && order) || MY_TEST(select_options & OPTION_BUFFER_RESULT))) || (rollup.state != ROLLUP::STATE_NONE && select_distinct)); } + + bool need_order_nest() + { + return ((const_tables != table_count && + ((select_distinct || group_list) || + MY_TEST(select_options & OPTION_BUFFER_RESULT))) || + (rollup.state != ROLLUP::STATE_NONE && select_distinct) || + select_lex->window_specs.elements > 0 || select_lex->agg_func_used()); + } bool choose_subquery_plan(table_map join_tables); void get_partial_cost_and_fanout(int end_tab_idx, table_map filter_map, @@ -1783,6 +1810,7 @@ class JOIN :public Sql_alloc bool fix_all_splittings_in_plan(); bool transform_in_predicates_into_in_subq(THD *thd); + bool remove_const_from_order_by(); private: /** Create a temporary table to be used for processing DISTINCT/ORDER @@ -1853,6 +1881,7 @@ bool is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args); bool simple_pred(Item_func *func_item, Item **args, bool *inv_order); int opt_sum_query(THD* thd, List<TABLE_LIST> &tables, List<Item> &all_fields, COND *conds); +double calculate_record_count_for_sort_nest(JOIN *join, uint n_tables); /* from sql_delete.cc, used by opt_range.cc */ extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b); @@ -2098,6 +2127,10 @@ bool mysql_select(THD *thd, void free_underlaid_joins(THD *thd, SELECT_LEX *select); bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result); +void propagate_equal_field_for_orderby(JOIN *join, ORDER *first_order); +bool check_join_prefix_contains_ordering(JOIN *join, JOIN_TAB *tab, + table_map previous_tables); +bool setup_sort_nest(JOIN *join); /* General routine to change field->ptr of a NULL-terminated array of Field @@ -2449,6 +2482,7 @@ double get_tmp_table_write_cost(THD *thd, double row_count, uint row_size); void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array); bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse, bool skip_unprefixed_keyparts); +double postjoin_oper_cost(THD *thd, double join_record_count, uint rec_len, uint idx); struct st_cond_statistic {
1 0
0 0
[Commits] 8d0dabc56b0: MDEV-20091 DROP TEMPORARY table is logged despite no CREATE was logged
by sujatha 25 Jul '19

25 Jul '19
revision-id: 8d0dabc56b09c6b40db15aac7da0da43d988759d (mariadb-10.1.39-122-g8d0dabc56b0) parent(s): f6ea0389a4ea61dd0accbc3f949ef6c6d9a91417 author: Sujatha committer: Sujatha timestamp: 2019-07-25 14:24:04 +0530 message: MDEV-20091 DROP TEMPORARY table is logged despite no CREATE was logged Fixing post push test issues. Recorded the result files for following: rpl_stm_drop_create_temp_table.result rpl_mixed_drop_create_temp_table.result. --- .../rpl/r/rpl_mixed_drop_create_temp_table.result | 31 +++++----------------- .../rpl/r/rpl_stm_drop_create_temp_table.result | 31 +++++----------------- 2 files changed, 14 insertions(+), 48 deletions(-) diff --git a/mysql-test/suite/rpl/r/rpl_mixed_drop_create_temp_table.result b/mysql-test/suite/rpl/r/rpl_mixed_drop_create_temp_table.result index da807748cee..d79b246c8d6 100644 --- a/mysql-test/suite/rpl/r/rpl_mixed_drop_create_temp_table.result +++ b/mysql-test/suite/rpl/r/rpl_mixed_drop_create_temp_table.result @@ -89,9 +89,6 @@ Warnings: Note 1051 Unknown table 'test.tt_xx_1' -b-b-b-b-b-b-b-b-b-b-b- >> Drop-Temp-If-Xe-Temp << -b-b-b-b-b-b-b-b-b-b-b- include/show_binlog_events.inc -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ -e-e-e-e-e-e-e-e-e-e-e- >> Drop-Temp-If-Xe-Temp << -e-e-e-e-e-e-e-e-e-e-e- SET @commands= 'Drop-Temp-TXe-Temp'; @@ -112,7 +109,7 @@ Note 1051 Unknown table 'test.tt_1' include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2`,`test`.`tt_1` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2` /* generated by server */ -e-e-e-e-e-e-e-e-e-e-e- >> Drop-Temp-If-TXe-Temp << -e-e-e-e-e-e-e-e-e-e-e- SET @commands= 'Drop-Temp-NXe-Temp'; @@ -134,8 +131,6 @@ include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`nt_tmp_2` /* generated by server */ -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ -e-e-e-e-e-e-e-e-e-e-e- >> Drop-Temp-If-NXe-Temp << -e-e-e-e-e-e-e-e-e-e-e- SET @commands= 'Drop-Temp-TN-Temp'; @@ -291,7 +286,6 @@ include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-Xe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -314,8 +308,6 @@ master-bin.000001 # Query # # use `test`; INSERT INTO nt_xx_1() VALUES (1) master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-Xe-Temp N Drop-Temp-If-Xe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -368,7 +360,7 @@ include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2`,`test`.`tt_1` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-TXe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -391,8 +383,8 @@ master-bin.000001 # Query # # use `test`; INSERT INTO nt_xx_1() VALUES (1) master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2`,`test`.`tt_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_1`,`test`.`tt_1` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-TXe-Temp N Drop-Temp-If-TXe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -454,7 +446,6 @@ master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`nt_tmp_2` / master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-NXe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -483,8 +474,6 @@ master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`nt_tmp_1` / master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-NXe-Temp N Drop-Temp-If-NXe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -801,7 +790,6 @@ include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-Xe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -826,8 +814,6 @@ master-bin.000001 # Query # # use `test`; INSERT INTO nt_xx_1() VALUES (1) master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-Xe-Temp N Drop-Temp-If-Xe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -882,7 +868,7 @@ include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2`,`test`.`tt_1` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-TXe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -907,8 +893,8 @@ master-bin.000001 # Query # # use `test`; INSERT INTO nt_xx_1() VALUES (1) master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2`,`test`.`tt_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_1`,`test`.`tt_1` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-TXe-Temp N Drop-Temp-If-TXe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -972,7 +958,6 @@ master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`nt_tmp_2` / master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-NXe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -1003,8 +988,6 @@ master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`nt_tmp_1` / master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-NXe-Temp N Drop-Temp-If-NXe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- diff --git a/mysql-test/suite/rpl/r/rpl_stm_drop_create_temp_table.result b/mysql-test/suite/rpl/r/rpl_stm_drop_create_temp_table.result index 9dc86c0bfa7..0cad8f122de 100644 --- a/mysql-test/suite/rpl/r/rpl_stm_drop_create_temp_table.result +++ b/mysql-test/suite/rpl/r/rpl_stm_drop_create_temp_table.result @@ -89,9 +89,6 @@ Warnings: Note 1051 Unknown table 'test.tt_xx_1' -b-b-b-b-b-b-b-b-b-b-b- >> Drop-Temp-If-Xe-Temp << -b-b-b-b-b-b-b-b-b-b-b- include/show_binlog_events.inc -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ -e-e-e-e-e-e-e-e-e-e-e- >> Drop-Temp-If-Xe-Temp << -e-e-e-e-e-e-e-e-e-e-e- SET @commands= 'Drop-Temp-TXe-Temp'; @@ -112,7 +109,7 @@ Note 1051 Unknown table 'test.tt_1' include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2`,`test`.`tt_1` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2` /* generated by server */ -e-e-e-e-e-e-e-e-e-e-e- >> Drop-Temp-If-TXe-Temp << -e-e-e-e-e-e-e-e-e-e-e- SET @commands= 'Drop-Temp-NXe-Temp'; @@ -134,8 +131,6 @@ include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`nt_tmp_2` /* generated by server */ -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ -e-e-e-e-e-e-e-e-e-e-e- >> Drop-Temp-If-NXe-Temp << -e-e-e-e-e-e-e-e-e-e-e- SET @commands= 'Drop-Temp-TN-Temp'; @@ -291,7 +286,6 @@ include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-Xe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -314,8 +308,6 @@ master-bin.000001 # Query # # use `test`; INSERT INTO nt_xx_1() VALUES (1) master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-Xe-Temp N Drop-Temp-If-Xe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -368,7 +360,7 @@ include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2`,`test`.`tt_1` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-TXe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -391,8 +383,8 @@ master-bin.000001 # Query # # use `test`; INSERT INTO nt_xx_1() VALUES (1) master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2`,`test`.`tt_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_1`,`test`.`tt_1` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-TXe-Temp N Drop-Temp-If-TXe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -454,7 +446,6 @@ master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`nt_tmp_2` / master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-NXe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -483,8 +474,6 @@ master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`nt_tmp_1` / master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-NXe-Temp N Drop-Temp-If-NXe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -801,7 +790,6 @@ include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-Xe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -826,8 +814,6 @@ master-bin.000001 # Query # # use `test`; INSERT INTO nt_xx_1() VALUES (1) master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-Xe-Temp N Drop-Temp-If-Xe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -882,7 +868,7 @@ include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2`,`test`.`tt_1` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-TXe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -907,8 +893,8 @@ master-bin.000001 # Query # # use `test`; INSERT INTO nt_xx_1() VALUES (1) master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2`,`test`.`tt_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_1`,`test`.`tt_1` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_2` /* generated by server */ +master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_tmp_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-TXe-Temp N Drop-Temp-If-TXe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -972,7 +958,6 @@ master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`nt_tmp_2` / master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-NXe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -1003,8 +988,6 @@ master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`nt_tmp_1` / master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Query # # use `test`; INSERT INTO tt_xx_1() VALUES (1) -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-NXe-Temp N Drop-Temp-If-NXe-Temp R << -e-e-e-e-e-e-e-e-e-e-e-
1 0
0 0
[Commits] 2c6d192a6cf: MDEV-20091 DROP TEMPORARY table is logged despite no CREATE was logged
by sujatha 23 Jul '19

23 Jul '19
revision-id: 2c6d192a6cfd7a00da3f0439c2f42c4f7b694119 (mariadb-10.1.39-106-g2c6d192a6cf) parent(s): a5e268a2931c39c7b3cc2ff2e6e3fe6b5e85a383 author: Sujatha committer: Sujatha timestamp: 2019-07-23 15:46:51 +0530 message: MDEV-20091 DROP TEMPORARY table is logged despite no CREATE was logged MDEV-5589 commit set up a policy to skip DROP TEMPORARY TABLE binary logging in case the target table has not been "CREATEed" in binlog (no CREATE Query-log-event was logged into the binary log). It turns out that 1. the rule did not cover non-existing table DROPped with IF-EXISTS clause. The logged-create knowledge for the non-existing one does not even need MDEV-5589 patch, and 2. connection close disobeys it to trigger automatic DROP-IF-EXISTS binlogging. Either 1 or 2 or even both is/are also responsible for unexpected binlog records observed in MDEV-17863, actually rendering a referred @@global.read_only irrelevant as far as the described stored procedure definition *and* the ROW binlog-format are concerned. --- mysql-test/extra/binlog_tests/drop_temp_table.test | 24 ++++--- .../rpl_tests/rpl_drop_create_temp_table.test | 4 ++ .../include/binlog_parallel_replication_marks.test | 9 ++- .../r/binlog_parallel_replication_marks_row.result | 2 - .../suite/binlog/r/binlog_row_drop_tmp_tbl.result | 8 --- .../binlog/r/binlog_row_mix_innodb_myisam.result | 2 - .../suite/binlog/r/binlog_stm_drop_tmp_tbl.result | 4 -- .../suite/rpl/r/create_or_replace_mix.result | 11 +++ .../suite/rpl/r/create_or_replace_row.result | 7 ++ .../suite/rpl/r/create_or_replace_statement.result | 11 +++ .../rpl/r/rpl_row_drop_create_temp_table.result | 27 ------- .../suite/rpl/r/rpl_row_drop_temp_table.result | 36 ++++++++++ mysql-test/suite/rpl/t/create_or_replace.inc | 13 ++++ .../suite/rpl/t/rpl_row_drop_temp_table.test | 54 ++++++++++++++ sql/sql_base.cc | 84 ++++++++++++---------- sql/sql_table.cc | 2 +- 16 files changed, 204 insertions(+), 94 deletions(-) diff --git a/mysql-test/extra/binlog_tests/drop_temp_table.test b/mysql-test/extra/binlog_tests/drop_temp_table.test index c852ee4c8a0..4241974d813 100644 --- a/mysql-test/extra/binlog_tests/drop_temp_table.test +++ b/mysql-test/extra/binlog_tests/drop_temp_table.test @@ -14,12 +14,8 @@ CREATE TEMPORARY TABLE `table:name` (a INT); CREATE TEMPORARY TABLE shortn2 (a INT); ############################################################################## -# BUG#46572 DROP TEMPORARY table IF EXISTS does not have a consistent behavior -# in ROW mode -# -# In RBR, 'DROP TEMPORARY TABLE ...' statement should never be binlogged no -# matter if the tables exist or not. In contrast, both in SBR and MBR, the -# statement should be always binlogged no matter if the tables exist or not. +# MDEV-20091: DROP TEMPORARY TABLE IF EXISTS statements will be written +# to binlog only if the corresponding temporary table exists. ############################################################################## CREATE TEMPORARY TABLE tmp(c1 int); CREATE TEMPORARY TABLE tmp1(c1 int); @@ -30,12 +26,12 @@ CREATE TABLE t(c1 int); DROP TEMPORARY TABLE IF EXISTS tmp; --disable_warnings -# Before fixing BUG#46572, 'DROP TEMPORARY TABLE IF EXISTS...' statement was -# binlogged when the table did not exist in RBR. +# Post MDEV-20091: Following DROP TEMPORARY TABLE statement should not be +# logged as the table is already dropped above. DROP TEMPORARY TABLE IF EXISTS tmp; -# In RBR, 'DROP TEMPORARY TABLE ...' statement is never binlogged no matter if -# the tables exist or not. +# Post MDEV-20091: Only DROP TEMPORARY TABLE statement should be written only +# for 'tmp1' table. DROP TEMPORARY TABLE IF EXISTS tmp, tmp1; DROP TEMPORARY TABLE tmp3; @@ -79,6 +75,12 @@ DROP DATABASE `drop-temp+table-test`; # if there are open temporary tables. As such the implicit drop # for temporary tables on session closing must be logged. # +# MDEV-20091: DROP TEMPORARY TABLE IF EXISTS statements will be written to +# binlog only if the corresponding temporary table exists. In row based +# replication temporary tables are not replicated hence their corresponding +# DROP TEMPORARY TABLE statement will be not be written to binary log upon +# session closure. +# RESET MASTER; @@ -92,8 +94,10 @@ SELECT @@session.binlog_format; --disconnect con1 -- connection default +if (!`SELECT @@BINLOG_FORMAT = 'ROW'`) { --let $wait_binlog_event= DROP --source include/wait_for_binlog_event.inc +} -- source include/show_binlog_events.inc RESET MASTER; diff --git a/mysql-test/extra/rpl_tests/rpl_drop_create_temp_table.test b/mysql-test/extra/rpl_tests/rpl_drop_create_temp_table.test index ffd7fe1a5c4..f8b521e3abf 100644 --- a/mysql-test/extra/rpl_tests/rpl_drop_create_temp_table.test +++ b/mysql-test/extra/rpl_tests/rpl_drop_create_temp_table.test @@ -32,6 +32,10 @@ # is any # Drop-Temp-TT-Temp - Drops two temporary T-tables if there is any # Drop-Temp-NN-Temp - Drops two temporary N-tables if there is any +# +# Note: MDEV-20091: DROP TEMPORARY TABLE IF EXISTS statements will be written +# to binlog only if the corresponding temporary table exists. +# # Drop-Temp-Xe-Temp - Tries to drop a temporary table that does not exist # Drop-Temp-NXe-Temp - Drops a temporary N-table if there is any and # a temporary table that does not exist diff --git a/mysql-test/include/binlog_parallel_replication_marks.test b/mysql-test/include/binlog_parallel_replication_marks.test index ff35875aece..4e673bd30c3 100644 --- a/mysql-test/include/binlog_parallel_replication_marks.test +++ b/mysql-test/include/binlog_parallel_replication_marks.test @@ -49,9 +49,16 @@ connection default; # We need to wait for the implicit DROP TEMPORARY TABLE to be logged after # tmp_con disconnect, otherwise we get sporadic test failures. +# MDEV-20091: DROP TEMPORARY TABLE IF EXISTS statements will be written to +# binlog only if the corresponding temporary table exists. In row based +# replication temporary tables are not replicated hence their corresponding +# DROP TEMPORARY TABLE statement will be not be written to binary log upon +# session closure. + +if (!`SELECT @@BINLOG_FORMAT = 'ROW'`) { --let $wait_condition= SELECT variable_value > $before_drop_pos FROM information_schema.global_status WHERE variable_name = 'binlog_snapshot_position' --source include/wait_condition.inc - +} --let $binlog_pos2=query_get_value(SHOW MASTER STATUS, Position, 1) --let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1) diff --git a/mysql-test/suite/binlog/r/binlog_parallel_replication_marks_row.result b/mysql-test/suite/binlog/r/binlog_parallel_replication_marks_row.result index e9a744a748e..1dafeee47e2 100644 --- a/mysql-test/suite/binlog/r/binlog_parallel_replication_marks_row.result +++ b/mysql-test/suite/binlog/r/binlog_parallel_replication_marks_row.result @@ -89,6 +89,4 @@ BEGIN # server id 1 end_log_pos # Table_map: `test`.`t1` mapped to number # # server id 1 end_log_pos # Write_rows: table id # flags: STMT_END_F COMMIT/*!*/; -# server id 1 end_log_pos # GTID #-#-# ddl -DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `t5` DROP TABLE t1; diff --git a/mysql-test/suite/binlog/r/binlog_row_drop_tmp_tbl.result b/mysql-test/suite/binlog/r/binlog_row_drop_tmp_tbl.result index dce2cc1408c..01ee24b9d48 100644 --- a/mysql-test/suite/binlog/r/binlog_row_drop_tmp_tbl.result +++ b/mysql-test/suite/binlog/r/binlog_row_drop_tmp_tbl.result @@ -30,15 +30,9 @@ master-bin.000001 # Query # # CREATE DATABASE `drop-temp+table-test` master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # use `drop-temp+table-test`; CREATE TABLE t(c1 int) master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `drop-temp+table-test`.`tmp` /* generated by server */ -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `drop-temp+table-test`.`tmp` /* generated by server */ -master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # use `drop-temp+table-test`; DROP TABLE IF EXISTS `t` /* generated by server */ master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # use `drop-temp+table-test`; DROP TABLE IF EXISTS `tmp2`,`t` /* generated by server */ -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # use `drop-temp+table-test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `shortn2`,`table:name`,`shortn1` DROP DATABASE `drop-temp+table-test`; RESET MASTER; CREATE TABLE t1 ( i text ); @@ -56,7 +50,5 @@ master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.t1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F master-bin.000001 # Query # # COMMIT -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `ttmp1` RESET MASTER; DROP TABLE t1; diff --git a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result index f8f4fa5ec31..37018da86a0 100644 --- a/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result +++ b/mysql-test/suite/binlog/r/binlog_row_mix_innodb_myisam.result @@ -303,8 +303,6 @@ master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # use `test`; create table t2 (n int) engine=innodb -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `t1`,`ti` do release_lock("lock1"); drop table t0,t2; set autocommit=0; diff --git a/mysql-test/suite/binlog/r/binlog_stm_drop_tmp_tbl.result b/mysql-test/suite/binlog/r/binlog_stm_drop_tmp_tbl.result index 127df219b02..4eeb8dd80c7 100644 --- a/mysql-test/suite/binlog/r/binlog_stm_drop_tmp_tbl.result +++ b/mysql-test/suite/binlog/r/binlog_stm_drop_tmp_tbl.result @@ -46,12 +46,8 @@ master-bin.000001 # Query # # use `drop-temp+table-test`; CREATE TABLE t(c1 int) master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `drop-temp+table-test`.`tmp` /* generated by server */ master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `drop-temp+table-test`.`tmp` /* generated by server */ -master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `drop-temp+table-test`.`tmp1` /* generated by server */ master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `drop-temp+table-test`.`tmp` /* generated by server */ -master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # use `drop-temp+table-test`; DROP TEMPORARY TABLE `tmp3` /* generated by server */ master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `drop-temp+table-test`.`tmp2` /* generated by server */ diff --git a/mysql-test/suite/rpl/r/create_or_replace_mix.result b/mysql-test/suite/rpl/r/create_or_replace_mix.result index c6cce358799..2c85bb3d93b 100644 --- a/mysql-test/suite/rpl/r/create_or_replace_mix.result +++ b/mysql-test/suite/rpl/r/create_or_replace_mix.result @@ -238,6 +238,13 @@ set binlog_format="STATEMENT"; ERROR HY000: Cannot switch out of the row-based binary log format when the session has open temporary tables drop temporary table t8; set @@binlog_format=@org_binlog_format; +set @@session.binlog_format=default; +drop temporary table if exists t9; +Warnings: +Note 1051 Unknown table 'test.t9' +set session binlog_format=default; +create temporary table t9 (i int); +*** Must be no DROP logged for t9 when there was no CREATE, at disconnect too *** include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # GTID #-#-# @@ -248,5 +255,9 @@ master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # use `test`; create temporary table t7 (a int) master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`t7` /* generated by server */ +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; create temporary table t9 (i int) +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `t9` drop table t2; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/r/create_or_replace_row.result b/mysql-test/suite/rpl/r/create_or_replace_row.result index 8f032107db9..cbb47cc4aba 100644 --- a/mysql-test/suite/rpl/r/create_or_replace_row.result +++ b/mysql-test/suite/rpl/r/create_or_replace_row.result @@ -260,6 +260,13 @@ set binlog_format="STATEMENT"; ERROR HY000: Cannot switch out of the row-based binary log format when the session has open temporary tables drop temporary table t8; set @@binlog_format=@org_binlog_format; +set @@session.binlog_format=default; +drop temporary table if exists t9; +Warnings: +Note 1051 Unknown table 'test.t9' +set session binlog_format=default; +create temporary table t9 (i int); +*** Must be no DROP logged for t9 when there was no CREATE, at disconnect too *** include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # GTID #-#-# diff --git a/mysql-test/suite/rpl/r/create_or_replace_statement.result b/mysql-test/suite/rpl/r/create_or_replace_statement.result index 7bdbc64e9f2..6b47fafbe23 100644 --- a/mysql-test/suite/rpl/r/create_or_replace_statement.result +++ b/mysql-test/suite/rpl/r/create_or_replace_statement.result @@ -230,6 +230,13 @@ set binlog_format="STATEMENT"; ERROR HY000: Cannot switch out of the row-based binary log format when the session has open temporary tables drop temporary table t8; set @@binlog_format=@org_binlog_format; +set @@session.binlog_format=default; +drop temporary table if exists t9; +Warnings: +Note 1051 Unknown table 'test.t9' +set session binlog_format=default; +create temporary table t9 (i int); +*** Must be no DROP logged for t9 when there was no CREATE, at disconnect too *** include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # GTID #-#-# @@ -240,5 +247,9 @@ master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # use `test`; create temporary table t7 (a int) master-bin.000001 # Gtid # # GTID #-#-# master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`t7` /* generated by server */ +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; create temporary table t9 (i int) +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `t9` drop table t2; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/r/rpl_row_drop_create_temp_table.result b/mysql-test/suite/rpl/r/rpl_row_drop_create_temp_table.result index 05b2c04e7ba..ec6d89551ae 100644 --- a/mysql-test/suite/rpl/r/rpl_row_drop_create_temp_table.result +++ b/mysql-test/suite/rpl/r/rpl_row_drop_create_temp_table.result @@ -83,9 +83,6 @@ Warnings: Note 1051 Unknown table 'test.tt_xx_1' -b-b-b-b-b-b-b-b-b-b-b- >> Drop-Temp-If-Xe-Temp << -b-b-b-b-b-b-b-b-b-b-b- include/show_binlog_events.inc -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ -e-e-e-e-e-e-e-e-e-e-e- >> Drop-Temp-If-Xe-Temp << -e-e-e-e-e-e-e-e-e-e-e- SET @commands= 'Drop-Temp-TXe-Temp'; @@ -101,9 +98,6 @@ Warnings: Note 1051 Unknown table 'test.tt_1' -b-b-b-b-b-b-b-b-b-b-b- >> Drop-Temp-If-TXe-Temp << -b-b-b-b-b-b-b-b-b-b-b- include/show_binlog_events.inc -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ -e-e-e-e-e-e-e-e-e-e-e- >> Drop-Temp-If-TXe-Temp << -e-e-e-e-e-e-e-e-e-e-e- SET @commands= 'Drop-Temp-NXe-Temp'; @@ -119,9 +113,6 @@ Warnings: Note 1051 Unknown table 'test.tt_1' -b-b-b-b-b-b-b-b-b-b-b- >> Drop-Temp-If-NXe-Temp << -b-b-b-b-b-b-b-b-b-b-b- include/show_binlog_events.inc -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ -e-e-e-e-e-e-e-e-e-e-e- >> Drop-Temp-If-NXe-Temp << -e-e-e-e-e-e-e-e-e-e-e- SET @commands= 'Drop-Temp-TN-Temp'; @@ -264,7 +255,6 @@ Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.tt_xx_1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-Xe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -289,8 +279,6 @@ master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.tt_xx_1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-Xe-Temp N Drop-Temp-If-Xe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -344,7 +332,6 @@ Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.tt_xx_1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-TXe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -369,8 +356,6 @@ master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.tt_xx_1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-TXe-Temp N Drop-Temp-If-TXe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -424,7 +409,6 @@ Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.tt_xx_1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-NXe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -449,8 +433,6 @@ master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.tt_xx_1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Xid # # COMMIT /* XID */ -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-NXe-Temp N Drop-Temp-If-NXe-Temp C << -e-e-e-e-e-e-e-e-e-e-e- @@ -755,7 +737,6 @@ Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.tt_xx_1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-Xe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -782,8 +763,6 @@ master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.tt_xx_1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_xx_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-Xe-Temp N Drop-Temp-If-Xe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -839,7 +818,6 @@ Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.tt_xx_1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-TXe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -866,8 +844,6 @@ master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.tt_xx_1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-TXe-Temp N Drop-Temp-If-TXe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -923,7 +899,6 @@ Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.tt_xx_1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-NXe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- @@ -950,8 +925,6 @@ master-bin.000001 # Query # # COMMIT master-bin.000001 # Gtid # # BEGIN GTID #-#-# master-bin.000001 # Table_map # # table_id: # (test.tt_xx_1) master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ -master-bin.000001 # Query # # DROP TEMPORARY TABLE IF EXISTS `test`.`tt_1` /* generated by server */ master-bin.000001 # Query # # ROLLBACK -e-e-e-e-e-e-e-e-e-e-e- >> B T Drop-Temp-If-NXe-Temp N Drop-Temp-If-NXe-Temp R << -e-e-e-e-e-e-e-e-e-e-e- diff --git a/mysql-test/suite/rpl/r/rpl_row_drop_temp_table.result b/mysql-test/suite/rpl/r/rpl_row_drop_temp_table.result new file mode 100644 index 00000000000..15b4d396522 --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_row_drop_temp_table.result @@ -0,0 +1,36 @@ +include/master-slave.inc +[connection master] +[connection slave] +SET GLOBAL read_only=1; +[connection master] +CREATE PROCEDURE testproc() +BEGIN +DROP TEMPORARY TABLE IF EXISTS t1_tmp; +DROP TEMPORARY TABLE IF EXISTS t2_tmp; +CREATE TEMPORARY TABLE IF NOT EXISTS t1_tmp ( t1 varchar(400) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TEMPORARY TABLE IF NOT EXISTS t2_tmp ( t2 varchar(16) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +END| +SET GLOBAL read_only=1; +CALL testproc(); +******** None of the above DROP TEMPORARY TABLE statement should be found in binary log ******** +include/show_binlog_events.inc +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Gtid # # GTID #-#-# +master-bin.000001 # Query # # use `test`; CREATE DEFINER=`root`@`localhost` PROCEDURE `testproc`() +BEGIN +DROP TEMPORARY TABLE IF EXISTS t1_tmp; +DROP TEMPORARY TABLE IF EXISTS t2_tmp; +CREATE TEMPORARY TABLE IF NOT EXISTS t1_tmp ( t1 varchar(400) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TEMPORARY TABLE IF NOT EXISTS t2_tmp ( t2 varchar(16) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +END +SELECT @@read_only; +@@read_only +1 +======== CLEAN UP ========= +[connection master] +DROP TEMPORARY TABLE t1_tmp; +DROP TEMPORARY TABLE t2_tmp; +DROP PROCEDURE testproc; +SET GLOBAL read_only=0; +SET GLOBAL read_only=0; +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/create_or_replace.inc b/mysql-test/suite/rpl/t/create_or_replace.inc index 7d0dc487061..35a6ead60ca 100644 --- a/mysql-test/suite/rpl/t/create_or_replace.inc +++ b/mysql-test/suite/rpl/t/create_or_replace.inc @@ -205,6 +205,19 @@ set binlog_format="STATEMENT"; drop temporary table t8; set @@binlog_format=@org_binlog_format; +# MDEV-20091: +# 1. No DROP should be logged for non-existing tmp table, nor +# 2. at the connection close when its creation has not been logged. +set @@session.binlog_format=default; +drop temporary table if exists t9; + +--connect(con1,localhost,root,,) +set session binlog_format=default; +create temporary table t9 (i int); +--echo *** Must be no DROP logged for t9 when there was no CREATE, at disconnect too *** +--disconnect con1 + +--connection server_1 --source include/show_binlog_events.inc # Clean up diff --git a/mysql-test/suite/rpl/t/rpl_row_drop_temp_table.test b/mysql-test/suite/rpl/t/rpl_row_drop_temp_table.test new file mode 100644 index 00000000000..5484928f383 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_row_drop_temp_table.test @@ -0,0 +1,54 @@ +# ==== Purpose ==== +# +# Test verifies that plain DROP TEMPORARY TABLE IF EXISTS statements are not +# replicated during row based replication. +# +# ==== Implementation ==== +# +# Steps: +# 0 - Have a read_only master and slave. Binlog format should be "ROW". +# 1 - Create a procedure which executes DROP TEMPORARY TABLE IF EXISTS +# statements prior to CREATE TEMPORARY TABLE. +# 2 - Execute the procedure. +# 3 - Verify that the DROP TEMPORARY TABLE IF EXISTS statements within the +# procedure are not written to the binary log. +# +# ==== References ==== +# +# MDEV-20091: DROP TEMPORARY table is logged despite no CREATE was logged +# + +--source include/have_binlog_format_row.inc +--source include/have_innodb.inc +--source include/master-slave.inc + +--source include/rpl_connection_slave.inc +SET GLOBAL read_only=1; + +--source include/rpl_connection_master.inc +DELIMITER |; +CREATE PROCEDURE testproc() +BEGIN + DROP TEMPORARY TABLE IF EXISTS t1_tmp; + DROP TEMPORARY TABLE IF EXISTS t2_tmp; + CREATE TEMPORARY TABLE IF NOT EXISTS t1_tmp ( t1 varchar(400) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8; + CREATE TEMPORARY TABLE IF NOT EXISTS t2_tmp ( t2 varchar(16) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +END| +DELIMITER ;| +SET GLOBAL read_only=1; +CALL testproc(); +--echo ******** None of the above DROP TEMPORARY TABLE statement should be found in binary log ******** +--source include/show_binlog_events.inc +--sync_slave_with_master +SELECT @@read_only; + +--echo ======== CLEAN UP ========= +--source include/rpl_connection_master.inc +DROP TEMPORARY TABLE t1_tmp; +DROP TEMPORARY TABLE t2_tmp; +DROP PROCEDURE testproc; +SET GLOBAL read_only=0; +--sync_slave_with_master +SET GLOBAL read_only=0; + +--source include/rpl_end.inc diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 335d3a9dfce..e8bdff8b48f 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1205,7 +1205,7 @@ bool close_temporary_tables(THD *thd) my_thread_id save_pseudo_thread_id= thd->variables.pseudo_thread_id; char db_buf[FN_REFLEN]; String db(db_buf, sizeof(db_buf), system_charset_info); - + bool at_least_one_create_logged; /* Set pseudo_thread_id to be that of the processed table */ thd->variables.pseudo_thread_id= tmpkeyval(thd, table); @@ -1217,56 +1217,62 @@ bool close_temporary_tables(THD *thd) within the sublist of common pseudo_thread_id to create single DROP query */ - for (; + for (at_least_one_create_logged= false; table && is_user_table(table) && tmpkeyval(thd, table) == thd->variables.pseudo_thread_id && table->s->db.length == db.length() && memcmp(table->s->db.str, db.ptr(), db.length()) == 0; table= next) { - /* - We are going to add ` around the table names and possible more - due to special characters - */ - append_identifier(thd, &s_query, table->s->table_name.str, - strlen(table->s->table_name.str)); - s_query.append(','); + if (table->s->table_creation_was_logged) + { + at_least_one_create_logged= true; + /* + We are going to add ` around the table names and possible more + due to special characters + */ + append_identifier(thd, &s_query, table->s->table_name.str, + strlen(table->s->table_name.str)); + s_query.append(','); + } next= table->next; mysql_lock_remove(thd, thd->lock, table); close_temporary(table, 1, 1); } - thd->clear_error(); - CHARSET_INFO *cs_save= thd->variables.character_set_client; - thd->variables.character_set_client= system_charset_info; - thd->thread_specific_used= TRUE; - Query_log_event qinfo(thd, s_query.ptr(), - s_query.length() - 1 /* to remove trailing ',' */, - FALSE, TRUE, FALSE, 0); - qinfo.db= db.ptr(); - qinfo.db_len= db.length(); - thd->variables.character_set_client= cs_save; - - thd->get_stmt_da()->set_overwrite_status(true); - thd->transaction.stmt.mark_dropped_temp_table(); - if ((error= (mysql_bin_log.write(&qinfo) || error))) + if (at_least_one_create_logged) { - /* - If we're here following THD::cleanup, thence the connection - has been closed already. So lets print a message to the - error log instead of pushing yet another error into the - stmt_da. - - Also, we keep the error flag so that we propagate the error - up in the stack. This way, if we're the SQL thread we notice - that close_temporary_tables failed. (Actually, the SQL - thread only calls close_temporary_tables while applying old - Start_log_event_v3 events.) - */ - sql_print_error("Failed to write the DROP statement for " - "temporary tables to binary log"); + thd->clear_error(); + CHARSET_INFO *cs_save= thd->variables.character_set_client; + thd->variables.character_set_client= system_charset_info; + thd->thread_specific_used= TRUE; + Query_log_event qinfo(thd, s_query.ptr(), + s_query.length() - 1 /* to remove trailing ',' */, + FALSE, TRUE, FALSE, 0); + qinfo.db= db.ptr(); + qinfo.db_len= db.length(); + thd->variables.character_set_client= cs_save; + + thd->get_stmt_da()->set_overwrite_status(true); + thd->transaction.stmt.mark_dropped_temp_table(); + if ((error= (mysql_bin_log.write(&qinfo) || error))) + { + /* + If we're here following THD::cleanup, thence the connection + has been closed already. So lets print a message to the + error log instead of pushing yet another error into the + stmt_da. + + Also, we keep the error flag so that we propagate the error + up in the stack. This way, if we're the SQL thread we notice + that close_temporary_tables failed. (Actually, the SQL + thread only calls close_temporary_tables while applying old + Start_log_event_v3 events.) + */ + sql_print_error("Failed to write the DROP statement for " + "temporary tables to binary log"); + } + thd->get_stmt_da()->set_overwrite_status(false); } - thd->get_stmt_da()->set_overwrite_status(false); - thd->variables.pseudo_thread_id= save_pseudo_thread_id; thd->thread_specific_used= save_thread_specific_used; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 2696513d95f..25bbaf23617 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2265,7 +2265,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, for (table= tables; table; table= table->next_local) { bool is_trans= 0; - bool table_creation_was_logged= 1; + bool table_creation_was_logged= 0; char *db=table->db; size_t db_length= table->db_length; handlerton *table_type= 0;
1 0
0 0
[Commits] 731ef751758: MDEV#20107: rocksdb.check_ignore_unknown_options fails on OS X again
by Sergei Petrunia 19 Jul '19

19 Jul '19
revision-id: 731ef751758e3267745bfce582a3692115753289 (mariadb-10.2.25-86-g731ef751758) parent(s): 8ec4aa4b6b4d22a5907b81603141769b2c720bd2 author: Sergei Petrunia committer: Sergei Petrunia timestamp: 2019-07-19 21:24:28 +0300 message: MDEV#20107: rocksdb.check_ignore_unknown_options fails on OS X again Make it to work on Windows, too. --- storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test b/storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test index d502dd256b0..c8c12626139 100644 --- a/storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test +++ b/storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test @@ -44,7 +44,7 @@ EOF --shutdown_server 10 --error 1 ---exec $MYSQLD_CMD --plugin_load=$HA_ROCKSDB_SO --rocksdb_ignore_unknown_options=0 --loose-console --log-error=$error_log +--exec $MYSQLD_CMD --plugin_load=$HA_ROCKSDB_SO --rocksdb_ignore_unknown_options=0 --log-error=$error_log let SEARCH_FILE= $error_log; let SEARCH_PATTERN= RocksDB: Compatibility check against existing database options failed;
1 0
0 0
[Commits] 590c7ab167c: MDEV#20107: rocksdb.check_ignore_unknown_options fails on OS X again
by Sergei Petrunia 19 Jul '19

19 Jul '19
revision-id: 590c7ab167c4a9567e268e149c2786141903b11d (mariadb-10.2.25-75-g590c7ab167c) parent(s): 52f6aa1c548b80cb93f9ec49c85adb843f7a5082 author: Sergei Petrunia committer: Sergei Petrunia timestamp: 2019-07-19 18:21:05 +0300 message: MDEV#20107: rocksdb.check_ignore_unknown_options fails on OS X again Rewrite the unportable sed/shell code in Perl. --- .../rocksdb/t/check_ignore_unknown_options.test | 40 ++++++++++++++++++---- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test b/storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test index b39c022fc46..d502dd256b0 100644 --- a/storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test +++ b/storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test @@ -1,16 +1,44 @@ -# MariaDB: "xargs" is not present on windows builders. -# we could work around this but this is not a priority. ---source include/not_windows.inc - --disable_warnings let $MYSQLD_DATADIR= `select @@datadir`; let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; let $error_log= $MYSQLTEST_VARDIR/log/my_restart.err; select variable_name, variable_value from information_schema.global_variables where variable_name="rocksdb_ignore_unknown_options"; ---exec find $MYSQLD_DATADIR/#rocksdb/OPTIONS* | sort -t- -k 2 -n | tail -1 | xargs -0 -I {} -t sh -c "sed -i 's/rocksdb_version=.*/rocksdb_version=99.9.9/' {}" ---exec find $MYSQLD_DATADIR/#rocksdb/OPTIONS* | sort -t- -k 2 -n | tail -1 | xargs -0 -I {} -t sh -c "echo hello=world>>{}" +# +# MariaDB: The following shell commands are not portable so we are +# using perl instead: +#--exec find $MYSQLD_DATADIR/#rocksdb/OPTIONS* | sort -t- -k 2 -n | tail -1 | xargs -0 -I {} -t sh -c "sed -i 's/rocksdb_version=.*/rocksdb_version=99.9.9/' {}" +#--exec find $MYSQLD_DATADIR/#rocksdb/OPTIONS* | sort -t- -k 2 -n | tail -1 | xargs -0 -I {} -t sh -c "echo hello=world>>{}" + +perl; + my $path=$ENV{MYSQLTEST_VARDIR} . "/mysqld.1/data/\#rocksdb"; + opendir(my $dh, $path) || die "Can't opendir $some_dir: $!"; + my @files = grep { /^OPTIONS/ } readdir($dh); + closedir($dh); + sub compare_second_as_number { + local $aa= shift; + local $bb= shift; + $aa =~ s/OPTIONS-//; + $bb =~ s/OPTIONS-//; + return $aa <=> $bb; + } + + @sorted_files = sort { compare_second_as_number($a, $b); } @files; + my $last_file= $sorted_files[-1]; + + my $contents=""; + open(my $fh, "<", "$path/$last_file") || die ("Couldn't open $path/$last_file"); + while (<$fh>) { + $_ =~ s/rocksdb_version=.*/rocksdb_version=99.9.9/; + $contents .= $_; + } + close($fh); + $contents .= "hello=world\n"; + open(my $fh, ">", "$path/$last_file") || die("Can't open $path/$file for writing"); + print $fh $contents; + close($fh); +EOF --exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --shutdown_server 10
1 0
0 0
[Commits] 6cbde735446: rocksdb.check_ignore_unknown_options fails on OS X again
by Sergei Petrunia 19 Jul '19

19 Jul '19
revision-id: 6cbde735446e616a1dc69d9ad91541d814890806 (mariadb-10.2.25-75-g6cbde735446) parent(s): 52f6aa1c548b80cb93f9ec49c85adb843f7a5082 author: Sergei Petrunia committer: Sergei Petrunia timestamp: 2019-07-19 18:20:17 +0300 message: rocksdb.check_ignore_unknown_options fails on OS X again --- .../rocksdb/t/check_ignore_unknown_options.test | 40 ++++++++++++++++++---- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test b/storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test index b39c022fc46..d502dd256b0 100644 --- a/storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test +++ b/storage/rocksdb/mysql-test/rocksdb/t/check_ignore_unknown_options.test @@ -1,16 +1,44 @@ -# MariaDB: "xargs" is not present on windows builders. -# we could work around this but this is not a priority. ---source include/not_windows.inc - --disable_warnings let $MYSQLD_DATADIR= `select @@datadir`; let $restart_file= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect; let $error_log= $MYSQLTEST_VARDIR/log/my_restart.err; select variable_name, variable_value from information_schema.global_variables where variable_name="rocksdb_ignore_unknown_options"; ---exec find $MYSQLD_DATADIR/#rocksdb/OPTIONS* | sort -t- -k 2 -n | tail -1 | xargs -0 -I {} -t sh -c "sed -i 's/rocksdb_version=.*/rocksdb_version=99.9.9/' {}" ---exec find $MYSQLD_DATADIR/#rocksdb/OPTIONS* | sort -t- -k 2 -n | tail -1 | xargs -0 -I {} -t sh -c "echo hello=world>>{}" +# +# MariaDB: The following shell commands are not portable so we are +# using perl instead: +#--exec find $MYSQLD_DATADIR/#rocksdb/OPTIONS* | sort -t- -k 2 -n | tail -1 | xargs -0 -I {} -t sh -c "sed -i 's/rocksdb_version=.*/rocksdb_version=99.9.9/' {}" +#--exec find $MYSQLD_DATADIR/#rocksdb/OPTIONS* | sort -t- -k 2 -n | tail -1 | xargs -0 -I {} -t sh -c "echo hello=world>>{}" + +perl; + my $path=$ENV{MYSQLTEST_VARDIR} . "/mysqld.1/data/\#rocksdb"; + opendir(my $dh, $path) || die "Can't opendir $some_dir: $!"; + my @files = grep { /^OPTIONS/ } readdir($dh); + closedir($dh); + sub compare_second_as_number { + local $aa= shift; + local $bb= shift; + $aa =~ s/OPTIONS-//; + $bb =~ s/OPTIONS-//; + return $aa <=> $bb; + } + + @sorted_files = sort { compare_second_as_number($a, $b); } @files; + my $last_file= $sorted_files[-1]; + + my $contents=""; + open(my $fh, "<", "$path/$last_file") || die ("Couldn't open $path/$last_file"); + while (<$fh>) { + $_ =~ s/rocksdb_version=.*/rocksdb_version=99.9.9/; + $contents .= $_; + } + close($fh); + $contents .= "hello=world\n"; + open(my $fh, ">", "$path/$last_file") || die("Can't open $path/$file for writing"); + print $fh $contents; + close($fh); +EOF --exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --shutdown_server 10
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • ...
  • 1461
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.