From d8acafcbf273120726dea27507a1a3ab7b0bd821 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 11 Jan 2013 12:44:21 +0100 Subject: [PATCH 01/11] MDEV-4020 : Make sure strmov symbol is exported by client library on Linux (even if the server and libraries itself use stpcpy instead of it) It is a workaround that allows myodbc built by certain distributions' (CentOS,Fedora) to peacefully coexist with mariadb client libraries. The problem is that MyODBC in these distros needs strmov() to be exported by mysql client shared library, or else myodbc fails to load. --- strings/strmov.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/strings/strmov.c b/strings/strmov.c index 1a945ad2a6f..7a3f2d02dcf 100644 --- a/strings/strmov.c +++ b/strings/strmov.c @@ -34,10 +34,6 @@ into dst, which seems useful. */ -#include "strings_def.h" - -#ifndef strmov - #if !defined(MC68000) && !defined(DS90) char *strmov(register char *dst, register const char *src) @@ -60,5 +56,3 @@ char *strmov(dst, src) } #endif - -#endif /* strmov */ From 12bf6fe85893f6a69a74ec1c733e533051058dd3 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 11 Jan 2013 20:26:34 -0800 Subject: [PATCH 02/11] Fixed bug mdev-4025. The bug could lead to a wrong estimate of the number of expected rows in the output of the EXPLAIN commands for queries with GROUP BY. This could be observed in the test case for LP bug 934348. --- mysql-test/r/subselect_sj_jcl6.result | 4 ++-- sql/sql_select.cc | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result index a189132b11a..6247688d635 100644 --- a/mysql-test/r/subselect_sj_jcl6.result +++ b/mysql-test/r/subselect_sj_jcl6.result @@ -2978,7 +2978,7 @@ EXPLAIN SELECT a FROM t1 t WHERE a IN (SELECT b FROM t1, t2 WHERE b = a) GROUP BY a HAVING a != 'z'; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t index idx_a idx_a 4 NULL 1 Using index +1 PRIMARY t index idx_a idx_a 4 NULL 3 Using index 1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 2 MATERIALIZED t2 ALL NULL NULL NULL NULL 2 Using where 2 MATERIALIZED t1 ref idx_a idx_a 4 test.t2.b 2 Using index @@ -2992,7 +2992,7 @@ EXPLAIN SELECT a FROM t1 t WHERE a IN (SELECT b FROM t1, t2 WHERE b = a) GROUP BY a HAVING a != 'z'; id select_type table type possible_keys key key_len ref rows Extra -1 PRIMARY t index idx_a idx_a 4 NULL 1 Using index +1 PRIMARY t index idx_a idx_a 4 NULL 3 Using index 1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 2 MATERIALIZED t2 ALL NULL NULL NULL NULL 2 Using where 2 MATERIALIZED t1 ref idx_a idx_a 4 test.t2.b 2 Using index diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ca7e1bf2a66..aa47793df8f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -18371,8 +18371,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit_arg, and as result we'll choose an index scan when using ref/range access + filesort will be cheaper. */ - select_limit= (ha_rows) (select_limit < fanout ? - 1 : select_limit/fanout); + if (select_limit_arg != HA_POS_ERROR) + select_limit= (ha_rows) (select_limit < fanout ? + 1 : select_limit/fanout); /* We assume that each of the tested indexes is not correlated with ref_key. Thus, to select first N records we have to scan From f8f90aa75fbe8ab5c543d788f2afe55926ae34cb Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 15 Jan 2013 16:46:27 -0800 Subject: [PATCH 03/11] Fixed bug mdev-3938. The original patch with the implementation of virtual columns did not support INSERT DELAYED into tables with virtual columns. This patch fixes the problem. --- mysql-test/suite/vcol/r/vcol_misc.result | 10 ++++++++ mysql-test/suite/vcol/t/vcol_misc.test | 18 +++++++++++++++ sql/mysql_priv.h | 3 +++ sql/sql_base.cc | 3 ++- sql/sql_insert.cc | 29 ++++++++++++++++++++++++ sql/table.cc | 12 ++++++---- 6 files changed, 70 insertions(+), 5 deletions(-) diff --git a/mysql-test/suite/vcol/r/vcol_misc.result b/mysql-test/suite/vcol/r/vcol_misc.result index a4b2cee4bf6..f679d5eb671 100644 --- a/mysql-test/suite/vcol/r/vcol_misc.result +++ b/mysql-test/suite/vcol/r/vcol_misc.result @@ -182,3 +182,13 @@ a b c 2 3 y 0 1 y,n drop table t1,t2; +CREATE TABLE t1 ( +ts TIMESTAMP, +tsv TIMESTAMP AS (ADDDATE(ts, INTERVAL 1 DAY)) VIRTUAL +) ENGINE=MyISAM; +INSERT INTO t1 (tsv) VALUES (DEFAULT); +INSERT DELAYED INTO t1 (tsv) VALUES (DEFAULT); +SELECT COUNT(*) FROM t1; +COUNT(*) +2 +DROP TABLE t1; diff --git a/mysql-test/suite/vcol/t/vcol_misc.test b/mysql-test/suite/vcol/t/vcol_misc.test index 732003da992..53c04898648 100644 --- a/mysql-test/suite/vcol/t/vcol_misc.test +++ b/mysql-test/suite/vcol/t/vcol_misc.test @@ -178,3 +178,21 @@ insert into t2(a,b) values (7,0), (2,3), (0,1); select * from t2; drop table t1,t2; + +# +# Bug mdev-3938: INSERT DELAYED for a table with virtual columns +# + +CREATE TABLE t1 ( + ts TIMESTAMP, + tsv TIMESTAMP AS (ADDDATE(ts, INTERVAL 1 DAY)) VIRTUAL +) ENGINE=MyISAM; + +INSERT INTO t1 (tsv) VALUES (DEFAULT); + +INSERT DELAYED INTO t1 (tsv) VALUES (DEFAULT); + +SELECT COUNT(*) FROM t1; + +DROP TABLE t1; + diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b40e40792f9..e9973f4c3de 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2292,6 +2292,9 @@ void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg); int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, bool is_create_table); +bool unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, + TABLE *table, Field *field, + LEX_STRING *vcol_expr, bool *error_reported); int readfrm(const char *name, uchar **data, size_t *length); int writefrm(const char* name, const uchar* data, size_t len); int closefrm(TABLE *table, bool free_share); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 04198af84b9..f1257a3668f 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8429,7 +8429,8 @@ fill_record(THD * thd, List &fields, List &values, rfield->field_name, table->s->table_name.str); thd->abort_on_warning= abort_on_warning_saved; } - if ((value->save_in_field(rfield, 0) < 0) && !ignore_errors) + if ((!rfield->vcol_info || rfield->stored_in_db) && + (value->save_in_field(rfield, 0) < 0) && !ignore_errors) { my_message(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), MYF(0)); goto err; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 9962789029e..f76c2252eb9 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2084,6 +2084,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) { my_ptrdiff_t adjust_ptrs; Field **field,**org_field, *found_next_number_field; + Field **vfield; TABLE *copy; TABLE_SHARE *share; uchar *bitmap; @@ -2127,12 +2128,20 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) if (!copy_tmp) goto error; + if (share->vfields) + { + vfield= (Field **) client_thd->alloc((share->vfields+1)*sizeof(Field*)); + if (!vfield) + goto error; + } + /* Copy the TABLE object. */ copy= new (copy_tmp) TABLE; *copy= *table; /* We don't need to change the file handler here */ /* Assign the pointers for the field pointers array and the record. */ field= copy->field= (Field**) (copy + 1); + copy->vfield= vfield; bitmap= (uchar*) (field + share->fields + 1); copy->record[0]= (bitmap + share->column_bitmap_size*3); memcpy((char*) copy->record[0], (char*) table->record[0], share->reclength); @@ -2156,6 +2165,26 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) } *field=0; + if (table->vfield) + { + for (field= copy->field; *field; field++) + { + if ((*field)->vcol_info) + { + bool error_reported= FALSE; + if (unpack_vcol_info_from_frm(client_thd, + client_thd->mem_root, + copy, + *field, + &(*field)->vcol_info->expr_str, + &error_reported)) + goto error; + *vfield++= *field; + } + } + *vfield= 0; + } + /* Adjust timestamp */ if (table->timestamp_field) { diff --git a/sql/table.cc b/sql/table.cc index 5c1e27b87c7..dfc9c2d933c 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1925,8 +1925,10 @@ end: @brief Unpack the definition of a virtual column from its linear representation - @parm + @param thd The thread object + @param + mem_root The mem_root object where to allocated memory @param table The table containing the virtual column @param @@ -1956,6 +1958,7 @@ end: TRUE Otherwise */ bool unpack_vcol_info_from_frm(THD *thd, + MEM_ROOT *mem_root, TABLE *table, Field *field, LEX_STRING *vcol_expr, @@ -1981,7 +1984,7 @@ bool unpack_vcol_info_from_frm(THD *thd, "PARSE_VCOL_EXPR ()". */ - if (!(vcol_expr_str= (char*) alloc_root(&table->mem_root, + if (!(vcol_expr_str= (char*) alloc_root(mem_root, vcol_expr->length + parse_vcol_keyword.length + 3))) { @@ -2011,8 +2014,8 @@ bool unpack_vcol_info_from_frm(THD *thd, vcol_arena= table->expr_arena; if (!vcol_arena) { - Query_arena expr_arena(&table->mem_root, Query_arena::INITIALIZED); - if (!(vcol_arena= (Query_arena *) alloc_root(&table->mem_root, + Query_arena expr_arena(mem_root, Query_arena::INITIALIZED); + if (!(vcol_arena= (Query_arena *) alloc_root(mem_root, sizeof(Query_arena)))) goto err; *vcol_arena= expr_arena; @@ -2265,6 +2268,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, if ((*field_ptr)->vcol_info) { if (unpack_vcol_info_from_frm(thd, + &outparam->mem_root, outparam, *field_ptr, &(*field_ptr)->vcol_info->expr_str, From a716b061676d01920fa83298cd1fbb57725d6ad9 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 16 Jan 2013 15:11:13 +0200 Subject: [PATCH 04/11] MDEV-3988 fix. Subquery turned into constant too late to be excluded from grouping list so test for constant added to the create_temp_table(). --- mysql-test/r/subselect_innodb.result | 18 ++++++++++++++++++ mysql-test/t/subselect_innodb.test | 14 ++++++++++++++ sql/sql_select.cc | 10 ++++++++++ 3 files changed, 42 insertions(+) diff --git a/mysql-test/r/subselect_innodb.result b/mysql-test/r/subselect_innodb.result index ee28a6cdb37..9750a53a76c 100644 --- a/mysql-test/r/subselect_innodb.result +++ b/mysql-test/r/subselect_innodb.result @@ -380,4 +380,22 @@ select 1 from t1 where 1 like (select 1 from t1 where 1 <=> (select 1 from t1 gr 1 1 drop table t1; +# +# MDEV-3988 crash in create_tmp_table +# +drop table if exists `t1`,`t2`; +Warnings: +Note 1051 Unknown table 't1' +Note 1051 Unknown table 't2' +create table `t1`(`a` char(1) character set utf8)engine=innodb; +create table `t2`(`b` char(1) character set utf8)engine=memory; +select distinct (select 1 from `t2` where `a`) `d2` from `t1`; +d2 +select distinct (select 1 from `t2` where `a`) `d2`, a from `t1`; +d2 a +select distinct a, (select 1 from `t2` where `a`) `d2` from `t1`; +a d2 +select distinct (1 + (select 1 from `t2` where `a`)) `d2` from `t1`; +d2 +drop table t1,t2; set optimizer_switch=@subselect_innodb_tmp; diff --git a/mysql-test/t/subselect_innodb.test b/mysql-test/t/subselect_innodb.test index 26ff1072e30..f6ac5204c3e 100644 --- a/mysql-test/t/subselect_innodb.test +++ b/mysql-test/t/subselect_innodb.test @@ -368,4 +368,18 @@ select 1 from t1 where 1 like (select 1 from t1 where 1 <=> (select 1 from t1 gr drop table t1; +--echo # +--echo # MDEV-3988 crash in create_tmp_table +--echo # + +drop table if exists `t1`,`t2`; +create table `t1`(`a` char(1) character set utf8)engine=innodb; +create table `t2`(`b` char(1) character set utf8)engine=memory; +select distinct (select 1 from `t2` where `a`) `d2` from `t1`; +select distinct (select 1 from `t2` where `a`) `d2`, a from `t1`; +select distinct a, (select 1 from `t2` where `a`) `d2` from `t1`; +select distinct (1 + (select 1 from `t2` where `a`)) `d2` from `t1`; + +drop table t1,t2; + set optimizer_switch=@subselect_innodb_tmp; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index aa47793df8f..0897aa287db 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13797,10 +13797,20 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, if (group) { + ORDER **prev= &group; if (!param->quick_group) group=0; // Can't use group key else for (ORDER *tmp=group ; tmp ; tmp=tmp->next) { + /* Exclude found constant from the list */ + if ((*tmp->item)->const_item()) + { + *prev= tmp->next; + param->group_parts--; + continue; + } + else + prev= &(tmp->next); /* marker == 4 means two things: - store NULLs in the key, and From 8a296e6ca2e55f9f1f3ce25d311291a20ee1c9e7 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 17 Jan 2013 13:53:15 +0200 Subject: [PATCH 05/11] backport of: Don't reset maybe_null in update_used_tables(); This breaks ROLLUP This fixed failing test in group_by.test --- mysql-test/r/join_outer.result | 8 ++++---- mysql-test/r/join_outer_jcl6.result | 8 ++++---- sql/item.cc | 2 +- sql/item.h | 4 ++-- sql/item_cmpfunc.h | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/join_outer.result b/mysql-test/r/join_outer.result index 4541cdbc752..17bc705b4f3 100644 --- a/mysql-test/r/join_outer.result +++ b/mysql-test/r/join_outer.result @@ -1770,10 +1770,10 @@ SELECT t1.pk FROM t2 LEFT JOIN t1 ON t2.pk = t1.a WHERE t1.b BETWEEN 5 AND 6 AND t1.pk IS NULL OR t1.pk = 5 ORDER BY t1.pk; id select_type table type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 100.00 +1 SIMPLE t1 const PRIMARY,idx PRIMARY 4 const 1 100.00 1 SIMPLE t2 const PRIMARY PRIMARY 4 const 1 100.00 Using index Warnings: -Note 1003 select 5 AS `pk` from `test`.`t2` join `test`.`t1` where (1) order by 5 +Note 1003 select 5 AS `pk` from `test`.`t2` join `test`.`t1` where ((((1 between 5 and 6) and isnull(5)) or 1)) order by 5 SELECT t1.pk FROM t2 LEFT JOIN t1 ON t2.pk = t1.a WHERE t1.b BETWEEN 5 AND 6 AND t1.pk IS NULL OR t1.pk = 5 ORDER BY t1.pk; @@ -1809,10 +1809,10 @@ SELECT t1.b, t2.c, t2.d FROM t2 LEFT JOIN t1 ON t2.c = t1.a WHERE t1.pk BETWEEN 5 AND 6 AND t1.b IS NULL OR t1.b = 5 ORDER BY t1.b; id select_type table type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 ref idx idx 4 const 2 100.00 Using where +1 SIMPLE t1 ref PRIMARY,idx idx 4 const 2 100.00 Using where; Using filesort 1 SIMPLE t2 ref c c 5 test.t1.a 2 100.00 Warnings: -Note 1003 select `test`.`t1`.`b` AS `b`,`test`.`t2`.`c` AS `c`,`test`.`t2`.`d` AS `d` from `test`.`t2` join `test`.`t1` where ((`test`.`t2`.`c` = `test`.`t1`.`a`) and (`test`.`t1`.`b` = 5)) order by `test`.`t1`.`b` +Note 1003 select `test`.`t1`.`b` AS `b`,`test`.`t2`.`c` AS `c`,`test`.`t2`.`d` AS `d` from `test`.`t2` join `test`.`t1` where ((`test`.`t2`.`c` = `test`.`t1`.`a`) and (((`test`.`t1`.`pk` between 5 and 6) and isnull(`test`.`t1`.`b`)) or (`test`.`t1`.`b` = 5))) order by `test`.`t1`.`b` SELECT t1.b, t2.c, t2.d FROM t2 LEFT JOIN t1 ON t2.c = t1.a WHERE t1.pk BETWEEN 5 AND 6 AND t1.b IS NULL OR t1.b = 5 ORDER BY t1.b; diff --git a/mysql-test/r/join_outer_jcl6.result b/mysql-test/r/join_outer_jcl6.result index 3272186d12f..981e8002ea0 100644 --- a/mysql-test/r/join_outer_jcl6.result +++ b/mysql-test/r/join_outer_jcl6.result @@ -1781,10 +1781,10 @@ SELECT t1.pk FROM t2 LEFT JOIN t1 ON t2.pk = t1.a WHERE t1.b BETWEEN 5 AND 6 AND t1.pk IS NULL OR t1.pk = 5 ORDER BY t1.pk; id select_type table type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 const PRIMARY PRIMARY 4 const 1 100.00 +1 SIMPLE t1 const PRIMARY,idx PRIMARY 4 const 1 100.00 1 SIMPLE t2 const PRIMARY PRIMARY 4 const 1 100.00 Using index Warnings: -Note 1003 select 5 AS `pk` from `test`.`t2` join `test`.`t1` where (1) order by 5 +Note 1003 select 5 AS `pk` from `test`.`t2` join `test`.`t1` where ((((1 between 5 and 6) and isnull(5)) or 1)) order by 5 SELECT t1.pk FROM t2 LEFT JOIN t1 ON t2.pk = t1.a WHERE t1.b BETWEEN 5 AND 6 AND t1.pk IS NULL OR t1.pk = 5 ORDER BY t1.pk; @@ -1820,10 +1820,10 @@ SELECT t1.b, t2.c, t2.d FROM t2 LEFT JOIN t1 ON t2.c = t1.a WHERE t1.pk BETWEEN 5 AND 6 AND t1.b IS NULL OR t1.b = 5 ORDER BY t1.b; id select_type table type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 ref idx idx 4 const 2 100.00 Using where +1 SIMPLE t1 ref PRIMARY,idx idx 4 const 2 100.00 Using where; Using filesort 1 SIMPLE t2 ref c c 5 test.t1.a 2 100.00 Warnings: -Note 1003 select `test`.`t1`.`b` AS `b`,`test`.`t2`.`c` AS `c`,`test`.`t2`.`d` AS `d` from `test`.`t2` join `test`.`t1` where ((`test`.`t2`.`c` = `test`.`t1`.`a`) and (`test`.`t1`.`b` = 5)) order by `test`.`t1`.`b` +Note 1003 select `test`.`t1`.`b` AS `b`,`test`.`t2`.`c` AS `c`,`test`.`t2`.`d` AS `d` from `test`.`t2` join `test`.`t1` where ((`test`.`t2`.`c` = `test`.`t1`.`a`) and (((`test`.`t1`.`pk` between 5 and 6) and isnull(`test`.`t1`.`b`)) or (`test`.`t1`.`b` = 5))) order by `test`.`t1`.`b` SELECT t1.b, t2.c, t2.d FROM t2 LEFT JOIN t1 ON t2.c = t1.a WHERE t1.pk BETWEEN 5 AND 6 AND t1.b IS NULL OR t1.b = 5 ORDER BY t1.b; diff --git a/sql/item.cc b/sql/item.cc index aee35b611e7..2e023168f34 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -9169,7 +9169,7 @@ void Item_ref::update_used_tables() { if (!get_depended_from()) (*ref)->update_used_tables(); - maybe_null= (*ref)->maybe_null; + maybe_null|= (*ref)->maybe_null; } void Item_direct_view_ref::update_used_tables() diff --git a/sql/item.h b/sql/item.h index 2b5e867feb7..1e35c7839de 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1855,7 +1855,7 @@ public: { update_table_bitmaps(); if (field && field->table) - maybe_null= field->maybe_null(); + maybe_null|= field->maybe_null(); } Item *get_tmp_table_item(THD *thd); bool collect_item_field_processor(uchar * arg); @@ -2882,7 +2882,7 @@ public: void update_used_tables() { orig_item->update_used_tables(); - maybe_null= orig_item->maybe_null; + maybe_null|= orig_item->maybe_null; } bool const_item() const { return orig_item->const_item(); } table_map not_null_tables() const { return orig_item->not_null_tables(); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index dca139e6321..8c505b36758 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -760,7 +760,7 @@ public: void update_used_tables() { Item_func_coalesce::update_used_tables(); - maybe_null= args[1]->maybe_null; + maybe_null|= args[1]->maybe_null; } const char *func_name() const { return "ifnull"; } Field *tmp_table_field(TABLE *table); @@ -787,7 +787,7 @@ public: void update_used_tables() { Item_func::update_used_tables(); - maybe_null= args[1]->maybe_null || args[2]->maybe_null; + maybe_null|= args[1]->maybe_null || args[2]->maybe_null; } uint decimal_precision() const; const char *func_name() const { return "if"; } From d51f96b16754cad5d2c9a91bb5b5e0673e59ded0 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 17 Jan 2013 16:08:05 +0200 Subject: [PATCH 06/11] MDEV-3900 Optimizer difference between MySQL and MariaDB with stored functions in WHERE clause of UPDATE or DELETE statements Analysis The reason for the less efficient plan was result of a prior design decision - to limit the eveluation of constant expressions during optimization to only non-expensive ones. With this approach all stored procedures were considered expensive, and were not evaluated during optimization. As a result, SPs didn't participate in range optimization, which resulted in a plan with table scan rather than index range scan. Solution Instead of considering all SPs expensive, consider expensive only those SPs that are non-deterministic. If an SP is deterministic, the optimizer will checj if it is constant, and may eventually evaluate it during optimization. --- mysql-test/r/sp.result | 36 +++++++++++++++++++++++++++++++++--- mysql-test/t/sp.test | 19 +++++++++++++++++++ sql/item_func.cc | 13 +++++++++++++ sql/item_func.h | 5 +++-- 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 454be15a1ad..cc5a1f6f65a 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -6420,16 +6420,16 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref c1 c1 5 const 1 Using index EXPLAIN SELECT * FROM t1 WHERE c1=f1(); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref c1 c1 5 const 0 Using index +1 SIMPLE t1 ref c1 c1 5 const 1 Using index EXPLAIN SELECT * FROM v1 WHERE c1=1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref c1 c1 5 const 1 Using index EXPLAIN SELECT * FROM v1 WHERE c1=f1(); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref c1 c1 5 const 0 Using index +1 SIMPLE t1 ref c1 c1 5 const 1 Using index EXPLAIN SELECT * FROM t1 WHERE c1=f2(10); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ref c1 c1 5 const 0 Using index +1 SIMPLE t1 ref c1 c1 5 const 1 Using index EXPLAIN SELECT * FROM t1 WHERE c1=f2(c1); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 index NULL c1 5 NULL 5 Using where; Using index @@ -7146,3 +7146,33 @@ c1 c2 count(c3) 2012-03-01 01:00:00 3 1 2012-03-01 02:00:00 3 1 DROP PROCEDURE p1; + +MDEV-3900 Optimizer difference between MySQL and MariaDB with stored functions in WHERE clause of UPDATE or DELETE statements + +CREATE FUNCTION tdn() RETURNS int(7) DETERMINISTIC RETURN to_days(now()); +CREATE TABLE t1 (pk INT NOT NULL AUTO_INCREMENT PRIMARY KEY, daynum INT, a CHAR(1), INDEX(daynum), INDEX(a)) ENGINE=MyISAM; +INSERT INTO t1 (daynum) VALUES (1),(2),(3),(4),(5),(TO_DAYS(NOW())),(7),(8); +INSERT INTO t1 (daynum) SELECT a1.daynum FROM t1 a1, t1 a2, t1 a3, t1 a4, t1 a5; +FLUSH TABLES; +FLUSH STATUS; +SHOW STATUS LIKE '%Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 0 +Handler_read_next 0 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 0 +UPDATE t1 SET a = '+' WHERE daynum=tdn(); +SHOW STATUS LIKE '%Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 2 +Handler_read_next 4097 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 0 +drop function tdn; +drop table t1; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index dcfbe127f8a..0fce174ecb7 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -8478,3 +8478,22 @@ CALL p1(1); DROP PROCEDURE p1; +--echo +--echo MDEV-3900 Optimizer difference between MySQL and MariaDB with stored functions in WHERE clause of UPDATE or DELETE statements +--echo + +CREATE FUNCTION tdn() RETURNS int(7) DETERMINISTIC RETURN to_days(now()); + +CREATE TABLE t1 (pk INT NOT NULL AUTO_INCREMENT PRIMARY KEY, daynum INT, a CHAR(1), INDEX(daynum), INDEX(a)) ENGINE=MyISAM; +INSERT INTO t1 (daynum) VALUES (1),(2),(3),(4),(5),(TO_DAYS(NOW())),(7),(8); +INSERT INTO t1 (daynum) SELECT a1.daynum FROM t1 a1, t1 a2, t1 a3, t1 a4, t1 a5; + +FLUSH TABLES; +FLUSH STATUS; + +SHOW STATUS LIKE '%Handler_read%'; +UPDATE t1 SET a = '+' WHERE daynum=tdn(); +SHOW STATUS LIKE '%Handler_read%'; + +drop function tdn; +drop table t1; diff --git a/sql/item_func.cc b/sql/item_func.cc index 18d0ce3a822..c1767fb71ca 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -6056,6 +6056,19 @@ Item_func_sp::init_result_field(THD *thd) } +/** + @note + Deterministic stored procedures are considered inexpensive. + Consequently such procedures may be evaluated during optimization, + if they are constant (checked by the optimizer). +*/ + +bool Item_func_sp::is_expensive() +{ + return !(m_sp->m_chistics->detistic); +} + + /** @brief Initialize local members with values from the Field interface. diff --git a/sql/item_func.h b/sql/item_func.h index b144b393225..2db8ab76ffe 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1807,7 +1807,8 @@ private: bool init_result_field(THD *thd); protected: - bool is_expensive_processor(uchar *arg) { return TRUE; } + bool is_expensive_processor(uchar *arg) + { return is_expensive(); } public: @@ -1881,7 +1882,7 @@ public: bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(void); - bool is_expensive() { return 1; } + bool is_expensive(); inline Field *get_sp_result_field() { From 2255132f200940186c6e9dfcedae6edb85e7cee7 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 16 Jan 2013 21:07:26 +0200 Subject: [PATCH 07/11] MDEV-4056 fix. The problem was that maybe_null of Item_row and its componetes was unsynced after update_used_tables() (and so pushed_cond_guards was not initialized but then requested). Fix updates Item_row::maybe_null on update_used_tables(). --- mysql-test/r/subselect4.result | 21 +++++++++++++++++++++ mysql-test/t/subselect4.test | 26 ++++++++++++++++++++++++++ sql/item_row.cc | 2 ++ 3 files changed, 49 insertions(+) diff --git a/mysql-test/r/subselect4.result b/mysql-test/r/subselect4.result index bd64aca7d95..83716429efe 100644 --- a/mysql-test/r/subselect4.result +++ b/mysql-test/r/subselect4.result @@ -2305,5 +2305,26 @@ SELECT a3 FROM t3 WHERE b2 = b1 AND b2 <= b1 ORDER BY b3 ); a1 b1 drop table t1, t2, t3; +# +# MDEV-4056:Server crashes in Item_func_trig_cond::val_int +# with FROM and NOT IN subqueries, LEFT JOIN, derived_merge+in_to_exists +# +set @optimizer_switch_MDEV4056 = @@optimizer_switch; +SET optimizer_switch = 'derived_merge=on,in_to_exists=on'; +CREATE TABLE t1 (a VARCHAR(1)) ENGINE=MyISAM; +INSERT INTO t1 VALUES ('x'),('d'); +CREATE TABLE t2 (pk INT PRIMARY KEY, b INT, c VARCHAR(1)) ENGINE=MyISAM; +INSERT INTO t2 VALUES (1,2,'v'),(2,150,'v'); +SELECT * FROM t1 LEFT JOIN ( +SELECT * FROM t2 WHERE ( pk, pk ) NOT IN ( +SELECT MIN(b), SUM(pk) FROM t1 +) +) AS alias1 ON (a = c) +WHERE b IS NULL OR a < 'u'; +a pk b c +x NULL NULL NULL +d NULL NULL NULL +drop table t1,t2; +set @@optimizer_switch = @optimizer_switch_MDEV4056; SET optimizer_switch= @@global.optimizer_switch; set @@tmp_table_size= @@global.tmp_table_size; diff --git a/mysql-test/t/subselect4.test b/mysql-test/t/subselect4.test index 5e1f3db2f4a..51247e2c3ea 100644 --- a/mysql-test/t/subselect4.test +++ b/mysql-test/t/subselect4.test @@ -1886,5 +1886,31 @@ SELECT * FROM t1 WHERE a1 IN ( drop table t1, t2, t3; +--echo # +--echo # MDEV-4056:Server crashes in Item_func_trig_cond::val_int +--echo # with FROM and NOT IN subqueries, LEFT JOIN, derived_merge+in_to_exists +--echo # + +set @optimizer_switch_MDEV4056 = @@optimizer_switch; +SET optimizer_switch = 'derived_merge=on,in_to_exists=on'; + +CREATE TABLE t1 (a VARCHAR(1)) ENGINE=MyISAM; +INSERT INTO t1 VALUES ('x'),('d'); + +CREATE TABLE t2 (pk INT PRIMARY KEY, b INT, c VARCHAR(1)) ENGINE=MyISAM; +INSERT INTO t2 VALUES (1,2,'v'),(2,150,'v'); + +SELECT * FROM t1 LEFT JOIN ( + SELECT * FROM t2 WHERE ( pk, pk ) NOT IN ( + SELECT MIN(b), SUM(pk) FROM t1 + ) +) AS alias1 ON (a = c) +WHERE b IS NULL OR a < 'u'; + +drop table t1,t2; +set @@optimizer_switch = @optimizer_switch_MDEV4056; + + + SET optimizer_switch= @@global.optimizer_switch; set @@tmp_table_size= @@global.tmp_table_size; diff --git a/sql/item_row.cc b/sql/item_row.cc index 5136c0100d6..72bae6f0900 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -139,11 +139,13 @@ void Item_row::update_used_tables() { used_tables_cache= 0; const_item_cache= 1; + maybe_null= 0; for (uint i= 0; i < arg_count; i++) { items[i]->update_used_tables(); used_tables_cache|= items[i]->used_tables(); const_item_cache&= items[i]->const_item(); + maybe_null|= items[i]->maybe_null; } } From 63afbba419fcf9fe1fd2acb3355c112453c15a66 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Wed, 16 Jan 2013 11:17:58 -0800 Subject: [PATCH 08/11] Corrected the fix for bug mdev-3938. --- sql/sql_insert.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index f76c2252eb9..f9179843810 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2141,7 +2141,6 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) /* We don't need to change the file handler here */ /* Assign the pointers for the field pointers array and the record. */ field= copy->field= (Field**) (copy + 1); - copy->vfield= vfield; bitmap= (uchar*) (field + share->fields + 1); copy->record[0]= (bitmap + share->column_bitmap_size*3); memcpy((char*) copy->record[0], (char*) table->record[0], share->reclength); @@ -2165,8 +2164,9 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) } *field=0; - if (table->vfield) + if (share->vfields) { + copy->vfield= vfield; for (field= copy->field; *field; field++) { if ((*field)->vcol_info) From 02d368ff9d2e5121ed27c221d9bfd2b3792177a3 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sat, 19 Jan 2013 23:40:53 -0800 Subject: [PATCH 09/11] Corrected the test case for bug mdev-3938. --- mysql-test/suite/vcol/r/vcol_misc.result | 1 + mysql-test/suite/vcol/t/vcol_misc.test | 2 ++ 2 files changed, 3 insertions(+) diff --git a/mysql-test/suite/vcol/r/vcol_misc.result b/mysql-test/suite/vcol/r/vcol_misc.result index f679d5eb671..4c301795f5c 100644 --- a/mysql-test/suite/vcol/r/vcol_misc.result +++ b/mysql-test/suite/vcol/r/vcol_misc.result @@ -188,6 +188,7 @@ tsv TIMESTAMP AS (ADDDATE(ts, INTERVAL 1 DAY)) VIRTUAL ) ENGINE=MyISAM; INSERT INTO t1 (tsv) VALUES (DEFAULT); INSERT DELAYED INTO t1 (tsv) VALUES (DEFAULT); +FLUSH TABLES; SELECT COUNT(*) FROM t1; COUNT(*) 2 diff --git a/mysql-test/suite/vcol/t/vcol_misc.test b/mysql-test/suite/vcol/t/vcol_misc.test index 53c04898648..0a689795b4c 100644 --- a/mysql-test/suite/vcol/t/vcol_misc.test +++ b/mysql-test/suite/vcol/t/vcol_misc.test @@ -192,6 +192,8 @@ INSERT INTO t1 (tsv) VALUES (DEFAULT); INSERT DELAYED INTO t1 (tsv) VALUES (DEFAULT); +FLUSH TABLES; + SELECT COUNT(*) FROM t1; DROP TABLE t1; From 43c6953fa1ba3aad4f065bfbd63cca6b5d0c5ce7 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 21 Jan 2013 10:52:39 +0100 Subject: [PATCH 10/11] MDEV-4029 SELECT on information_schema using a subquery locks up the information_schema table due to incorrect mutexes handling Early evaluation of subqueries in the WHERE conditions on I_S.*_STATUS tables, otherwise the subquery on this same table will try to acquire LOCK_status twice. --- mysql-test/r/information_schema2.result | 8 ++++++++ mysql-test/t/information_schema2.test | 9 +++++++++ sql/sql_show.cc | 18 ++++++++++++------ 3 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 mysql-test/r/information_schema2.result create mode 100644 mysql-test/t/information_schema2.test diff --git a/mysql-test/r/information_schema2.result b/mysql-test/r/information_schema2.result new file mode 100644 index 00000000000..60a20944839 --- /dev/null +++ b/mysql-test/r/information_schema2.result @@ -0,0 +1,8 @@ +select variable_name from information_schema.session_status where variable_name = +(select variable_name from information_schema.session_status where variable_name = 'uptime'); +variable_name +UPTIME +select variable_name from information_schema.session_variables where variable_name = +(select variable_name from information_schema.session_variables where variable_name = 'basedir'); +variable_name +BASEDIR diff --git a/mysql-test/t/information_schema2.test b/mysql-test/t/information_schema2.test new file mode 100644 index 00000000000..c2479087f47 --- /dev/null +++ b/mysql-test/t/information_schema2.test @@ -0,0 +1,9 @@ + +# +# MDEV-4029 SELECT on information_schema using a subquery locks up the information_schema table due to incorrect mutexes handling +# +select variable_name from information_schema.session_status where variable_name = +(select variable_name from information_schema.session_status where variable_name = 'uptime'); +select variable_name from information_schema.session_variables where variable_name = +(select variable_name from information_schema.session_variables where variable_name = 'basedir'); + diff --git a/sql/sql_show.cc b/sql/sql_show.cc index d544ff4c52c..f9c2d114596 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2260,7 +2260,6 @@ static bool show_status_array(THD *thd, const char *wild, int len; LEX_STRING null_lex_str; SHOW_VAR tmp, *var; - COND *partial_cond= 0; enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; bool res= FALSE; CHARSET_INFO *charset= system_charset_info; @@ -2274,7 +2273,6 @@ static bool show_status_array(THD *thd, const char *wild, if (*prefix) *prefix_end++= '_'; len=name_buffer + sizeof(name_buffer) - prefix_end; - partial_cond= make_cond_for_info_schema(cond, table->pos_in_table_list); for (; variables->name; variables++) { @@ -2297,13 +2295,13 @@ static bool show_status_array(THD *thd, const char *wild, if (show_type == SHOW_ARRAY) { show_status_array(thd, wild, (SHOW_VAR *) var->value, value_type, - status_var, name_buffer, table, ucase_names, partial_cond); + status_var, name_buffer, table, ucase_names, cond); } else { if (!(wild && wild[0] && wild_case_compare(system_charset_info, name_buffer, wild)) && - (!partial_cond || partial_cond->val_int())) + (!cond || cond->val_int())) { char *value=var->value; const char *pos, *end; // We assign a lot of const's @@ -5562,9 +5560,12 @@ int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond) schema_table_idx == SCH_GLOBAL_VARIABLES) option_type= OPT_GLOBAL; + COND *partial_cond= make_cond_for_info_schema(cond, tables); + rw_rdlock(&LOCK_system_variables_hash); res= show_status_array(thd, wild, enumerate_sys_vars(thd, sorted_vars), - option_type, NULL, "", tables->table, upper_case_names, cond); + option_type, NULL, "", tables->table, upper_case_names, + partial_cond); rw_unlock(&LOCK_system_variables_hash); DBUG_RETURN(res); } @@ -5601,13 +5602,18 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond) tmp1= &thd->status_var; } + COND *partial_cond= make_cond_for_info_schema(cond, tables); + // Evaluate and cache const subqueries now, before the mutex. + if (partial_cond) + partial_cond->val_int(); + pthread_mutex_lock(&LOCK_status); if (option_type == OPT_GLOBAL) calc_sum_of_all_status(&tmp); res= show_status_array(thd, wild, (SHOW_VAR *)all_status_vars.buffer, option_type, tmp1, "", tables->table, - upper_case_names, cond); + upper_case_names, partial_cond); pthread_mutex_unlock(&LOCK_status); DBUG_RETURN(res); } From fade3647ecb18a90d9c89a924a076d714ec45888 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 21 Jan 2013 11:47:45 -0800 Subject: [PATCH 11/11] Fixed bug mdev-4063 (bug #56927). This bug could result in returning 0 for the expressions of the form (distinct field) when the system variable max_heap_table_size was set to a small enough number. It happened because the method Unique::walk() did not support the case when more than one pass was needed to merge the trees of distinct values saved in an external file. Backported a fix in grant_lowercase.test from mariadb 5.5. --- mysql-test/r/sum_distinct-big.result | 15 +++ mysql-test/t/grant_lowercase.test | 1 + mysql-test/t/sum_distinct-big.test | 17 +++ sql/item_sum.cc | 4 +- sql/sql_class.h | 4 +- sql/uniques.cc | 174 +++++++++++++++++---------- 6 files changed, 146 insertions(+), 69 deletions(-) diff --git a/mysql-test/r/sum_distinct-big.result b/mysql-test/r/sum_distinct-big.result index 9b55d59ab91..d4933b31f80 100644 --- a/mysql-test/r/sum_distinct-big.result +++ b/mysql-test/r/sum_distinct-big.result @@ -103,5 +103,20 @@ sm 10323810 10325070 10326330 +# +# Bug mdev-4063: SUM(DISTINCT...) with small'max_heap_table_size +# (bug #56927) +# +SET max_heap_table_size=default; +INSERT INTO t1 SELECT id+16384 FROM t1; +DELETE FROM t2; +INSERT INTO t2 SELECT id FROM t1 ORDER BY id*rand(); +SELECT SUM(DISTINCT id) sm FROM t2; +sm +536887296 +SET max_heap_table_size=16384; +SELECT SUM(DISTINCT id) sm FROM t2; +sm +536887296 DROP TABLE t1; DROP TABLE t2; diff --git a/mysql-test/t/grant_lowercase.test b/mysql-test/t/grant_lowercase.test index 157e13449c2..b07cb88afd6 100644 --- a/mysql-test/t/grant_lowercase.test +++ b/mysql-test/t/grant_lowercase.test @@ -1,4 +1,5 @@ # test cases for strmov(tmp_db, db) -> strnmov replacement in sql_acl.cc +--source include/not_embedded.inc # # http://seclists.org/fulldisclosure/2012/Dec/4 diff --git a/mysql-test/t/sum_distinct-big.test b/mysql-test/t/sum_distinct-big.test index 0859f4b3d89..d3710056c9a 100644 --- a/mysql-test/t/sum_distinct-big.test +++ b/mysql-test/t/sum_distinct-big.test @@ -63,5 +63,22 @@ SELECT SUM(DISTINCT id) sm FROM t1; SELECT SUM(DISTINCT id) sm FROM t2; SELECT SUM(DISTINCT id) sm FROM t1 GROUP BY id % 13; +--echo # +--echo # Bug mdev-4063: SUM(DISTINCT...) with small'max_heap_table_size +--echo # (bug #56927) +--echo # + +SET max_heap_table_size=default; + +INSERT INTO t1 SELECT id+16384 FROM t1; +DELETE FROM t2; +INSERT INTO t2 SELECT id FROM t1 ORDER BY id*rand(); + +SELECT SUM(DISTINCT id) sm FROM t2; + +SET max_heap_table_size=16384; + +SELECT SUM(DISTINCT id) sm FROM t2; + DROP TABLE t1; DROP TABLE t2; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 92c2ba83f23..debba23438d 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1084,7 +1084,7 @@ void Item_sum_distinct::calculate_val_and_count() if (tree) { table->field[0]->set_notnull(); - tree->walk(item_sum_distinct_walk, (void*) this); + tree->walk(table, item_sum_distinct_walk, (void*) this); } is_evaluated= TRUE; } @@ -2583,7 +2583,7 @@ longlong Item_sum_count_distinct::val_int() if (tree->elements == 0) return (longlong) tree->elements_in_tree(); // everything fits in memory count= 0; - tree->walk(count_distinct_walk, (void*) &count); + tree->walk(table, count_distinct_walk, (void*) &count); is_evaluated= TRUE; return (longlong) count; } diff --git a/sql/sql_class.h b/sql/sql_class.h index fb4e13ad9c6..2fd8e8cd04b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3008,6 +3008,8 @@ class Unique :public Sql_alloc bool flush(); uint size; + bool merge(TABLE *table, uchar *buff, bool without_last_merge); + public: ulong elements; Unique(qsort_cmp2 comp_func, void *comp_func_fixed_arg, @@ -3035,7 +3037,7 @@ public: } void reset(); - bool walk(tree_walk_action action, void *walk_action_arg); + bool walk(TABLE *table, tree_walk_action action, void *walk_action_arg); uint get_size() const { return size; } ulonglong get_max_in_memory_size() const { return max_in_memory_size; } diff --git a/sql/uniques.cc b/sql/uniques.cc index fd6056ae8a6..5788edc673e 100644 --- a/sql/uniques.cc +++ b/sql/uniques.cc @@ -538,6 +538,7 @@ end: SYNOPSIS Unique:walk() All params are 'IN': + table parameter for the call of the merge method action function-visitor, typed in include/my_tree.h function is called for each unique element arg argument for visitor, which is passed to it on each call @@ -546,30 +547,117 @@ end: <> 0 error */ -bool Unique::walk(tree_walk_action action, void *walk_action_arg) +bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg) { - int res; + int res= 0; uchar *merge_buffer; if (elements == 0) /* the whole tree is in memory */ return tree_walk(&tree, action, walk_action_arg, left_root_right); + table->sort.found_records=elements+tree.elements_in_tree; /* flush current tree to the file to have some memory for merge buffer */ if (flush()) return 1; if (flush_io_cache(&file) || reinit_io_cache(&file, READ_CACHE, 0L, 0, 0)) return 1; - if (!(merge_buffer= (uchar *) my_malloc((ulong) max_in_memory_size, MYF(0)))) + ulong buff_sz= (max_in_memory_size / size + 1) * size; + if (!(merge_buffer= (uchar *) my_malloc((ulong) buff_sz, MYF(0)))) return 1; - res= merge_walk(merge_buffer, (ulong) max_in_memory_size, size, - (BUFFPEK *) file_ptrs.buffer, - (BUFFPEK *) file_ptrs.buffer + file_ptrs.elements, - action, walk_action_arg, - tree.compare, tree.custom_arg, &file); + if (buff_sz < (ulong) (size * (file_ptrs.elements + 1))) + res= merge(table, merge_buffer, buff_sz >= size * MERGEBUFF2) ; + + if (!res) + { + res= merge_walk(merge_buffer, (ulong) max_in_memory_size, size, + (BUFFPEK *) file_ptrs.buffer, + (BUFFPEK *) file_ptrs.buffer + file_ptrs.elements, + action, walk_action_arg, + tree.compare, tree.custom_arg, &file); + } my_free((char*) merge_buffer, MYF(0)); return res; } + +/* + DESCRIPTION + Perform multi-pass sort merge of the elements accessed through table->sort, + using the buffer buff as the merge buffer. The last pass is not performed + if without_last_merge is TRUE. + SYNOPSIS + Unique:merge() + All params are 'IN': + table the parameter to access sort context + buff merge buffer + without_last_merge TRUE <=> do not perform the last merge + RETURN VALUE + 0 OK + <> 0 error + */ + +bool Unique::merge(TABLE *table, uchar *buff, bool without_last_merge) +{ + SORTPARAM sort_param; + IO_CACHE *outfile= table->sort.io_cache; + BUFFPEK *file_ptr= (BUFFPEK*) file_ptrs.buffer; + uint maxbuffer= file_ptrs.elements - 1; + my_off_t save_pos; + bool error= 1; + + /* Open cached file if it isn't open */ + if (!outfile) + outfile= table->sort.io_cache= (IO_CACHE*) my_malloc(sizeof(IO_CACHE), + MYF(MY_ZEROFILL)); + if (!outfile || + (! my_b_inited(outfile) && + open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER, + MYF(MY_WME)))) + return 1; + reinit_io_cache(outfile,WRITE_CACHE,0L,0,0); + + bzero((char*) &sort_param,sizeof(sort_param)); + sort_param.max_rows= elements; + sort_param.sort_form= table; + sort_param.rec_length= sort_param.sort_length= sort_param.ref_length= + size; + sort_param.keys= (uint) (max_in_memory_size / sort_param.sort_length); + sort_param.not_killable= 1; + + sort_param.unique_buff= buff + (sort_param.keys * sort_param.sort_length); + + sort_param.compare= (qsort2_cmp) buffpek_compare; + sort_param.cmp_context.key_compare= tree.compare; + sort_param.cmp_context.key_compare_arg= tree.custom_arg; + + /* Merge the buffers to one file, removing duplicates */ + if (merge_many_buff(&sort_param,buff,file_ptr,&maxbuffer,&file)) + goto err; + if (flush_io_cache(&file) || + reinit_io_cache(&file,READ_CACHE,0L,0,0)) + goto err; + if (without_last_merge) + { + file_ptrs.elements= maxbuffer+1; + return 0; + } + if (merge_buffers(&sort_param, &file, outfile, buff, file_ptr, + file_ptr, file_ptr+maxbuffer,0)) + goto err; + error= 0; +err: + if (flush_io_cache(outfile)) + error= 1; + + /* Setup io_cache for reading */ + save_pos= outfile->pos_in_file; + if (reinit_io_cache(outfile,READ_CACHE,0L,0,0)) + error= 1; + outfile->end_of_file=save_pos; + return error; +} + + /* Modify the TABLE element so that when one calls init_records() the rows will be read in priority order. @@ -577,8 +665,9 @@ bool Unique::walk(tree_walk_action action, void *walk_action_arg) bool Unique::get(TABLE *table) { - SORTPARAM sort_param; - table->sort.found_records=elements+tree.elements_in_tree; + bool rc= 1; + uchar *sort_buffer= NULL; + table->sort.found_records= elements+tree.elements_in_tree; if (my_b_tell(&file) == 0) { @@ -594,63 +683,16 @@ bool Unique::get(TABLE *table) /* Not enough memory; Save the result to file && free memory used by tree */ if (flush()) return 1; - - IO_CACHE *outfile=table->sort.io_cache; - BUFFPEK *file_ptr= (BUFFPEK*) file_ptrs.buffer; - uint maxbuffer= file_ptrs.elements - 1; - uchar *sort_buffer; - my_off_t save_pos; - bool error=1; - - /* Open cached file if it isn't open */ - outfile=table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), - MYF(MY_ZEROFILL)); - - if (!outfile || - (! my_b_inited(outfile) && - open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER, - MYF(MY_WME)))) + + ulong buff_sz= (max_in_memory_size / size + 1) * size; + if (!(sort_buffer= (uchar*) my_malloc(buff_sz, MYF(0)))) return 1; - reinit_io_cache(outfile,WRITE_CACHE,0L,0,0); - bzero((char*) &sort_param,sizeof(sort_param)); - sort_param.max_rows= elements; - sort_param.sort_form=table; - sort_param.rec_length= sort_param.sort_length= sort_param.ref_length= - size; - sort_param.keys= (uint) (max_in_memory_size / sort_param.sort_length); - sort_param.not_killable=1; + if (merge(table, sort_buffer, FALSE)) + goto err; + rc= 0; - if (!(sort_buffer=(uchar*) my_malloc((sort_param.keys+1) * - sort_param.sort_length, - MYF(0)))) - return 1; - sort_param.unique_buff= sort_buffer+(sort_param.keys* - sort_param.sort_length); - - sort_param.compare= (qsort2_cmp) buffpek_compare; - sort_param.cmp_context.key_compare= tree.compare; - sort_param.cmp_context.key_compare_arg= tree.custom_arg; - - /* Merge the buffers to one file, removing duplicates */ - if (merge_many_buff(&sort_param,sort_buffer,file_ptr,&maxbuffer,&file)) - goto err; - if (flush_io_cache(&file) || - reinit_io_cache(&file,READ_CACHE,0L,0,0)) - goto err; - if (merge_buffers(&sort_param, &file, outfile, sort_buffer, file_ptr, - file_ptr, file_ptr+maxbuffer,0)) - goto err; - error=0; -err: - x_free(sort_buffer); - if (flush_io_cache(outfile)) - error=1; - - /* Setup io_cache for reading */ - save_pos=outfile->pos_in_file; - if (reinit_io_cache(outfile,READ_CACHE,0L,0,0)) - error=1; - outfile->end_of_file=save_pos; - return error; +err: + x_free(sort_buffer); + return rc; }