diff options
author | unknown <ndbdev@dl145b.mysql.com> | 2005-09-09 15:26:47 +0200 |
---|---|---|
committer | unknown <ndbdev@dl145b.mysql.com> | 2005-09-09 15:26:47 +0200 |
commit | 36aeee3682fa975df244f44fb34e1c05f3263551 (patch) | |
tree | 8b8e4544543c49e86d358196df4f951aa55502dd /sql | |
parent | de82d9eec0fd1d55c225a2420572b6e5e59ff8f7 (diff) | |
parent | 057855a34a2efbbca11ab2428c44790a52c401d9 (diff) | |
download | mariadb-git-36aeee3682fa975df244f44fb34e1c05f3263551.tar.gz |
Merge tulin@bk-internal.mysql.com:/home/bk/mysql-5.0
into dl145b.mysql.com:/home/ndbdev/tomas/mysql-5.1
configure.in:
Auto merged
include/my_global.h:
Auto merged
sql/handler.cc:
Auto merged
sql/item.cc:
Auto merged
sql/log.cc:
Auto merged
sql/opt_range.cc:
Auto merged
sql/set_var.cc:
Auto merged
sql/set_var.h:
Auto merged
sql/sql_base.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_parse.cc:
Auto merged
sql/examples/ha_tina.cc:
Auto merged
sql/sql_select.cc:
Auto merged
sql/sql_select.h:
Auto merged
sql/sql_show.cc:
Auto merged
sql/sql_yacc.yy:
Auto merged
storage/ndb/include/kernel/signaldata/BackupImpl.hpp:
Auto merged
storage/ndb/include/kernel/signaldata/BackupSignalData.hpp:
Auto merged
storage/ndb/include/kernel/signaldata/NFCompleteRep.hpp:
Auto merged
storage/ndb/include/kernel/signaldata/NodeFailRep.hpp:
Auto merged
storage/ndb/src/kernel/blocks/backup/Backup.cpp:
Auto merged
storage/ndb/src/kernel/blocks/backup/Backup.hpp:
Auto merged
storage/ndb/src/kernel/main.cpp:
Auto merged
storage/ndb/src/mgmsrv/MgmtSrvr.cpp:
Auto merged
storage/ndb/src/mgmsrv/MgmtSrvr.hpp:
Auto merged
storage/ndb/src/ndbapi/Makefile.am:
Auto merged
storage/ndb/src/ndbapi/NdbTransaction.cpp:
Auto merged
storage/ndb/test/src/NdbBackup.cpp:
Auto merged
Diffstat (limited to 'sql')
-rw-r--r-- | sql/examples/ha_tina.cc | 3 | ||||
-rw-r--r-- | sql/handler.cc | 18 | ||||
-rw-r--r-- | sql/item.cc | 35 | ||||
-rw-r--r-- | sql/item.h | 12 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 198 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 59 | ||||
-rw-r--r-- | sql/item_func.cc | 15 | ||||
-rw-r--r-- | sql/item_sum.cc | 6 | ||||
-rw-r--r-- | sql/item_sum.h | 5 | ||||
-rw-r--r-- | sql/log.cc | 7 | ||||
-rw-r--r-- | sql/opt_range.cc | 15 | ||||
-rw-r--r-- | sql/set_var.cc | 29 | ||||
-rw-r--r-- | sql/set_var.h | 3 | ||||
-rw-r--r-- | sql/sp_head.cc | 76 | ||||
-rw-r--r-- | sql/sp_head.h | 5 | ||||
-rw-r--r-- | sql/sql_base.cc | 45 | ||||
-rw-r--r-- | sql/sql_class.cc | 9 | ||||
-rw-r--r-- | sql/sql_class.h | 12 | ||||
-rw-r--r-- | sql/sql_parse.cc | 9 | ||||
-rw-r--r-- | sql/sql_select.cc | 30 | ||||
-rw-r--r-- | sql/sql_select.h | 4 | ||||
-rw-r--r-- | sql/sql_show.cc | 21 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 25 |
23 files changed, 488 insertions, 153 deletions
diff --git a/sql/examples/ha_tina.cc b/sql/examples/ha_tina.cc index a336a4379bd..3a9302483b4 100644 --- a/sql/examples/ha_tina.cc +++ b/sql/examples/ha_tina.cc @@ -652,7 +652,8 @@ int ha_tina::rnd_init(bool scan) records= 0; chain_ptr= chain; #ifdef HAVE_MADVISE - (void)madvise(share->mapped_file,share->file_stat.st_size,MADV_SEQUENTIAL); + if (scan) + (void)madvise(share->mapped_file,share->file_stat.st_size,MADV_SEQUENTIAL); #endif DBUG_RETURN(0); diff --git a/sql/handler.cc b/sql/handler.cc index 451615bead1..3e85e73cab5 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -632,12 +632,20 @@ int ha_prepare(THD *thd) { int err; statistic_increment(thd->status_var.ha_prepare_count,&LOCK_status); - if ((err= (*(*ht)->prepare)(thd, all))) + if ((*ht)->prepare) { - my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); - ha_rollback_trans(thd, all); - error=1; - break; + if ((err= (*(*ht)->prepare)(thd, all))) + { + my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); + ha_rollback_trans(thd, all); + error=1; + break; + } + } + else + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), (*ht)->name); } } } diff --git a/sql/item.cc b/sql/item.cc index 66552fa7b04..1cb6734d373 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -818,8 +818,25 @@ String *Item_splocal::val_str(String *sp) DBUG_ASSERT(fixed); Item *it= this_item(); String *ret= it->val_str(sp); + /* + This way we mark returned value of val_str as const, + so that various functions (e.g. CONCAT) won't try to + modify the value of the Item. Analogous mechanism is + implemented for Item_param. + Without this trick Item_splocal could be changed as a + side-effect of expression computation. Here is an example + of what happens without it: suppose x is varchar local + variable in a SP with initial value 'ab' Then + select concat(x,'c'); + would change x's value to 'abc', as Item_func_concat::val_str() + would use x's internal buffer to compute the result. + This is intended behaviour of Item_func_concat. Comments to + Item_param class contain some more details on the topic. + */ + str_value_ptr.set(ret->ptr(), ret->length(), + ret->charset()); null_value= it->null_value; - return ret; + return &str_value_ptr; } @@ -1022,9 +1039,9 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, /* Will split complicated items and ignore simple ones */ split_sum_func(thd, ref_pointer_array, fields); } - else if ((type() == SUM_FUNC_ITEM || - (used_tables() & ~PARAM_TABLE_BIT)) && - type() != REF_ITEM) + else if ((type() == SUM_FUNC_ITEM || (used_tables() & ~PARAM_TABLE_BIT)) && + (type() != REF_ITEM || + ((Item_ref*)this)->ref_type() == Item_ref::VIEW_REF)) { /* Replace item with a reference so that we can easily calculate @@ -1033,15 +1050,17 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, The test above is to ensure we don't do a reference for things that are constants (PARAM_TABLE_BIT is in effect a constant) or already referenced (for example an item in HAVING) + Exception is Item_direct_view_ref which we need to convert to + Item_ref to allow fields from view being stored in tmp table. */ uint el= fields.elements; - Item *new_item; - ref_pointer_array[el]= this; + Item *new_item, *real_itm= real_item(); + + ref_pointer_array[el]= real_itm; if (!(new_item= new Item_ref(&thd->lex->current_select->context, ref_pointer_array + el, 0, name))) return; // fatal_error is set - fields.push_front(this); - ref_pointer_array[el]= this; + fields.push_front(real_itm); thd->change_item_tree(ref, new_item); } } diff --git a/sql/item.h b/sql/item.h index b934e1f9f3f..381ba98e193 100644 --- a/sql/item.h +++ b/sql/item.h @@ -715,9 +715,17 @@ public: class Item_splocal : public Item { uint m_offset; + public: LEX_STRING m_name; + /* + Buffer, pointing to the string value of the item. We need it to + protect internal buffer from changes. See comment to analogous + member in Item_param for more details. + */ + String str_value_ptr; + /* Position of this reference to SP variable in the statement (the statement itself is in sp_instr_stmt::m_query). @@ -1537,6 +1545,7 @@ class Item_ref :public Item_ident protected: void set_properties(); public: + enum Ref_Type { REF, DIRECT_REF, VIEW_REF }; Field *result_field; /* Save result here */ Item **ref; Item_ref(Name_resolution_context *context_arg, @@ -1617,6 +1626,7 @@ public: void cleanup(); Item_field *filed_for_view_update() { return (*ref)->filed_for_view_update(); } + virtual Ref_Type ref_type() { return REF; } }; @@ -1641,6 +1651,7 @@ public: bool val_bool(); bool is_null(); bool get_date(TIME *ltime,uint fuzzydate); + virtual Ref_Type ref_type() { return DIRECT_REF; } }; /* @@ -1660,6 +1671,7 @@ public: bool fix_fields(THD *, Item **); bool eq(const Item *item, bool binary_cmp) const; + virtual Ref_Type ref_type() { return VIEW_REF; } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index cc2849ff7e6..5079c462ac0 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -988,6 +988,53 @@ longlong Item_func_interval::val_int() } +/* + Perform context analysis of a BETWEEN item tree + + SYNOPSIS: + fix_fields() + thd reference to the global context of the query thread + tables list of all open tables involved in the query + ref pointer to Item* variable where pointer to resulting "fixed" + item is to be assigned + + DESCRIPTION + This function performs context analysis (name resolution) and calculates + various attributes of the item tree with Item_func_between as its root. + The function saves in ref the pointer to the item or to a newly created + item that is considered as a replacement for the original one. + + NOTES + Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on + a predicate/function level. Then it's easy to show that: + T0(e BETWEEN e1 AND e2) = union(T1(e),T1(e1),T1(e2)) + T1(e BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2))) + T0(e NOT BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2))) + T1(e NOT BETWEEN e1 AND e2) = union(T1(e),intersection(T1(e1),T1(e2))) + + RETURN + 0 ok + 1 got error +*/ + +bool +Item_func_between::fix_fields(THD *thd, Item **ref) +{ + if (Item_func_opt_neg::fix_fields(thd, ref)) + return 1; + + /* not_null_tables_cache == union(T1(e),T1(e1),T1(e2)) */ + if (pred_level && !negated) + return 0; + + /* not_null_tables_cache == union(T1(e), intersection(T1(e1),T1(e2))) */ + not_null_tables_cache= args[0]->not_null_tables() | + (args[1]->not_null_tables() & args[2]->not_null_tables()); + + return 0; +} + + void Item_func_between::fix_length_and_dec() { max_length= 1; @@ -1040,8 +1087,9 @@ longlong Item_func_between::val_int() a=args[1]->val_str(&value1); b=args[2]->val_str(&value2); if (!args[1]->null_value && !args[2]->null_value) - return (sortcmp(value,a,cmp_collation.collation) >= 0 && - sortcmp(value,b,cmp_collation.collation) <= 0) ? 1 : 0; + return (longlong) ((sortcmp(value,a,cmp_collation.collation) >= 0 && + sortcmp(value,b,cmp_collation.collation) <= 0) != + negated); if (args[1]->null_value && args[2]->null_value) null_value=1; else if (args[1]->null_value) @@ -1063,7 +1111,7 @@ longlong Item_func_between::val_int() a=args[1]->val_int(); b=args[2]->val_int(); if (!args[1]->null_value && !args[2]->null_value) - return (value >= a && value <= b) ? 1 : 0; + return (longlong) ((value >= a && value <= b) != negated); if (args[1]->null_value && args[2]->null_value) null_value=1; else if (args[1]->null_value) @@ -1084,8 +1132,8 @@ longlong Item_func_between::val_int() a_dec= args[1]->val_decimal(&a_buf); b_dec= args[2]->val_decimal(&b_buf); if (!args[1]->null_value && !args[2]->null_value) - return (my_decimal_cmp(dec, a_dec)>=0) && (my_decimal_cmp(dec, b_dec)<=0); - + return (longlong) ((my_decimal_cmp(dec, a_dec) >= 0 && + my_decimal_cmp(dec, b_dec) <= 0) != negated); if (args[1]->null_value && args[2]->null_value) null_value=1; else if (args[1]->null_value) @@ -1101,7 +1149,7 @@ longlong Item_func_between::val_int() a= args[1]->val_real(); b= args[2]->val_real(); if (!args[1]->null_value && !args[2]->null_value) - return (value >= a && value <= b) ? 1 : 0; + return (longlong) ((value >= a && value <= b) != negated); if (args[1]->null_value && args[2]->null_value) null_value=1; else if (args[1]->null_value) @@ -1113,7 +1161,7 @@ longlong Item_func_between::val_int() null_value= value >= a; } } - return 0; + return (longlong) (!null_value && negated); } @@ -1244,6 +1292,49 @@ Item_func_ifnull::str_op(String *str) } +/* + Perform context analysis of an IF item tree + + SYNOPSIS: + fix_fields() + thd reference to the global context of the query thread + tables list of all open tables involved in the query + ref pointer to Item* variable where pointer to resulting "fixed" + item is to be assigned + + DESCRIPTION + This function performs context analysis (name resolution) and calculates + various attributes of the item tree with Item_func_if as its root. + The function saves in ref the pointer to the item or to a newly created + item that is considered as a replacement for the original one. + + NOTES + Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on + a predicate/function level. Then it's easy to show that: + T0(IF(e,e1,e2) = T1(IF(e,e1,e2)) + T1(IF(e,e1,e2)) = intersection(T1(e1),T1(e2)) + + RETURN + 0 ok + 1 got error +*/ + +bool +Item_func_if::fix_fields(THD *thd, Item **ref) +{ + DBUG_ASSERT(fixed == 0); + args[0]->top_level_item(); + + if (Item_func::fix_fields(thd, ref)) + return 1; + + not_null_tables_cache= (args[1]->not_null_tables() + & args[2]->not_null_tables()); + + return 0; +} + + void Item_func_if::fix_length_and_dec() { @@ -2184,6 +2275,56 @@ bool Item_func_in::nulls_in_row() } +/* + Perform context analysis of an IN item tree + + SYNOPSIS: + fix_fields() + thd reference to the global context of the query thread + tables list of all open tables involved in the query + ref pointer to Item* variable where pointer to resulting "fixed" + item is to be assigned + + DESCRIPTION + This function performs context analysis (name resolution) and calculates + various attributes of the item tree with Item_func_in as its root. + The function saves in ref the pointer to the item or to a newly created + item that is considered as a replacement for the original one. + + NOTES + Let T0(e)/T1(e) be the value of not_null_tables(e) when e is used on + a predicate/function level. Then it's easy to show that: + T0(e IN(e1,...,en)) = union(T1(e),intersection(T1(ei))) + T1(e IN(e1,...,en)) = union(T1(e),intersection(T1(ei))) + T0(e NOT IN(e1,...,en)) = union(T1(e),union(T1(ei))) + T1(e NOT IN(e1,...,en)) = union(T1(e),intersection(T1(ei))) + + RETURN + 0 ok + 1 got error +*/ + +bool +Item_func_in::fix_fields(THD *thd, Item **ref) +{ + Item **arg, **arg_end; + + if (Item_func_opt_neg::fix_fields(thd, ref)) + return 1; + + /* not_null_tables_cache == union(T1(e),union(T1(ei))) */ + if (pred_level && negated) + return 0; + + /* not_null_tables_cache = union(T1(e),intersection(T1(ei))) */ + not_null_tables_cache= ~(table_map) 0; + for (arg= args + 1, arg_end= args + arg_count; arg != arg_end; arg++) + not_null_tables_cache&= (*arg)->not_null_tables(); + not_null_tables_cache|= (*args)->not_null_tables(); + return 0; +} + + static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y) { return cs->coll->strnncollsp(cs, @@ -2283,7 +2424,7 @@ longlong Item_func_in::val_int() { int tmp=array->find(args[0]); null_value=args[0]->null_value || (!tmp && have_null); - return tmp; + return (longlong) (!null_value && tmp != negated); } in_item->store_value(args[0]); if ((null_value=args[0]->null_value)) @@ -2292,11 +2433,11 @@ longlong Item_func_in::val_int() for (uint i=1 ; i < arg_count ; i++) { if (!in_item->cmp(args[i]) && !args[i]->null_value) - return 1; // Would maybe be nice with i ? + return (longlong) (!negated); have_null|= args[i]->null_value; } null_value= have_null; - return 0; + return (longlong) (!null_value && negated); } @@ -2811,7 +2952,42 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref) { /* If we are on execution stage */ String *escape_str= escape_item->val_str(&tmp_value1); - escape= escape_str ? *(escape_str->ptr()) : '\\'; + if (escape_str) + { + CHARSET_INFO *cs= cmp.cmp_collation.collation; + if (use_mb(cs)) + { + my_wc_t wc; + int rc= cs->cset->mb_wc(cs, &wc, + (const uchar*) escape_str->ptr(), + (const uchar*) escape_str->ptr() + + escape_str->length()); + escape= (int) (rc > 0 ? wc : '\\'); + } + else + { + /* + In the case of 8bit character set, we pass native + code instead of Unicode code as "escape" argument. + Convert to "cs" if charset of escape differs. + */ + uint32 unused; + if (escape_str->needs_conversion(escape_str->length(), + escape_str->charset(), cs, &unused)) + { + char ch; + uint errors; + uint32 cnvlen= copy_and_convert(&ch, 1, cs, escape_str->ptr(), + escape_str->length(), + escape_str->charset(), &errors); + escape= cnvlen ? ch : '\\'; + } + else + escape= *(escape_str->ptr()); + } + } + else + escape= '\\'; /* We could also do boyer-more for non-const items, but as we would have to diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 1915dbaabf6..09a0fa8c357 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -401,17 +401,49 @@ public: }; -class Item_func_between :public Item_int_func +/* + The class Item_func_opt_neg is defined to factor out the functionality + common for the classes Item_func_between and Item_func_in. The objects + of these classes can express predicates or there negations. + The alternative approach would be to create pairs Item_func_between, + Item_func_notbetween and Item_func_in, Item_func_notin. + +*/ + +class Item_func_opt_neg :public Item_int_func +{ +public: + bool negated; /* <=> the item represents NOT <func> */ + bool pred_level; /* <=> [NOT] <func> is used on a predicate level */ +public: + Item_func_opt_neg(Item *a, Item *b, Item *c) + :Item_int_func(a, b, c), negated(0), pred_level(0) {} + Item_func_opt_neg(List<Item> &list) + :Item_int_func(list), negated(0), pred_level(0) {} +public: + inline void negate() { negated= !negated; } + inline void top_level_item() { pred_level= 1; } + Item *neg_transformer(THD *thd) + { + negated= !negated; + return this; + } +}; + + +class Item_func_between :public Item_func_opt_neg { DTCollation cmp_collation; public: Item_result cmp_type; String value0,value1,value2; - Item_func_between(Item *a,Item *b,Item *c) :Item_int_func(a,b,c) {} + Item_func_between(Item *a, Item *b, Item *c) + :Item_func_opt_neg(a, b, c) {} longlong val_int(); optimize_type select_optimize() const { return OPTIMIZE_KEY; } enum Functype functype() const { return BETWEEN; } const char *func_name() const { return "between"; } + bool fix_fields(THD *, Item **); void fix_length_and_dec(); void print(String *str); bool is_bool_func() { return 1; } @@ -505,16 +537,10 @@ public: String *val_str(String *str); my_decimal *val_decimal(my_decimal *); enum Item_result result_type () const { return cached_result_type; } - bool fix_fields(THD *thd, Item **ref) - { - DBUG_ASSERT(fixed == 0); - args[0]->top_level_item(); - return Item_func::fix_fields(thd, ref); - } + bool fix_fields(THD *, Item **); void fix_length_and_dec(); uint decimal_precision() const; const char *func_name() const { return "if"; } - table_map not_null_tables() const { return 0; } }; @@ -819,7 +845,7 @@ public: } }; -class Item_func_in :public Item_int_func +class Item_func_in :public Item_func_opt_neg { Item_result cmp_type; in_vector *array; @@ -828,11 +854,12 @@ class Item_func_in :public Item_int_func DTCollation cmp_collation; public: Item_func_in(List<Item> &list) - :Item_int_func(list), array(0), in_item(0), have_null(0) + :Item_func_opt_neg(list), array(0), in_item(0), have_null(0) { allowed_arg_cols= 0; // Fetch this value from first argument } longlong val_int(); + bool fix_fields(THD *, Item **); void fix_length_and_dec(); uint decimal_precision() const { return 1; } void cleanup() @@ -853,12 +880,6 @@ class Item_func_in :public Item_int_func bool nulls_in_row(); bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } - /* - IN() protect from NULL only first argument, if construction like - "expression IN ()" will be allowed, we will need to check number of - argument here, because "NOT(NULL IN ())" is TRUE. - */ - table_map not_null_tables() const { return args[0]->not_null_tables(); } }; /* Functions used by where clause */ @@ -889,7 +910,7 @@ public: else { args[0]->update_used_tables(); - if (!(used_tables_cache=args[0]->used_tables())) + if ((const_item_cache= !(used_tables_cache= args[0]->used_tables()))) { /* Remember if the value is always NULL or never NULL */ cached_value= (longlong) args[0]->is_null(); @@ -966,7 +987,7 @@ class Item_func_like :public Item_bool_func2 Item *escape_item; public: - char escape; + int escape; Item_func_like(Item *a,Item *b, Item *escape_arg) :Item_bool_func2(a,b), canDoTurboBM(FALSE), pattern(0), pattern_len(0), diff --git a/sql/item_func.cc b/sql/item_func.cc index 7400a569342..b47d7d19fbd 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3884,7 +3884,8 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command, if (!(var_entry= get_variable(&thd->user_vars, name, 0))) goto err; } - else if (var_entry->used_query_id == thd->query_id) + else if (var_entry->used_query_id == thd->query_id || + mysql_bin_log.is_query_in_union(thd, var_entry->used_query_id)) { /* If this variable was already stored in user_var_events by this query @@ -3901,10 +3902,16 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command, appears: > set @a:=1; > insert into t1 values (@a), (@a:=@a+1), (@a:=@a+1); - We have to write to binlog value @a= 1; + We have to write to binlog value @a= 1. + + We allocate the user_var_event on user_var_events_alloc pool, not on + the this-statement-execution pool because in SPs user_var_event objects + may need to be valid after current [SP] statement execution pool is + destroyed. */ - size= ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT)) + var_entry->length; - if (!(user_var_event= (BINLOG_USER_VAR_EVENT *) thd->alloc(size))) + size= ALIGN_SIZE(sizeof(BINLOG_USER_VAR_EVENT)) + var_entry->length; + if (!(user_var_event= (BINLOG_USER_VAR_EVENT *) + alloc_root(thd->user_var_events_alloc, size))) goto err; user_var_event->value= (char*) user_var_event + diff --git a/sql/item_sum.cc b/sql/item_sum.cc index f6544d76504..4f991615bfa 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -2740,7 +2740,9 @@ int dump_leaf_key(byte* key, element_count count __attribute__((unused)), String *result= &item->result; Item **arg= item->args, **arg_end= item->args + item->arg_count_field; - if (result->length()) + if (item->no_appended) + item->no_appended= FALSE; + else result->append(*item->separator); tmp.length(0); @@ -2925,6 +2927,7 @@ void Item_func_group_concat::clear() result.copy(); null_value= TRUE; warning_for_row= FALSE; + no_appended= TRUE; if (tree) reset_tree(tree); /* No need to reset the table as we never call write_row */ @@ -3001,6 +3004,7 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref) args, arg_count, MY_COLL_ALLOW_CONV)) return 1; + result.set_charset(collation.collation); result_field= 0; null_value= 1; thd->allow_sum_func= 1; diff --git a/sql/item_sum.h b/sql/item_sum.h index 0da9178eabf..87cc248e5e4 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -881,6 +881,7 @@ class Item_func_group_concat : public Item_sum bool distinct; bool warning_for_row; bool always_null; + bool no_appended; /* Following is 0 normal object and pointer to original one for copy (to correctly free resources) @@ -912,8 +913,8 @@ public: virtual Item_result result_type () const { return STRING_RESULT; } void clear(); bool add(); - void reset_field() {} // not used - void update_field() {} // not used + void reset_field() { DBUG_ASSERT(0); } // not used + void update_field() { DBUG_ASSERT(0); } // not used bool fix_fields(THD *,Item **); bool setup(THD *thd); void make_unique(); diff --git a/sql/log.cc b/sql/log.cc index d3bb44b3083..9d9f500fe80 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1560,6 +1560,7 @@ void MYSQL_LOG::start_union_events(THD *thd) thd->binlog_evt_union.do_union= TRUE; thd->binlog_evt_union.unioned_events= FALSE; thd->binlog_evt_union.unioned_events_trans= FALSE; + thd->binlog_evt_union.first_query_id= thd->query_id; } void MYSQL_LOG::stop_union_events(THD *thd) @@ -1568,6 +1569,12 @@ void MYSQL_LOG::stop_union_events(THD *thd) thd->binlog_evt_union.do_union= FALSE; } +bool MYSQL_LOG::is_query_in_union(THD *thd, query_id_t query_id_param) +{ + return (thd->binlog_evt_union.do_union && + query_id_param >= thd->binlog_evt_union.first_query_id); +} + /* Write an event to the binary log */ diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 31b55219636..b69822d201b 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -3529,18 +3529,9 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) } Item_func *cond_func= (Item_func*) cond; - if (cond_func->functype() == Item_func::NOT_FUNC) - { - /* Optimize NOT BETWEEN and NOT IN */ - Item *arg= cond_func->arguments()[0]; - if (arg->type() != Item::FUNC_ITEM) - DBUG_RETURN(0); - cond_func= (Item_func*) arg; - if (cond_func->functype() != Item_func::BETWEEN && - cond_func->functype() != Item_func::IN_FUNC) - DBUG_RETURN(0); - inv= TRUE; - } + if (cond_func->functype() == Item_func::BETWEEN || + cond_func->functype() == Item_func::IN_FUNC) + inv= ((Item_func_opt_neg *) cond_func)->negated; else if (cond_func->select_optimize() == Item_func::OPTIMIZE_NONE) DBUG_RETURN(0); diff --git a/sql/set_var.cc b/sql/set_var.cc index 0f1b523529f..f1ab86ace51 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -142,11 +142,8 @@ sys_var_long_ptr sys_binlog_cache_size("binlog_cache_size", sys_var_thd_ulong sys_bulk_insert_buff_size("bulk_insert_buffer_size", &SV::bulk_insert_buff_size); sys_var_character_set_server sys_character_set_server("character_set_server"); -sys_var_str sys_charset_system("character_set_system", - sys_check_charset, - sys_update_charset, - sys_set_default_charset, - (char *)my_charset_utf8_general_ci.name); +sys_var_const_str sys_charset_system("character_set_system", + (char *)my_charset_utf8_general_ci.name); sys_var_character_set_database sys_character_set_database("character_set_database"); sys_var_character_set_client sys_character_set_client("character_set_client"); sys_var_character_set_connection sys_character_set_connection("character_set_connection"); @@ -571,6 +568,7 @@ sys_var *sys_variables[]= &sys_character_set_client, &sys_character_set_connection, &sys_character_set_results, + &sys_charset_system, &sys_collation_connection, &sys_collation_database, &sys_collation_server, @@ -1122,27 +1120,6 @@ static void sys_default_ftb_syntax(THD *thd, enum_var_type type) sizeof(ft_boolean_syntax)-1); } -/* - The following 3 functions need to be changed in 4.1 when we allow - one to change character sets -*/ - -static int sys_check_charset(THD *thd, set_var *var) -{ - return 0; -} - - -static bool sys_update_charset(THD *thd, set_var *var) -{ - return 0; -} - - -static void sys_set_default_charset(THD *thd, enum_var_type type) -{ -} - /* If one sets the LOW_PRIORIY UPDATES flag, we also must change the diff --git a/sql/set_var.h b/sql/set_var.h index b1c847973c1..8c1444870eb 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -190,6 +190,7 @@ public: return 1; } bool check_default(enum_var_type type) { return 1; } + bool is_readonly() const { return 1; } }; @@ -901,7 +902,7 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list); bool not_all_support_one_shot(List<set_var_base> *var_list); void fix_delay_key_write(THD *thd, enum_var_type type); ulong fix_sql_mode(ulong sql_mode); -extern sys_var_str sys_charset_system; +extern sys_var_const_str sys_charset_system; extern sys_var_str sys_init_connect; extern sys_var_str sys_init_slave; extern sys_var_thd_time_zone sys_time_zone; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 14956138cbf..1ab8e0c205c 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -275,8 +275,19 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, } DBUG_PRINT("info",("STRING_RESULT: %*s", s->length(), s->c_ptr_quick())); - CHARSET_INFO *itcs= it->collation.collation; - CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_string(itcs), + /* + Reuse mechanism in sp_eval_func_item() is only employed for assignments + to local variables and OUT/INOUT SP parameters repsesented by + Item_splocal. Usually we have some expression, which needs + to be calculated and stored into the local variable. However in the + case if "it" equals to "reuse", there is no "calculation" step. So, + no reason to employ reuse mechanism to save variable into itself. + */ + if (it == reuse) + DBUG_RETURN(it); + + CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) + Item_string(it->collation.collation), use_callers_arena, &backup_arena); /* We have to use special constructor and allocate string @@ -678,10 +689,35 @@ int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b) * If this function invocation is done from a statement that is written into the binary log. * If there were any attempts to write events to the binary log during - function execution. + function execution (grep for start_union_events and stop_union_events) + If the answers are No and Yes, we write the function call into the binary log as "DO spfunc(<param1value>, <param2value>, ...)" - + + + 4. Miscellaneous issues. + + 4.1 User variables. + + When we call mysql_bin_log.write() for an SP statement, thd->user_var_events + must hold set<{var_name, value}> pairs for all user variables used during + the statement execution. + This set is produced by tracking user variable reads during statement + execution. + + Fo SPs, this has the following implications: + 1) thd->user_var_events may contain events from several SP statements and + needs to be valid after exection of these statements was finished. In + order to achieve that, we + * Allocate user_var_events array elements on appropriate mem_root (grep + for user_var_events_alloc). + * Use is_query_in_union() to determine if user_var_event is created. + + 2) We need to empty thd->user_var_events after we have wrote a function + call. This is currently done by making + reset_dynamic(&thd->user_var_events); + calls in several different places. (TODO cosider moving this into + mysql_bin_log.write() function) */ @@ -897,6 +933,7 @@ int sp_head::execute(THD *thd) /* Don't change NOW() in FUNCTION or TRIGGER */ if (!thd->in_sub_stmt) thd->set_time(); // Make current_time() et al work + /* We have to set thd->stmt_arena before executing the instruction to store in the instruction free_list all new items, created @@ -904,6 +941,13 @@ int sp_head::execute(THD *thd) items made during other permanent subquery transformations). */ thd->stmt_arena= i; + + /* will binlog this separately */ + if (thd->prelocked_mode == NON_PRELOCKED) //TODO: change to event union? + { + thd->user_var_events_alloc= thd->mem_root; + } + ret= i->execute(thd, &ip); /* @@ -918,15 +962,6 @@ int sp_head::execute(THD *thd) /* we should cleanup free_list and memroot, used by instruction */ thd->free_items(); - /* - FIXME: we must free user var events only if the routine is executed - in non-prelocked mode and statement-by-statement replication is used. - But if we don't free them now, the server crashes because user var - events are allocated in execute_mem_root. This is Bug#12637, and when - it's fixed, please add if (thd->options & OPTION_BIN_LOG) here. - */ - if (opt_bin_log) - reset_dynamic(&thd->user_var_events); free_root(&execute_mem_root, MYF(0)); /* @@ -1084,7 +1119,10 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) binlog_save_options= thd->options; need_binlog_call= mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG); if (need_binlog_call) + { + reset_dynamic(&thd->user_var_events); mysql_bin_log.start_union_events(thd); + } thd->options&= ~OPTION_BIN_LOG; ret= execute(thd); @@ -1118,6 +1156,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) "Invoked ROUTINE modified a transactional table but MySQL " "failed to reflect this change in the binary log"); } + reset_dynamic(&thd->user_var_events); } if (m_type == TYPE_ENUM_FUNCTION && ret == 0) @@ -1827,17 +1866,6 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, thd->query_id= next_query_id(); VOID(pthread_mutex_unlock(&LOCK_thread_count)); - /* - FIXME. Resetting statement (and using it) is not reentrant, thus recursive - functions which try to use the same LEX twice will crash server. - We should prevent such situations by tracking if LEX is already - in use and throwing error about unallowed recursion if needed. - OTOH it is nice to allow recursion in cases when LEX is not really - used (e.g. in mathematical functions), so such tracking should be - implemented at the same time as ability not to store LEX for - instruction if it is not really used. - */ - if (thd->prelocked_mode == NON_PRELOCKED) { /* diff --git a/sql/sp_head.h b/sql/sp_head.h index 1c54b1a567d..271119ff2fb 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -108,13 +108,14 @@ class sp_head :private Query_arena MEM_ROOT main_mem_root; public: /* Possible values of m_flags */ - const static int + enum { HAS_RETURN= 1, // For FUNCTIONs only: is set if has RETURN IN_SIMPLE_CASE= 2, // Is set if parsing a simple CASE IN_HANDLER= 4, // Is set if the parser is in a handler body MULTI_RESULTS= 8, // Is set if a procedure with SELECT(s) CONTAINS_DYNAMIC_SQL= 16, // Is set if a procedure with PREPARE/EXECUTE - IS_INVOKED= 32; // Is set if this sp_head is being used. + IS_INVOKED= 32 // Is set if this sp_head is being used. + }; int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE uint m_flags; // Boolean attributes of a stored routine diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b7831a2a5b5..f578876bc5e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2617,6 +2617,8 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, table_list->alias, name, item_name, (ulong) ref)); Field_iterator_view field_it; field_it.set(table_list); + Query_arena *arena, backup; + DBUG_ASSERT(table_list->schema_table_reformed || (ref != 0 && table_list->view != 0)); for (; !field_it.end_of_fields(); field_it.next()) @@ -2638,7 +2640,13 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, name, length)) DBUG_RETURN(WRONG_GRANT); #endif + // in PS use own arena or data will be freed after prepare + if (register_tree_change) + arena= thd->activate_stmt_arena_if_needed(&backup); Item *item= field_it.create_item(thd); + if (register_tree_change && arena) + thd->restore_active_arena(arena, &backup); + if (!item) DBUG_RETURN(0); /* @@ -2700,6 +2708,8 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, field_it(*(table_ref->join_columns)); Natural_join_column *nj_col; Field *found_field; + Query_arena *arena, backup; + DBUG_ENTER("find_field_in_natural_join"); DBUG_PRINT("enter", ("field name: '%s', ref 0x%lx", name, (ulong) ref)); @@ -2728,7 +2738,14 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, The found field is a view field, we do as in find_field_in_view() and return a pointer to pointer to the Item of that field. */ + if (register_tree_change) + arena= thd->activate_stmt_arena_if_needed(&backup); + Item *item= nj_col->create_item(thd); + + if (register_tree_change && arena) + thd->restore_active_arena(arena, &backup); + if (!item) DBUG_RETURN(NULL); DBUG_ASSERT(nj_col->table_field == NULL); @@ -2882,14 +2899,15 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, /* Check that the table and database that qualify the current field name are the same as the table we are going to search for the field. - This is done differently for NATURAL/USING joins because there we can't - simply compare the qualifying table and database names with the ones of + This is done differently for NATURAL/USING joins or nested joins that + are operands of NATURAL/USING joins because there we can't simply + compare the qualifying table and database names with the ones of 'table_list' because each field in such a join may originate from a different table. TODO: Ensure that table_name, db_name and tables->db always points to something ! */ - if (!table_list->is_natural_join && + if (!(table_list->nested_join && table_list->join_columns) && table_name && table_name[0] && (my_strcasecmp(table_alias_charset, table_list->alias, table_name) || (db_name && db_name[0] && table_list->db && table_list->db[0] && @@ -2904,8 +2922,13 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, register_tree_change))) *actual_table= table_list; } - else if (table_list->is_natural_join) + else if (table_list->nested_join && table_list->join_columns) { + /* + If this is a NATURAL/USING join, or an operand of such join which is a + join itself, and the field name is qualified, then search for the field + in the operands of the join. + */ if (table_name && table_name[0]) { /* @@ -2927,7 +2950,9 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, } /* Non-qualified field, search directly in the result columns of the - natural join. + natural join. The condition of the outer IF is true for the top-most + natural join, thus if the field is not qualified, we will search + directly the top-most NATURAL/USING join. */ fld= find_field_in_natural_join(thd, table_list, name, length, ref, /* TIMOUR_TODO: check this with Sanja */ @@ -3569,10 +3594,16 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, if (add_columns && is_created_2) table_ref_2->join_columns->push_back(cur_nj_col_2); - /* Compare the two columns and check for duplicate common fields. */ + /* + Compare the two columns and check for duplicate common fields. + A common field is duplicate either if it was already found in + table_ref_2 (then found == TRUE), or if a field in table_ref_2 + was already matched by some previous field in table_ref_1 + (then cur_nj_col_2->is_common == TRUE). + */ if (!my_strcasecmp(system_charset_info, field_name_1, cur_field_name_2)) { - if (found) + if (found || cur_nj_col_2->is_common) { my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1, thd->where); goto err; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 2ff0413e05e..975014b9780 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -377,14 +377,16 @@ void THD::cleanup(void) mysql_ha_flush(this, (TABLE_LIST*) 0, MYSQL_HA_CLOSE_FINAL | MYSQL_HA_FLUSH_ALL); hash_free(&handler_tables_hash); + delete_dynamic(&user_var_events); + hash_free(&user_vars); close_temporary_tables(this); my_free((char*) variables.time_format, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) variables.date_format, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) variables.datetime_format, MYF(MY_ALLOW_ZERO_PTR)); - delete_dynamic(&user_var_events); - hash_free(&user_vars); + sp_cache_clear(&sp_proc_cache); sp_cache_clear(&sp_func_cache); + if (global_read_lock) unlock_global_read_lock(this); if (ull) @@ -424,9 +426,6 @@ THD::~THD() ha_close_connection(this); - sp_cache_clear(&sp_proc_cache); - sp_cache_clear(&sp_func_cache); - DBUG_PRINT("info", ("freeing host")); if (host != my_localhost) // If not pointer to constant safeFree(host); diff --git a/sql/sql_class.h b/sql/sql_class.h index 4f2b3dad5a3..5ce2f7d8847 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -313,6 +313,7 @@ public: void start_union_events(THD *thd); void stop_union_events(THD *thd); + bool is_query_in_union(THD *thd, query_id_t query_id_param); /* v stands for vector @@ -1312,8 +1313,9 @@ public: /* variables.transaction_isolation is reset to this after each commit */ enum_tx_isolation session_tx_isolation; enum_check_fields count_cuted_fields; - /* for user variables replication*/ - DYNAMIC_ARRAY user_var_events; + + DYNAMIC_ARRAY user_var_events; /* For user variables replication */ + MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */ enum killed_state { NOT_KILLED=0, KILL_BAD_DATA=1, KILL_CONNECTION=ER_SERVER_SHUTDOWN, KILL_QUERY=ER_QUERY_INTERRUPTED }; killed_state volatile killed; @@ -1375,6 +1377,12 @@ public: mysql_bin_log.start_union_events() call. */ bool unioned_events_trans; + + /* + 'queries' (actually SP statements) that run under inside this binlog + union have thd->query_id >= first_query_id. + */ + query_id_t first_query_id; } binlog_evt_union; THD(); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 38a8d6fd3bb..c2616ff3ef3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2442,6 +2442,12 @@ mysql_execute_command(THD *thd) { if (lex->describe) { + /* + We always use select_send for EXPLAIN, even if it's an EXPLAIN + for SELECT ... INTO OUTFILE: a user application should be able + to prepend EXPLAIN to any query and receive output for it, + even if the query itself redirects the output. + */ if (!(result= new select_send())) goto error; else @@ -5166,7 +5172,10 @@ void mysql_reset_thd_for_next_command(THD *thd) if (!thd->in_sub_stmt) { if (opt_bin_log) + { reset_dynamic(&thd->user_var_events); + thd->user_var_events_alloc= thd->mem_root; + } thd->clear_error(); thd->total_warn_count=0; // Warnings for this query thd->rand_used= 0; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index dbf8ea62e88..8d415419336 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2874,19 +2874,6 @@ add_key_fields(KEY_FIELD **key_fields,uint *and_level, if (cond->type() != Item::FUNC_ITEM) return; Item_func *cond_func= (Item_func*) cond; - if (cond_func->functype() == Item_func::NOT_FUNC) - { - Item *item= cond_func->arguments()[0]; - /* - At this moment all NOT before simple comparison predicates - are eliminated. NOT IN and NOT BETWEEN are treated similar - IN and BETWEEN respectively. - */ - if (item->type() == Item::FUNC_ITEM && - ((Item_func *) item)->select_optimize() == Item_func::OPTIMIZE_KEY) - add_key_fields(key_fields,and_level,item,usable_tables); - return; - } switch (cond_func->select_optimize()) { case Item_func::OPTIMIZE_NONE: break; @@ -13087,6 +13074,8 @@ void free_underlaid_joins(THD *thd, SELECT_LEX *select) The function replaces occurrences of group by fields in expr by ref objects for these fields unless they are under aggregate functions. + The function also corrects value of the the maybe_null attribute + for the items of all subexpressions containing group by fields. IMPLEMENTATION The function recursively traverses the tree of the expr expression, @@ -13097,6 +13086,9 @@ void free_underlaid_joins(THD *thd, SELECT_LEX *select) This substitution is needed GROUP BY queries with ROLLUP if SELECT list contains expressions over group by attributes. + TODO: Some functions are not null-preserving. For those functions + updating of the maybe_null attribute is an overkill. + EXAMPLES SELECT a+1 FROM t1 GROUP BY a WITH ROLLUP SELECT SUM(a)+a FROM t1 GROUP BY a WITH ROLLUP @@ -13118,6 +13110,7 @@ static bool change_group_ref(THD *thd, Item_func *expr, ORDER *group_list, arg != arg_end; arg++) { Item *item= *arg; + bool arg_changed= FALSE; if (item->type() == Item::FIELD_ITEM || item->type() == Item::REF_ITEM) { ORDER *group_tmp; @@ -13130,15 +13123,20 @@ static bool change_group_ref(THD *thd, Item_func *expr, ORDER *group_list, item->name))) return 1; // fatal_error is set thd->change_item_tree(arg, new_item); - *changed= TRUE; + arg_changed= TRUE; } } } else if (item->type() == Item::FUNC_ITEM) { - if (change_group_ref(thd, (Item_func *) item, group_list, changed)) + if (change_group_ref(thd, (Item_func *) item, group_list, &arg_changed)) return 1; } + if (arg_changed) + { + expr->maybe_null= 1; + *changed= TRUE; + } } } return 0; @@ -13201,7 +13199,7 @@ bool JOIN::rollup_init() } if (item->type() == Item::FUNC_ITEM) { - bool changed= 0; + bool changed= FALSE; if (change_group_ref(thd, (Item_func *) item, group_list, &changed)) return 1; /* diff --git a/sql/sql_select.h b/sql/sql_select.h index 77a3f872064..ce40f657a8e 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -231,7 +231,7 @@ class JOIN :public Sql_alloc /* Is set if we have a GROUP BY and we have ORDER BY on a constant. */ bool skip_sort_order; - bool need_tmp, hidden_group_fields, buffer_result; + bool need_tmp, hidden_group_fields; DYNAMIC_ARRAY keyuse; Item::cond_result cond_value; List<Item> all_fields; // to store all fields that used in query @@ -300,8 +300,6 @@ class JOIN :public Sql_alloc skip_sort_order= 0; need_tmp= 0; hidden_group_fields= 0; /*safety*/ - buffer_result= test(select_options & OPTION_BUFFER_RESULT) && - !test(select_options & OPTION_FOUND_ROWS); error= 0; select= 0; return_tab= 0; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 934f8bc9952..c346f4cc291 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2010,10 +2010,20 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) /* get_all_tables() returns 1 on failure and 0 on success thus return only these and not the result code of ::process_table() + + We should use show_table_list->alias instead of + show_table_list->table_name because table_name + could be changed during opening of I_S tables. It's safe + to use alias because alias contains original table name + in this case(this part of code is used only for + 'show columns' & 'show statistics' commands). */ error= test(schema_table->process_table(thd, show_table_list, - table, res, show_table_list->db, - show_table_list->alias)); + table, res, + (show_table_list->view ? + show_table_list->view_db.str : + show_table_list->db), + show_table_list->alias)); close_thread_tables(thd); show_table_list->table= 0; goto err; @@ -2114,6 +2124,13 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) lex->derived_tables= 0; res= open_normal_and_derived_tables(thd, show_table_list, MYSQL_LOCK_IGNORE_FLUSH); + /* + We should use show_table_list->alias instead of + show_table_list->table_name because table_name + could be changed during opening of I_S tables. It's safe + to use alias because alias contains original table name + in this case. + */ res= schema_table->process_table(thd, show_table_list, table, res, base_name, show_table_list->alias); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 892663b4220..fc877511357 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4866,7 +4866,9 @@ predicate: else { $5->push_front($1); - $$= negate_expression(YYTHD, new Item_func_in(*$5)); + Item_func_in *item = new Item_func_in(*$5); + item->negate(); + $$= item; } } | bit_expr IN_SYM in_subselect @@ -4876,7 +4878,11 @@ predicate: | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate { $$= new Item_func_between($1,$3,$5); } | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate - { $$= negate_expression(YYTHD, new Item_func_between($1,$4,$6)); } + { + Item_func_between *item= new Item_func_between($1,$4,$6); + item->negate(); + $$= item; + } | bit_expr SOUNDS_SYM LIKE bit_expr { $$= new Item_func_eq(new Item_func_soundex($1), new Item_func_soundex($4)); } @@ -8755,6 +8761,11 @@ handler: HANDLER_SYM table_ident OPEN_SYM opt_table_alias { LEX *lex= Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); + YYABORT; + } lex->sql_command = SQLCOM_HA_OPEN; if (!lex->current_select->add_table_to_list(lex->thd, $2, $4, 0)) YYABORT; @@ -8762,6 +8773,11 @@ handler: | HANDLER_SYM table_ident_nodb CLOSE_SYM { LEX *lex= Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); + YYABORT; + } lex->sql_command = SQLCOM_HA_CLOSE; if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0)) YYABORT; @@ -8769,6 +8785,11 @@ handler: | HANDLER_SYM table_ident_nodb READ_SYM { LEX *lex=Lex; + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HANDLER"); + YYABORT; + } lex->sql_command = SQLCOM_HA_READ; lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */ lex->current_select->select_limit= new Item_int((int32) 1); |