summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <ndbdev@dl145b.mysql.com>2005-09-09 15:26:47 +0200
committerunknown <ndbdev@dl145b.mysql.com>2005-09-09 15:26:47 +0200
commit36aeee3682fa975df244f44fb34e1c05f3263551 (patch)
tree8b8e4544543c49e86d358196df4f951aa55502dd /sql
parentde82d9eec0fd1d55c225a2420572b6e5e59ff8f7 (diff)
parent057855a34a2efbbca11ab2428c44790a52c401d9 (diff)
downloadmariadb-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.cc3
-rw-r--r--sql/handler.cc18
-rw-r--r--sql/item.cc35
-rw-r--r--sql/item.h12
-rw-r--r--sql/item_cmpfunc.cc198
-rw-r--r--sql/item_cmpfunc.h59
-rw-r--r--sql/item_func.cc15
-rw-r--r--sql/item_sum.cc6
-rw-r--r--sql/item_sum.h5
-rw-r--r--sql/log.cc7
-rw-r--r--sql/opt_range.cc15
-rw-r--r--sql/set_var.cc29
-rw-r--r--sql/set_var.h3
-rw-r--r--sql/sp_head.cc76
-rw-r--r--sql/sp_head.h5
-rw-r--r--sql/sql_base.cc45
-rw-r--r--sql/sql_class.cc9
-rw-r--r--sql/sql_class.h12
-rw-r--r--sql/sql_parse.cc9
-rw-r--r--sql/sql_select.cc30
-rw-r--r--sql/sql_select.h4
-rw-r--r--sql/sql_show.cc21
-rw-r--r--sql/sql_yacc.yy25
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);