summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <msvensson@pilot.mysql.com>2007-01-17 11:53:00 +0100
committerunknown <msvensson@pilot.mysql.com>2007-01-17 11:53:00 +0100
commitbd6afc39ed1d6ad04ede299d694bbfa8df79c286 (patch)
tree769da28ca256c82711b0b73af5ec9e75f6f36628 /sql
parent2e3d65833a911a728826a7f5dd0cf23e3f1d59b2 (diff)
parentf6015ed5ef0df4be29c8c584d45c78b315c3b166 (diff)
downloadmariadb-git-bd6afc39ed1d6ad04ede299d694bbfa8df79c286.tar.gz
Merge pilot.mysql.com:/home/msvensson/mysql/mysql-5.0
into pilot.mysql.com:/home/msvensson/mysql/mysql-5.0-maint mysys/my_read.c: Auto merged scripts/make_binary_distribution.sh: Auto merged sql/field.h: Auto merged sql/mysqld.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_parse.cc: Auto merged
Diffstat (limited to 'sql')
-rw-r--r--sql/field.h3
-rw-r--r--sql/item.cc2
-rw-r--r--sql/item_cmpfunc.cc6
-rw-r--r--sql/item_cmpfunc.h3
-rw-r--r--sql/item_func.cc104
-rw-r--r--sql/item_func.h1
-rw-r--r--sql/item_subselect.cc19
-rw-r--r--sql/item_subselect.h3
-rw-r--r--sql/mysql_priv.h1
-rw-r--r--sql/mysqld.cc2
-rw-r--r--sql/sp_head.cc96
-rw-r--r--sql/sp_head.h39
-rw-r--r--sql/sql_base.cc56
-rw-r--r--sql/sql_class.cc14
-rw-r--r--sql/sql_class.h12
-rw-r--r--sql/sql_lex.cc1
-rw-r--r--sql/sql_lex.h1
-rw-r--r--sql/sql_prepare.cc3
-rw-r--r--sql/sql_select.cc56
-rw-r--r--sql/sql_string.cc4
-rw-r--r--sql/sql_udf.cc6
-rw-r--r--sql/sql_yacc.yy412
22 files changed, 648 insertions, 196 deletions
diff --git a/sql/field.h b/sql/field.h
index 6656ddb36c3..565342637ba 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1034,7 +1034,8 @@ public:
bool zero_pack() const { return 0; }
int reset(void)
{
- charset()->cset->fill(charset(),ptr,field_length,' ');
+ charset()->cset->fill(charset(),ptr,field_length,
+ (has_charset() ? ' ' : 0));
return 0;
}
int store(const char *to,uint length,CHARSET_INFO *charset);
diff --git a/sql/item.cc b/sql/item.cc
index 80a5609852f..cc653a7db14 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3698,6 +3698,8 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
Item** res= find_item_in_list(this, thd->lex->current_select->item_list,
&counter, REPORT_EXCEPT_NOT_FOUND,
&not_used);
+ if (!res)
+ return 1;
if (res != (Item **)not_found_item)
{
if ((*res)->type() == Item::FIELD_ITEM)
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index a5a7f2a051f..936ae04e93d 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -2989,7 +2989,7 @@ longlong Item_func_isnull::val_int()
Handle optimization if the argument can't be null
This has to be here because of the test in update_used_tables().
*/
- if (!used_tables_cache)
+ if (!used_tables_cache && !with_subselect)
return cached_value;
return args[0]->is_null() ? 1: 0;
}
@@ -2998,7 +2998,7 @@ longlong Item_is_not_null_test::val_int()
{
DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_is_not_null_test::val_int");
- if (!used_tables_cache)
+ if (!used_tables_cache && !with_subselect)
{
owner->was_null|= (!cached_value);
DBUG_PRINT("info", ("cached :%ld", (long) cached_value));
@@ -3025,7 +3025,7 @@ void Item_is_not_null_test::update_used_tables()
else
{
args[0]->update_used_tables();
- if (!(used_tables_cache=args[0]->used_tables()))
+ if (!(used_tables_cache=args[0]->used_tables()) && !with_subselect)
{
/* Remember if the value is always NULL or never NULL */
cached_value= (longlong) !args[0]->is_null();
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 454aed01aff..7bdc90adcee 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -1027,7 +1027,8 @@ public:
else
{
args[0]->update_used_tables();
- if ((const_item_cache= !(used_tables_cache= args[0]->used_tables())))
+ if ((const_item_cache= !(used_tables_cache= args[0]->used_tables())) &&
+ !with_subselect)
{
/* Remember if the value is always NULL or never NULL */
cached_value= (longlong) args[0]->is_null();
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 0eab370237d..1ef77208469 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -3688,8 +3688,9 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length,
char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
if (entry->value == pos)
entry->value=0;
- if (!(entry->value=(char*) my_realloc(entry->value, length,
- MYF(MY_ALLOW_ZERO_PTR))))
+ entry->value= (char*) my_realloc(entry->value, length,
+ MYF(MY_ALLOW_ZERO_PTR | MY_WME));
+ if (!entry->value)
return 1;
}
}
@@ -4056,6 +4057,105 @@ void Item_func_set_user_var::make_field(Send_field *tmp_field)
Item::make_field(tmp_field);
}
+
+/*
+ Save the value of a user variable into a field
+
+ SYNOPSIS
+ save_in_field()
+ field target field to save the value to
+ no_conversion flag indicating whether conversions are allowed
+
+ DESCRIPTION
+ Save the function value into a field and update the user variable
+ accordingly. If a result field is defined and the target field doesn't
+ coincide with it then the value from the result field will be used as
+ the new value of the user variable.
+
+ The reason to have this method rather than simply using the result
+ field in the val_xxx() methods is that the value from the result field
+ not always can be used when the result field is defined.
+ Let's consider the following cases:
+ 1) when filling a tmp table the result field is defined but the value of it
+ is undefined because it has to be produced yet. Thus we can't use it.
+ 2) on execution of an INSERT ... SELECT statement the save_in_field()
+ function will be called to fill the data in the new record. If the SELECT
+ part uses a tmp table then the result field is defined and should be
+ used in order to get the correct result.
+
+ The difference between the SET_USER_VAR function and regular functions
+ like CONCAT is that the Item_func objects for the regular functions are
+ replaced by Item_field objects after the values of these functions have
+ been stored in a tmp table. Yet an object of the Item_field class cannot
+ be used to update a user variable.
+ Due to this we have to handle the result field in a special way here and
+ in the Item_func_set_user_var::send() function.
+
+ RETURN VALUES
+ FALSE Ok
+ TRUE Error
+*/
+
+int Item_func_set_user_var::save_in_field(Field *field, bool no_conversions)
+{
+ bool use_result_field= (result_field && result_field != field);
+ int error;
+
+ /* Update the value of the user variable */
+ check(use_result_field);
+ update();
+
+ if (result_type() == STRING_RESULT ||
+ result_type() == REAL_RESULT &&
+ field->result_type() == STRING_RESULT)
+ {
+ String *result;
+ CHARSET_INFO *cs= collation.collation;
+ char buff[MAX_FIELD_WIDTH]; // Alloc buffer for small columns
+ str_value.set_quick(buff, sizeof(buff), cs);
+ result= entry->val_str(&null_value, &str_value, decimals);
+
+ if (null_value)
+ {
+ str_value.set_quick(0, 0, cs);
+ return set_field_to_null_with_conversions(field, no_conversions);
+ }
+
+ /* NOTE: If null_value == FALSE, "result" must be not NULL. */
+
+ field->set_notnull();
+ error=field->store(result->ptr(),result->length(),cs);
+ str_value.set_quick(0, 0, cs);
+ }
+ else if (result_type() == REAL_RESULT)
+ {
+ double nr= entry->val_real(&null_value);
+ if (null_value)
+ return set_field_to_null(field);
+ field->set_notnull();
+ error=field->store(nr);
+ }
+ else if (result_type() == DECIMAL_RESULT)
+ {
+ my_decimal decimal_value;
+ my_decimal *value= entry->val_decimal(&null_value, &decimal_value);
+ if (null_value)
+ return set_field_to_null(field);
+ field->set_notnull();
+ error=field->store_decimal(value);
+ }
+ else
+ {
+ longlong nr= entry->val_int(&null_value);
+ if (null_value)
+ return set_field_to_null_with_conversions(field, no_conversions);
+ field->set_notnull();
+ error=field->store(nr, unsigned_flag);
+ }
+ return error;
+}
+
+
String *
Item_func_get_user_var::val_str(String *str)
{
diff --git a/sql/item_func.h b/sql/item_func.h
index c116c18bc50..68591f9c6f5 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1188,6 +1188,7 @@ public:
void print(String *str);
void print_as_stmt(String *str);
const char *func_name() const { return "set_user_var"; }
+ int save_in_field(Field *field, bool no_conversions);
};
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 28aaf41b39f..d5d26b7b741 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -348,6 +348,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
*/
!(select_lex->item_list.head()->type() == FIELD_ITEM ||
select_lex->item_list.head()->type() == REF_ITEM) &&
+ !join->conds && !join->having &&
/*
switch off this optimization for prepare statement,
because we do not rollback this changes
@@ -372,8 +373,6 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
*/
substitution->walk(&Item::remove_dependence_processor,
(byte *) select_lex->outer_select());
- /* SELECT without FROM clause can't have WHERE or HAVING clause */
- DBUG_ASSERT(join->conds == 0 && join->having == 0);
return RES_REDUCE;
}
return RES_OK;
@@ -2277,6 +2276,22 @@ bool subselect_single_select_engine::no_tables()
/*
+ Check statically whether the subquery can return NULL
+
+ SINOPSYS
+ subselect_single_select_engine::may_be_null()
+
+ RETURN
+ FALSE can guarantee that the subquery never return NULL
+ TRUE otherwise
+*/
+bool subselect_single_select_engine::may_be_null()
+{
+ return ((no_tables() && !join->conds && !join->having) ? maybe_null : 1);
+}
+
+
+/*
Report about presence of tables in subquery
SYNOPSIS
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 5b2ab419b77..69ac78d859b 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -362,7 +362,7 @@ public:
enum Item_result type() { return res_type; }
enum_field_types field_type() { return res_field_type; }
virtual void exclude()= 0;
- bool may_be_null() { return maybe_null; };
+ virtual bool may_be_null() { return maybe_null; };
virtual table_map upper_select_const_tables()= 0;
static table_map calc_const_tables(TABLE_LIST *);
virtual void print(String *str)= 0;
@@ -399,6 +399,7 @@ public:
void print (String *str);
bool change_result(Item_subselect *si, select_subselect *result);
bool no_tables();
+ bool may_be_null();
bool is_executed() const { return executed; }
bool no_rows();
};
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 67d2d29422c..534d42e1c17 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1157,6 +1157,7 @@ File open_binlog(IO_CACHE *log, const char *log_file_name,
/* mysqld.cc */
extern void MYSQLerror(const char*);
void refresh_status(THD *thd);
+my_bool mysql_rm_tmp_tables(void);
/* item_func.cc */
extern bool check_reserved_words(LEX_STRING *name);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index eb2c0ca68d1..47d5ec6a2a0 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -3496,7 +3496,7 @@ we force server id to 2, but this MySQL server will not act as a slave.");
*/
error_handler_hook= my_message_sql;
start_signal_handler(); // Creates pidfile
- if (acl_init(opt_noacl) ||
+ if (mysql_rm_tmp_tables() || acl_init(opt_noacl) ||
my_tz_init((THD *)0, default_tz_name, opt_bootstrap))
{
abort_loop=1;
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index ff39421cef7..fb99ed1bb5c 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -605,27 +605,6 @@ sp_head::create(THD *thd)
DBUG_PRINT("info", ("type: %d name: %s params: %s body: %s",
m_type, m_name.str, m_params.str, m_body.str));
-#ifndef DBUG_OFF
- optimize();
- {
- String s;
- sp_instr *i;
- uint ip= 0;
- while ((i = get_instr(ip)))
- {
- char buf[8];
-
- sprintf(buf, "%4u: ", ip);
- s.append(buf);
- i->print(&s);
- s.append('\n');
- ip+= 1;
- }
- s.append('\0');
- DBUG_PRINT("info", ("Code %s\n%s", m_qname.str, s.ptr()));
- }
-#endif
-
if (m_type == TYPE_ENUM_FUNCTION)
ret= sp_create_function(thd, this);
else
@@ -2173,7 +2152,7 @@ sp_head::show_create_function(THD *thd)
This is the main mark and move loop; it relies on the following methods
in sp_instr and its subclasses:
- opt_mark() Mark instruction as reachable (will recurse for jumps)
+ opt_mark() Mark instruction as reachable
opt_shortcut_jump() Shortcut jumps to the final destination;
used by opt_mark().
opt_move() Update moved instruction
@@ -2186,7 +2165,7 @@ void sp_head::optimize()
sp_instr *i;
uint src, dst;
- opt_mark(0);
+ opt_mark();
bp.empty();
src= dst= 0;
@@ -2220,13 +2199,50 @@ void sp_head::optimize()
bp.empty();
}
+void sp_head::add_mark_lead(uint ip, List<sp_instr> *leads)
+{
+ sp_instr *i= get_instr(ip);
+
+ if (i && ! i->marked)
+ leads->push_front(i);
+}
+
void
-sp_head::opt_mark(uint ip)
+sp_head::opt_mark()
{
+ uint ip;
sp_instr *i;
+ List<sp_instr> leads;
- while ((i= get_instr(ip)) && !i->marked)
- ip= i->opt_mark(this);
+ /*
+ Forward flow analysis algorithm in the instruction graph:
+ - first, add the entry point in the graph (the first instruction) to the
+ 'leads' list of paths to explore.
+ - while there are still leads to explore:
+ - pick one lead, and follow the path forward. Mark instruction reached.
+ Stop only if the end of the routine is reached, or the path converge
+ to code already explored (marked).
+ - while following a path, collect in the 'leads' list any fork to
+ another path (caused by conditional jumps instructions), so that these
+ paths can be explored as well.
+ */
+
+ /* Add the entry point */
+ i= get_instr(0);
+ leads.push_front(i);
+
+ /* For each path of code ... */
+ while (leads.elements != 0)
+ {
+ i= leads.pop();
+
+ /* Mark the entire path, collecting new leads. */
+ while (i && ! i->marked)
+ {
+ ip= i->opt_mark(this, & leads);
+ i= get_instr(ip);
+ }
+ }
}
@@ -2619,7 +2635,7 @@ sp_instr_jump::print(String *str)
}
uint
-sp_instr_jump::opt_mark(sp_head *sp)
+sp_instr_jump::opt_mark(sp_head *sp, List<sp_instr> *leads)
{
m_dest= opt_shortcut_jump(sp, this);
if (m_dest != m_ip+1) /* Jumping to following instruction? */
@@ -2713,7 +2729,7 @@ sp_instr_jump_if_not::print(String *str)
uint
-sp_instr_jump_if_not::opt_mark(sp_head *sp)
+sp_instr_jump_if_not::opt_mark(sp_head *sp, List<sp_instr> *leads)
{
sp_instr *i;
@@ -2723,13 +2739,13 @@ sp_instr_jump_if_not::opt_mark(sp_head *sp)
m_dest= i->opt_shortcut_jump(sp, this);
m_optdest= sp->get_instr(m_dest);
}
- sp->opt_mark(m_dest);
+ sp->add_mark_lead(m_dest, leads);
if ((i= sp->get_instr(m_cont_dest)))
{
m_cont_dest= i->opt_shortcut_jump(sp, this);
m_cont_optdest= sp->get_instr(m_cont_dest);
}
- sp->opt_mark(m_cont_dest);
+ sp->add_mark_lead(m_cont_dest, leads);
return m_ip+1;
}
@@ -2850,7 +2866,7 @@ sp_instr_hpush_jump::print(String *str)
uint
-sp_instr_hpush_jump::opt_mark(sp_head *sp)
+sp_instr_hpush_jump::opt_mark(sp_head *sp, List<sp_instr> *leads)
{
sp_instr *i;
@@ -2860,7 +2876,7 @@ sp_instr_hpush_jump::opt_mark(sp_head *sp)
m_dest= i->opt_shortcut_jump(sp, this);
m_optdest= sp->get_instr(m_dest);
}
- sp->opt_mark(m_dest);
+ sp->add_mark_lead(m_dest, leads);
return m_ip+1;
}
@@ -2925,15 +2941,13 @@ sp_instr_hreturn::print(String *str)
uint
-sp_instr_hreturn::opt_mark(sp_head *sp)
+sp_instr_hreturn::opt_mark(sp_head *sp, List<sp_instr> *leads)
{
if (m_dest)
- return sp_instr_jump::opt_mark(sp);
- else
- {
- marked= 1;
- return UINT_MAX;
- }
+ return sp_instr_jump::opt_mark(sp, leads);
+
+ marked= 1;
+ return UINT_MAX;
}
@@ -3276,7 +3290,7 @@ sp_instr_set_case_expr::print(String *str)
}
uint
-sp_instr_set_case_expr::opt_mark(sp_head *sp)
+sp_instr_set_case_expr::opt_mark(sp_head *sp, List<sp_instr> *leads)
{
sp_instr *i;
@@ -3286,7 +3300,7 @@ sp_instr_set_case_expr::opt_mark(sp_head *sp)
m_cont_dest= i->opt_shortcut_jump(sp, this);
m_cont_optdest= sp->get_instr(m_cont_dest);
}
- sp->opt_mark(m_cont_dest);
+ sp->add_mark_lead(m_cont_dest, leads);
return m_ip+1;
}
diff --git a/sql/sp_head.h b/sql/sp_head.h
index a82a65458ea..0139f879ce4 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -301,8 +301,19 @@ public:
void restore_thd_mem_root(THD *thd);
+ /**
+ Optimize the code.
+ */
void optimize();
- void opt_mark(uint ip);
+
+ /**
+ Helper used during flow analysis during code optimization.
+ See the implementation of <code>opt_mark()</code>.
+ @param ip the instruction to add to the leads list
+ @param leads the list of remaining paths to explore in the graph that
+ represents the code, during flow analysis.
+ */
+ void add_mark_lead(uint ip, List<sp_instr> *leads);
void recursion_level_error(THD *thd);
@@ -392,6 +403,12 @@ private:
bool
execute(THD *thd);
+ /**
+ Perform a forward flow analysis in the generated code.
+ Mark reachable instructions, for the optimizer.
+ */
+ void opt_mark();
+
/*
Merge the list of tables used by query into the multi-set of tables used
by routine.
@@ -459,10 +476,10 @@ public:
/*
Mark this instruction as reachable during optimization and return the
- index to the next instruction. Jump instruction will mark their
- destination too recursively.
+ index to the next instruction. Jump instruction will add their
+ destination to the leads list.
*/
- virtual uint opt_mark(sp_head *sp)
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads)
{
marked= 1;
return m_ip+1;
@@ -734,7 +751,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp);
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start);
@@ -784,7 +801,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp);
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
/* Override sp_instr_jump's shortcut; we stop here */
virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
@@ -830,7 +847,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp)
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads)
{
marked= 1;
return UINT_MAX;
@@ -867,7 +884,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp);
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
/* Override sp_instr_jump's shortcut; we stop here. */
virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start)
@@ -932,7 +949,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp);
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
private:
@@ -1102,7 +1119,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp)
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads)
{
marked= 1;
return UINT_MAX;
@@ -1135,7 +1152,7 @@ public:
virtual void print(String *str);
- virtual uint opt_mark(sp_head *sp);
+ virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads);
virtual void opt_move(uint dst, List<sp_instr> *ibp);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index c5a9fad333d..a4318f7b4bf 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -36,7 +36,6 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
TABLE_LIST *table_list, MEM_ROOT *mem_root,
uint flags);
static void free_cache_entry(TABLE *entry);
-static void mysql_rm_tmp_tables(void);
static bool open_new_frm(THD *thd, const char *path, const char *alias,
const char *db, const char *table_name,
uint db_stat, uint prgflag,
@@ -53,7 +52,6 @@ extern "C" byte *table_cache_key(const byte *record,uint *length,
bool table_cache_init(void)
{
- mysql_rm_tmp_tables();
return hash_init(&open_cache, &my_charset_bin, table_cache_size+16,
0, 0,table_cache_key,
(hash_free_key) free_cache_entry, 0) != 0;
@@ -2977,6 +2975,19 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
column reference. See create_view_field() for details.
*/
item= nj_col->create_item(thd);
+ /*
+ *ref != NULL means that *ref contains the item that we need to
+ replace. If the item was aliased by the user, set the alias to
+ the replacing item.
+ We need to set alias on both ref itself and on ref real item.
+ */
+ if (*ref && !(*ref)->is_autogenerated_name)
+ {
+ item->set_name((*ref)->name, (*ref)->name_length,
+ system_charset_info);
+ item->real_item()->set_name((*ref)->name, (*ref)->name_length,
+ system_charset_info);
+ }
if (register_tree_change && arena)
thd->restore_active_arena(arena, &backup);
@@ -5157,14 +5168,21 @@ fill_record_n_invoke_before_triggers(THD *thd, Field **ptr,
}
-static void mysql_rm_tmp_tables(void)
+my_bool mysql_rm_tmp_tables(void)
{
uint i, idx;
- char filePath[FN_REFLEN], *tmpdir;
+ char filePath[FN_REFLEN], *tmpdir, filePathCopy[FN_REFLEN];
MY_DIR *dirp;
FILEINFO *file;
+ TABLE tmp_table;
+ THD *thd;
DBUG_ENTER("mysql_rm_tmp_tables");
+ if (!(thd= new THD))
+ DBUG_RETURN(1);
+ thd->thread_stack= (char*) &thd;
+ thd->store_globals();
+
for (i=0; i<=mysql_tmpdir_list.max; i++)
{
tmpdir=mysql_tmpdir_list.list[i];
@@ -5185,13 +5203,37 @@ static void mysql_rm_tmp_tables(void)
if (!bcmp(file->name,tmp_file_prefix,tmp_file_prefix_length))
{
- sprintf(filePath,"%s%s",tmpdir,file->name);
- VOID(my_delete(filePath,MYF(MY_WME)));
+ char *ext= fn_ext(file->name);
+ uint ext_len= strlen(ext);
+ uint filePath_len= my_snprintf(filePath, sizeof(filePath),
+ "%s%s", tmpdir, file->name);
+ if (!bcmp(reg_ext, ext, ext_len))
+ {
+ TABLE tmp_table;
+ if (!openfrm(thd, filePath, "tmp_table", (uint) 0,
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
+ 0, &tmp_table))
+ {
+ /* We should cut file extention before deleting of table */
+ memcpy(filePathCopy, filePath, filePath_len - ext_len);
+ filePathCopy[filePath_len - ext_len]= 0;
+ tmp_table.file->delete_table(filePathCopy);
+ closefrm(&tmp_table);
+ }
+ }
+ /*
+ File can be already deleted by tmp_table.file->delete_table().
+ So we hide error messages which happnes during deleting of these
+ files(MYF(0)).
+ */
+ VOID(my_delete(filePath, MYF(0)));
}
}
my_dirend(dirp);
}
- DBUG_VOID_RETURN;
+ delete thd;
+ my_pthread_setspecific_ptr(THR_THD, 0);
+ DBUG_RETURN(0);
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 84d2ce77014..0794d4c797a 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -884,6 +884,13 @@ void select_result::cleanup()
/* do nothing */
}
+bool select_result::check_simple_select() const
+{
+ my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0));
+ return TRUE;
+}
+
+
static String default_line_term("\n",default_charset_info);
static String default_escaped("\\",default_charset_info);
static String default_field_term("\t",default_charset_info);
@@ -1521,6 +1528,13 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
}
+bool select_dumpvar::check_simple_select() const
+{
+ my_error(ER_SP_BAD_CURSOR_SELECT, MYF(0));
+ return TRUE;
+}
+
+
void select_dumpvar::cleanup()
{
row_count= 0;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index efc13c02a59..d74158ec07c 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1723,7 +1723,14 @@ public:
virtual bool initialize_tables (JOIN *join=0) { return 0; }
virtual void send_error(uint errcode,const char *err);
virtual bool send_eof()=0;
- virtual bool simple_select() { return 0; }
+ /**
+ Check if this query returns a result set and therefore is allowed in
+ cursors and set an error message if it is not the case.
+
+ @retval FALSE success
+ @retval TRUE error, an error message is set
+ */
+ virtual bool check_simple_select() const;
virtual void abort() {}
/*
Cleanup instance of this class for next execution of a prepared
@@ -1761,7 +1768,7 @@ public:
bool send_fields(List<Item> &list, uint flags);
bool send_data(List<Item> &items);
bool send_eof();
- bool simple_select() { return 1; }
+ virtual bool check_simple_select() const { return FALSE; }
void abort();
};
@@ -2200,6 +2207,7 @@ public:
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_data(List<Item> &items);
bool send_eof();
+ virtual bool check_simple_select() const;
void cleanup();
};
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 45272645633..986cb4760de 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1163,7 +1163,6 @@ void st_select_lex::init_select()
options= 0;
sql_cache= SQL_CACHE_UNSPECIFIED;
braces= 0;
- when_list.empty();
expr_list.empty();
udf_list.empty();
interval_list.empty();
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index db119d527d9..a7b162c6062 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -517,7 +517,6 @@ public:
SQL_LIST order_list; /* ORDER clause */
List<List_item> expr_list;
- List<List_item> when_list; /* WHEN clause (expression) */
SQL_LIST *gorder_list;
Item *select_limit, *offset_limit; /* LIMIT clause parameters */
// Arrays of pointers to top elements of all_fields list
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index e9f33a172c4..152cc3aa385 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2902,10 +2902,9 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
in INSERT ... SELECT and similar commands.
*/
- if (open_cursor && lex->result && !lex->result->simple_select())
+ if (open_cursor && lex->result && lex->result->check_simple_select())
{
DBUG_PRINT("info",("Cursor asked for not SELECT stmt"));
- my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0));
return TRUE;
}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 30514f09493..abb949ae473 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -2305,8 +2305,18 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
substitution of a const table the key value happens to be null
then we can state that there are no matches for this equi-join.
*/
- if ((keyuse= s->keyuse) && *s->on_expr_ref)
+ if ((keyuse= s->keyuse) && *s->on_expr_ref && !s->embedding_map)
{
+ /*
+ When performing an outer join operation if there are no matching rows
+ for the single row of the outer table all the inner tables are to be
+ null complemented and thus considered as constant tables.
+ Here we apply this consideration to the case of outer join operations
+ with a single inner table only because the case with nested tables
+ would require a more thorough analysis.
+ TODO. Apply single row substitution to null complemented inner tables
+ for nested outer join operations.
+ */
while (keyuse->table == table)
{
if (!(keyuse->val->used_tables() & ~join->const_table_map) &&
@@ -8432,6 +8442,46 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
return cond; // Point at next and level
}
+/*
+ Check if equality can be used in removing components of GROUP BY/DISTINCT
+
+ SYNOPSIS
+ test_if_equality_guarantees_uniqueness()
+ l the left comparison argument (a field if any)
+ r the right comparison argument (a const of any)
+
+ DESCRIPTION
+ Checks if an equality predicate can be used to take away
+ DISTINCT/GROUP BY because it is known to be true for exactly one
+ distinct value (e.g. <expr> == <const>).
+ Arguments must be of the same type because e.g.
+ <string_field> = <int_const> may match more than 1 distinct value from
+ the column.
+ We must take into consideration and the optimization done for various
+ string constants when compared to dates etc (see Item_int_with_ref) as
+ well as the collation of the arguments.
+
+ RETURN VALUE
+ TRUE can be used
+ FALSE cannot be used
+*/
+static bool
+test_if_equality_guarantees_uniqueness(Item *l, Item *r)
+{
+ return r->const_item() &&
+ /* elements must be of the same result type */
+ (r->result_type() == l->result_type() ||
+ /* or dates compared to longs */
+ (((l->type() == Item::FIELD_ITEM &&
+ ((Item_field *)l)->field->can_be_compared_as_longlong()) ||
+ (l->type() == Item::FUNC_ITEM &&
+ ((Item_func *)l)->result_as_longlong())) &&
+ r->result_type() == INT_RESULT))
+ /* and must have the same collation if compared as strings */
+ && (l->result_type() != STRING_RESULT ||
+ l->collation.collation == r->collation.collation);
+}
+
/*
Return 1 if the item is a const value in all the WHERE clause
*/
@@ -8468,7 +8518,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
Item *right_item= ((Item_func*) cond)->arguments()[1];
if (left_item->eq(comp_item,1))
{
- if (right_item->const_item())
+ if (test_if_equality_guarantees_uniqueness (left_item, right_item))
{
if (*const_item)
return right_item->eq(*const_item, 1);
@@ -8478,7 +8528,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
}
else if (right_item->eq(comp_item,1))
{
- if (left_item->const_item())
+ if (test_if_equality_guarantees_uniqueness (right_item, left_item))
{
if (*const_item)
return left_item->eq(*const_item, 1);
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 16f35e09e02..ef50e5864a5 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -1032,8 +1032,8 @@ void String::print(String *str)
case '\r':
str->append(STRING_WITH_LEN("\\r"));
break;
- case 26: //Ctrl-Z
- str->append(STRING_WITH_LEN("\\z"));
+ case '\032': // Ctrl-Z
+ str->append(STRING_WITH_LEN("\\Z"));
break;
default:
str->append(c);
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index 1ebf2a79d7c..077660f0bb9 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -509,6 +509,8 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
TABLE *table;
TABLE_LIST tables;
udf_func *udf;
+ char *exact_name_str;
+ uint exact_name_len;
DBUG_ENTER("mysql_drop_function");
if (!initialized)
{
@@ -522,6 +524,8 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
goto err;
}
+ exact_name_str= udf->name.str;
+ exact_name_len= udf->name.length;
del_udf(udf);
/*
Close the handle if this was function that was found during boot or
@@ -535,7 +539,7 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
tables.table_name= tables.alias= (char*) "func";
if (!(table = open_ltable(thd,&tables,TL_WRITE)))
goto err;
- table->field[0]->store(udf_name->str, udf_name->length, system_charset_info);
+ table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
if (!table->file->index_read_idx(table->record[0], 0,
(byte*) table->field[0]->ptr,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 90dc6d54fe1..1fa23beb2f7 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -95,6 +95,187 @@ void turn_parser_debug_on()
}
#endif
+
+/**
+ Helper action for a case statement (entering the CASE).
+ This helper is used for both 'simple' and 'searched' cases.
+ This helper, with the other case_stmt_action_..., is executed when
+ the following SQL code is parsed:
+<pre>
+CREATE PROCEDURE proc_19194_simple(i int)
+BEGIN
+ DECLARE str CHAR(10);
+
+ CASE i
+ WHEN 1 THEN SET str="1";
+ WHEN 2 THEN SET str="2";
+ WHEN 3 THEN SET str="3";
+ ELSE SET str="unknown";
+ END CASE;
+
+ SELECT str;
+END
+</pre>
+ The actions are used to generate the following code:
+<pre>
+SHOW PROCEDURE CODE proc_19194_simple;
+Pos Instruction
+0 set str@1 NULL
+1 set_case_expr (12) 0 i@0
+2 jump_if_not 5(12) (case_expr@0 = 1)
+3 set str@1 _latin1'1'
+4 jump 12
+5 jump_if_not 8(12) (case_expr@0 = 2)
+6 set str@1 _latin1'2'
+7 jump 12
+8 jump_if_not 11(12) (case_expr@0 = 3)
+9 set str@1 _latin1'3'
+10 jump 12
+11 set str@1 _latin1'unknown'
+12 stmt 0 "SELECT str"
+</pre>
+
+ @param lex the parser lex context
+*/
+
+void case_stmt_action_case(LEX *lex)
+{
+ lex->sphead->new_cont_backpatch(NULL);
+
+ /*
+ BACKPATCH: Creating target label for the jump to
+ "case_stmt_action_end_case"
+ (Instruction 12 in the example)
+ */
+
+ lex->spcont->push_label((char *)"", lex->sphead->instructions());
+}
+
+/**
+ Helper action for a case expression statement (the expr in 'CASE expr').
+ This helper is used for 'searched' cases only.
+ @param lex the parser lex context
+ @param expr the parsed expression
+ @return 0 on success
+*/
+
+int case_stmt_action_expr(LEX *lex, Item* expr)
+{
+ sp_head *sp= lex->sphead;
+ sp_pcontext *parsing_ctx= lex->spcont;
+ int case_expr_id= parsing_ctx->register_case_expr();
+ sp_instr_set_case_expr *i;
+
+ if (parsing_ctx->push_case_expr_id(case_expr_id))
+ return 1;
+
+ i= new sp_instr_set_case_expr(sp->instructions(),
+ parsing_ctx, case_expr_id, expr, lex);
+
+ sp->add_cont_backpatch(i);
+ sp->add_instr(i);
+
+ return 0;
+}
+
+/**
+ Helper action for a case when condition.
+ This helper is used for both 'simple' and 'searched' cases.
+ @param lex the parser lex context
+ @param when the parsed expression for the WHEN clause
+ @param simple true for simple cases, false for searched cases
+*/
+
+void case_stmt_action_when(LEX *lex, Item *when, bool simple)
+{
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i;
+ Item_case_expr *var;
+ Item *expr;
+
+ if (simple)
+ {
+ var= new Item_case_expr(ctx->get_current_case_expr_id());
+
+#ifndef DBUG_OFF
+ if (var)
+ {
+ var->m_sp= sp;
+ }
+#endif
+
+ expr= new Item_func_eq(var, when);
+ i= new sp_instr_jump_if_not(ip, ctx, expr, lex);
+ }
+ else
+ i= new sp_instr_jump_if_not(ip, ctx, when, lex);
+
+ /*
+ BACKPATCH: Registering forward jump from
+ "case_stmt_action_when" to "case_stmt_action_then"
+ (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
+ */
+
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ sp->add_cont_backpatch(i);
+ sp->add_instr(i);
+}
+
+/**
+ Helper action for a case then statements.
+ This helper is used for both 'simple' and 'searched' cases.
+ @param lex the parser lex context
+*/
+
+void case_stmt_action_then(LEX *lex)
+{
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump *i = new sp_instr_jump(ip, ctx);
+ sp->add_instr(i);
+
+ /*
+ BACKPATCH: Resolving forward jump from
+ "case_stmt_action_when" to "case_stmt_action_then"
+ (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
+ */
+
+ sp->backpatch(ctx->pop_label());
+
+ /*
+ BACKPATCH: Registering forward jump from
+ "case_stmt_action_then" to "case_stmt_action_end_case"
+ (jump from instruction 4 to 12, 7 to 12 ... in the example)
+ */
+
+ sp->push_backpatch(i, ctx->last_label());
+}
+
+/**
+ Helper action for an end case.
+ This helper is used for both 'simple' and 'searched' cases.
+ @param lex the parser lex context
+ @param simple true for simple cases, false for searched cases
+*/
+
+void case_stmt_action_end_case(LEX *lex, bool simple)
+{
+ /*
+ BACKPATCH: Resolving forward jump from
+ "case_stmt_action_then" to "case_stmt_action_end_case"
+ (jump from instruction 4 to 12, 7 to 12 ... in the example)
+ */
+ lex->sphead->backpatch(lex->spcont->pop_label());
+
+ if (simple)
+ lex->spcont->pop_case_expr_id();
+
+ lex->sphead->do_cont_backpatch();
+}
+
%}
%union {
int num;
@@ -831,7 +1012,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
select_item_list select_item values_list no_braces
opt_limit_clause delete_limit_clause fields opt_values values
procedure_list procedure_list2 procedure_item
- when_list2 expr_list2 udf_expr_list3 handler
+ expr_list2 udf_expr_list3 handler
opt_precision opt_ignore opt_column opt_restrict
grant revoke set lock unlock string_list field_options field_option
field_opt_list opt_binary table_lock_list table_lock
@@ -859,6 +1040,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
view_algorithm view_or_trigger_or_sp view_or_trigger_or_sp_tail
view_suid view_tail view_list_opt view_list view_select
view_check_option trigger_tail sp_tail
+ case_stmt_specification simple_case_stmt searched_case_stmt
END_OF_INPUT
%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
@@ -1993,43 +2175,7 @@ sp_proc_stmt:
{ Lex->sphead->new_cont_backpatch(NULL); }
sp_if END IF
{ Lex->sphead->do_cont_backpatch(); }
- | CASE_SYM WHEN_SYM
- {
- Lex->sphead->m_flags&= ~sp_head::IN_SIMPLE_CASE;
- Lex->sphead->new_cont_backpatch(NULL);
- }
- sp_case END CASE_SYM { Lex->sphead->do_cont_backpatch(); }
- | CASE_SYM
- {
- Lex->sphead->reset_lex(YYTHD);
- Lex->sphead->new_cont_backpatch(NULL);
- }
- expr WHEN_SYM
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *parsing_ctx= lex->spcont;
- int case_expr_id= parsing_ctx->register_case_expr();
- sp_instr_set_case_expr *i;
-
- if (parsing_ctx->push_case_expr_id(case_expr_id))
- YYABORT;
-
- i= new sp_instr_set_case_expr(sp->instructions(),
- parsing_ctx,
- case_expr_id,
- $3,
- lex);
- sp->add_cont_backpatch(i);
- sp->add_instr(i);
- sp->m_flags|= sp_head::IN_SIMPLE_CASE;
- sp->restore_lex(YYTHD);
- }
- sp_case END CASE_SYM
- {
- Lex->spcont->pop_case_expr_id();
- Lex->sphead->do_cont_backpatch();
- }
+ | case_stmt_specification
| sp_labeled_control
{}
| { /* Unlabeled controls get a secret label. */
@@ -2240,72 +2386,114 @@ sp_elseifs:
| ELSE sp_proc_stmts1
;
-sp_case:
- { Lex->sphead->reset_lex(YYTHD); }
- expr THEN_SYM
- {
- LEX *lex= Lex;
- sp_head *sp= lex->sphead;
- sp_pcontext *ctx= Lex->spcont;
- uint ip= sp->instructions();
- sp_instr_jump_if_not *i;
-
- if (! (sp->m_flags & sp_head::IN_SIMPLE_CASE))
- i= new sp_instr_jump_if_not(ip, ctx, $2, lex);
- else
- { /* Simple case: <caseval> = <whenval> */
+case_stmt_specification:
+ simple_case_stmt
+ | searched_case_stmt
+ ;
- Item_case_expr *var;
- Item *expr;
+simple_case_stmt:
+ CASE_SYM
+ {
+ LEX *lex= Lex;
+ case_stmt_action_case(lex);
+ lex->sphead->reset_lex(YYTHD); /* For expr $3 */
+ }
+ expr
+ {
+ LEX *lex= Lex;
+ if (case_stmt_action_expr(lex, $3))
+ YYABORT;
- var= new Item_case_expr(ctx->get_current_case_expr_id());
+ lex->sphead->restore_lex(YYTHD); /* For expr $3 */
+ }
+ simple_when_clause_list
+ else_clause_opt
+ END
+ CASE_SYM
+ {
+ LEX *lex= Lex;
+ case_stmt_action_end_case(lex, true);
+ }
+ ;
-#ifndef DBUG_OFF
- if (var)
- var->m_sp= sp;
-#endif
+searched_case_stmt:
+ CASE_SYM
+ {
+ LEX *lex= Lex;
+ case_stmt_action_case(lex);
+ }
+ searched_when_clause_list
+ else_clause_opt
+ END
+ CASE_SYM
+ {
+ LEX *lex= Lex;
+ case_stmt_action_end_case(lex, false);
+ }
+ ;
- expr= new Item_func_eq(var, $2);
+simple_when_clause_list:
+ simple_when_clause
+ | simple_when_clause_list simple_when_clause
+ ;
- i= new sp_instr_jump_if_not(ip, ctx, expr, lex);
- }
- sp->push_backpatch(i, ctx->push_label((char *)"", 0));
- sp->add_cont_backpatch(i);
- sp->add_instr(i);
- sp->restore_lex(YYTHD);
- }
- sp_proc_stmts1
- {
- sp_head *sp= Lex->sphead;
- sp_pcontext *ctx= Lex->spcont;
- uint ip= sp->instructions();
- sp_instr_jump *i = new sp_instr_jump(ip, ctx);
+searched_when_clause_list:
+ searched_when_clause
+ | searched_when_clause_list searched_when_clause
+ ;
- sp->add_instr(i);
- sp->backpatch(ctx->pop_label());
- sp->push_backpatch(i, ctx->push_label((char *)"", 0));
- }
- sp_whens
- {
- LEX *lex= Lex;
+simple_when_clause:
+ WHEN_SYM
+ {
+ Lex->sphead->reset_lex(YYTHD); /* For expr $3 */
+ }
+ expr
+ {
+ /* Simple case: <caseval> = <whenval> */
- lex->sphead->backpatch(lex->spcont->pop_label());
- }
- ;
+ LEX *lex= Lex;
+ case_stmt_action_when(lex, $3, true);
+ lex->sphead->restore_lex(YYTHD); /* For expr $3 */
+ }
+ THEN_SYM
+ sp_proc_stmts1
+ {
+ LEX *lex= Lex;
+ case_stmt_action_then(lex);
+ }
+ ;
-sp_whens:
- /* Empty */
- {
- sp_head *sp= Lex->sphead;
- uint ip= sp->instructions();
- sp_instr_error *i= new sp_instr_error(ip, Lex->spcont,
- ER_SP_CASE_NOT_FOUND);
+searched_when_clause:
+ WHEN_SYM
+ {
+ Lex->sphead->reset_lex(YYTHD); /* For expr $3 */
+ }
+ expr
+ {
+ LEX *lex= Lex;
+ case_stmt_action_when(lex, $3, false);
+ lex->sphead->restore_lex(YYTHD); /* For expr $3 */
+ }
+ THEN_SYM
+ sp_proc_stmts1
+ {
+ LEX *lex= Lex;
+ case_stmt_action_then(lex);
+ }
+ ;
- sp->add_instr(i);
- }
- | ELSE sp_proc_stmts1 {}
- | WHEN_SYM sp_case {}
- ;
+else_clause_opt:
+ /* empty */
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint ip= sp->instructions();
+ sp_instr_error *i= new sp_instr_error(ip, lex->spcont,
+ ER_SP_CASE_NOT_FOUND);
+ sp->add_instr(i);
+ }
+ | ELSE sp_proc_stmts1
+ ;
sp_labeled_control:
label_ident ':'
@@ -4372,8 +4560,8 @@ simple_expr:
if (!$$)
YYABORT;
}
- | CASE_SYM opt_expr WHEN_SYM when_list opt_else END
- { $$= new Item_func_case(* $4, $2, $5 ); }
+ | CASE_SYM opt_expr when_list opt_else END
+ { $$= new Item_func_case(* $3, $2, $4 ); }
| CONVERT_SYM '(' expr ',' cast_type ')'
{
$$= create_func_cast($3, $5,
@@ -5182,23 +5370,19 @@ opt_else:
| ELSE expr { $$= $2; };
when_list:
- { Select->when_list.push_front(new List<Item>); }
- when_list2
- { $$= Select->when_list.pop(); };
-
-when_list2:
- expr THEN_SYM expr
- {
- SELECT_LEX *sel=Select;
- sel->when_list.head()->push_back($1);
- sel->when_list.head()->push_back($3);
- }
- | when_list2 WHEN_SYM expr THEN_SYM expr
- {
- SELECT_LEX *sel=Select;
- sel->when_list.head()->push_back($3);
- sel->when_list.head()->push_back($5);
- };
+ WHEN_SYM expr THEN_SYM expr
+ {
+ $$= new List<Item>;
+ $$->push_back($2);
+ $$->push_back($4);
+ }
+ | when_list WHEN_SYM expr THEN_SYM expr
+ {
+ $1->push_back($3);
+ $1->push_back($5);
+ $$= $1;
+ }
+ ;
/* Warning - may return NULL in case of incomplete SELECT */
table_ref: