diff --git a/.bzrignore b/.bzrignore index d6d84003198dde64fef4bed3e6120f1384072870..21383779fa73682e78c527aa10a2c56cb8f70ffc 100644 --- a/.bzrignore +++ b/.bzrignore @@ -934,6 +934,7 @@ scripts/mysql_install_db scripts/mysql_secure_installation scripts/mysql_setpermission scripts/mysql_tableinfo +scripts/mysql_upgrade scripts/mysql_zap scripts/mysqlaccess scripts/mysqlbug @@ -1613,4 +1614,3 @@ vio/viotest-sslconnect.cpp vio/viotest.cpp zlib/*.ds? zlib/*.vcproj -scripts/mysql_upgrade diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index 806ccf9653784400ced19d3cae8c68dbd9ca7ea5..f862ee29986c4ec1e8efa7e3e8fb62edfb8b4fc9 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -1023,3 +1023,10 @@ select format(d, 2) from t1; format(d, 2) NULL drop table t1; +create table t1 (c varchar(40)); +insert into t1 values ('y,abc'),('y,abc'); +select c, substring_index(lcase(c), @q:=',', -1) as res from t1; +c res +y,abc abc +y,abc abc +drop table t1; diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 85976c211c5881632f44d6b765c61bc5b0afe203..6094d23b0d070f05bd63cbcb02371a0e4fceb48a 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -215,9 +215,9 @@ select * from t1 where t1.a=(select t2.a from t2 where t2.b=(select max(a) from a select b,(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2) from t4; b (select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2) -8 7.5 -8 4.5 -9 7.5 +8 7.5000 +8 4.5000 +9 7.5000 explain extended select b,(select avg(t2.a+(select min(t3.a) from t3 where t3.a >= t4.a)) from t2) from t4; id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t4 ALL NULL NULL NULL NULL 3 diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index b9f1e36349c2c2c6e4b82f0b39ac0fc904df1ef0..0d4a0737bb91cb1744542986327012f3078bf400 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -42,7 +42,6 @@ rpl_row_basic_3innodb : Bug #17385 rpl_sp : Bug#16456 rpl_until : Unstable test case, bug#15886 sp-goto : GOTO is currently is disabled - will be fixed in the future -subselect : Bug#15706 (ps mode) [PATCH PENDING] rpl_ndb_blob : Bug #17505 rpl_ndb_blob2 : Bug #17505 rpl_ndb_log : results are not deterministic diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index ac2bf8202572759d1f0305fc48d6a4d05fdb37a3..ef20d766bcecc4b6882a6ad24f2b1b8540f05a95 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -676,4 +676,12 @@ insert into t1 values (null); select format(d, 2) from t1; drop table t1; +# +# Bug #14676: substring_index() returns incorrect results +# +create table t1 (c varchar(40)); +insert into t1 values ('y,abc'),('y,abc'); +select c, substring_index(lcase(c), @q:=',', -1) as res from t1; +drop table t1; + # End of 5.0 tests diff --git a/sql/item.cc b/sql/item.cc index a3bfd71c010112cdabfa8a41e52016c0f09ea4ff..9f09a8fa02c7572eb534ec3fd8273c26d8ba688d 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3211,6 +3211,252 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) } +/* + Resolve the name of an outer select column reference. + + SYNOPSIS + Item_field::fix_outer_field() + thd [in] current thread + from_field [in/out] found field reference or (Field*)not_found_field + reference [in/out] view column if this item was resolved to a view column + + DESCRIPTION + The method resolves the column reference represented by 'this' as a column + present in outer selects that contain current select. + + NOTES + This is the inner loop of Item_field::fix_fields: + + for each outer query Q_k beginning from the inner-most one + { + search for a column or derived column named col_ref_i + [in table T_j] in the FROM clause of Q_k; + + if such a column is not found + Search for a column or derived column named col_ref_i + [in table T_j] in the SELECT and GROUP clauses of Q_k. + } + + IMPLEMENTATION + In prepared statements, because of cache, find_field_in_tables() + can resolve fields even if they don't belong to current context. + In this case this method only finds appropriate context and marks + current select as dependent. The found reference of field should be + provided in 'from_field'. + + RETURN + 1 - column succefully resolved and fix_fields() should continue. + 0 - column fully fixed and fix_fields() should return FALSE + -1 - error occured +*/ +int +Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) +{ + enum_parsing_place place= NO_MATTER; + bool field_found= (*from_field != not_found_field); + bool upward_lookup= FALSE; + + /* + If there are outer contexts (outer selects, but current select is + not derived table or view) try to resolve this reference in the + outer contexts. + + We treat each subselect as a separate namespace, so that different + subselects may contain columns with the same names. The subselects + are searched starting from the innermost. + */ + Name_resolution_context *last_checked_context= context; + Item **ref= (Item **) not_found_item; + Name_resolution_context *outer_context= context->outer_context; + for (; + outer_context; + outer_context= outer_context->outer_context) + { + SELECT_LEX *select= outer_context->select_lex; + Item_subselect *prev_subselect_item= + last_checked_context->select_lex->master_unit()->item; + last_checked_context= outer_context; + upward_lookup= TRUE; + + place= prev_subselect_item->parsing_place; + /* + If outer_field is set, field was already found by first call + to find_field_in_tables(). Only need to find appropriate context. + */ + if (field_found && outer_context->select_lex != + cached_table->select_lex) + continue; + /* + In case of a view, find_field_in_tables() writes the pointer to + the found view field into '*reference', in other words, it + substitutes this Item_field with the found expression. + */ + if (field_found || (*from_field= find_field_in_tables(thd, this, + outer_context-> + first_name_resolution_table, + outer_context-> + last_name_resolution_table, + reference, + IGNORE_EXCEPT_NON_UNIQUE, + TRUE, TRUE)) != + not_found_field) + { + if (*from_field) + { + if (*from_field != view_ref_found) + { + prev_subselect_item->used_tables_cache|= (*from_field)->table->map; + prev_subselect_item->const_item_cache= 0; + if (thd->lex->in_sum_func && + thd->lex->in_sum_func->nest_level == + thd->lex->current_select->nest_level) + { + Item::Type type= (*reference)->type(); + set_if_bigger(thd->lex->in_sum_func->max_arg_level, + select->nest_level); + set_field(*from_field); + fixed= 1; + mark_as_dependent(thd, last_checked_context->select_lex, + context->select_lex, this, + ((type == REF_ITEM || type == FIELD_ITEM) ? + (Item_ident*) (*reference) : 0)); + return 0; + } + } + else + { + Item::Type type= (*reference)->type(); + prev_subselect_item->used_tables_cache|= + (*reference)->used_tables(); + prev_subselect_item->const_item_cache&= + (*reference)->const_item(); + mark_as_dependent(thd, last_checked_context->select_lex, + context->select_lex, this, + ((type == REF_ITEM || type == FIELD_ITEM) ? + (Item_ident*) (*reference) : + 0)); + /* + A reference to a view field had been found and we + substituted it instead of this Item (find_field_in_tables + does it by assigning the new value to *reference), so now + we can return from this function. + */ + return 0; + } + } + break; + } + + /* Search in SELECT and GROUP lists of the outer select. */ + if (outer_context->resolve_in_select_list) + { + if (!(ref= resolve_ref_in_select_and_group(thd, this, select))) + return -1; /* Some error occurred (e.g. ambiguous names). */ + if (ref != not_found_item) + { + DBUG_ASSERT(*ref && (*ref)->fixed); + prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); + prev_subselect_item->const_item_cache&= (*ref)->const_item(); + break; + } + } + + /* + Reference is not found in this select => this subquery depend on + outer select (or we just trying to find wrong identifier, in this + case it does not matter which used tables bits we set) + */ + prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; + prev_subselect_item->const_item_cache= 0; + } + + DBUG_ASSERT(ref != 0); + if (!*from_field) + return -1; + if (ref == not_found_item && *from_field == not_found_field) + { + if (upward_lookup) + { + // We can't say exactly what absent table or field + my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd->where); + } + else + { + /* Call find_field_in_tables only to report the error */ + find_field_in_tables(thd, this, + context->first_name_resolution_table, + context->last_name_resolution_table, + reference, REPORT_ALL_ERRORS, + !any_privileges && + TRUE, TRUE); + } + return -1; + } + else if (ref != not_found_item) + { + Item *save; + Item_ref *rf; + + /* Should have been checked in resolve_ref_in_select_and_group(). */ + DBUG_ASSERT(*ref && (*ref)->fixed); + /* + Here, a subset of actions performed by Item_ref::set_properties + is not enough. So we pass ptr to NULL into Item_[direct]_ref + constructor, so no initialization is performed, and call + fix_fields() below. + */ + save= *ref; + *ref= NULL; // Don't call set_properties() + rf= (place == IN_HAVING ? + new Item_ref(context, ref, (char*) table_name, + (char*) field_name) : + new Item_direct_ref(context, ref, (char*) table_name, + (char*) field_name)); + *ref= save; + if (!rf) + return -1; + thd->change_item_tree(reference, rf); + /* + rf is Item_ref => never substitute other items (in this case) + during fix_fields() => we can use rf after fix_fields() + */ + DBUG_ASSERT(!rf->fixed); // Assured by Item_ref() + if (rf->fix_fields(thd, reference) || rf->check_cols(1)) + return -1; + + mark_as_dependent(thd, last_checked_context->select_lex, + context->select_lex, this, + rf); + return 0; + } + else + { + mark_as_dependent(thd, last_checked_context->select_lex, + context->select_lex, + this, this); + if (last_checked_context->select_lex->having_fix_field) + { + Item_ref *rf; + rf= new Item_ref(context, + (cached_table->db[0] ? cached_table->db : 0), + (char*) cached_table->alias, (char*) field_name); + if (!rf) + return -1; + thd->change_item_tree(reference, rf); + /* + rf is Item_ref => never substitute other items (in this case) + during fix_fields() => we can use rf after fix_fields() + */ + DBUG_ASSERT(!rf->fixed); // Assured by Item_ref() + if (rf->fix_fields(thd, reference) || rf->check_cols(1)) + return -1; + return 0; + } + } + return 1; +} + + /* Resolve the name of a column reference. @@ -3258,12 +3504,11 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) bool Item_field::fix_fields(THD *thd, Item **reference) { - enum_parsing_place place= NO_MATTER; DBUG_ASSERT(fixed == 0); if (!field) // If field is not checked { - bool upward_lookup= FALSE; Field *from_field= (Field *)not_found_field; + bool outer_fixed= false; /* In case of view, find_field_in_tables() write pointer to view field expression to 'reference', i.e. it substitute that expression instead @@ -3278,7 +3523,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) TRUE)) == not_found_field) { - + int ret; /* Look up in current select's item_list to find aliased fields */ if (thd->lex->current_select->is_item_list_lookup) { @@ -3293,197 +3538,11 @@ bool Item_field::fix_fields(THD *thd, Item **reference) return 0; } } - - /* - If there are outer contexts (outer selects, but current select is - not derived table or view) try to resolve this reference in the - outer contexts. - - We treat each subselect as a separate namespace, so that different - subselects may contain columns with the same names. The subselects - are searched starting from the innermost. - */ - Name_resolution_context *last_checked_context= context; - Item **ref= (Item **) not_found_item; - Name_resolution_context *outer_context= context->outer_context; - for (; - outer_context; - outer_context= outer_context->outer_context) - { - SELECT_LEX *select= outer_context->select_lex; - Item_subselect *prev_subselect_item= - last_checked_context->select_lex->master_unit()->item; - last_checked_context= outer_context; - upward_lookup= TRUE; - - place= prev_subselect_item->parsing_place; - /* - In case of a view, find_field_in_tables() writes the pointer to - the found view field into '*reference', in other words, it - substitutes this Item_field with the found expression. - */ - if ((from_field= find_field_in_tables(thd, this, - outer_context-> - first_name_resolution_table, - outer_context-> - last_name_resolution_table, - reference, - IGNORE_EXCEPT_NON_UNIQUE, - TRUE, TRUE)) != - not_found_field) - { - if (from_field) - { - if (from_field != view_ref_found) - { - prev_subselect_item->used_tables_cache|= from_field->table->map; - prev_subselect_item->const_item_cache= 0; - if (thd->lex->in_sum_func && - thd->lex->in_sum_func->nest_level == - thd->lex->current_select->nest_level) - { - Item::Type type= (*reference)->type(); - set_if_bigger(thd->lex->in_sum_func->max_arg_level, - select->nest_level); - set_field(from_field); - fixed= 1; - mark_as_dependent(thd, last_checked_context->select_lex, - context->select_lex, this, - ((type == REF_ITEM || type == FIELD_ITEM) ? - (Item_ident*) (*reference) : 0)); - return FALSE; - } - } - else - { - Item::Type type= (*reference)->type(); - prev_subselect_item->used_tables_cache|= - (*reference)->used_tables(); - prev_subselect_item->const_item_cache&= - (*reference)->const_item(); - mark_as_dependent(thd, last_checked_context->select_lex, - context->select_lex, this, - ((type == REF_ITEM || type == FIELD_ITEM) ? - (Item_ident*) (*reference) : - 0)); - /* - A reference to a view field had been found and we - substituted it instead of this Item (find_field_in_tables - does it by assigning the new value to *reference), so now - we can return from this function. - */ - return FALSE; - } - } - break; - } - - /* Search in SELECT and GROUP lists of the outer select. */ - if (outer_context->resolve_in_select_list) - { - if (!(ref= resolve_ref_in_select_and_group(thd, this, select))) - goto error; /* Some error occurred (e.g. ambiguous names). */ - if (ref != not_found_item) - { - DBUG_ASSERT(*ref && (*ref)->fixed); - prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); - prev_subselect_item->const_item_cache&= (*ref)->const_item(); - break; - } - } - - /* - Reference is not found in this select => this subquery depend on - outer select (or we just trying to find wrong identifier, in this - case it does not matter which used tables bits we set) - */ - prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; - prev_subselect_item->const_item_cache= 0; - } - - DBUG_ASSERT(ref != 0); - if (!from_field) - goto error; - if (ref == not_found_item && from_field == not_found_field) - { - if (upward_lookup) - { - // We can't say exactly what absent table or field - my_error(ER_BAD_FIELD_ERROR, MYF(0), full_name(), thd->where); - } - else - { - /* Call find_field_in_tables only to report the error */ - find_field_in_tables(thd, this, - context->first_name_resolution_table, - context->last_name_resolution_table, - reference, REPORT_ALL_ERRORS, - !any_privileges && - TRUE, TRUE); - } - goto error; - } - else if (ref != not_found_item) - { - Item *save; - Item_ref *rf; - - /* Should have been checked in resolve_ref_in_select_and_group(). */ - DBUG_ASSERT(*ref && (*ref)->fixed); - /* - Here, a subset of actions performed by Item_ref::set_properties - is not enough. So we pass ptr to NULL into Item_[direct]_ref - constructor, so no initialization is performed, and call - fix_fields() below. - */ - save= *ref; - *ref= NULL; // Don't call set_properties() - rf= (place == IN_HAVING ? - new Item_ref(context, ref, (char*) table_name, - (char*) field_name) : - new Item_direct_ref(context, ref, (char*) table_name, - (char*) field_name)); - *ref= save; - if (!rf) - goto error; - thd->change_item_tree(reference, rf); - /* - rf is Item_ref => never substitute other items (in this case) - during fix_fields() => we can use rf after fix_fields() - */ - DBUG_ASSERT(!rf->fixed); // Assured by Item_ref() - if (rf->fix_fields(thd, reference) || rf->check_cols(1)) - goto error; - - mark_as_dependent(thd, last_checked_context->select_lex, - context->select_lex, this, - rf); - return FALSE; - } - else - { - mark_as_dependent(thd, last_checked_context->select_lex, - context->select_lex, - this, this); - if (last_checked_context->select_lex->having_fix_field) - { - Item_ref *rf; - rf= new Item_ref(context, - (cached_table->db[0] ? cached_table->db : 0), - (char*) cached_table->alias, (char*) field_name); - if (!rf) - goto error; - thd->change_item_tree(reference, rf); - /* - rf is Item_ref => never substitute other items (in this case) - during fix_fields() => we can use rf after fix_fields() - */ - DBUG_ASSERT(!rf->fixed); // Assured by Item_ref() - if (rf->fix_fields(thd, reference) || rf->check_cols(1)) - goto error; - return FALSE; - } - } + if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) + goto error; + else if (!ret) + return FALSE; + outer_fixed= TRUE; } else if (!from_field) goto error; @@ -3503,6 +3562,17 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (from_field == view_ref_found) return FALSE; + if (!outer_fixed && cached_table && cached_table->select_lex && + context->select_lex && + cached_table->select_lex != context->select_lex) + { + int ret; + if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) + goto error; + else if (!ret) + return FALSE; + } + set_field(from_field); if (thd->lex->in_sum_func && thd->lex->in_sum_func->nest_level == @@ -4655,6 +4725,25 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) } if (from_field != not_found_field) { + if (cached_table && cached_table->select_lex && + outer_context->select_lex && + cached_table->select_lex != outer_context->select_lex) + { + /* + Due to cache, find_field_in_tables() can return field which + doesn't belong to provided outer_context. In this case we have + to find proper field context in order to fix field correcly. + */ + do + { + outer_context= outer_context->outer_context; + select= outer_context->select_lex; + prev_subselect_item= + last_checked_context->select_lex->master_unit()->item; + last_checked_context= outer_context; + } while (outer_context && outer_context->select_lex && + cached_table->select_lex != outer_context->select_lex); + } prev_subselect_item->used_tables_cache|= from_field->table->map; prev_subselect_item->const_item_cache= 0; break; diff --git a/sql/item.h b/sql/item.h index 030b2c40b4a0e7432a2670c460a83c3b7e8b4460..ae6aaeb82f09beea2d51a2efda0d7d13786dd69c 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1205,6 +1205,7 @@ public: inline uint32 max_disp_length() { return field->max_length(); } Item_field *filed_for_view_update() { return this; } Item *safe_charset_converter(CHARSET_INFO *tocs); + int fix_outer_field(THD *thd, Field **field, Item **reference); friend class Item_default_value; friend class Item_insert_value; friend class st_select_lex_unit; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index fe02e7c5b49121f50e54398ccece79365c613f49..a3e47154bc3f037691e0884e05ed161ee4abd3bc 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1128,9 +1128,9 @@ void Item_func_substr_index::fix_length_and_dec() String *Item_func_substr_index::val_str(String *str) { DBUG_ASSERT(fixed == 1); - String *res =args[0]->val_str(str); - String *delimeter =args[1]->val_str(&tmp_value); - int32 count = (int32) args[2]->val_int(); + String *res= args[0]->val_str(str); + String *delimiter= args[1]->val_str(&tmp_value); + int32 count= (int32) args[2]->val_int(); uint offset; if (args[0]->null_value || args[1]->null_value || args[2]->null_value) @@ -1139,8 +1139,8 @@ String *Item_func_substr_index::val_str(String *str) return 0; } null_value=0; - uint delimeter_length=delimeter->length(); - if (!res->length() || !delimeter_length || !count) + uint delimiter_length= delimiter->length(); + if (!res->length() || !delimiter_length || !count) return &my_empty_string; // Wrong parameters res->set_charset(collation.collation); @@ -1148,11 +1148,11 @@ String *Item_func_substr_index::val_str(String *str) #ifdef USE_MB if (use_mb(res->charset())) { - const char *ptr=res->ptr(); - const char *strend = ptr+res->length(); - const char *end=strend-delimeter_length+1; - const char *search=delimeter->ptr(); - const char *search_end=search+delimeter_length; + const char *ptr= res->ptr(); + const char *strend= ptr+res->length(); + const char *end= strend-delimiter_length+1; + const char *search= delimiter->ptr(); + const char *search_end= search+delimiter_length; int32 n=0,c=count,pass; register uint32 l; for (pass=(count>0);pass<2;++pass) @@ -1167,7 +1167,7 @@ String *Item_func_substr_index::val_str(String *str) if (*i++ != *j++) goto skip; if (pass==0) ++n; else if (!--c) break; - ptr+=delimeter_length; + ptr+= delimiter_length; continue; } skip: @@ -1189,7 +1189,7 @@ String *Item_func_substr_index::val_str(String *str) } else /* return right part */ { - ptr+=delimeter_length; + ptr+= delimiter_length; tmp_value.set(*res,(ulong) (ptr-res->ptr()), (ulong) (strend-ptr)); } } @@ -1200,9 +1200,9 @@ String *Item_func_substr_index::val_str(String *str) { if (count > 0) { // start counting from the beginning - for (offset=0 ;; offset+=delimeter_length) + for (offset=0; ; offset+= delimiter_length) { - if ((int) (offset=res->strstr(*delimeter,offset)) < 0) + if ((int) (offset= res->strstr(*delimiter, offset)) < 0) return res; // Didn't find, return org string if (!--count) { @@ -1223,7 +1223,7 @@ String *Item_func_substr_index::val_str(String *str) address space less than where the found substring is located in res */ - if ((int) (offset=res->strrstr(*delimeter,offset)) < 0) + if ((int) (offset= res->strrstr(*delimiter, offset)) < 0) return res; // Didn't find, return org string /* At this point, we've searched for the substring @@ -1231,13 +1231,19 @@ String *Item_func_substr_index::val_str(String *str) */ if (!++count) { - offset+=delimeter_length; + offset+= delimiter_length; tmp_value.set(*res,offset,res->length()- offset); break; } } } } + /* + We always mark tmp_value as const so that if val_str() is called again + on this object, we don't disrupt the contents of tmp_value when it was + derived from another String. + */ + tmp_value.mark_as_const(); return (&tmp_value); } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index f1c99f744985d2bee67a427e503be0d60b7d312c..a4dac5bda876e74d5dde581c248e91c1ab75347f 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -125,6 +125,7 @@ public: friend class select_subselect; friend class Item_in_optimizer; friend bool Item_field::fix_fields(THD *, Item **); + friend int Item_field::fix_outer_field(THD *, Field **, Item **); friend bool Item_ref::fix_fields(THD *, Item **); friend void mark_select_range_as_dependent(THD*, st_select_lex*, st_select_lex*,