diff options
author | Sergei Golubchik <serg@mariadb.org> | 2015-10-09 17:12:26 +0200 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2015-10-09 17:12:26 +0200 |
commit | cfeedbfd3e292f61c7da8f0a7f86307cbeeddb64 (patch) | |
tree | 3629d1de148b14915a35a21f809e1a2ea6a08619 /sql | |
parent | bff1af983ad7b0bed6c3973e4d13297df5fe2791 (diff) | |
parent | 16c4b3c68b06653592a9500050ad977a38f4ebae (diff) | |
download | mariadb-git-cfeedbfd3e292f61c7da8f0a7f86307cbeeddb64.tar.gz |
Merge branch '5.5' into 10.0
Diffstat (limited to 'sql')
-rw-r--r-- | sql/field.h | 17 | ||||
-rw-r--r-- | sql/field_conv.cc | 16 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 54 | ||||
-rw-r--r-- | sql/item_func.cc | 53 | ||||
-rw-r--r-- | sql/item_func.h | 39 | ||||
-rw-r--r-- | sql/item_subselect.cc | 26 | ||||
-rw-r--r-- | sql/item_subselect.h | 6 | ||||
-rw-r--r-- | sql/log_event.cc | 15 | ||||
-rw-r--r-- | sql/mysqld.cc | 18 | ||||
-rw-r--r-- | sql/opt_range.cc | 17 | ||||
-rw-r--r-- | sql/opt_subselect.cc | 49 | ||||
-rw-r--r-- | sql/partition_info.cc | 16 | ||||
-rw-r--r-- | sql/sql_acl.cc | 56 | ||||
-rw-r--r-- | sql/sql_base.cc | 14 | ||||
-rw-r--r-- | sql/sql_handler.cc | 37 | ||||
-rw-r--r-- | sql/sql_handler.h | 5 | ||||
-rw-r--r-- | sql/sql_insert.cc | 56 | ||||
-rw-r--r-- | sql/sql_load.cc | 13 | ||||
-rw-r--r-- | sql/sql_parse.cc | 29 | ||||
-rw-r--r-- | sql/sql_select.cc | 5 | ||||
-rw-r--r-- | sql/sql_show.cc | 15 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 12 |
22 files changed, 386 insertions, 182 deletions
diff --git a/sql/field.h b/sql/field.h index 4e3a9f4c7b1..c64d6bc443c 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1,6 +1,6 @@ #ifndef FIELD_INCLUDED #define FIELD_INCLUDED -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. +/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. Copyright (c) 2008, 2015, MariaDB This program is free software; you can redistribute it and/or modify @@ -977,6 +977,16 @@ public: /* Hash value */ virtual void hash(ulong *nr, ulong *nr2); +/** + Checks whether a string field is part of write_set. + + @return + FALSE - If field is not char/varchar/.... + - If field is char/varchar/.. and is not part of write set. + TRUE - If field is char/varchar/.. and is part of write set. +*/ + virtual bool is_updatable() const { return FALSE; } + /* Check whether the field can be used as a join attribute in hash join */ virtual bool hash_join_is_possible() { return TRUE; } virtual bool eq_cmp_as_binary() { return TRUE; } @@ -1174,6 +1184,11 @@ public: int store_decimal(const my_decimal *d); uint32 max_data_length() const; + bool is_updatable() const + { + DBUG_ASSERT(table && table->write_set); + return bitmap_is_set(table->write_set, field_index); + } bool match_collation_to_optimize_range() const { return true; } }; diff --git a/sql/field_conv.cc b/sql/field_conv.cc index e31f7c5f005..79d579b6828 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -1,7 +1,6 @@ /* - Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2010, 2012, Monty Program Ab - + Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -905,15 +904,10 @@ int field_conv_incompatible(Field *to, Field *from) { // Be sure the value is stored Field_blob *blob=(Field_blob*) to; from->val_str(&blob->value); - /* - Copy value if copy_blobs is set, or source is not a string and - we have a pointer to its internal string conversion buffer. - */ - if (to->table->copy_blobs || - (!blob->value.is_alloced() && - from_real_type != MYSQL_TYPE_STRING && - from_real_type != MYSQL_TYPE_VARCHAR)) + + if (!blob->value.is_alloced() && from->is_updatable()) blob->value.copy(); + return blob->store(blob->value.ptr(),blob->value.length(),from->charset()); } if (from_real_type == MYSQL_TYPE_ENUM && diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 90eef1ea55c..830a12ea48d 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1466,9 +1466,36 @@ bool Item_in_optimizer::eval_not_null_tables(uchar *opt_arg) bool Item_in_optimizer::fix_left(THD *thd) { DBUG_ENTER("Item_in_optimizer::fix_left"); - if ((!args[0]->fixed && args[0]->fix_fields(thd, args)) || - (!cache && !(cache= Item_cache::get_cache(args[0])))) + /* + Here we will store pointer on place of main storage of left expression. + For usual IN (ALL/ANY) it is subquery left_expr. + For other cases (MAX/MIN optimization, non-transformed EXISTS (10.0)) + it is args[0]. + */ + Item **ref0= args; + if (args[1]->type() == Item::SUBSELECT_ITEM && + ((Item_subselect *)args[1])->is_in_predicate()) + { + /* + left_expr->fix_fields() may cause left_expr to be substituted for + another item. (e.g. an Item_field may be changed into Item_ref). This + transformation is undone at the end of statement execution (e.g. the + Item_ref is deleted). However, Item_in_optimizer::args[0] may keep + the pointer to the post-transformation item. Because of that, on the + next execution we need to copy args[1]->left_expr again. + */ + ref0= &(((Item_in_subselect *)args[1])->left_expr); + args[0]= ((Item_in_subselect *)args[1])->left_expr; + } + if ((!(*ref0)->fixed && (*ref0)->fix_fields(thd, ref0)) || + (!cache && !(cache= Item_cache::get_cache(*ref0)))) DBUG_RETURN(1); + /* + During fix_field() expression could be substituted. + So we copy changes before use + */ + if (args[0] != (*ref0)) + args[0]= (*ref0); DBUG_PRINT("info", ("actual fix fields")); cache->setup(args[0]); @@ -1531,6 +1558,16 @@ bool Item_in_optimizer::fix_left(THD *thd) bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); + Item_subselect *sub= 0; + uint col; + + /* + MAX/MIN optimization can convert the subquery into + expr + Item_singlerow_subselect + */ + if (args[1]->type() == Item::SUBSELECT_ITEM) + sub= (Item_subselect *)args[1]; + if (fix_left(thd)) return TRUE; if (args[0]->maybe_null) @@ -1538,12 +1575,11 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) if (!args[1]->fixed && args[1]->fix_fields(thd, args+1)) return TRUE; - - Item_in_subselect * sub= (Item_in_subselect *)args[1]; if (!invisible_mode() && - args[0]->cols() != sub->engine->cols()) + ((sub && ((col= args[0]->cols()) != sub->engine->cols())) || + (!sub && (args[1]->cols() != (col= 1))))) { - my_error(ER_OPERAND_COLUMNS, MYF(0), args[0]->cols()); + my_error(ER_OPERAND_COLUMNS, MYF(0), col); return TRUE; } if (args[1]->maybe_null) @@ -2756,7 +2792,8 @@ Item_func_if::str_op(String *str) String *res=arg->val_str(str); if (res) res->set_charset(collation.collation); - null_value=arg->null_value; + if ((null_value=arg->null_value)) + res= NULL; return res; } @@ -2767,7 +2804,8 @@ Item_func_if::decimal_op(my_decimal *decimal_value) DBUG_ASSERT(fixed == 1); Item *arg= args[0]->val_bool() ? args[1] : args[2]; my_decimal *value= arg->val_decimal(decimal_value); - null_value= arg->null_value; + if ((null_value= arg->null_value)) + value= NULL; return value; } diff --git a/sql/item_func.cc b/sql/item_func.cc index da688689148..6e21f357f41 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -885,7 +885,7 @@ String *Item_func_hybrid_result_type::val_str(String *str) case DECIMAL_RESULT: { my_decimal decimal_value, *val; - if (!(val= decimal_op(&decimal_value))) + if (!(val= decimal_op_with_null_check(&decimal_value))) return 0; // null is set my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val); str->set_charset(collation.collation); @@ -912,24 +912,22 @@ String *Item_func_hybrid_result_type::val_str(String *str) if (is_temporal_type(field_type())) { MYSQL_TIME ltime; - if (date_op(<ime, - field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0) || - str->alloc(MAX_DATE_STRING_REP_LENGTH)) - { - null_value= 1; + if (date_op_with_null_check(<ime) || + (null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH))) return (String *) 0; - } ltime.time_type= mysql_type_to_time_type(field_type()); str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals)); str->set_charset(&my_charset_bin); + DBUG_ASSERT(!null_value); return str; } - return str_op(&str_value); + return str_op_with_null_check(&str_value); case TIME_RESULT: case ROW_RESULT: case IMPOSSIBLE_RESULT: DBUG_ASSERT(0); } + DBUG_ASSERT(!null_value || (str == NULL)); return str; } @@ -942,7 +940,7 @@ double Item_func_hybrid_result_type::val_real() { my_decimal decimal_value, *val; double result; - if (!(val= decimal_op(&decimal_value))) + if (!(val= decimal_op_with_null_check(&decimal_value))) return 0.0; // null is set my_decimal2double(E_DEC_FATAL_ERROR, val, &result); return result; @@ -959,18 +957,14 @@ double Item_func_hybrid_result_type::val_real() if (is_temporal_type(field_type())) { MYSQL_TIME ltime; - if (date_op(<ime, - field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0 )) - { - null_value= 1; + if (date_op_with_null_check(<ime)) return 0; - } ltime.time_type= mysql_type_to_time_type(field_type()); return TIME_to_double(<ime); } char *end_not_used; int err_not_used; - String *res= str_op(&str_value); + String *res= str_op_with_null_check(&str_value); return (res ? my_strntod(res->charset(), (char*) res->ptr(), res->length(), &end_not_used, &err_not_used) : 0.0); } @@ -990,7 +984,7 @@ longlong Item_func_hybrid_result_type::val_int() case DECIMAL_RESULT: { my_decimal decimal_value, *val; - if (!(val= decimal_op(&decimal_value))) + if (!(val= decimal_op_with_null_check(&decimal_value))) return 0; // null is set longlong result; my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result); @@ -1005,18 +999,14 @@ longlong Item_func_hybrid_result_type::val_int() if (is_temporal_type(field_type())) { MYSQL_TIME ltime; - if (date_op(<ime, - field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0)) - { - null_value= 1; + if (date_op_with_null_check(<ime)) return 0; - } ltime.time_type= mysql_type_to_time_type(field_type()); return TIME_to_ulonglong(<ime); } int err_not_used; String *res; - if (!(res= str_op(&str_value))) + if (!(res= str_op_with_null_check(&str_value))) return 0; char *end= (char*) res->ptr() + res->length(); @@ -1038,17 +1028,21 @@ my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value) DBUG_ASSERT(fixed == 1); switch (cached_result_type) { case DECIMAL_RESULT: - val= decimal_op(decimal_value); + val= decimal_op_with_null_check(decimal_value); break; case INT_RESULT: { longlong result= int_op(); + if (null_value) + return NULL; int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, decimal_value); break; } case REAL_RESULT: { double result= (double)real_op(); + if (null_value) + return NULL; double2my_decimal(E_DEC_FATAL_ERROR, result, decimal_value); break; } @@ -1057,19 +1051,20 @@ my_decimal *Item_func_hybrid_result_type::val_decimal(my_decimal *decimal_value) if (is_temporal_type(field_type())) { MYSQL_TIME ltime; - if (date_op(<ime, - field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0)) + if (date_op_with_null_check(<ime)) { my_decimal_set_zero(decimal_value); - null_value= 1; return 0; } ltime.time_type= mysql_type_to_time_type(field_type()); return date2my_decimal(<ime, decimal_value); } String *res; - if (!(res= str_op(&str_value))) + if (!(res= str_op_with_null_check(&str_value))) + { + null_value= 1; return NULL; + } str2my_decimal(E_DEC_FATAL_ERROR, (char*) res->ptr(), res->length(), res->charset(), decimal_value); @@ -1092,7 +1087,7 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime, case DECIMAL_RESULT: { my_decimal value, *res; - if (!(res= decimal_op(&value)) || + if (!(res= decimal_op_with_null_check(&value)) || decimal_to_datetime_with_warn(res, ltime, fuzzydate, field_name_or_null())) goto err; @@ -1122,7 +1117,7 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime, return date_op(ltime, fuzzydate); char buff[40]; String tmp(buff,sizeof(buff), &my_charset_bin),*res; - if (!(res= str_op(&tmp)) || + if (!(res= str_op_with_null_check(&tmp)) || str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), ltime, fuzzydate)) goto err; diff --git a/sql/item_func.h b/sql/item_func.h index ce1f2fdd676..0b3454fa4b0 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -385,17 +385,17 @@ public: void no_rows_in_result() { - bool_func_call_args info; - info.original_func_item= this; - info.bool_function= &Item::no_rows_in_result; - walk(&Item::call_bool_func_processor, FALSE, (uchar*) &info); + for (uint i= 0; i < arg_count; i++) + { + args[i]->no_rows_in_result(); + } } void restore_to_before_no_rows_in_result() { - bool_func_call_args info; - info.original_func_item= this; - info.bool_function= &Item::restore_to_before_no_rows_in_result; - walk(&Item::call_bool_func_processor, FALSE, (uchar*) &info); + for (uint i= 0; i < arg_count; i++) + { + args[i]->no_rows_in_result(); + } } }; @@ -419,6 +419,29 @@ public: class Item_func_hybrid_result_type: public Item_func { + /* + Helper methods to make sure that the result of + decimal_op(), str_op() and date_op() is properly synched with null_value. + */ + bool date_op_with_null_check(MYSQL_TIME *ltime) + { + bool rc= date_op(ltime, + field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0); + DBUG_ASSERT(!rc ^ null_value); + return rc; + } + String *str_op_with_null_check(String *str) + { + String *res= str_op(str); + DBUG_ASSERT((res != NULL) ^ null_value); + return res; + } + my_decimal *decimal_op_with_null_check(my_decimal *decimal_buffer) + { + my_decimal *res= decimal_op(decimal_buffer); + DBUG_ASSERT((res != NULL) ^ null_value); + return res; + } protected: Item_result cached_result_type; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index fe2352f3008..ce01fc7a3b8 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1378,7 +1378,7 @@ Item_in_subselect::Item_in_subselect(Item * left_exp, { DBUG_ENTER("Item_in_subselect::Item_in_subselect"); DBUG_PRINT("info", ("in_strategy: %u", (uint)in_strategy)); - left_expr= left_exp; + left_expr_orig= left_expr= left_exp; func= &eq_creator; init(select_lex, new select_exists_subselect(this)); max_columns= UINT_MAX; @@ -1401,7 +1401,7 @@ Item_allany_subselect::Item_allany_subselect(Item * left_exp, :Item_in_subselect(), func_creator(fc), all(all_arg) { DBUG_ENTER("Item_allany_subselect::Item_allany_subselect"); - left_expr= left_exp; + left_expr_orig= left_expr= left_exp; func= func_creator(all_arg); init(select_lex, new select_exists_subselect(this)); max_columns= 1; @@ -3032,15 +3032,13 @@ Item_in_subselect::select_in_like_transformer(JOIN *join) arena= thd->activate_stmt_arena_if_needed(&backup); if (!optimizer) { - result= (!(optimizer= new Item_in_optimizer(left_expr, this))); + result= (!(optimizer= new Item_in_optimizer(left_expr_orig, this))); if (result) goto out; } thd->lex->current_select= current->return_after_parsing(); result= optimizer->fix_left(thd); - /* fix_fields can change reference to left_expr, we need reassign it */ - left_expr= optimizer->arguments()[0]; thd->lex->current_select= current; if (changed) @@ -3107,11 +3105,13 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) { uint outer_cols_num; List<Item> *inner_cols; + char const *save_where= thd->where; DBUG_ENTER("Item_in_subselect::fix_fields"); if (test_strategy(SUBS_SEMI_JOIN)) DBUG_RETURN( !( (*ref)= new Item_int(1)) ); + thd->where= "IN/ALL/ANY subquery"; /* Check if the outer and inner IN operands match in those cases when we will not perform IN=>EXISTS transformation. Currently this is when we @@ -3142,7 +3142,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) if (outer_cols_num != inner_cols->elements) { my_error(ER_OPERAND_COLUMNS, MYF(0), outer_cols_num); - DBUG_RETURN(TRUE); + goto err; } if (outer_cols_num > 1) { @@ -3152,20 +3152,24 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) { inner_col= inner_col_it++; if (inner_col->check_cols(left_expr->element_index(i)->cols())) - DBUG_RETURN(TRUE); + goto err; } } } - if (thd_arg->lex->is_view_context_analysis() && - left_expr && !left_expr->fixed && + if (left_expr && !left_expr->fixed && left_expr->fix_fields(thd_arg, &left_expr)) - DBUG_RETURN(TRUE); + goto err; else if (Item_subselect::fix_fields(thd_arg, ref)) - DBUG_RETURN(TRUE); + goto err; fixed= TRUE; + thd->where= save_where; DBUG_RETURN(FALSE); + +err: + thd->where= save_where; + DBUG_RETURN(TRUE); } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 92b269d02f1..3c0b7bd6ade 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -482,6 +482,12 @@ protected: Item **having_item); public: Item *left_expr; + /* + Important for PS/SP: left_expr_orig is the item that left_expr originally + pointed at. That item is allocated on the statement arena, while + left_expr could later be changed to something on the execution arena. + */ + Item *left_expr_orig; /* Priority of this predicate in the convert-to-semi-join-nest process. */ int sj_convert_priority; /* diff --git a/sql/log_event.cc b/sql/log_event.cc index 411229d319a..215bb6df0f8 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -2224,20 +2224,13 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr, uint precision= meta >> 8; uint decimals= meta & 0xFF; uint bin_size= my_decimal_get_binary_size(precision, decimals); - uint length; my_decimal dec; binary2my_decimal(E_DEC_FATAL_ERROR, (uchar*) ptr, &dec, precision, decimals); - int i, end; - char buff[512], *pos; - pos= buff; - pos+= sprintf(buff, "%s", dec.sign() ? "-" : ""); - end= ROUND_UP(dec.frac) + ROUND_UP(dec.intg)-1; - for (i=0; i < end; i++) - pos+= sprintf(pos, "%09d.", dec.buf[i]); - pos+= sprintf(pos, "%09d", dec.buf[i]); - length= (uint) (pos - buff); - my_b_write(file, buff, length); + int length= DECIMAL_MAX_STR_LENGTH; + char buff[DECIMAL_MAX_STR_LENGTH + 1]; + decimal2string(&dec, buff, &length, 0, 0, 0); + my_b_write(file, (uchar*)buff, length); my_snprintf(typestr, typestr_length, "DECIMAL(%d,%d)", precision, decimals); return bin_size; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f542977a28e..204bf75909f 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4344,14 +4344,24 @@ static int init_common_variables() { if (lower_case_table_names_used) { +#if MYSQL_VERSION_ID < 100100 if (global_system_variables.log_warnings) sql_print_warning("You have forced lower_case_table_names to 0 through " "a command-line option, even though your file system " "'%s' is case insensitive. This means that you can " - "corrupt a MyISAM table by accessing it with " - "different cases. You should consider changing " - "lower_case_table_names to 1 or 2", - mysql_real_data_home); + "corrupt your tables if you access them using names " + "with different letter case. You should consider " + "changing lower_case_table_names to 1 or 2", + mysql_real_data_home); +#else + sql_print_error("The server option 'lower_case_table_names' is " + "configured to use case sensitive table names but the " + "data directory resides on a case-insensitive file system. " + "Please use a case sensitive file system for your data " + "directory or switch to a case-insensitive table name " + "mode."); +#endif + return 1; } else { diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 3f12748c071..da18a30ac78 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. - Copyright (c) 2008, 2014, Monty Program Ab. +/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2008, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -4634,10 +4634,19 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) key_tree->min_flag | key_tree->max_flag, &subpart_iter); - DBUG_ASSERT(res); /* We can't get "no satisfying subpartitions" */ + if (res == 0) + { + /* + The only case where we can get "no satisfying subpartitions" + returned from the above call is when an error has occurred. + */ + DBUG_ASSERT(range_par->thd->is_error()); + return 0; + } + if (res == -1) goto pop_and_go_right; /* all subpartitions satisfy */ - + uint32 subpart_id; bitmap_clear_all(&ppar->subparts_bitmap); while ((subpart_id= subpart_iter.get_next(&subpart_iter)) != diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 062b43291fb..3d470b6ff5c 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -617,6 +617,18 @@ int check_and_do_in_subquery_rewrites(JOIN *join) thd->stmt_arena->state != Query_arena::PREPARED) */ { + SELECT_LEX *current= thd->lex->current_select; + thd->lex->current_select= current->return_after_parsing(); + char const *save_where= thd->where; + thd->where= "IN/ALL/ANY subquery"; + + bool failure= !in_subs->left_expr->fixed && + in_subs->left_expr->fix_fields(thd, &in_subs->left_expr); + thd->lex->current_select= current; + thd->where= save_where; + if (failure) + DBUG_RETURN(-1); /* purecov: deadcode */ + /* Check if the left and right expressions have the same # of columns, i.e. we don't have a case like @@ -630,18 +642,6 @@ int check_and_do_in_subquery_rewrites(JOIN *join) my_error(ER_OPERAND_COLUMNS, MYF(0), in_subs->left_expr->cols()); DBUG_RETURN(-1); } - - SELECT_LEX *current= thd->lex->current_select; - thd->lex->current_select= current->return_after_parsing(); - char const *save_where= thd->where; - thd->where= "IN/ALL/ANY subquery"; - - bool failure= !in_subs->left_expr->fixed && - in_subs->left_expr->fix_fields(thd, &in_subs->left_expr); - thd->lex->current_select= current; - thd->where= save_where; - if (failure) - DBUG_RETURN(-1); /* purecov: deadcode */ } DBUG_PRINT("info", ("Checking if subq can be converted to semi-join")); @@ -704,6 +704,12 @@ int check_and_do_in_subquery_rewrites(JOIN *join) if (!optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS) && !optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION)) my_error(ER_ILLEGAL_SUBQUERY_OPTIMIZER_SWITCHES, MYF(0)); + /* + Transform each subquery predicate according to its overloaded + transformer. + */ + if (subselect->select_transformer(join)) + DBUG_RETURN(-1); /* If the subquery predicate is IN/=ANY, analyse and set all possible @@ -755,12 +761,6 @@ int check_and_do_in_subquery_rewrites(JOIN *join) allany_subs->add_strategy(strategy); } - /* - Transform each subquery predicate according to its overloaded - transformer. - */ - if (subselect->select_transformer(join)) - DBUG_RETURN(-1); } } DBUG_RETURN(0); @@ -1593,8 +1593,19 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) if (subq_pred->left_expr->cols() == 1) { nested_join->sj_outer_expr_list.push_back(subq_pred->left_expr); + /* + Create Item_func_eq. Note that + 1. this is done on the statement, not execution, arena + 2. if it's a PS then this happens only once - on the first execution. + On following re-executions, the item will be fix_field-ed normally. + 3. Thus it should be created as if it was fix_field'ed, in particular + all pointers to items in the execution arena should be protected + with thd->change_item_tree + */ Item_func_eq *item_eq= - new Item_func_eq(subq_pred->left_expr, subq_lex->ref_pointer_array[0]); + new Item_func_eq(subq_pred->left_expr_orig, subq_lex->ref_pointer_array[0]); + if (subq_pred->left_expr_orig != subq_pred->left_expr) + thd->change_item_tree(item_eq->arguments(), subq_pred->left_expr); item_eq->in_equality_no= 0; sj_nest->sj_on_expr= and_items(sj_nest->sj_on_expr, item_eq); } diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 4a42d7965b8..1607b1937df 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. +/* Copyright (c) 2006, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1665,15 +1666,22 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, { int err= 0; + /* Check for partition expression. */ if (!list_of_part_fields) { DBUG_ASSERT(part_expr); err= part_expr->walk(&Item::check_partition_func_processor, 0, NULL); - if (!err && is_sub_partitioned() && !list_of_subpart_fields) - err= subpart_expr->walk(&Item::check_partition_func_processor, 0, - NULL); } + + /* Check for sub partition expression. */ + if (!err && is_sub_partitioned() && !list_of_subpart_fields) + { + DBUG_ASSERT(subpart_expr); + err= subpart_expr->walk(&Item::check_partition_func_processor, 0, + NULL); + } + if (err) { my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 9cc9efae6f8..7d0fefeabd4 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6720,16 +6720,18 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, for (tl= tables; number-- ; tl= tl->next_global) { - sctx= MY_TEST(tl->security_ctx) ? tl->security_ctx : thd->security_ctx; + TABLE_LIST *const t_ref= + tl->correspondent_table ? tl->correspondent_table : tl; + sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx; const ACL_internal_table_access *access= - get_cached_table_access(&tl->grant.m_internal, - tl->get_db_name(), - tl->get_table_name()); + get_cached_table_access(&t_ref->grant.m_internal, + t_ref->get_db_name(), + t_ref->get_table_name()); if (access) { - switch(access->check(orig_want_access, &tl->grant.privilege)) + switch(access->check(orig_want_access, &t_ref->grant.privilege)) { case ACL_INTERNAL_ACCESS_GRANTED: /* @@ -6753,26 +6755,26 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, if (!want_access) continue; // ok - if (!(~tl->grant.privilege & want_access) || - tl->is_anonymous_derived_table() || tl->schema_table) + if (!(~t_ref->grant.privilege & want_access) || + t_ref->is_anonymous_derived_table() || t_ref->schema_table) { /* - It is subquery in the FROM clause. VIEW set tl->derived after + It is subquery in the FROM clause. VIEW set t_ref->derived after table opening, but this function always called before table opening. */ - if (!tl->referencing_view) + if (!t_ref->referencing_view) { /* If it's a temporary table created for a subquery in the FROM clause, or an INFORMATION_SCHEMA table, drop the request for a privilege. */ - tl->grant.want_privilege= 0; + t_ref->grant.want_privilege= 0; } continue; } - if (is_temporary_table(tl)) + if (is_temporary_table(t_ref)) { /* If this table list element corresponds to a pre-opened temporary @@ -6780,8 +6782,8 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, Note that during creation of temporary table we still need to check if user has CREATE_TMP_ACL. */ - tl->grant.privilege|= TMP_TABLE_ACLS; - tl->grant.want_privilege= 0; + t_ref->grant.privilege|= TMP_TABLE_ACLS; + t_ref->grant.want_privilege= 0; continue; } @@ -6792,20 +6794,20 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, } grant_table= table_hash_search(sctx->host, sctx->ip, - tl->get_db_name(), + t_ref->get_db_name(), sctx->priv_user, - tl->get_table_name(), + t_ref->get_table_name(), FALSE); if (sctx->priv_role[0]) - grant_table_role= table_hash_search("", NULL, tl->get_db_name(), + grant_table_role= table_hash_search("", NULL, t_ref->get_db_name(), sctx->priv_role, - tl->get_table_name(), + t_ref->get_table_name(), TRUE); if (!grant_table && !grant_table_role) { - want_access&= ~tl->grant.privilege; - goto err; + want_access&= ~t_ref->grant.privilege; + goto err; // No grants } /* @@ -6815,19 +6817,19 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, if (any_combination_will_do) continue; - tl->grant.grant_table_user= grant_table; // Remember for column test - tl->grant.grant_table_role= grant_table_role; - tl->grant.version= grant_version; - tl->grant.privilege|= grant_table ? grant_table->privs : 0; - tl->grant.privilege|= grant_table_role ? grant_table_role->privs : 0; - tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege); + t_ref->grant.grant_table_user= grant_table; // Remember for column test + t_ref->grant.grant_table_role= grant_table_role; + t_ref->grant.version= grant_version; + t_ref->grant.privilege|= grant_table ? grant_table->privs : 0; + t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : 0; + t_ref->grant.want_privilege= ((want_access & COL_ACLS) & ~t_ref->grant.privilege); - if (!(~tl->grant.privilege & want_access)) + if (!(~t_ref->grant.privilege & want_access)) continue; if ((want_access&= ~((grant_table ? grant_table->cols : 0) | (grant_table_role ? grant_table_role->cols : 0) | - tl->grant.privilege))) + t_ref->grant.privilege))) { goto err; // impossible } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 7e2fe7b1b63..8f222761c60 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1094,13 +1094,20 @@ bool close_temporary_tables(THD *thd) DBUG_RETURN(FALSE); DBUG_ASSERT(!thd->rgi_slave); + /* + Ensure we don't have open HANDLERs for tables we are about to close. + This is necessary when close_temporary_tables() is called as part + of execution of BINLOG statement (e.g. for format description event). + */ + mysql_ha_rm_temporary_tables(thd); if (!mysql_bin_log.is_open()) { TABLE *tmp_next; - for (table= thd->temporary_tables; table; table= tmp_next) + for (TABLE *t= thd->temporary_tables; t; t= tmp_next) { - tmp_next= table->next; - close_temporary(table, 1, 1); + tmp_next= t->next; + mysql_lock_remove(thd, thd->lock, t); + close_temporary(t, 1, 1); } thd->temporary_tables= 0; DBUG_RETURN(FALSE); @@ -1196,6 +1203,7 @@ bool close_temporary_tables(THD *thd) 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(); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 110bca96530..c2d4c32ab71 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2001, 2013, Oracle and/or its affiliates. - Copyright (c) 2011, 2013, Monty Program Ab. +/* Copyright (c) 2001, 2015, Oracle and/or its affiliates. + Copyright (c) 2011, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1190,3 +1190,36 @@ void mysql_ha_set_explicit_lock_duration(THD *thd) DBUG_VOID_RETURN; } + +/** + Remove temporary tables from the HANDLER's hash table. The reason + for having a separate function, rather than calling + mysql_ha_rm_tables() is that it is not always feasible (e.g. in + close_temporary_tables) to obtain a TABLE_LIST containing the + temporary tables. + + @See close_temporary_tables + @param thd Thread identifier. +*/ +void mysql_ha_rm_temporary_tables(THD *thd) +{ + DBUG_ENTER("mysql_ha_rm_temporary_tables"); + + TABLE_LIST *tmp_handler_tables= NULL; + for (uint i= 0; i < thd->handler_tables_hash.records; i++) + { + TABLE_LIST *handler_table= reinterpret_cast<TABLE_LIST*> + (my_hash_element(&thd->handler_tables_hash, i)); + + if (handler_table->table && handler_table->table->s->tmp_table) + { + handler_table->next_local= tmp_handler_tables; + tmp_handler_tables= handler_table; + } + } + + if (tmp_handler_tables) + mysql_ha_rm_tables(thd, tmp_handler_tables); + + DBUG_VOID_RETURN; +} diff --git a/sql/sql_handler.h b/sql/sql_handler.h index 133f553675e..7fe5ae5bba8 100644 --- a/sql/sql_handler.h +++ b/sql/sql_handler.h @@ -1,6 +1,8 @@ #ifndef SQL_HANDLER_INCLUDED #define SQL_HANDLER_INCLUDED -/* Copyright (C) 2010 Monty Program Ab +/* Copyright (c) 2006, 2015, Oracle and/or its affiliates. + Copyright (C) 2010, 2015, MariaDB + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. @@ -73,6 +75,7 @@ void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables); void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables); void mysql_ha_cleanup(THD *thd); void mysql_ha_set_explicit_lock_duration(THD *thd); +void mysql_ha_rm_temporary_tables(THD *thd); SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables, enum enum_ha_read_modes mode, char *keyname, diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 1ec33a0a0ac..522f55cd102 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2010, 2014, SkySQL Ab. + Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -3852,7 +3852,6 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, /* Add selected items to field list */ List_iterator_fast<Item> it(*items); Item *item; - Field *tmp_field; DBUG_ENTER("create_table_from_items"); tmp_table.alias= 0; @@ -3867,24 +3866,49 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, while ((item=it++)) { - Create_field *cr_field; - Field *field, *def_field; + Field *tmp_table_field; if (item->type() == Item::FUNC_ITEM) { if (item->result_type() != STRING_RESULT) - field= item->tmp_table_field(&tmp_table); + tmp_table_field= item->tmp_table_field(&tmp_table); else - field= item->tmp_table_field_from_field_type(&tmp_table, 0); + tmp_table_field= item->tmp_table_field_from_field_type(&tmp_table, + false); } else - field= create_tmp_field(thd, &tmp_table, item, item->type(), - (Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0, - 0); - if (!field || - !(cr_field=new Create_field(field,(item->type() == Item::FIELD_ITEM ? - ((Item_field *)item)->field : - (Field*) 0)))) - DBUG_RETURN(0); + { + Field *from_field, * default_field; + tmp_table_field= create_tmp_field(thd, &tmp_table, item, item->type(), + (Item ***) NULL, &from_field, &default_field, + 0, 0, 0, 0, 0); + } + + if (!tmp_table_field) + DBUG_RETURN(NULL); + + Field *table_field; + + switch (item->type()) + { + /* + We have to take into account both the real table's fields and + pseudo-fields used in trigger's body. These fields are used + to copy defaults values later inside constructor of + the class Create_field. + */ + case Item::FIELD_ITEM: + case Item::TRIGGER_FIELD_ITEM: + table_field= ((Item_field *) item)->field; + break; + default: + table_field= NULL; + } + + Create_field *cr_field= new Create_field(tmp_table_field, table_field); + + if (!cr_field) + DBUG_RETURN(NULL); + if (item->maybe_null) cr_field->flags &= ~NOT_NULL_FLAG; alter_info->create_list.push_back(cr_field); @@ -3974,7 +3998,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, { if (!thd->is_error()) // CREATE ... IF NOT EXISTS my_ok(thd); // succeed, but did nothing - DBUG_RETURN(0); + DBUG_RETURN(NULL); } DEBUG_SYNC(thd,"create_table_select_before_lock"); diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 503cc579dd7..08687b20b00 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2014, Oracle and/or its affiliates. - Copyright (c) 2010, 2014, SkySQL Ab. + Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2028,8 +2028,15 @@ int READ_INFO::read_xml() break; case '/': /* close tag */ - level--; chr= my_tospace(GET); + /* Decrease the 'level' only when (i) It's not an */ + /* (without space) empty tag i.e. <tag/> or, (ii) */ + /* It is of format <row col="val" .../> */ + if(chr != '>' || in_tag) + { + level--; + in_tag= false; + } if(chr != '>') /* if this is an empty tag <tag /> */ tag.length(0); /* we should keep tag value */ while(chr != '>' && chr != my_b_EOF) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b2636cfff95..a665f0314e8 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2008, 2014, SkySQL Ab. +/* Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2008, 2015, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -5825,9 +5825,12 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, for (; i < number && tables != first_not_own_table && tables; tables= tables->next_global, i++) { + TABLE_LIST *const table_ref= tables->correspondent_table ? + tables->correspondent_table : tables; + ulong want_access= requirements; - if (tables->security_ctx) - sctx= tables->security_ctx; + if (table_ref->security_ctx) + sctx= table_ref->security_ctx; else sctx= backup_ctx; @@ -5835,26 +5838,26 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, Register access for view underlying table. Remove SHOW_VIEW_ACL, because it will be checked during making view */ - tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); + table_ref->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); - if (tables->schema_table_reformed) + if (table_ref->schema_table_reformed) { - if (check_show_access(thd, tables)) + if (check_show_access(thd, table_ref)) goto deny; continue; } - DBUG_PRINT("info", ("derived: %d view: %d", tables->derived != 0, - tables->view != 0)); + DBUG_PRINT("info", ("derived: %d view: %d", table_ref->derived != 0, + table_ref->view != 0)); - if (tables->is_anonymous_derived_table()) + if (table_ref->is_anonymous_derived_table()) continue; thd->security_ctx= sctx; - if (check_access(thd, want_access, tables->get_db_name(), - &tables->grant.privilege, - &tables->grant.m_internal, + if (check_access(thd, want_access, table_ref->get_db_name(), + &table_ref->grant.privilege, + &table_ref->grant.m_internal, 0, no_errors)) goto deny; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 22c14733ffa..edfd9f5ebd3 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -15600,6 +15600,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, case Item::FIELD_ITEM: case Item::DEFAULT_VALUE_ITEM: case Item::INSERT_VALUE_ITEM: + case Item::TRIGGER_FIELD_ITEM: { Item_field *field= (Item_field*) item; bool orig_modify= modify_item; @@ -18051,6 +18052,10 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, if (return_tab < join->return_tab) join->return_tab= return_tab; + /* check for errors evaluating the condition */ + if (join->thd->is_error()) + DBUG_RETURN(NESTED_LOOP_ERROR); + if (join->return_tab < join_tab) DBUG_RETURN(NESTED_LOOP_OK); /* diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 103d39e7fd0..853c2888a6a 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -8104,13 +8104,14 @@ bool get_schema_tables_result(JOIN *join, TABLE_LIST *table_list= tab->table->pos_in_table_list; if (table_list->schema_table && thd->fill_information_schema_tables()) { -#if MYSQL_VERSION_ID > 100105 -#error I_S tables only need to be re-populated if make_cond_for_info_schema() will preserve outer fields - bool is_subselect= (&lex->unit != lex->current_select->master_unit() && - lex->current_select->master_unit()->item); -#else -#define is_subselect false -#endif + /* + I_S tables only need to be re-populated if make_cond_for_info_schema() + preserves outer fields + */ + bool is_subselect= &lex->unit != lex->current_select->master_unit() && + lex->current_select->master_unit()->item && + tab->select_cond && + tab->select_cond->used_tables() & OUTER_REF_TABLE_BIT; /* A value of 0 indicates a dummy implementation */ if (table_list->schema_table->fill_table == 0) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index d980de7e1a5..bb8215aaaec 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11507,8 +11507,20 @@ procedure_clause: if (add_proc_to_list(lex->thd, item)) MYSQL_YYABORT; Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + + /* + PROCEDURE CLAUSE cannot handle subquery as one of its parameter, + so set expr_allows_subselect as false to disallow any subqueries + further. Reset expr_allows_subselect back to true once the + parameters are reduced. + */ + Lex->expr_allows_subselect= false; } '(' procedure_list ')' + { + /* Subqueries are allowed from now.*/ + Lex->expr_allows_subselect= true; + } ; procedure_list: |