From 55b27888005083d30339d6f3a2aee034121d8693 Mon Sep 17 00:00:00 2001 From: Monty Date: Mon, 9 Nov 2020 20:57:27 +0200 Subject: [PATCH 1/7] Fixed length estimate for REPLACE() --- mysql-test/main/func_str.result | 5 +++++ mysql-test/main/func_str.test | 3 +++ mysql-test/main/null.result | 2 +- sql/item_strfunc.cc | 6 +++--- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/mysql-test/main/func_str.result b/mysql-test/main/func_str.result index 0e9cebd0cb4..d354bdf1220 100644 --- a/mysql-test/main/func_str.result +++ b/mysql-test/main/func_str.result @@ -211,6 +211,11 @@ this is test select replace('aaaa','a','b'),replace('aaaa','aa','b'),replace('aaaa','a','bb'),replace('aaaa','','b'),replace('bbbb','a','c'); replace('aaaa','a','b') replace('aaaa','aa','b') replace('aaaa','a','bb') replace('aaaa','','b') replace('bbbb','a','c') bbbb bb bbbbbbbb aaaa bbbb +select replace('aaaa','a','bbbb'); +Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr +def replace('aaaa','a','bbbb') 253 16 16 Y 0 39 8 +replace('aaaa','a','bbbb') +bbbbbbbbbbbbbbbb select replace(concat(lcase(concat('THIS',' ','IS',' ','A',' ')),ucase('false'),' ','test'),'FALSE','REAL') ; replace(concat(lcase(concat('THIS',' ','IS',' ','A',' ')),ucase('false'),' ','test'),'FALSE','REAL') this is a REAL test diff --git a/mysql-test/main/func_str.test b/mysql-test/main/func_str.test index ba5b671ca4f..59ef8b0a805 100644 --- a/mysql-test/main/func_str.test +++ b/mysql-test/main/func_str.test @@ -91,6 +91,9 @@ SELECT CONCAT('"',CONCAT_WS('";"',repeat('a',60),repeat('b',60),repeat('c',60),r select insert('txs',2,1,'hi'),insert('is ',4,0,'a'),insert('txxxxt',2,4,'es'); select replace('aaaa','a','b'),replace('aaaa','aa','b'),replace('aaaa','a','bb'),replace('aaaa','','b'),replace('bbbb','a','c'); +--enable_metadata +select replace('aaaa','a','bbbb'); +--disable_metadata select replace(concat(lcase(concat('THIS',' ','IS',' ','A',' ')),ucase('false'),' ','test'),'FALSE','REAL') ; select soundex(''),soundex('he'),soundex('hello all folks'),soundex('#3556 in bugdb'); select 'mood' sounds like 'mud'; diff --git a/mysql-test/main/null.result b/mysql-test/main/null.result index fc29f68baf0..73ac958cacd 100644 --- a/mysql-test/main/null.result +++ b/mysql-test/main/null.result @@ -267,7 +267,7 @@ t1 CREATE TABLE `t1` ( `c30` varchar(317) CHARACTER SET latin2 DEFAULT NULL, `c31` varchar(192) CHARACTER SET latin2 DEFAULT NULL, `c32` char(0) CHARACTER SET latin2 DEFAULT NULL, - `c33` varchar(3) CHARACTER SET latin2 DEFAULT NULL, + `c33` varchar(6) CHARACTER SET latin2 DEFAULT NULL, `c34` varchar(3) CHARACTER SET latin2 DEFAULT NULL, `c35` varchar(3) CHARACTER SET latin2 DEFAULT NULL, `c36` varchar(3) CHARACTER SET latin2 DEFAULT NULL, diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index c2de296a109..b4f706984a5 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1286,10 +1286,10 @@ null: bool Item_func_replace::fix_length_and_dec() { ulonglong char_length= (ulonglong) args[0]->max_char_length(); - int diff=(int) (args[2]->max_char_length() - args[1]->max_char_length()); - if (diff > 0 && args[1]->max_char_length()) + int diff=(int) (args[2]->max_char_length() - 1); + if (diff > 0) { // Calculate of maxreplaces - ulonglong max_substrs= char_length / args[1]->max_char_length(); + ulonglong max_substrs= char_length; char_length+= max_substrs * (uint) diff; } From 3d56bea3ef5b6e9dcde8d9a32fa6e2bef268e9e0 Mon Sep 17 00:00:00 2001 From: Monty Date: Tue, 17 Nov 2020 13:44:43 +0200 Subject: [PATCH 2/7] Allow field_name NOT NULL ENABLED This is for Oracle compatiblity. ENABLED is in Oracle the default case and just ensures that the NOT NULL constraints will be tested, which is also default in MariaDB --- sql/sql_yacc.yy | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c5864887677..ebea70065e8 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6555,6 +6555,11 @@ opt_compressed: | compressed { } ; +opt_enable: + /* empty */ {} + | ENABLE_SYM { } + ; + compressed: COMPRESSED_SYM opt_compression_method { @@ -6581,7 +6586,7 @@ compressed_deprecated_column_attribute: ; asrow_attribute: - not NULL_SYM + not NULL_SYM opt_enable { Lex->last_field->flags|= NOT_NULL_FLAG; } From c8992fc35be7db8dde588232827da5fdc0f9372f Mon Sep 17 00:00:00 2001 From: Monty Date: Tue, 17 Nov 2020 14:28:31 +0200 Subject: [PATCH 3/7] Trivial cleanups, no logic changes - Fold long comment rows and updated comments - Moved one private function in class Item_func_rand among other private functions --- sql/item_func.h | 6 ++--- sql/sql_lex.cc | 2 +- sql/sql_lex.h | 6 ++--- sql/sql_select.cc | 65 ++++++++++++++++++++++++++--------------------- 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/sql/item_func.h b/sql/item_func.h index 6a4a9fa5dae..e774d9c53bd 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1962,6 +1962,7 @@ class Item_func_rand :public Item_real_func bool first_eval; // TRUE if val_real() is called 1st time bool check_arguments() const { return check_argument_types_can_return_int(0, arg_count); } + void seed_random (Item * val); public: Item_func_rand(THD *thd, Item *a): Item_real_func(thd, a), rand(0), first_eval(TRUE) {} @@ -1974,12 +1975,11 @@ public: void cleanup() { first_eval= TRUE; Item_real_func::cleanup(); } bool check_vcol_func_processor(void *arg) { - return mark_unsupported_function(func_name(), "()", arg, VCOL_NON_DETERMINISTIC); + return mark_unsupported_function(func_name(), "()", arg, + VCOL_NON_DETERMINISTIC); } Item *get_copy(THD *thd) { return get_item_copy(thd, this); } -private: - void seed_random (Item * val); }; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 9620b0d0961..ce3bf902931 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -10279,7 +10279,7 @@ bool LEX::sp_proc_stmt_statement_finalize(THD *thd, bool no_lookahead) It is done by transformer. The extracted condition is saved in cond_pushed_into_where of this select. - cond can remain un empty after the extraction of the condition that can be + COND can remain not empty after the extraction of the conditions that can be pushed into WHERE. It is saved in remaining_cond. @note diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 861eb614592..10b71781ce4 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1113,7 +1113,7 @@ public: */ LEX *parent_lex; enum olap_type olap; - /* FROM clause - points to the beginning of the TABLE_LIST::next_local list. */ + /* FROM clause - points to the beginning of the TABLE_LIST::next_local list */ SQL_I_List table_list; /* @@ -1129,8 +1129,8 @@ public: List pre_fix; /* above list before fix_fields */ bool is_item_list_lookup; /* - Usualy it is pointer to ftfunc_list_alloc, but in union used to create fake - select_lex for calling mysql_select under results of union + Usually it is pointer to ftfunc_list_alloc, but in union used to create + fake select_lex for calling mysql_select under results of union */ List *ftfunc_list; List ftfunc_list_alloc; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a18dc0d3d37..a2cc265ec5b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3747,14 +3747,16 @@ JOIN::create_postjoin_aggr_table(JOIN_TAB *tab, List *table_fields, tab->tmp_table_param->skip_create_table= true; TABLE* table= create_tmp_table(thd, tab->tmp_table_param, *table_fields, table_group, distinct, - save_sum_fields, select_options, table_rows_limit, + save_sum_fields, select_options, + table_rows_limit, &empty_clex_str, true, keep_row_order); if (!table) DBUG_RETURN(true); tmp_table_param.using_outer_summary_function= tab->tmp_table_param->using_outer_summary_function; tab->join= this; - DBUG_ASSERT(tab > tab->join->join_tab || !top_join_tab_count || !tables_list); + DBUG_ASSERT(tab > tab->join->join_tab || !top_join_tab_count || + !tables_list); if (tab > join_tab) (tab - 1)->next_select= sub_select_postjoin_aggr; if (!(tab->aggr= new (thd->mem_root) AGGR_OP(tab))) @@ -4260,8 +4262,9 @@ void JOIN::exec_inner() /* Enable LIMIT ROWS EXAMINED during query execution if: (1) This JOIN is the outermost query (not a subquery or derived table) - This ensures that the limit is enabled when actual execution begins, and - not if a subquery is evaluated during optimization of the outer query. + This ensures that the limit is enabled when actual execution begins, + and not if a subquery is evaluated during optimization of the outer + query. (2) This JOIN is not the result of a UNION. In this case do not apply the limit in order to produce the partial query result stored in the UNION temp table. @@ -14091,7 +14094,7 @@ static void update_depend_map_for_order(JOIN *join, ORDER *order) /** - Remove all constants and check if ORDER only contains simple + Remove all constants from ORDER and check if ORDER only contains simple expressions. We also remove all duplicate expressions, keeping only the first one. @@ -16665,13 +16668,13 @@ static uint reset_nj_counters(JOIN *join, List *join_list) @verbatim IMPLEMENTATION LIMITATIONS ON JOIN ORDER - The nested [outer] joins executioner algorithm imposes these limitations - on join order: + The nested [outer] joins executioner algorithm imposes these + limitations on join order: 1. "Outer tables first" - any "outer" table must be before any corresponding "inner" table. - 2. "No interleaving" - tables inside a nested join must form a continuous - sequence in join order (i.e. the sequence must not be interrupted by - tables that are outside of this nested join). + 2. "No interleaving" - tables inside a nested join must form a + continuous sequence in join order (i.e. the sequence must not be + interrupted by tables that are outside of this nested join). #1 is checked elsewhere, this function checks #2 provided that #1 has been already checked. @@ -16683,34 +16686,36 @@ static uint reset_nj_counters(JOIN *join, List *join_list) The join order "t1 t2 t0 t3" is invalid: - table t0 is outside of the nested join, so WHERE condition for t0 is - attached directly to t0 (without triggers, and it may be used to access - t0). Applying WHERE(t0) to (t2,t0,t3) record is invalid as we may miss - combinations of (t1, t2, t3) that satisfy condition cond1, and produce a - null-complemented (t1, t2.NULLs, t3.NULLs) row, which should not have - been produced. + table t0 is outside of the nested join, so WHERE condition + for t0 is attached directly to t0 (without triggers, and it + may be used to access t0). Applying WHERE(t0) to (t2,t0,t3) + record is invalid as we may miss combinations of (t1, t2, t3) + that satisfy condition cond1, and produce a null-complemented + (t1, t2.NULLs, t3.NULLs) row, which should not have been + produced. If table t0 is not between t2 and t3, the problem doesn't exist: - If t0 is located after (t2,t3), WHERE(t0) is applied after nested join - processing has finished. - If t0 is located before (t2,t3), predicates like WHERE_cond(t0, t2) are - wrapped into condition triggers, which takes care of correct nested - join processing. + If t0 is located after (t2,t3), WHERE(t0) is applied after nested + join processing has finished. + If t0 is located before (t2,t3), predicates like WHERE_cond(t0, t2) + are wrapped into condition triggers, which takes care of correct + nested join processing. HOW IT IS IMPLEMENTED The limitations on join order can be rephrased as follows: for valid join order one must be able to: 1. write down the used tables in the join order on one line. - 2. for each nested join, put one '(' and one ')' on the said line + 2. for each nested join, put one '(' and one ')' on the said line 3. write "LEFT JOIN" and "ON (...)" where appropriate 4. get a query equivalent to the query we're trying to execute. Calls to check_interleaving_with_nj() are equivalent to writing the above described line from left to right. - A single check_interleaving_with_nj(A,B) call is equivalent to writing - table B and appropriate brackets on condition that table A and - appropriate brackets is the last what was written. Graphically the - transition is as follows: + + A single check_interleaving_with_nj(A,B) call is equivalent + to writing table B and appropriate brackets on condition that + table A and appropriate brackets is the last what was + written. Graphically the transition is as follows: +---- current position | @@ -21896,8 +21901,8 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), /* @brief - Perform a GROUP BY operation over a stream of rows ordered by their group. The - result is sent into join->result. + Perform a GROUP BY operation over a stream of rows ordered by their group. + The result is sent into join->result. @detail Also applies HAVING, etc. @@ -22177,7 +22182,9 @@ end: } -/** Like end_update, but this is done with unique constraints instead of keys. */ +/** + Like end_update, but this is done with unique constraints instead of keys. +*/ static enum_nested_loop_state end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), From 55f734ed878f2e2e3ad1ca31838b85039e6d67c4 Mon Sep 17 00:00:00 2001 From: Monty Date: Tue, 24 Nov 2020 16:44:51 +0200 Subject: [PATCH 4/7] Change to LONGLONG_BUFFER_SIZE usage to avoid extra mallocs This change is needed in 10.5 to avoid extra malloc calls in val_str(). In 10.6 it's not needed anymore but the extra +1 byte doesn't harm that much. --- sql/field.cc | 2 +- sql/item.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/field.cc b/sql/field.cc index fe3aebce05d..2173670572d 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1085,7 +1085,7 @@ Field_longstr::make_packed_sort_key_part(uchar *buff, uchar* Field_longstr::pack_sort_string(uchar *to, const SORT_FIELD_ATTR *sort_field) { - StringBuffer buf; + StringBuffer buf; val_str(&buf, &buf); return to + sort_field->pack_sort_string(to, &buf, field_charset()); } diff --git a/sql/item.cc b/sql/item.cc index 52274380cd1..dc90b9fe1b3 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3634,7 +3634,7 @@ String *Item_int::val_str(String *str) void Item_int::print(String *str, enum_query_type query_type) { - StringBuffer buf; + StringBuffer buf; // my_charset_bin is good enough for numbers buf.set_int(value, unsigned_flag, &my_charset_bin); str->append(buf); From 279b5f87de44817fe14509b9d880a84d8730bc42 Mon Sep 17 00:00:00 2001 From: Monty Date: Tue, 24 Nov 2020 16:45:26 +0200 Subject: [PATCH 5/7] Avoid some DBUG prints from idle server in thread pool --- tpool/tpool_generic.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tpool/tpool_generic.cc b/tpool/tpool_generic.cc index 98237063ec2..e508c84f442 100644 --- a/tpool/tpool_generic.cc +++ b/tpool/tpool_generic.cc @@ -305,8 +305,10 @@ public: std::unique_lock lk(m_mtx); if (m_on) { + DBUG_PUSH_EMPTY; thr_timer_end(this); thr_timer_settime(this, 1000ULL * m_period); + DBUG_POP_EMPTY; } } } From 1555c6d1251a79ec7194803b8f2be8bc37b18ae5 Mon Sep 17 00:00:00 2001 From: Monty Date: Thu, 26 Nov 2020 12:40:41 +0200 Subject: [PATCH 6/7] Fixed compiler warnings from crc32c.cc --- mysys/crc32/crc32c.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/mysys/crc32/crc32c.cc b/mysys/crc32/crc32c.cc index 4eaceb8c438..c0f8c518691 100644 --- a/mysys/crc32/crc32c.cc +++ b/mysys/crc32/crc32c.cc @@ -26,11 +26,6 @@ static inline uint32_t DecodeFixed32(const char *ptr) return uint4korr(ptr); } -static inline uint64_t DecodeFixed64(const char *ptr) -{ - return uint8korr(ptr); -} - #include #ifdef _MSC_VER #include @@ -343,6 +338,12 @@ static inline uint32_t LE_LOAD32(const uint8_t *p) { } #if defined(HAVE_SSE42) && (SIZEOF_SIZE_T == 8) + +static inline uint64_t DecodeFixed64(const char *ptr) +{ + return uint8korr(ptr); +} + static inline uint64_t LE_LOAD64(const uint8_t *p) { return DecodeFixed64(reinterpret_cast(p)); } @@ -364,7 +365,7 @@ static inline void Slow_CRC32(uint64_t* l, uint8_t const **p) { table0_[c >> 24]; } -static inline void Fast_CRC32(uint64_t* l, uint8_t const **p) { +__attribute__((unused)) static inline void Fast_CRC32(uint64_t* l, uint8_t const **p) { #ifndef HAVE_SSE42 Slow_CRC32(l, p); #elif (SIZEOF_SIZE_T == 8) From b92391d5b19dd372422b0cfadcc4f6f0bbc7b300 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 24 Nov 2020 20:05:54 -0800 Subject: [PATCH 7/7] MDEV-24242 Query returns wrong result while using big_tables=1 When executing set operations in a pipeline using only one temporary table additional scans of intermediate results may be needed. The scans are performed with usage of the rnd_next() handler function that might leave record buffers used for the temporary table not in a state that is good for following writes into the table. For example it happens for aria engine when the last call of rnd_next() encounters only deleted records. Thus a cleanup of record buffers is needed after each such scan of the temporary table. Approved by Oleksandr Byelkin --- mysql-test/main/set_operation.result | 34 ++++++++++++++++++++++++++++ mysql-test/main/set_operation.test | 28 +++++++++++++++++++++++ sql/sql_union.cc | 4 ++++ 3 files changed, 66 insertions(+) diff --git a/mysql-test/main/set_operation.result b/mysql-test/main/set_operation.result index a0210331d93..24d2c7fbdd6 100644 --- a/mysql-test/main/set_operation.result +++ b/mysql-test/main/set_operation.result @@ -1155,3 +1155,37 @@ count(*) 319 drop table t1; drop table t2; +# +# MDEV-24242: set expression with empty intermediate result +# when tmp_memory_table_size is set to 0 +# +create table t1 (a int, b int) engine=MyISAM; +insert into t1 values (1,1), (2,2); +create table t2 (a int, b int) engine=MyISAM; +insert into t2 values (11,11), (12,12), (13,13); +select * from t1 +except all +select * from t1 +except +select * from t1 +union all +select * from t2; +a b +12 12 +11 11 +13 13 +set tmp_memory_table_size=0; +select * from t1 +except all +select * from t1 +except +select * from t1 +union all +select * from t2; +a b +12 12 +11 11 +13 13 +set tmp_memory_table_size=default; +drop table t1,t2; +# End of 10.4 tests diff --git a/mysql-test/main/set_operation.test b/mysql-test/main/set_operation.test index c43725c733e..c422042f371 100644 --- a/mysql-test/main/set_operation.test +++ b/mysql-test/main/set_operation.test @@ -524,3 +524,31 @@ select count(*) from drop table t1; drop table t2; + +--echo # +--echo # MDEV-24242: set expression with empty intermediate result +--echo # when tmp_memory_table_size is set to 0 +--echo # + +create table t1 (a int, b int) engine=MyISAM; +insert into t1 values (1,1), (2,2); +create table t2 (a int, b int) engine=MyISAM; +insert into t2 values (11,11), (12,12), (13,13); + +let $q= +select * from t1 +except all +select * from t1 +except +select * from t1 +union all +select * from t2; + +eval $q; +set tmp_memory_table_size=0; +eval $q; +set tmp_memory_table_size=default; + +drop table t1,t2; + +--echo # End of 10.4 tests diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 021720f594e..9e51bb43a74 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -880,6 +880,10 @@ bool select_unit_ext::send_eof() table->file->ha_rnd_end(); } + /* Clean up table buffers for the next set operation from pipeline */ + if (next_sl) + restore_record(table,s->default_values); + if (unlikely(error)) table->file->print_error(error, MYF(0));