summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/Makefile.am7
-rw-r--r--sql/filesort.cc66
-rw-r--r--sql/ha_berkeley.cc2
-rw-r--r--sql/ha_innodb.cc4
-rw-r--r--sql/ha_myisam.cc8
-rw-r--r--sql/item.cc64
-rw-r--r--sql/item.h91
-rw-r--r--sql/item_cmpfunc.cc2
-rw-r--r--sql/item_create.cc19
-rw-r--r--sql/item_create.h1
-rw-r--r--sql/item_func.cc117
-rw-r--r--sql/item_func.h71
-rw-r--r--sql/item_subselect.cc46
-rw-r--r--sql/item_sum.cc127
-rw-r--r--sql/item_sum.h39
-rw-r--r--sql/item_timefunc.cc307
-rw-r--r--sql/item_timefunc.h27
-rw-r--r--sql/lex.h54
-rw-r--r--sql/lock.cc2
-rw-r--r--sql/log.cc16
-rw-r--r--sql/log_event.cc2
-rw-r--r--sql/mysql_priv.h14
-rw-r--r--sql/mysqld.cc86
-rw-r--r--sql/net_serv.cc1
-rw-r--r--sql/opt_ft.cc2
-rw-r--r--sql/opt_ft.h7
-rw-r--r--sql/opt_range.cc1004
-rw-r--r--sql/opt_range.h193
-rw-r--r--sql/protocol.cc26
-rw-r--r--sql/protocol.h8
-rw-r--r--sql/protocol_cursor.cc12
-rw-r--r--sql/records.cc2
-rw-r--r--sql/repl_failsafe.cc2
-rw-r--r--sql/set_var.cc28
-rw-r--r--sql/share/czech/errmsg.txt33
-rw-r--r--sql/share/danish/errmsg.txt33
-rw-r--r--sql/share/dutch/errmsg.txt33
-rw-r--r--sql/share/english/errmsg.txt33
-rw-r--r--sql/share/estonian/errmsg.txt33
-rw-r--r--sql/share/french/errmsg.txt33
-rw-r--r--sql/share/german/errmsg.txt33
-rw-r--r--sql/share/greek/errmsg.txt33
-rw-r--r--sql/share/hungarian/errmsg.txt33
-rw-r--r--sql/share/italian/errmsg.txt33
-rw-r--r--sql/share/japanese/errmsg.txt33
-rw-r--r--sql/share/korean/errmsg.txt33
-rw-r--r--sql/share/norwegian-ny/errmsg.txt33
-rw-r--r--sql/share/norwegian/errmsg.txt33
-rw-r--r--sql/share/polish/errmsg.txt33
-rw-r--r--sql/share/portuguese/errmsg.txt33
-rw-r--r--sql/share/romanian/errmsg.txt33
-rw-r--r--sql/share/russian/errmsg.txt33
-rw-r--r--sql/share/serbian/errmsg.txt33
-rw-r--r--sql/share/slovak/errmsg.txt33
-rw-r--r--sql/share/spanish/errmsg.txt33
-rw-r--r--sql/share/swedish/errmsg.txt33
-rw-r--r--sql/share/ukrainian/errmsg.txt33
-rw-r--r--sql/slave.cc12
-rw-r--r--sql/sp.cc853
-rw-r--r--sql/sp.h86
-rw-r--r--sql/sp_cache.cc156
-rw-r--r--sql/sp_cache.h104
-rw-r--r--sql/sp_head.cc1140
-rw-r--r--sql/sp_head.h659
-rw-r--r--sql/sp_pcontext.cc246
-rw-r--r--sql/sp_pcontext.h256
-rw-r--r--sql/sp_rcontext.cc245
-rw-r--r--sql/sp_rcontext.h252
-rw-r--r--sql/sql_acl.cc103
-rw-r--r--sql/sql_acl.h1
-rw-r--r--sql/sql_base.cc8
-rw-r--r--sql/sql_cache.cc27
-rw-r--r--sql/sql_class.cc224
-rw-r--r--sql/sql_class.h197
-rw-r--r--sql/sql_db.cc3
-rw-r--r--sql/sql_delete.cc31
-rw-r--r--sql/sql_error.cc2
-rw-r--r--sql/sql_insert.cc69
-rw-r--r--sql/sql_lex.cc26
-rw-r--r--sql/sql_lex.h45
-rw-r--r--sql/sql_list.h7
-rw-r--r--sql/sql_load.cc7
-rw-r--r--sql/sql_parse.cc476
-rw-r--r--sql/sql_prepare.cc55
-rw-r--r--sql/sql_rename.cc1
-rw-r--r--sql/sql_repl.cc32
-rw-r--r--sql/sql_repl.h2
-rw-r--r--sql/sql_select.cc156
-rw-r--r--sql/sql_select.h5
-rw-r--r--sql/sql_show.cc2
-rw-r--r--sql/sql_sort.h1
-rw-r--r--sql/sql_table.cc43
-rw-r--r--sql/sql_test.cc32
-rw-r--r--sql/sql_union.cc26
-rw-r--r--sql/sql_update.cc64
-rw-r--r--sql/sql_yacc.yy1357
-rw-r--r--sql/table.h1
-rw-r--r--sql/udf_example.cc45
-rw-r--r--sql/uniques.cc234
99 files changed, 9646 insertions, 861 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am
index e2ab55da6a2..7353b58df07 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -57,7 +57,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
lex.h lex_symbol.h sql_acl.h sql_crypt.h \
log_event.h sql_repl.h slave.h \
stacktrace.h sql_sort.h sql_cache.h set_var.h \
- spatial.h gstream.h client_settings.h
+ spatial.h gstream.h client_settings.h \
+ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \
item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \
@@ -86,7 +87,9 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \
slave.cc sql_repl.cc sql_union.cc sql_derived.cc \
client.c sql_client.cc mini_client_errors.c pack.c\
stacktrace.c repl_failsafe.h repl_failsafe.cc sql_olap.cc\
- gstream.cc spatial.cc sql_help.cc protocol_cursor.cc
+ gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \
+ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \
+ sp_cache.cc
gen_lex_hash_SOURCES = gen_lex_hash.cc
gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS)
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 1b481924690..3722e8be32c 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -330,7 +330,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
byte *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH];
my_off_t record;
TABLE *sort_form;
- volatile bool *killed= &current_thd->killed;
+ volatile THD::killed_state *killed= &current_thd->killed;
handler *file;
DBUG_ENTER("find_all_keys");
DBUG_PRINT("info",("using: %s",(select?select->quick?"ranges":"where":"every row")));
@@ -756,6 +756,39 @@ uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
} /* read_to_buffer */
+/*
+ Put all room used by freed buffer to use in adjacent buffer. Note, that
+ we can't simply distribute memory evenly between all buffers, because
+ new areas must not overlap with old ones.
+ SYNOPSYS
+ reuse_freed_buff()
+ queue IN list of non-empty buffers, without freed buffer
+ reuse IN empty buffer
+ key_length IN key length
+*/
+
+void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
+{
+ uchar *reuse_end= reuse->base + reuse->max_keys * key_length;
+ for (uint i= 0; i < queue->elements; ++i)
+ {
+ BUFFPEK *bp= (BUFFPEK *) queue_element(queue, i);
+ if (bp->base + bp->max_keys * key_length == reuse->base)
+ {
+ bp->max_keys+= reuse->max_keys;
+ return;
+ }
+ else if (bp->base == reuse_end)
+ {
+ bp->base= reuse->base;
+ bp->max_keys+= reuse->max_keys;
+ return;
+ }
+ }
+ DBUG_ASSERT(0);
+}
+
+
/*
Merge buffers to one buffer
*/
@@ -774,15 +807,15 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
BUFFPEK *buffpek,**refpek;
QUEUE queue;
qsort2_cmp cmp;
- volatile bool *killed= &current_thd->killed;
- bool not_killable;
+ volatile THD::killed_state *killed= &current_thd->killed;
+ THD::killed_state not_killable;
DBUG_ENTER("merge_buffers");
statistic_increment(filesort_merge_passes, &LOCK_status);
if (param->not_killable)
{
killed= &not_killable;
- not_killable= 0;
+ not_killable=THD::NOT_KILLED;
}
error=0;
@@ -881,29 +914,8 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
if (!(error= (int) read_to_buffer(from_file,buffpek,
rec_length)))
{
- uchar *base= buffpek->base;
- ulong max_keys= buffpek->max_keys;
-
VOID(queue_remove(&queue,0));
-
- /* Put room used by buffer to use in other buffer */
- for (refpek= (BUFFPEK**) &queue_top(&queue);
- refpek <= (BUFFPEK**) &queue_end(&queue);
- refpek++)
- {
- buffpek= *refpek;
- if (buffpek->base+buffpek->max_keys*rec_length == base)
- {
- buffpek->max_keys+= max_keys;
- break;
- }
- else if (base+max_keys*rec_length == buffpek->base)
- {
- buffpek->base= base;
- buffpek->max_keys+= max_keys;
- break;
- }
- }
+ reuse_freed_buff(&queue, buffpek, rec_length);
break; /* One buffer have been removed */
}
else if (error == -1)
@@ -1128,7 +1140,7 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
The fact is the filter 'field->query_id != thd->query_id'
doesn't work for alter table
*/
- if (thd->lex.sql_command != SQLCOM_SELECT)
+ if (thd->lex->sql_command != SQLCOM_SELECT)
return 0;
for (pfield= ptabfield; (field= *pfield) ; pfield++)
{
diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc
index e87e31708f3..c4735403267 100644
--- a/sql/ha_berkeley.cc
+++ b/sql/ha_berkeley.cc
@@ -2113,7 +2113,7 @@ static void print_msg(THD *thd, const char *table_name, const char *op_name,
protocol->store(msg_type);
protocol->store(msgbuf);
if (protocol->write())
- thd->killed=1;
+ thd->killed=THD::KILL_CONNECTION;
}
#endif
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 419eac6d1c3..7796c063424 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -2253,8 +2253,8 @@ ha_innobase::write_row(
skip_auto_inc_decr = FALSE;
if (error == DB_DUPLICATE_KEY
- && (user_thd->lex.sql_command == SQLCOM_REPLACE
- || user_thd->lex.sql_command
+ && (user_thd->lex->sql_command == SQLCOM_REPLACE
+ || user_thd->lex->sql_command
== SQLCOM_REPLACE_SELECT)) {
skip_auto_inc_decr= TRUE;
diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc
index a10eeb3c934..6356f209ba2 100644
--- a/sql/ha_myisam.cc
+++ b/sql/ha_myisam.cc
@@ -89,9 +89,9 @@ static void mi_check_print_msg(MI_CHECK *param, const char* msg_type,
extern "C" {
-volatile bool *killed_ptr(MI_CHECK *param)
+int *killed_ptr(void *thd)
{
- return &(((THD *)(param->thd))->killed);
+ return (int*)&((THD *)thd)->killed;
}
void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
@@ -390,7 +390,7 @@ int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt)
int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt)
{
HA_CHECK_OPT tmp_check_opt;
- char* backup_dir = thd->lex.backup_dir;
+ char* backup_dir = thd->lex->backup_dir;
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
char* table_name = table->real_name;
int error;
@@ -430,7 +430,7 @@ int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt)
int ha_myisam::backup(THD* thd, HA_CHECK_OPT *check_opt)
{
- char* backup_dir = thd->lex.backup_dir;
+ char* backup_dir = thd->lex->backup_dir;
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
char* table_name = table->real_name;
int error;
diff --git a/sql/item.cc b/sql/item.cc
index 301d2990e0b..9ac4fbd6840 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -22,6 +22,7 @@
#include "mysql_priv.h"
#include <m_ctype.h>
#include "my_dir.h"
+#include "sp_rcontext.h"
static void mark_as_dependent(THD *thd,
SELECT_LEX *last, SELECT_LEX *current,
@@ -39,7 +40,7 @@ void item_init(void)
}
Item::Item():
- fixed(0)
+ name_length(0), fixed(0)
{
marker= 0;
maybe_null=null_value=with_sum_func=unsigned_flag=0;
@@ -56,13 +57,13 @@ Item::Item():
command => we should check thd->lex.current_select on zero (thd->lex
can be uninitialised)
*/
- if (thd->lex.current_select)
+ if (thd->lex->current_select)
{
SELECT_LEX_NODE::enum_parsing_place place=
- thd->lex.current_select->parsing_place;
+ thd->lex->current_select->parsing_place;
if (place == SELECT_LEX_NODE::SELECT_LIST ||
place == SELECT_LEX_NODE::IN_HAVING)
- thd->lex.current_select->select_n_having_items++;
+ thd->lex->current_select->select_n_having_items++;
}
}
@@ -144,6 +145,7 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
{
/* Empty string, used by AS or internal function like last_insert_id() */
name= (char*) str;
+ name_length= 0;
return;
}
while (length && !my_isgraph(cs,*str))
@@ -154,12 +156,12 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
if (!my_charset_same(cs, system_charset_info))
{
uint32 res_length;
- name= sql_strmake_with_convert(str, length, cs,
+ name= sql_strmake_with_convert(str, name_length= length, cs,
MAX_ALIAS_NAME, system_charset_info,
&res_length);
}
else
- name=sql_strmake(str, min(length,MAX_ALIAS_NAME));
+ name= sql_strmake(str, (name_length= min(length,MAX_ALIAS_NAME)));
}
@@ -229,6 +231,34 @@ CHARSET_INFO * Item::default_charset() const
return current_thd->variables.collation_connection;
}
+
+Item *
+Item_splocal::this_item()
+{
+ THD *thd= current_thd;
+
+ return thd->spcont->get_item(m_offset);
+}
+
+Item *
+Item_splocal::this_const_item() const
+{
+ THD *thd= current_thd;
+
+ return thd->spcont->get_item(m_offset);
+}
+
+Item::Type
+Item_splocal::type() const
+{
+ THD *thd= current_thd;
+
+ if (thd->spcont)
+ return thd->spcont->get_item(m_offset)->type();
+ return NULL_ITEM; // Anything but SUBSELECT_ITEM
+}
+
+
bool DTCollation::aggregate(DTCollation &dt)
{
if (!my_charset_same(collation, dt.collation))
@@ -802,7 +832,7 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
// store pointer on SELECT_LEX from wich item is dependent
item->depended_from= last;
current->mark_as_dependent(last);
- if (thd->lex.describe & DESCRIBE_EXTENDED)
+ if (thd->lex->describe & DESCRIBE_EXTENDED)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
sprintf(warn_buff, ER(ER_WARN_FIELD_RESOLVED),
@@ -843,7 +873,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
Item **refer= (Item **)not_found_item;
uint counter;
// Prevent using outer fields in subselects, that is not supported now
- SELECT_LEX *cursel=(SELECT_LEX *) thd->lex.current_select;
+ SELECT_LEX *cursel=(SELECT_LEX *) thd->lex->current_select;
if (cursel->master_unit()->first_select()->linkage != DERIVED_TABLE_TYPE)
{
SELECT_LEX_UNIT *prev_unit= cursel->master_unit();
@@ -1439,7 +1469,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
{
TABLE_LIST *where= 0, *table_list;
bool upward_lookup= 0;
- SELECT_LEX_UNIT *prev_unit= thd->lex.current_select->master_unit();
+ SELECT_LEX_UNIT *prev_unit= thd->lex->current_select->master_unit();
SELECT_LEX *sl= prev_unit->outer_select();
/*
Finding only in current select will be performed for selects that have
@@ -1447,10 +1477,10 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
fields for now)
*/
if ((ref= find_item_in_list(this,
- *(thd->lex.current_select->get_item_list()),
+ *(thd->lex->current_select->get_item_list()),
&counter,
((sl &&
- thd->lex.current_select->master_unit()->
+ thd->lex->current_select->master_unit()->
first_select()->linkage !=
DERIVED_TABLE_TYPE) ?
REPORT_EXCEPT_NOT_FOUND :
@@ -1526,7 +1556,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
{
// Call to report error
find_item_in_list(this,
- *(thd->lex.current_select->get_item_list()),
+ *(thd->lex->current_select->get_item_list()),
&counter,
REPORT_ALL_ERRORS);
}
@@ -1539,7 +1569,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
Item_field* fld;
if (!((*reference)= fld= new Item_field(tmp)))
return 1;
- mark_as_dependent(thd, last, thd->lex.current_select, fld);
+ mark_as_dependent(thd, last, thd->lex->current_select, fld);
return 0;
}
else
@@ -1550,7 +1580,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
"forward reference in item list");
return -1;
}
- mark_as_dependent(thd, last, thd->lex.current_select,
+ mark_as_dependent(thd, last, thd->lex->current_select,
this);
ref= last->ref_pointer_array + counter;
}
@@ -1565,7 +1595,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
"forward reference in item list");
return -1;
}
- ref= thd->lex.current_select->ref_pointer_array + counter;
+ ref= thd->lex->current_select->ref_pointer_array + counter;
}
}
@@ -1578,8 +1608,8 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
*/
if (((*ref)->with_sum_func && name &&
(depended_from ||
- !(thd->lex.current_select->linkage != GLOBAL_OPTIONS_TYPE &&
- thd->lex.current_select->having_fix_field))) ||
+ !(thd->lex->current_select->linkage != GLOBAL_OPTIONS_TYPE &&
+ thd->lex->current_select->having_fix_field))) ||
!(*ref)->fixed)
{
my_error(ER_ILLEGAL_REFERENCE, MYF(0), name,
diff --git a/sql/item.h b/sql/item.h
index c6258518213..4c6be932376 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -106,6 +106,7 @@ public:
my_string name; /* Name from select */
Item *next;
uint32 max_length;
+ uint name_length; /* Length of name */
uint8 marker,decimals;
my_bool maybe_null; /* If item may be null */
my_bool null_value; /* if item is null */
@@ -213,6 +214,9 @@ public:
virtual bool remove_dependence_processor(byte * arg) { return 0; }
virtual bool remove_fixed(byte * arg) { fixed= 0; return 0; }
+ virtual Item *this_item() { return this; } /* For SPs mostly. */
+ virtual Item *this_const_item() const { return const_cast<Item*>(this); } /* For SPs mostly. */
+
// Row emulation
virtual uint cols() { return 1; }
virtual Item* el(uint i) { return this; }
@@ -230,6 +234,93 @@ public:
};
+// A local SP variable (incl. parameters), used in runtime
+class Item_splocal : public Item
+{
+private:
+
+ uint m_offset;
+ LEX_STRING m_name;
+
+public:
+
+ Item_splocal(LEX_STRING name, uint offset)
+ : m_offset(offset), m_name(name)
+ {
+ Item::maybe_null= TRUE;
+ }
+
+ Item *this_item();
+ Item *this_const_item() const;
+
+ inline uint get_offset()
+ {
+ return m_offset;
+ }
+
+ // Abstract methods inherited from Item. Just defer the call to
+ // the item in the frame
+ enum Type type() const;
+
+ inline double val()
+ {
+ Item *it= this_item();
+ double ret= it->val();
+ Item::null_value= it->null_value;
+ return ret;
+ }
+
+ inline longlong val_int()
+ {
+ Item *it= this_item();
+ longlong ret= it->val_int();
+ Item::null_value= it->null_value;
+ return ret;
+ }
+
+ inline String *val_str(String *sp)
+ {
+ Item *it= this_item();
+ String *ret= it->val_str(sp);
+ Item::null_value= it->null_value;
+ return ret;
+ }
+
+ inline bool is_null()
+ {
+ Item *it= this_item();
+ bool ret= it->is_null();
+ Item::null_value= it->null_value;
+ return ret;
+ }
+
+ inline void make_field(Send_field *field)
+ {
+ this_item()->make_field(field);
+ }
+
+ inline Item_result result_type() const
+ {
+ return this_const_item()->result_type();
+ }
+
+ inline bool const_item() const
+ {
+ return FALSE;
+ }
+
+ inline int save_in_field(Field *field, bool no_conversions)
+ {
+ return this_item()->save_in_field(field, no_conversions);
+ }
+
+ inline void print(String *str)
+ {
+ str->append(m_name.str, m_name.length);
+ }
+};
+
+
class st_select_lex;
class Item_ident :public Item
{
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 2252c1a0e40..bd88c79c8fb 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1723,7 +1723,7 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
if (item->maybe_null)
maybe_null=1;
}
- thd->lex.current_select->cond_count+=list.elements;
+ thd->lex->current_select->cond_count+=list.elements;
fix_length_and_dec();
fixed= 1;
return 0;
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 1c0588b06a8..7a548962cde 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -76,7 +76,7 @@ Item *create_func_ceiling(Item* a)
Item *create_func_connection_id(void)
{
THD *thd=current_thd;
- thd->lex.safe_to_cache_query=0;
+ thd->lex->safe_to_cache_query=0;
return new Item_int(NullS,(longlong)
((thd->slave_thread) ?
thd->variables.pseudo_thread_id :
@@ -148,7 +148,7 @@ Item *create_func_floor(Item* a)
Item *create_func_found_rows(void)
{
THD *thd=current_thd;
- thd->lex.safe_to_cache_query=0;
+ thd->lex->safe_to_cache_query=0;
return new Item_int(NullS,(longlong) thd->found_rows(),21);
}
@@ -159,7 +159,7 @@ Item *create_func_from_days(Item* a)
Item *create_func_get_lock(Item* a, Item *b)
{
- current_thd->lex.uncacheable(UNCACHEABLE_SIDEEFFECT);
+ current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
return new Item_func_get_lock(a, b);
}
@@ -312,11 +312,6 @@ Item *create_func_current_user()
system_charset_info);
}
-Item *create_func_quarter(Item* a)
-{
- return new Item_func_quarter(a);
-}
-
Item *create_func_radians(Item *a)
{
return new Item_func_units((char*) "radians",a,M_PI/180,0.0);
@@ -324,7 +319,7 @@ Item *create_func_radians(Item *a)
Item *create_func_release_lock(Item* a)
{
- current_thd->lex.uncacheable(UNCACHEABLE_SIDEEFFECT);
+ current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
return new Item_func_release_lock(a);
}
@@ -445,7 +440,7 @@ Item *create_func_year(Item* a)
Item *create_load_file(Item* a)
{
- current_thd->lex.uncacheable(UNCACHEABLE_SIDEEFFECT);
+ current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
return new Item_load_file(a);
}
@@ -472,13 +467,13 @@ Item *create_func_cast(Item *a, Cast_target cast_type, int len,
Item *create_func_is_free_lock(Item* a)
{
- current_thd->lex.uncacheable(UNCACHEABLE_SIDEEFFECT);
+ current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
return new Item_func_is_free_lock(a);
}
Item *create_func_is_used_lock(Item* a)
{
- current_thd->lex.uncacheable(UNCACHEABLE_SIDEEFFECT);
+ current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
return new Item_func_is_used_lock(a);
}
diff --git a/sql/item_create.h b/sql/item_create.h
index 083868d87a6..b445eab3bac 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -72,7 +72,6 @@ Item *create_func_period_diff(Item* a, Item *b);
Item *create_func_pi(void);
Item *create_func_pow(Item* a, Item *b);
Item *create_func_current_user(void);
-Item *create_func_quarter(Item* a);
Item *create_func_radians(Item *a);
Item *create_func_release_lock(Item* a);
Item *create_func_repeat(Item* a, Item *b);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index b9cbb38db1a..600258159e6 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -28,6 +28,9 @@
#include <time.h>
#include <ft_global.h>
+#include "sp_head.h"
+#include "sp_rcontext.h"
+#include "sp.h"
static void my_coll_agg_error(DTCollation &c1, DTCollation &c2,
const char *fname)
@@ -1489,11 +1492,16 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func,
const_item_cache&=item->const_item();
f_args.arg_type[i]=item->result_type();
}
+ //TODO: why all folowing memory is not allocated with 1 call of sql_alloc?
if (!(buffers=new String[arg_count]) ||
!(f_args.args= (char**) sql_alloc(arg_count * sizeof(char *))) ||
- !(f_args.lengths=(ulong*) sql_alloc(arg_count * sizeof(long))) ||
- !(f_args.maybe_null=(char*) sql_alloc(arg_count * sizeof(char))) ||
- !(num_buffer= (char*) sql_alloc(ALIGN_SIZE(sizeof(double))*arg_count)))
+ !(f_args.lengths= (ulong*) sql_alloc(arg_count * sizeof(long))) ||
+ !(f_args.maybe_null= (char*) sql_alloc(arg_count * sizeof(char))) ||
+ !(num_buffer= (char*) sql_alloc(arg_count *
+ ALIGN_SIZE(sizeof(double)))) ||
+ !(f_args.attributes= (char**) sql_alloc(arg_count * sizeof(char *))) ||
+ !(f_args.attribute_lengths= (ulong*) sql_alloc(arg_count *
+ sizeof(long))))
{
free_udf(u_d);
DBUG_RETURN(1);
@@ -1512,8 +1520,10 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func,
for (uint i=0; i < arg_count; i++)
{
f_args.args[i]=0;
- f_args.lengths[i]=arguments[i]->max_length;
- f_args.maybe_null[i]=(char) arguments[i]->maybe_null;
+ f_args.lengths[i]= arguments[i]->max_length;
+ f_args.maybe_null[i]= (char) arguments[i]->maybe_null;
+ f_args.attributes[i]= arguments[i]->name;
+ f_args.attribute_lengths[i]= arguments[i]->name_length;
switch(arguments[i]->type()) {
case Item::STRING_ITEM: // Constant string !
@@ -2480,8 +2490,7 @@ void Item_func_get_user_var::fix_length_and_dec()
if (!(var_entry= get_variable(&thd->user_vars, name, 0)))
null_value= 1;
-
- if (!(opt_bin_log && is_update_query(thd->lex.sql_command)))
+ if (!(opt_bin_log && is_update_query(thd->lex->sql_command)))
return;
if (!var_entry)
@@ -2950,7 +2959,7 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
}
if (!(item=var->item(thd, var_type, component_name)))
return 0; // Impossible
- thd->lex.uncacheable(UNCACHEABLE_SIDEEFFECT);
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
buff[0]='@';
buff[1]='@';
pos=buff+2;
@@ -2990,7 +2999,7 @@ Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name,
DBUG_ASSERT(var != 0);
if (!(item=var->item(thd, var_type, &null_lex_string)))
return 0; // Impossible
- thd->lex.uncacheable(UNCACHEABLE_SIDEEFFECT);
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
item->set_name(item_name, 0, system_charset_info); // Will use original name
return item;
}
@@ -3050,3 +3059,93 @@ longlong Item_func_is_used_lock::val_int()
null_value=0;
return ull->thread_id;
}
+
+int
+Item_func_sp::execute(Item **itp)
+{
+ DBUG_ENTER("Item_func_sp::execute");
+ THD *thd= current_thd;
+ int res;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ st_sp_security_context save_ctx;
+#endif
+
+ if (! m_sp)
+ m_sp= sp_find_function(thd, &m_name);
+ if (! m_sp)
+ DBUG_RETURN(-1);
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_change_security_context(thd, m_sp, &save_ctx);
+#endif
+
+ res= m_sp->execute_function(thd, args, arg_count, itp);
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_restore_security_context(thd, m_sp, &save_ctx);
+#endif
+
+ DBUG_RETURN(res);
+}
+
+enum enum_field_types
+Item_func_sp::field_type() const
+{
+ DBUG_ENTER("Item_func_sp::field_type");
+
+ if (! m_sp)
+ m_sp= sp_find_function(current_thd, const_cast<LEX_STRING*>(&m_name));
+ if (m_sp)
+ {
+ DBUG_PRINT("info", ("m_returns = %d", m_sp->m_returns));
+ DBUG_RETURN(m_sp->m_returns);
+ }
+ DBUG_RETURN(MYSQL_TYPE_STRING);
+}
+
+Item_result
+Item_func_sp::result_type() const
+{
+ DBUG_ENTER("Item_func_sp::result_type");
+ DBUG_PRINT("info", ("m_sp = %p", m_sp));
+
+ if (! m_sp)
+ m_sp= sp_find_function(current_thd, const_cast<LEX_STRING*>(&m_name));
+ if (m_sp)
+ {
+ DBUG_RETURN(m_sp->result());
+ }
+ DBUG_RETURN(STRING_RESULT);
+}
+
+void
+Item_func_sp::fix_length_and_dec()
+{
+ DBUG_ENTER("Item_func_sp::fix_length_and_dec");
+
+ if (! m_sp)
+ m_sp= sp_find_function(current_thd, &m_name);
+ if (m_sp)
+ {
+ switch (m_sp->result()) {
+ case STRING_RESULT:
+ maybe_null= 1;
+ max_length= 0;
+ break;
+ case REAL_RESULT:
+ decimals= NOT_FIXED_DEC;
+ max_length= float_length(decimals);
+ break;
+ case INT_RESULT:
+ decimals= 0;
+ max_length= 21;
+ break;
+ case ROW_RESULT:
+ default:
+ // This case should never be choosen
+ DBUG_ASSERT(0);
+ break;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/item_func.h b/sql/item_func.h
index ac67b5a4065..c2aa62ec2d7 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -47,7 +47,8 @@ public:
SP_CONTAINS_FUNC,SP_OVERLAPS_FUNC,
SP_STARTPOINT,SP_ENDPOINT,SP_EXTERIORRING,
SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN,
- NOT_FUNC, NOT_ALL_FUNC};
+ NOT_FUNC, NOT_ALL_FUNC,
+ GUSERVAR_FUNC};
enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL };
enum Type type() const { return FUNC_ITEM; }
virtual enum Functype functype() const { return UNKNOWN_FUNC; }
@@ -950,6 +951,8 @@ class Item_func_get_user_var :public Item_func
public:
Item_func_get_user_var(LEX_STRING a):
Item_func(), name(a) {}
+ enum Functype functype() const { return GUSERVAR_FUNC; }
+ LEX_STRING get_name() { return name; }
double val();
longlong val_int();
String *val_str(String* str);
@@ -1060,3 +1063,69 @@ enum Cast_target
ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT,
ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR
};
+
+
+/*
+ *
+ * Stored FUNCTIONs
+ *
+ */
+
+class sp_head;
+
+class Item_func_sp :public Item_func
+{
+private:
+ LEX_STRING m_name;
+ mutable sp_head *m_sp;
+
+ int execute(Item **itp);
+
+public:
+
+ Item_func_sp(LEX_STRING name)
+ :Item_func(), m_name(name), m_sp(NULL)
+ {}
+
+ Item_func_sp(LEX_STRING name, List<Item> &list)
+ :Item_func(list), m_name(name), m_sp(NULL)
+ {}
+
+ virtual ~Item_func_sp()
+ {}
+
+ const char *func_name() const
+ {
+ return m_name.str;
+ }
+
+ enum enum_field_types field_type() const;
+
+ Item_result result_type() const;
+
+ longlong val_int()
+ {
+ return (longlong)Item_func_sp::val();
+ }
+
+ double val()
+ {
+ Item *it;
+
+ if (execute(&it))
+ return 0.0;
+ return it->val();
+ }
+
+ String *val_str(String *str)
+ {
+ Item *it;
+
+ if (execute(&it))
+ return NULL;
+ return it->val_str(str);
+ }
+
+ void fix_length_and_dec();
+
+};
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index bc16fe87c2a..43775e1c96c 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -253,7 +253,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
{
have_to_be_excluded= 1;
- if (join->thd->lex.describe)
+ if (join->thd->lex->describe)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number);
@@ -608,14 +608,14 @@ Item_in_subselect::single_value_transformer(JOIN *join,
subs= new Item_maxmin_subselect(this, select_lex, func->l_op());
}
// left expression belong to outer select
- SELECT_LEX *current= thd_tmp->lex.current_select, *up;
- thd_tmp->lex.current_select= up= current->return_after_parsing();
+ SELECT_LEX *current= thd_tmp->lex->current_select, *up;
+ thd_tmp->lex->current_select= up= current->return_after_parsing();
if (left_expr->fix_fields(thd_tmp, up->get_table_list(), &left_expr))
{
- thd_tmp->lex.current_select= current;
+ thd_tmp->lex->current_select= current;
DBUG_RETURN(RES_ERROR);
}
- thd_tmp->lex.current_select= current;
+ thd_tmp->lex->current_select= current;
substitution= func->create(left_expr, subs);
DBUG_RETURN(RES_OK);
}
@@ -626,16 +626,16 @@ Item_in_subselect::single_value_transformer(JOIN *join,
SELECT_LEX_UNIT *unit= select_lex->master_unit();
substitution= optimizer= new Item_in_optimizer(left_expr, this);
- SELECT_LEX *current= thd_tmp->lex.current_select, *up;
+ SELECT_LEX *current= thd_tmp->lex->current_select, *up;
- thd_tmp->lex.current_select= up= current->return_after_parsing();
+ thd_tmp->lex->current_select= up= current->return_after_parsing();
//optimizer never use Item **ref => we can pass 0 as parameter
if (!optimizer || optimizer->fix_left(thd_tmp, up->get_table_list(), 0))
{
- thd_tmp->lex.current_select= current;
+ thd_tmp->lex->current_select= current;
DBUG_RETURN(RES_ERROR);
}
- thd_tmp->lex.current_select= current;
+ thd_tmp->lex->current_select= current;
/*
As far as Item_ref_in_optimizer do not substitude itself on fix_fields
@@ -726,7 +726,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
// fix_field of item will be done in time of substituting
substitution= item;
have_to_be_excluded= 1;
- if (thd_tmp->lex.describe)
+ if (thd_tmp->lex->describe)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number);
@@ -763,15 +763,15 @@ Item_in_subselect::row_value_transformer(JOIN *join)
SELECT_LEX_UNIT *unit= select_lex->master_unit();
substitution= optimizer= new Item_in_optimizer(left_expr, this);
- SELECT_LEX *current= thd_tmp->lex.current_select, *up;
- thd_tmp->lex.current_select= up= current->return_after_parsing();
+ SELECT_LEX *current= thd_tmp->lex->current_select, *up;
+ thd_tmp->lex->current_select= up= current->return_after_parsing();
//optimizer never use Item **ref => we can pass 0 as parameter
if (!optimizer || optimizer->fix_left(thd_tmp, up->get_table_list(), 0))
{
- thd_tmp->lex.current_select= current;
+ thd_tmp->lex->current_select= current;
DBUG_RETURN(RES_ERROR);
}
- thd_tmp->lex.current_select= current;
+ thd_tmp->lex->current_select= current;
unit->uncacheable|= UNCACHEABLE_DEPENDENT;
}
@@ -913,8 +913,8 @@ int subselect_single_select_engine::prepare()
return 1;
}
prepared= 1;
- SELECT_LEX *save_select= thd->lex.current_select;
- thd->lex.current_select= select_lex;
+ SELECT_LEX *save_select= thd->lex->current_select;
+ thd->lex->current_select= select_lex;
if (join->prepare(&select_lex->ref_pointer_array,
(TABLE_LIST*) select_lex->table_list.first,
select_lex->with_wild,
@@ -927,7 +927,7 @@ int subselect_single_select_engine::prepare()
(ORDER*) 0, select_lex,
select_lex->master_unit()))
return 1;
- thd->lex.current_select= save_select;
+ thd->lex->current_select= save_select;
return 0;
}
@@ -1000,8 +1000,8 @@ int subselect_single_select_engine::exec()
{
DBUG_ENTER("subselect_single_select_engine::exec");
char const *save_where= join->thd->where;
- SELECT_LEX *save_select= join->thd->lex.current_select;
- join->thd->lex.current_select= select_lex;
+ SELECT_LEX *save_select= join->thd->lex->current_select;
+ join->thd->lex->current_select= select_lex;
if (!optimized)
{
optimized=1;
@@ -1009,7 +1009,7 @@ int subselect_single_select_engine::exec()
{
join->thd->where= save_where;
executed= 1;
- join->thd->lex.current_select= save_select;
+ join->thd->lex->current_select= save_select;
DBUG_RETURN(join->error?join->error:1);
}
if (item->engine_changed)
@@ -1022,7 +1022,7 @@ int subselect_single_select_engine::exec()
if (join->reinit())
{
join->thd->where= save_where;
- join->thd->lex.current_select= save_select;
+ join->thd->lex->current_select= save_select;
DBUG_RETURN(1);
}
item->reset();
@@ -1033,11 +1033,11 @@ int subselect_single_select_engine::exec()
join->exec();
executed= 1;
join->thd->where= save_where;
- join->thd->lex.current_select= save_select;
+ join->thd->lex->current_select= save_select;
DBUG_RETURN(join->error||thd->is_fatal_error);
}
join->thd->where= save_where;
- join->thd->lex.current_select= save_select;
+ join->thd->lex->current_select= save_select;
DBUG_RETURN(0);
}
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index a31374a3fc3..3bc52159a96 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -57,7 +57,7 @@ Item_sum::Item_sum(THD *thd, Item_sum &item):
void Item_sum::mark_as_sum_func()
{
- current_thd->lex.current_select->with_sum_func= 1;
+ current_thd->lex->current_select->with_sum_func= 1;
with_sum_func= 1;
}
@@ -265,6 +265,122 @@ double Item_sum_sum::val()
}
+/* Item_sum_sum_distinct */
+
+Item_sum_sum_distinct::Item_sum_sum_distinct(Item *item)
+ :Item_sum_num(item), sum(0.0), tree(0)
+{
+ /*
+ quick_group is an optimizer hint, which means that GROUP BY can be
+ handled with help of index on grouped columns.
+ By setting quick_group to zero we force creation of temporary table
+ to perform GROUP BY.
+ */
+ quick_group= 0;
+}
+
+
+Item_sum_sum_distinct::Item_sum_sum_distinct(THD *thd,
+ Item_sum_sum_distinct &original)
+ :Item_sum_num(thd, original), sum(0.0), tree(0)
+{
+ quick_group= 0;
+}
+
+
+Item_sum_sum_distinct::~Item_sum_sum_distinct()
+{
+ delete tree;
+}
+
+
+Item *
+Item_sum_sum_distinct::copy_or_same(THD *thd)
+{
+ return new (&thd->mem_root) Item_sum_sum_distinct(thd, *this);
+}
+
+C_MODE_START
+
+static int simple_raw_key_cmp(void* arg, const void* key1, const void* key2)
+{
+ return memcmp(key1, key2, *(uint *) arg);
+}
+
+C_MODE_END
+
+bool Item_sum_sum_distinct::setup(THD *thd)
+{
+ SELECT_LEX *select_lex= thd->lex->current_select;
+ /* what does it mean??? */
+ if (select_lex->linkage == GLOBAL_OPTIONS_TYPE)
+ return 1;
+
+ DBUG_ASSERT(tree == 0); /* setup can not be called twice */
+
+ /*
+ Uniques handles all unique elements in a tree until they can't fit in.
+ Then thee tree is dumped to the temporary file.
+ See class Unique for details.
+ */
+ null_value= maybe_null= 1;
+ /*
+ TODO: if underlying item result fits in 4 bytes we can take advantage
+ of it and have tree of long/ulong. It gives 10% performance boost
+ */
+ static uint key_length= sizeof(double);
+ tree= new Unique(simple_raw_key_cmp, &key_length, key_length,
+ thd->variables.max_heap_table_size);
+ return tree == 0;
+}
+
+void Item_sum_sum_distinct::clear()
+{
+ DBUG_ASSERT(tree); /* we always have a tree */
+ null_value= 1;
+ tree->reset();
+}
+
+bool Item_sum_sum_distinct::add()
+{
+ /* args[0]->val() may reset args[0]->null_value */
+ double val= args[0]->val();
+ if (!args[0]->null_value)
+ {
+ DBUG_ASSERT(tree);
+ null_value= 0;
+ if (val)
+ return tree->unique_add(&val);
+ }
+ return 0;
+}
+
+C_MODE_START
+
+static int sum_sum_distinct(void *element, element_count num_of_dups,
+ void *item_sum_sum_distinct)
+{
+ ((Item_sum_sum_distinct *)
+ (item_sum_sum_distinct))->add(* (double *) element);
+ return 0;
+}
+
+C_MODE_END
+
+double Item_sum_sum_distinct::val()
+{
+ /*
+ We don't have a tree only if 'setup()' hasn't been called;
+ this is the case of sql_select.cc:return_zero_rows.
+ */
+ sum= 0.0;
+ if (tree)
+ tree->walk(sum_sum_distinct, (void *) this);
+ return sum;
+}
+
+/* end of Item_sum_sum_distinct */
+
Item *Item_sum_count::copy_or_same(THD* thd)
{
return new (&thd->mem_root) Item_sum_count(thd, *this);
@@ -1016,11 +1132,6 @@ String *Item_variance_field::val_str(String *str)
#include "sql_select.h"
-int simple_raw_key_cmp(void* arg, byte* key1, byte* key2)
-{
- return memcmp(key1, key2, *(uint*) arg);
-}
-
int simple_str_key_cmp(void* arg, byte* key1, byte* key2)
{
Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg;
@@ -1120,7 +1231,7 @@ void Item_sum_count_distinct::make_unique()
bool Item_sum_count_distinct::setup(THD *thd)
{
List<Item> list;
- SELECT_LEX *select_lex= thd->lex.current_select;
+ SELECT_LEX *select_lex= thd->lex->current_select;
if (select_lex->linkage == GLOBAL_OPTIONS_TYPE)
return 1;
@@ -1802,7 +1913,7 @@ bool Item_func_group_concat::setup(THD *thd)
{
DBUG_ENTER("Item_func_group_concat::setup");
List<Item> list;
- SELECT_LEX *select_lex= thd->lex.current_select;
+ SELECT_LEX *select_lex= thd->lex->current_select;
if (select_lex->linkage == GLOBAL_OPTIONS_TYPE)
DBUG_RETURN(1);
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 8065218df97..afed152bb0e 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -27,9 +27,9 @@ class Item_sum :public Item_result_field
{
public:
enum Sumfunctype
- { COUNT_FUNC,COUNT_DISTINCT_FUNC,SUM_FUNC,AVG_FUNC,MIN_FUNC,
- MAX_FUNC, UNIQUE_USERS_FUNC,STD_FUNC,VARIANCE_FUNC,SUM_BIT_FUNC,
- UDF_SUM_FUNC, GROUP_CONCAT_FUNC
+ { COUNT_FUNC, COUNT_DISTINCT_FUNC, SUM_FUNC, SUM_DISTINCT_FUNC, AVG_FUNC,
+ MIN_FUNC, MAX_FUNC, UNIQUE_USERS_FUNC, STD_FUNC, VARIANCE_FUNC,
+ SUM_BIT_FUNC, UDF_SUM_FUNC, GROUP_CONCAT_FUNC
};
Item **args,*tmp_args[2];
@@ -149,6 +149,39 @@ class Item_sum_sum :public Item_sum_num
};
+/*
+ Item_sum_sum_distinct - SELECT SUM(DISTINCT expr) FROM ...
+ support. See also: MySQL manual, chapter 'Adding New Functions To MySQL'
+ and comments in item_sum.cc.
+*/
+
+class Unique;
+
+class Item_sum_sum_distinct :public Item_sum_num
+{
+ double sum;
+ Unique *tree;
+private:
+ Item_sum_sum_distinct(THD *thd, Item_sum_sum_distinct &item);
+public:
+ Item_sum_sum_distinct(Item *item_par);
+ ~Item_sum_sum_distinct();
+
+ bool setup(THD *thd);
+ void clear();
+ bool add();
+ double val();
+
+ inline void add(double val) { sum+= val; }
+ enum Sumfunctype sum_func () const { return SUM_DISTINCT_FUNC; }
+ void reset_field() {} // not used
+ void update_field() {} // not used
+ const char *func_name() const { return "sum_distinct"; }
+ Item *copy_or_same(THD* thd);
+ virtual void no_rows_in_result() {}
+};
+
+
class Item_sum_count :public Item_sum_int
{
longlong count;
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 9799c5814c4..ae9d2be80f6 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -906,9 +906,15 @@ static bool get_interval_value(Item *args,interval_type int_type,
case INTERVAL_YEAR:
interval->year=value;
break;
+ case INTERVAL_QUARTER:
+ interval->month=value*3;
+ break;
case INTERVAL_MONTH:
interval->month=value;
break;
+ case INTERVAL_WEEK:
+ interval->day=value*7;
+ break;
case INTERVAL_DAY:
interval->day=value;
break;
@@ -1584,6 +1590,7 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
goto null_date;
break;
case INTERVAL_DAY:
+ case INTERVAL_WEEK:
period= calc_daynr(ltime->year,ltime->month,ltime->day) +
sign*interval.day;
if (period < 0 || period >= MAX_DAY_NUMBER) // Daynumber from year 0 to 9999-12-31
@@ -1599,6 +1606,7 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
ltime->day=28; // Was leap-year
break;
case INTERVAL_YEAR_MONTH:
+ case INTERVAL_QUARTER:
case INTERVAL_MONTH:
period= (ltime->year*12 + sign*interval.year*12 +
ltime->month-1 + sign*interval.month);
@@ -1660,12 +1668,13 @@ longlong Item_date_add_interval::val_int()
static const char *interval_names[]=
{
- "year", "month", "day", "hour", "minute",
- "second", "microsecond", "year_month",
- "day_hour", "day_minute", "day_second",
- "hour_minute", "hour_second", "minute_second",
- "day_microsecond", "hour_microsecond",
- "minute_microsecond", "second_microsecond"
+ "year", "quarter", "month", "day", "hour",
+ "minute", "week", "second", "microsecond",
+ "year_month", "day_hour", "day_minute",
+ "day_second", "hour_minute", "hour_second",
+ "minute_second", "day_microsecond",
+ "hour_microsecond", "minute_microsecond",
+ "second_microsecond"
};
void Item_date_add_interval::print(String *str)
@@ -1696,7 +1705,9 @@ void Item_extract::fix_length_and_dec()
switch (int_type) {
case INTERVAL_YEAR: max_length=4; date_value=1; break;
case INTERVAL_YEAR_MONTH: max_length=6; date_value=1; break;
+ case INTERVAL_QUARTER: max_length=2; date_value=1; break;
case INTERVAL_MONTH: max_length=2; date_value=1; break;
+ case INTERVAL_WEEK: max_length=2; date_value=1; break;
case INTERVAL_DAY: max_length=2; date_value=1; break;
case INTERVAL_DAY_HOUR: max_length=9; date_value=0; break;
case INTERVAL_DAY_MINUTE: max_length=11; date_value=0; break;
@@ -1719,6 +1730,8 @@ void Item_extract::fix_length_and_dec()
longlong Item_extract::val_int()
{
TIME ltime;
+ uint year;
+ ulong week_format;
long neg;
if (date_value)
{
@@ -1740,7 +1753,16 @@ longlong Item_extract::val_int()
switch (int_type) {
case INTERVAL_YEAR: return ltime.year;
case INTERVAL_YEAR_MONTH: return ltime.year*100L+ltime.month;
+ case INTERVAL_QUARTER: return ltime.month/3 + 1;
case INTERVAL_MONTH: return ltime.month;
+ case INTERVAL_WEEK:
+ {
+ week_format= current_thd->variables.default_week_format;
+ return calc_week(&ltime,
+ (week_format & 2) != 0,
+ (week_format & 1) == 0,
+ &year);
+ }
case INTERVAL_DAY: return ltime.day;
case INTERVAL_DAY_HOUR: return (long) (ltime.day*100L+ltime.hour)*neg;
case INTERVAL_DAY_MINUTE: return (long) (ltime.day*10000L+
@@ -2123,6 +2145,79 @@ void Item_func_add_time::print(String *str)
/*
+ SYNOPSIS
+ calc_time_diff()
+ l_time1 TIME/DATE/DATETIME value
+ l_time2 TIME/DATE/DATETIME value
+ l_sign Can be 1 (operation of addition)
+ or -1 (substraction)
+ seconds_out Returns count of seconds bitween
+ l_time1 and l_time2
+ microseconds_out Returns count of microseconds bitween
+ l_time1 and l_time2.
+
+ DESCRIPTION
+ Calculates difference in seconds(seconds_out)
+ and microseconds(microseconds_out)
+ bitween two TIME/DATE/DATETIME values.
+
+ RETURN VALUES
+ Rertuns sign of difference.
+ 1 means negative result
+ 0 means positive result
+
+*/
+
+bool calc_time_diff(TIME *l_time1,TIME *l_time2, int l_sign,
+ longlong *seconds_out, long *microseconds_out)
+{
+ long days;
+ bool neg;
+ longlong seconds= *seconds_out;
+ long microseconds= *microseconds_out;
+
+ /*
+ We suppose that if first argument is TIMESTAMP_TIME
+ the second argument should be TIMESTAMP_TIME also.
+ We should check it before calc_time_diff call.
+ */
+ if (l_time1->time_type == TIMESTAMP_TIME) // Time value
+ days= l_time1->day - l_sign*l_time2->day;
+ else // DateTime value
+ days= (calc_daynr((uint) l_time1->year,
+ (uint) l_time1->month,
+ (uint) l_time1->day) -
+ l_sign*calc_daynr((uint) l_time2->year,
+ (uint) l_time2->month,
+ (uint) l_time2->day));
+
+ microseconds= l_time1->second_part - l_sign*l_time2->second_part;
+ seconds= ((longlong) days*86400L + l_time1->hour*3600L +
+ l_time1->minute*60L + l_time1->second + microseconds/1000000L -
+ (longlong)l_sign*(l_time2->hour*3600L+l_time2->minute*60L+l_time2->second));
+
+ neg= 0;
+ if (seconds < 0)
+ {
+ seconds= -seconds;
+ neg= 1;
+ }
+ else if (seconds == 0 && microseconds < 0)
+ {
+ microseconds= -microseconds;
+ neg= 1;
+ }
+ if (microseconds < 0)
+ {
+ microseconds+= 1000000L;
+ seconds--;
+ }
+ *seconds_out= seconds;
+ *microseconds_out= microseconds;
+ return neg;
+}
+
+/*
TIMEDIFF(t,s) is a time function that calculates the
time value between a start and end time.
@@ -2147,40 +2242,16 @@ String *Item_func_timediff::val_str(String *str)
if (l_time1.neg != l_time2.neg)
l_sign= -l_sign;
- if (l_time1.time_type == TIMESTAMP_TIME) // Time value
- days= l_time1.day - l_sign*l_time2.day;
- else // DateTime value
- days= (calc_daynr((uint) l_time1.year,
- (uint) l_time1.month,
- (uint) l_time1.day) -
- l_sign*calc_daynr((uint) l_time2.year,
- (uint) l_time2.month,
- (uint) l_time2.day));
-
- microseconds= l_time1.second_part - l_sign*l_time2.second_part;
- seconds= ((longlong) days*86400L + l_time1.hour*3600L +
- l_time1.minute*60L + l_time1.second + microseconds/1000000L -
- (longlong)l_sign*(l_time2.hour*3600L+l_time2.minute*60L+
- l_time2.second));
+ l_time3.neg= calc_time_diff(&l_time1,&l_time2, l_sign,
+ &seconds, &microseconds);
- l_time3.neg= 0;
- if (seconds < 0)
- {
- seconds= -seconds;
- l_time3.neg= 1;
- }
- else if (seconds == 0 && microseconds < 0)
- {
- microseconds= -microseconds;
- l_time3.neg= 1;
- }
- if (microseconds < 0)
- {
- microseconds+= 1000000L;
- seconds--;
- }
+ /*
+ For TIMESTAMP_TIME only:
+ If both argumets are negative values and diff between them
+ is negative we need to swap sign as result should be positive.
+ */
if ((l_time2.neg == l_time1.neg) && l_time1.neg)
- l_time3.neg= l_time3.neg ? 0 : 1;
+ l_time3.neg= 1-l_time3.neg; // Swap sign of result
calc_time_from_sec(&l_time3, (long) seconds, microseconds);
@@ -2247,6 +2318,166 @@ longlong Item_func_microsecond::val_int()
}
+longlong Item_func_timestamp_diff::val_int()
+{
+ TIME ltime1, ltime2;
+ longlong seconds;
+ long microseconds;
+ long months= 0;
+ int neg= 1;
+
+ null_value= 0;
+ if (args[0]->get_date(&ltime1, 0) ||
+ args[1]->get_date(&ltime2, 0))
+ goto null_date;
+
+ if (calc_time_diff(&ltime2,&ltime1, 1,
+ &seconds, &microseconds))
+ neg= -1;
+
+ if (int_type == INTERVAL_YEAR ||
+ int_type == INTERVAL_QUARTER ||
+ int_type == INTERVAL_MONTH)
+ {
+ uint year, year_tmp;
+ uint year_beg, year_end, month_beg, month_end;
+ uint diff_days= seconds/86400L;
+ uint diff_months= 0;
+ uint diff_years= 0;
+ if (neg == -1)
+ {
+ year_beg= ltime2.year;
+ year_end= ltime1.year;
+ month_beg= ltime2.month;
+ month_end= ltime1.month;
+ }
+ else
+ {
+ year_beg= ltime1.year;
+ year_end= ltime2.year;
+ month_beg= ltime1.month;
+ month_end= ltime2.month;
+ }
+ /* calc years*/
+ for (year= year_beg;year < year_end; year++)
+ {
+ uint days=calc_days_in_year(year);
+ if (days > diff_days)
+ break;
+ diff_days-= days;
+ diff_years++;
+ }
+
+ /* calc months; Current year is in the 'year' variable */
+ month_beg--; /* Change months to be 0-11 for easier calculation */
+ month_end--;
+
+ months= 12*diff_years;
+ while (month_beg != month_end)
+ {
+ uint m_days= (uint) days_in_month[month_beg];
+ if (month_beg == 1)
+ {
+ /* This is only calculated once so there is no reason to cache it*/
+ uint leap= (uint) ((year & 3) == 0 && (year%100 ||
+ (year%400 == 0 && year)));
+ m_days+= leap;
+ }
+ if (m_days > diff_days)
+ break;
+ diff_days-= m_days;
+ months++;
+ if (month_beg++ == 11) /* if we wrap to next year */
+ {
+ month_beg= 0;
+ year++;
+ }
+ }
+ if (neg == -1)
+ months= -months;
+ }
+
+ switch (int_type) {
+ case INTERVAL_YEAR:
+ return months/12;
+ case INTERVAL_QUARTER:
+ return months/3;
+ case INTERVAL_MONTH:
+ return months;
+ case INTERVAL_WEEK:
+ return seconds/86400L/7L*neg;
+ case INTERVAL_DAY:
+ return seconds/86400L*neg;
+ case INTERVAL_HOUR:
+ return seconds/3600L*neg;
+ case INTERVAL_MINUTE:
+ return seconds/60L*neg;
+ case INTERVAL_SECOND:
+ return seconds*neg;
+ case INTERVAL_MICROSECOND:
+ {
+ longlong max_sec= LONGLONG_MAX/1000000;
+ if (max_sec > seconds ||
+ max_sec == seconds && LONGLONG_MAX%1000000 >= microseconds)
+ return (longlong) (seconds*1000000L+microseconds)*neg;
+ goto null_date;
+ }
+ default:
+ break;
+ }
+
+null_date:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_timestamp_diff::print(String *str)
+{
+ str->append(func_name());
+ str->append('(');
+
+ switch (int_type) {
+ case INTERVAL_YEAR:
+ str->append("YEAR");
+ break;
+ case INTERVAL_QUARTER:
+ str->append("QUARTER");
+ break;
+ case INTERVAL_MONTH:
+ str->append("MONTH");
+ break;
+ case INTERVAL_WEEK:
+ str->append("WEEK");
+ break;
+ case INTERVAL_DAY:
+ str->append("DAY");
+ break;
+ case INTERVAL_HOUR:
+ str->append("HOUR");
+ break;
+ case INTERVAL_MINUTE:
+ str->append("MINUTE");
+ break;
+ case INTERVAL_SECOND:
+ str->append("SECOND");
+ break;
+ case INTERVAL_MICROSECOND:
+ str->append("SECOND_FRAC");
+ break;
+ default:
+ break;
+ }
+
+ for (uint i=0 ; i < 2 ; i++)
+ {
+ str->append(',');
+ args[i]->print(str);
+ }
+ str->append(')');
+}
+
+
String *Item_func_get_format::val_str(String *str)
{
const char *format_name;
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index a81b9f28d92..9f1d7dbd85a 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -543,11 +543,11 @@ public:
enum interval_type
{
- INTERVAL_YEAR, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE,
- INTERVAL_SECOND, INTERVAL_MICROSECOND ,INTERVAL_YEAR_MONTH,
- INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE, INTERVAL_DAY_SECOND,
- INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND, INTERVAL_MINUTE_SECOND,
- INTERVAL_DAY_MICROSECOND, INTERVAL_HOUR_MICROSECOND,
+ INTERVAL_YEAR, INTERVAL_QUARTER, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR,
+ INTERVAL_MINUTE, INTERVAL_WEEK, INTERVAL_SECOND, INTERVAL_MICROSECOND ,
+ INTERVAL_YEAR_MONTH, INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE,
+ INTERVAL_DAY_SECOND, INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND,
+ INTERVAL_MINUTE_SECOND, INTERVAL_DAY_MICROSECOND, INTERVAL_HOUR_MICROSECOND,
INTERVAL_MINUTE_MICROSECOND, INTERVAL_SECOND_MICROSECOND
};
@@ -769,6 +769,23 @@ public:
};
+class Item_func_timestamp_diff :public Item_int_func
+{
+ const interval_type int_type;
+public:
+ Item_func_timestamp_diff(Item *a,Item *b,interval_type type_arg)
+ :Item_int_func(a,b), int_type(type_arg) {}
+ const char *func_name() const { return "timestamp_diff"; }
+ longlong val_int();
+ void fix_length_and_dec()
+ {
+ decimals=0;
+ maybe_null=1;
+ }
+ void print(String *str);
+};
+
+
enum date_time_format
{
USA_FORMAT, JIS_FORMAT, ISO_FORMAT, EUR_FORMAT, INTERNAL_FORMAT
diff --git a/sql/lex.h b/sql/lex.h
index 6e69df6f96c..114e1a6b960 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -60,6 +60,7 @@ static SYMBOL symbols[] = {
{ "AS", SYM(AS),0,0},
{ "ASC", SYM(ASC),0,0},
{ "ASCII", SYM(ASCII_SYM),0,0},
+ { "ASENSITIVE", SYM(ASENSITIVE_SYM),0,0},
{ "AUTO_INCREMENT", SYM(AUTO_INC),0,0},
{ "AVG", SYM(AVG_SYM),0,0},
{ "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH),0,0},
@@ -81,6 +82,7 @@ static SYMBOL symbols[] = {
{ "BY", SYM(BY),0,0},
{ "BYTE", SYM(BYTE_SYM), 0, 0},
{ "CACHE", SYM(CACHE_SYM),0,0},
+ { "CALL", SYM(CALL_SYM),0,0},
{ "CASCADE", SYM(CASCADE),0,0},
{ "CASE", SYM(CASE_SYM),0,0},
{ "CHANGE", SYM(CHANGE),0,0},
@@ -102,13 +104,17 @@ static SYMBOL symbols[] = {
{ "COMMITTED", SYM(COMMITTED_SYM),0,0},
{ "COMPRESSED", SYM(COMPRESSED_SYM),0,0},
{ "CONCURRENT", SYM(CONCURRENT),0,0},
+ { "CONDITION", SYM(CONDITION_SYM),0,0},
+ { "CONNECTION", SYM(CONNECTION_SYM),0,0},
{ "CONSTRAINT", SYM(CONSTRAINT),0,0},
+ { "CONTINUE", SYM(CONTINUE_SYM),0,0},
{ "CREATE", SYM(CREATE),0,0},
{ "CROSS", SYM(CROSS),0,0},
{ "CUBE", SYM(CUBE_SYM),0,0},
{ "CURRENT_DATE", SYM(CURDATE),0,0},
{ "CURRENT_TIME", SYM(CURTIME),0,0},
{ "CURRENT_TIMESTAMP", SYM(NOW_SYM),0,0},
+ { "CURSOR", SYM(CURSOR_SYM),0,0},
{ "DATA", SYM(DATA_SYM),0,0},
{ "DATABASE", SYM(DATABASE),0,0},
{ "DATABASES", SYM(DATABASES),0,0},
@@ -121,13 +127,16 @@ static SYMBOL symbols[] = {
{ "DAY_SECOND", SYM(DAY_SECOND_SYM),0,0},
{ "DEC", SYM(DECIMAL_SYM),0,0},
{ "DECIMAL", SYM(DECIMAL_SYM),0,0},
+ { "DECLARE", SYM(DECLARE_SYM),0,0},
{ "DEFAULT", SYM(DEFAULT),0,0},
+ { "DEFINER", SYM(DEFINER_SYM),0,0},
{ "DELAYED", SYM(DELAYED_SYM),0,0},
{ "DELAY_KEY_WRITE", SYM(DELAY_KEY_WRITE_SYM),0,0},
{ "DELETE", SYM(DELETE_SYM),0,0},
{ "DESC", SYM(DESC),0,0},
{ "DESCRIBE", SYM(DESCRIBE),0,0},
{ "DES_KEY_FILE", SYM(DES_KEY_FILE),0,0},
+ { "DETERMINISTIC", SYM(DETERMINISTIC_SYM),0,0},
{ "DIRECTORY", SYM(DIRECTORY_SYM),0,0},
{ "DISABLE", SYM(DISABLE_SYM),0,0},
{ "DISCARD", SYM(DISCARD),0,0},
@@ -142,6 +151,7 @@ static SYMBOL symbols[] = {
{ "DUPLICATE", SYM(DUPLICATE_SYM),0,0},
{ "DYNAMIC", SYM(DYNAMIC_SYM),0,0},
{ "ELSE", SYM(ELSE),0,0},
+ { "ELSEIF", SYM(ELSEIF_SYM),0,0},
{ "ENABLE", SYM(ENABLE_SYM),0,0},
{ "ENCLOSED", SYM(ENCLOSED),0,0},
{ "END", SYM(END),0,0},
@@ -154,11 +164,13 @@ static SYMBOL symbols[] = {
{ "EVENTS", SYM(EVENTS_SYM),0,0},
{ "EXECUTE", SYM(EXECUTE_SYM),0,0},
{ "EXISTS", SYM(EXISTS),0,0},
+ { "EXIT", SYM(EXIT_SYM),0,0},
{ "EXPANSION", SYM(EXPANSION_SYM),0,0},
{ "EXPLAIN", SYM(DESCRIBE),0,0},
{ "EXTENDED", SYM(EXTENDED_SYM),0,0},
{ "FALSE", SYM(FALSE_SYM),0,0},
{ "FAST", SYM(FAST_SYM),0,0},
+ { "FETCH", SYM(FETCH_SYM),0,0},
{ "FIELDS", SYM(COLUMNS),0,0},
{ "FILE", SYM(FILE_SYM),0,0},
{ "FIRST", SYM(FIRST_SYM),0,0},
@@ -170,10 +182,12 @@ static SYMBOL symbols[] = {
{ "FOR", SYM(FOR_SYM),0,0},
{ "FORCE", SYM(FORCE_SYM),0,0},
{ "FOREIGN", SYM(FOREIGN),0,0},
+ { "FOUND", SYM(FOUND_SYM),0,0},
+ { "FRAC_SECOND", SYM(FRAC_SECOND_SYM),0,0},
{ "FROM", SYM(FROM),0,0},
{ "FULL", SYM(FULL),0,0},
{ "FULLTEXT", SYM(FULLTEXT_SYM),0,0},
- { "FUNCTION", SYM(UDF_SYM),0,0},
+ { "FUNCTION", SYM(FUNCTION_SYM),0,0},
{ "GEOMETRY", SYM(GEOMETRY_SYM),0,0},
{ "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION),0,0},
{ "GET_FORMAT", SYM(GET_FORMAT),0,0},
@@ -202,6 +216,8 @@ static SYMBOL symbols[] = {
{ "INNER", SYM(INNER_SYM),0,0},
{ "INNOBASE", SYM(INNOBASE_SYM),0,0},
{ "INNODB", SYM(INNOBASE_SYM),0,0},
+ { "INOUT", SYM(INOUT_SYM),0,0},
+ { "INSENSITIVE", SYM(INSENSITIVE_SYM),0,0},
{ "INSERT", SYM(INSERT),0,0},
{ "INSERT_METHOD", SYM(INSERT_METHOD),0,0},
{ "INT", SYM(INT_SYM),0,0},
@@ -217,12 +233,16 @@ static SYMBOL symbols[] = {
{ "IS", SYM(IS),0,0},
{ "ISOLATION", SYM(ISOLATION),0,0},
{ "ISSUER", SYM(ISSUER_SYM),0,0},
+ { "ITERATE", SYM(ITERATE_SYM),0,0},
+ { "INVOKER", SYM(INVOKER_SYM),0,0},
{ "JOIN", SYM(JOIN_SYM),0,0},
{ "KEY", SYM(KEY_SYM),0,0},
{ "KEYS", SYM(KEYS),0,0},
{ "KILL", SYM(KILL_SYM),0,0},
+ { "LANGUAGE", SYM(LANGUAGE_SYM),0,0},
{ "LAST", SYM(LAST_SYM),0,0},
{ "LEADING", SYM(LEADING),0,0},
+ { "LEAVE", SYM(LEAVE_SYM),0,0},
{ "LEAVES", SYM(LEAVES),0,0},
{ "LEFT", SYM(LEFT),0,0},
{ "LEVEL", SYM(LEVEL_SYM),0,0},
@@ -240,6 +260,7 @@ static SYMBOL symbols[] = {
{ "LONG", SYM(LONG_SYM),0,0},
{ "LONGBLOB", SYM(LONGBLOB),0,0},
{ "LONGTEXT", SYM(LONGTEXT),0,0},
+ { "LOOP", SYM(LOOP_SYM),0,0},
{ "LOW_PRIORITY", SYM(LOW_PRIORITY),0,0},
{ "MASTER", SYM(MASTER_SYM),0,0},
{ "MASTER_CONNECT_RETRY", SYM(MASTER_CONNECT_RETRY_SYM),0,0},
@@ -278,6 +299,7 @@ static SYMBOL symbols[] = {
{ "MULTILINESTRING", SYM(MULTILINESTRING),0,0},
{ "MULTIPOINT", SYM(MULTIPOINT),0,0},
{ "MULTIPOLYGON", SYM(MULTIPOLYGON),0,0},
+ { "NAME", SYM(NAME_SYM),0,0},
{ "NAMES", SYM(NAMES_SYM),0,0},
{ "NATIONAL", SYM(NATIONAL_SYM),0,0},
{ "NATURAL", SYM(NATURAL),0,0},
@@ -300,6 +322,7 @@ static SYMBOL symbols[] = {
{ "OPTIONALLY", SYM(OPTIONALLY),0,0},
{ "OR", SYM(OR),0,0},
{ "ORDER", SYM(ORDER_SYM),0,0},
+ { "OUT", SYM(OUT_SYM),0,0},
{ "OUTER", SYM(OUTER),0,0},
{ "OUTFILE", SYM(OUTFILE),0,0},
{ "PACK_KEYS", SYM(PACK_KEYS_SYM),0,0},
@@ -315,6 +338,7 @@ static SYMBOL symbols[] = {
{ "PROCESS" , SYM(PROCESS),0,0},
{ "PROCESSLIST", SYM(PROCESSLIST_SYM),0,0},
{ "PURGE", SYM(PURGE),0,0},
+ { "QUARTER", SYM(QUARTER_SYM),0,0},
{ "QUERY", SYM(QUERY_SYM),0,0},
{ "QUICK", SYM(QUICK),0,0},
{ "RAID0", SYM(RAID_0_SYM),0,0},
@@ -334,11 +358,13 @@ static SYMBOL symbols[] = {
{ "REPEATABLE", SYM(REPEATABLE_SYM),0,0},
{ "REPLACE", SYM(REPLACE),0,0},
{ "REPLICATION", SYM(REPLICATION),0,0},
+ { "REPEAT", SYM(REPEAT_SYM),0,0},
{ "REQUIRE", SYM(REQUIRE_SYM),0,0},
{ "RESET", SYM(RESET_SYM),0,0},
{ "RESTORE", SYM(RESTORE_SYM),0,0},
{ "RESTRICT", SYM(RESTRICT),0,0},
- { "RETURNS", SYM(UDF_RETURNS_SYM),0,0},
+ { "RETURN", SYM(RETURN_SYM),0,0},
+ { "RETURNS", SYM(RETURNS_SYM),0,0},
{ "REVOKE", SYM(REVOKE),0,0},
{ "RIGHT", SYM(RIGHT),0,0},
{ "RLIKE", SYM(REGEXP),0,0}, /* Like in mSQL2 */
@@ -351,7 +377,9 @@ static SYMBOL symbols[] = {
{ "SAVEPOINT", SYM(SAVEPOINT_SYM),0,0},
{ "SECOND", SYM(SECOND_SYM),0,0},
{ "SECOND_MICROSECOND", SYM(SECOND_MICROSECOND_SYM),0,0},
+ { "SECURITY", SYM(SECURITY_SYM),0,0},
{ "SELECT", SYM(SELECT_SYM),0,0},
+ { "SENSITIVE", SYM(SENSITIVE_SYM),0,0},
{ "SEPARATOR", SYM(SEPARATOR_SYM),0,0},
{ "SERIAL", SYM(SERIAL_SYM),0,0},
{ "SERIALIZABLE", SYM(SERIALIZABLE_SYM),0,0},
@@ -368,6 +396,11 @@ static SYMBOL symbols[] = {
{ "SONAME", SYM(UDF_SONAME_SYM),0,0},
{ "SOUNDS", SYM(SOUNDS_SYM),0,0},
{ "SPATIAL", SYM(SPATIAL_SYM),0,0},
+ { "SPECIFIC", SYM(SPECIFIC_SYM),0,0},
+ { "SQL", SYM(SQL_SYM),0,0},
+ { "SQLEXCEPTION", SYM(SQLEXCEPTION_SYM),0,0},
+ { "SQLSTATE", SYM(SQLSTATE_SYM),0,0},
+ { "SQLWARNING", SYM(SQLWARNING_SYM),0,0},
{ "SQL_BIG_RESULT", SYM(SQL_BIG_RESULT),0,0},
{ "SQL_BUFFER_RESULT", SYM(SQL_BUFFER_RESULT),0,0},
{ "SQL_CACHE", SYM(SQL_CACHE_SYM), 0, 0},
@@ -375,6 +408,15 @@ static SYMBOL symbols[] = {
{ "SQL_NO_CACHE", SYM(SQL_NO_CACHE_SYM), 0, 0},
{ "SQL_SMALL_RESULT", SYM(SQL_SMALL_RESULT),0,0},
{ "SQL_THREAD", SYM(SQL_THREAD),0,0},
+ { "SQL_TSI_FRAC_SECOND", SYM(FRAC_SECOND_SYM),0,0},
+ { "SQL_TSI_SECOND", SYM(SECOND_SYM),0,0},
+ { "SQL_TSI_MINUTE", SYM(MINUTE_SYM),0,0},
+ { "SQL_TSI_HOUR", SYM(HOUR_SYM),0,0},
+ { "SQL_TSI_DAY", SYM(DAY_SYM),0,0},
+ { "SQL_TSI_WEEK", SYM(WEEK_SYM),0,0},
+ { "SQL_TSI_MONTH", SYM(MONTH_SYM),0,0},
+ { "SQL_TSI_QUARTER", SYM(QUARTER_SYM),0,0},
+ { "SQL_TSI_YEAR", SYM(YEAR_SYM),0,0},
{ "SSL", SYM(SSL_SYM),0,0},
{ "START", SYM(START_SYM),0,0},
{ "STARTING", SYM(STARTING),0,0},
@@ -395,6 +437,8 @@ static SYMBOL symbols[] = {
{ "THEN", SYM(THEN_SYM),0,0},
{ "TIME", SYM(TIME_SYM),0,0},
{ "TIMESTAMP", SYM(TIMESTAMP),0,0},
+ { "TIMESTAMPADD", SYM(TIMESTAMP_ADD),0,0},
+ { "TIMESTAMPDIFF", SYM(TIMESTAMP_DIFF),0,0},
{ "TINYBLOB", SYM(TINYBLOB),0,0},
{ "TINYINT", SYM(TINYINT),0,0},
{ "TINYTEXT", SYM(TINYTEXT),0,0},
@@ -406,6 +450,7 @@ static SYMBOL symbols[] = {
{ "TYPE", SYM(TYPE_SYM),0,0},
{ "TYPES", SYM(TYPES_SYM),0,0},
{ "UNCOMMITTED", SYM(UNCOMMITTED_SYM),0,0},
+ { "UNDO", SYM(UNDO_SYM),0,0},
{ "UNICODE", SYM(UNICODE_SYM),0,0},
{ "UNION", SYM(UNION_SYM),0,0},
{ "UNIQUE", SYM(UNIQUE_SYM),0,0},
@@ -430,11 +475,13 @@ static SYMBOL symbols[] = {
{ "VARIABLES", SYM(VARIABLES),0,0},
{ "VARYING", SYM(VARYING),0,0},
{ "WARNINGS", SYM(WARNINGS),0,0},
+ { "WEEK", SYM(WEEK_SYM),0,0},
{ "WHEN", SYM(WHEN_SYM),0,0},
{ "WHERE", SYM(WHERE),0,0},
{ "WITH", SYM(WITH),0,0},
{ "WORK", SYM(WORK_SYM),0,0},
{ "WRITE", SYM(WRITE_SYM),0,0},
+ { "WHILE", SYM(WHILE_SYM),0,0},
{ "X509", SYM(X509_SYM),0,0},
{ "XOR", SYM(XOR),0,0},
{ "YEAR", SYM(YEAR_SYM),0,0},
@@ -616,12 +663,10 @@ static SYMBOL sql_functions[] = {
{ "POSITION", SYM(POSITION_SYM),0,0},
{ "POW", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)},
{ "POWER", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)},
- { "QUARTER", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_quarter)},
{ "QUOTE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_quote)},
{ "RADIANS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_radians)},
{ "RAND", SYM(RAND),0,0},
{ "RELEASE_LOCK", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_release_lock)},
- { "REPEAT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_repeat)},
{ "REVERSE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_reverse)},
{ "ROUND", SYM(ROUND),0,0},
{ "RPAD", SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_rpad)},
@@ -664,7 +709,6 @@ static SYMBOL sql_functions[] = {
{ "UPPER", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)},
{ "VARIANCE", SYM(VARIANCE_SYM),0,0},
{ "VERSION", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_version)},
- { "WEEK", SYM(WEEK_SYM),0,0},
{ "WEEKDAY", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekday)},
{ "WEEKOFYEAR", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekofyear)},
{ "WITHIN", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_within)},
diff --git a/sql/lock.cc b/sql/lock.cc
index 29795415720..0a2f91812a7 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -152,7 +152,7 @@ retry:
thd->proc_info=0;
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
if (sql_lock)
{
mysql_unlock_tables(thd,sql_lock);
diff --git a/sql/log.cc b/sql/log.cc
index b490845c256..59bc7c7fae2 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -30,7 +30,7 @@
#include <stdarg.h>
#include <m_ctype.h> // For test_if_number
-MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log;
+MYSQL_LOG mysql_log, mysql_slow_log, mysql_bin_log;
static bool test_if_number(const char *str,
long *res, bool allow_wildcards);
@@ -1069,7 +1069,7 @@ err:
/*
Write to normal (not rotable) log
- This is the format for the 'normal', 'slow' and 'update' logs.
+ This is the format for the 'normal' log.
*/
bool MYSQL_LOG::write(THD *thd,enum enum_server_command command,
@@ -1534,8 +1534,7 @@ err:
/*
- Write update log in a format suitable for incremental backup
- This is also used by the slow query log.
+ Write to the slow query log.
*/
bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
@@ -1551,15 +1550,6 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
int tmp_errno=0;
char buff[80],*end;
end=buff;
- if (!(thd->options & OPTION_UPDATE_LOG)
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- && (thd->master_access & SUPER_ACL)
-#endif
- )
- {
- VOID(pthread_mutex_unlock(&LOCK_log));
- return 0;
- }
if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT) || query_start_arg)
{
current_time=time(NULL);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index b609e6c6b11..ca1b296009d 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -839,7 +839,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
0 : LOG_EVENT_THREAD_SPECIFIC_F, using_trans),
data_buf(0), query(query_arg),
db(thd_arg->db), q_len((uint32) query_length),
- error_code(thd_arg->killed ? ER_SERVER_SHUTDOWN: thd_arg->net.last_errno),
+ error_code(thd_arg->killed != THD::NOT_KILLED ? thd->killed_errno() : thd_arg->net.last_errno),
thread_id(thd_arg->thread_id),
/* save the original thread id; we already know the server id */
slave_proxy_id(thd_arg->variables.pseudo_thread_id)
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index b7a2d1a74b9..51c944d52ad 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -54,7 +54,7 @@ char *sql_strmake_with_convert(const char *str, uint32 arg_length,
CHARSET_INFO *from_cs,
uint32 max_res_length,
CHARSET_INFO *to_cs, uint32 *result_length);
-void kill_one_thread(THD *thd, ulong id);
+void kill_one_thread(THD *thd, ulong id, bool only_kill_query);
bool net_request_file(NET* net, const char* fname);
char* query_table_status(THD *thd,const char *db,const char *table_name);
@@ -408,7 +408,7 @@ bool is_update_query(enum enum_sql_command command);
void free_items(Item *item);
bool alloc_query(THD *thd, char *packet, ulong packet_length);
void mysql_init_select(LEX *lex);
-void mysql_init_query(THD *thd);
+void mysql_init_query(THD *thd, bool lexonly=0);
bool mysql_new_select(LEX *lex, bool move_down);
void create_select_for_variable(const char *var_name);
void mysql_init_multi_delete(LEX *lex);
@@ -419,7 +419,7 @@ extern "C" pthread_handler_decl(handle_one_connection,arg);
extern "C" pthread_handler_decl(handle_bootstrap,arg);
void end_thread(THD *thd,bool put_in_cache);
void flush_thread_cache();
-void mysql_execute_command(THD *thd);
+int mysql_execute_command(THD *thd);
bool do_command(THD *thd);
bool dispatch_command(enum enum_server_command command, THD *thd,
char* packet, uint packet_length);
@@ -1036,22 +1036,22 @@ SQL_CRYPT *get_crypt_for_frm(void);
inline bool add_item_to_list(THD *thd, Item *item)
{
- return thd->lex.current_select->add_item_to_list(thd, item);
+ return thd->lex->current_select->add_item_to_list(thd, item);
}
inline bool add_value_to_list(THD *thd, Item *value)
{
- return thd->lex.value_list.push_back(value);
+ return thd->lex->value_list.push_back(value);
}
inline bool add_order_to_list(THD *thd, Item *item, bool asc)
{
- return thd->lex.current_select->add_order_to_list(thd, item, asc);
+ return thd->lex->current_select->add_order_to_list(thd, item, asc);
}
inline bool add_group_to_list(THD *thd, Item *item, bool asc)
{
- return thd->lex.current_select->add_group_to_list(thd, item, asc);
+ return thd->lex->current_select->add_group_to_list(thd, item, asc);
}
inline void mark_as_null_row(TABLE *table)
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index f4148e26df5..14ebf475692 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -22,6 +22,7 @@
#include "sql_repl.h"
#include "repl_failsafe.h"
#include "stacktrace.h"
+#include "mysys_err.h"
#ifdef HAVE_BERKELEY_DB
#include "ha_berkeley.h"
#endif
@@ -36,6 +37,8 @@
#include <thr_alarm.h>
#include <ft_global.h>
#include <errmsg.h>
+#include "sp_rcontext.h"
+#include "sp_cache.h"
#define mysqld_charset &my_charset_latin1
@@ -616,7 +619,7 @@ static void close_connections(void)
{
DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
tmp->thread_id));
- tmp->killed=1;
+ tmp->killed= THD::KILL_CONNECTION;
if (tmp->mysys_var)
{
tmp->mysys_var->abort=1;
@@ -799,6 +802,7 @@ static void __cdecl kill_server(int sig_ptr)
unireg_abort(1); /* purecov: inspected */
else
unireg_end();
+
#ifdef __NETWARE__
pthread_join(select_thread, NULL); // wait for main thread
#endif /* __NETWARE__ */
@@ -890,7 +894,6 @@ void clean_up(bool print_message)
mysql_log.cleanup();
mysql_slow_log.cleanup();
- mysql_update_log.cleanup();
mysql_bin_log.cleanup();
#ifdef HAVE_REPLICATION
@@ -1275,12 +1278,12 @@ static void server_init(void)
void yyerror(const char *s)
{
THD *thd=current_thd;
- char *yytext=(char*) thd->lex.tok_start;
+ char *yytext=(char*) thd->lex->tok_start;
/* "parse error" changed into "syntax error" between bison 1.75 and 1.875 */
if (strcmp(s,"parse error") == 0 || strcmp(s,"syntax error") == 0)
s=ER(ER_SYNTAX_ERROR);
net_printf(thd,ER_PARSE_ERROR, s, yytext ? (char*) yytext : "",
- thd->lex.yylineno);
+ thd->lex->yylineno);
}
@@ -1410,7 +1413,7 @@ extern "C" sig_handler abort_thread(int sig __attribute__((unused)))
THD *thd=current_thd;
DBUG_ENTER("abort_thread");
if (thd)
- thd->killed=1;
+ thd->killed= THD::KILL_CONNECTION;
DBUG_VOID_RETURN;
}
#endif
@@ -1877,12 +1880,16 @@ extern "C" int my_message_sql(uint error, const char *str,
DBUG_PRINT("error", ("Message: '%s'", str));
if ((thd= current_thd))
{
+ if (thd->spcont && thd->spcont->find_handler(error))
+ {
+ DBUG_RETURN(0);
+ }
/*
thd->lex.current_select == 0 if lex structure is not inited
(not query command (COM_QUERY))
*/
- if (thd->lex.current_select &&
- thd->lex.current_select->no_error && !thd->is_fatal_error)
+ if (thd->lex->current_select &&
+ thd->lex->current_select->no_error && !thd->is_fatal_error)
{
DBUG_PRINT("error", ("above error converted to warning"));
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error, str);
@@ -2084,7 +2091,6 @@ static int init_common_variables(const char *conf_file_name, int argc,
before MY_INIT(). So we do it here.
*/
mysql_log.init_pthread_objects();
- mysql_update_log.init_pthread_objects();
mysql_slow_log.init_pthread_objects();
mysql_bin_log.init_pthread_objects();
@@ -2211,6 +2217,7 @@ static int init_thread_environment()
(void) pthread_mutex_init(&LOCK_rpl_status, MY_MUTEX_INIT_FAST);
(void) pthread_cond_init(&COND_rpl_status, NULL);
#endif
+ sp_cache_init();
/* Parameter for threads created for connections */
(void) pthread_attr_init(&connection_attrib);
(void) pthread_attr_setdetachstate(&connection_attrib,
@@ -2268,9 +2275,55 @@ static int init_server_components()
LOG_NORMAL, 0, 0, 0);
if (opt_update_log)
{
- open_log(&mysql_update_log, glob_hostname, opt_update_logname, "",
- NullS, LOG_NEW, 0, 0, 0);
- using_update_log=1;
+ /*
+ Update log is removed since 5.0. But we still accept the option.
+ The idea is if the user already uses the binlog and the update log,
+ we completely ignore any option/variable related to the update log, like
+ if the update log did not exist. But if the user uses only the update log,
+ then we translate everything into binlog for him (with warnings).
+ Implementation of the above :
+ - If mysqld is started with --log-update and --log-bin,
+ ignore --log-update (print a warning), push a warning when SQL_LOG_UPDATE
+ is used, and turn off --sql-bin-update-same.
+ This will completely ignore SQL_LOG_UPDATE
+ - If mysqld is started with --log-update only,
+ change it to --log-bin (with the filename passed to log-update,
+ plus '-bin') (print a warning), push a warning when SQL_LOG_UPDATE is
+ used, and turn on --sql-bin-update-same.
+ This will translate SQL_LOG_UPDATE to SQL_LOG_BIN.
+
+ Note that we tell the user that --sql-bin-update-same is deprecated and
+ does nothing, and we don't take into account if he used this option or
+ not; but internally we give this variable a value to have the behaviour we
+ want (i.e. have SQL_LOG_UPDATE influence SQL_LOG_BIN or not).
+ As sql-bin-update-same, log-update and log-bin cannot be changed by the user
+ after starting the server (they are not variables), the user will not
+ later interfere with the settings we do here.
+ */
+ if (opt_bin_log)
+ {
+ opt_sql_bin_update= 0;
+ sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log.");
+ }
+ else
+ {
+ opt_sql_bin_update= 1;
+ opt_bin_log= 1;
+ if (opt_update_logname)
+ {
+ // as opt_bin_log==0, no need to free opt_bin_logname
+ if (!(opt_bin_logname= my_strdup(opt_update_logname, MYF(MY_WME))))
+ exit(EXIT_OUT_OF_MEMORY);
+ sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
+with --log-bin='%s' instead.",opt_bin_logname);
+ }
+ else
+ sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
+with --log-bin instead.");
+ }
}
if (opt_slow_log)
open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log",
@@ -2971,7 +3024,7 @@ static void create_new_thread(THD *thd)
("Can't create thread to handle request (error %d)",
error));
thread_count--;
- thd->killed=1; // Safety
+ thd->killed= THD::KILL_CONNECTION; // Safety
(void) pthread_mutex_unlock(&LOCK_thread_count);
statistic_increment(aborted_connects,&LOCK_status);
net_printf(thd,ER_CANT_CREATE_THREAD,error);
@@ -3862,7 +3915,8 @@ Disable with --skip-bdb (will save memory).",
(gptr*) &myisam_log_filename, (gptr*) &myisam_log_filename, 0, GET_STR,
OPT_ARG, 0, 0, 0, 0, 0, 0},
{"log-update", OPT_UPDATE_LOG,
- "Log updates to file.# where # is a unique number if not given.",
+ "The update log is deprecated since version 5.0, is replaced by the binary \
+log and this option justs turns on --log-bin instead.",
(gptr*) &opt_update_logname, (gptr*) &opt_update_logname, 0, GET_STR,
OPT_ARG, 0, 0, 0, 0, 0, 0},
{"log-slow-queries", OPT_SLOW_QUERY_LOG,
@@ -4140,9 +4194,9 @@ replicating a LOAD DATA INFILE command.",
(gptr*) &mysqld_unix_port, (gptr*) &mysqld_unix_port, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"sql-bin-update-same", OPT_SQL_BIN_UPDATE_SAME,
- "If set, setting SQL_LOG_BIN to a value will automatically set SQL_LOG_UPDATE to the same value and vice versa.",
- (gptr*) &opt_sql_bin_update, (gptr*) &opt_sql_bin_update, 0, GET_BOOL,
- NO_ARG, 0, 0, 0, 0, 0, 0},
+ "The update log is deprecated since version 5.0, is replaced by the binary \
+log and this option does nothing anymore.",
+ 0, 0, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"sql-mode", OPT_SQL_MODE,
"Syntax: sql-mode=option[,option[,option...]] where option can be one of: REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, ONLY_FULL_GROUP_BY, NO_UNSIGNED_SUBTRACTION.",
(gptr*) &sql_mode_str, (gptr*) &sql_mode_str, 0, GET_STR, REQUIRED_ARG, 0,
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index a0f7a779894..5e5d4b14c89 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -129,6 +129,7 @@ my_bool my_net_init(NET *net, Vio* vio)
net->buff_end=net->buff+net->max_packet;
net->vio = vio;
net->no_send_ok = 0;
+ net->no_send_eof = 0;
net->error=0; net->return_errno=0; net->return_status=0;
net->pkt_nr=net->compress_pkt_nr=0;
net->write_pos=net->read_pos = net->buff;
diff --git a/sql/opt_ft.cc b/sql/opt_ft.cc
index 74349819937..9d1fc55c777 100644
--- a/sql/opt_ft.cc
+++ b/sql/opt_ft.cc
@@ -26,7 +26,7 @@
** Create a FT or QUICK RANGE based on a key
****************************************************************************/
-QUICK_SELECT *get_ft_or_quick_select_for_ref(THD *thd, TABLE *table,
+QUICK_RANGE_SELECT *get_ft_or_quick_select_for_ref(THD *thd, TABLE *table,
JOIN_TAB *tab)
{
if (tab->type == JT_FT)
diff --git a/sql/opt_ft.h b/sql/opt_ft.h
index 69b6b72f3fc..954c25b6caa 100644
--- a/sql/opt_ft.h
+++ b/sql/opt_ft.h
@@ -24,18 +24,19 @@
#pragma interface /* gcc class implementation */
#endif
-class FT_SELECT: public QUICK_SELECT {
+class FT_SELECT: public QUICK_RANGE_SELECT {
public:
TABLE_REF *ref;
FT_SELECT(THD *thd, TABLE *table, TABLE_REF *tref) :
- QUICK_SELECT (thd, table, tref->key, 1), ref(tref) { init(); }
+ QUICK_RANGE_SELECT (thd, table, tref->key, 1), ref(tref) { init(); }
int init() { return error=file->ft_init(); }
int get_next() { return error=file->ft_read(record); }
+ int get_type() { return QS_TYPE_FULLTEXT; }
};
-QUICK_SELECT *get_ft_or_quick_select_for_ref(THD *thd, TABLE *table,
+QUICK_RANGE_SELECT *get_ft_or_quick_select_for_ref(THD *thd, TABLE *table,
JOIN_TAB *tab);
#endif
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index acc23924f75..f734a9a2334 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -267,14 +267,17 @@ public:
SEL_ARG *clone_tree();
};
+class SEL_IMERGE;
class SEL_TREE :public Sql_alloc
{
public:
enum Type { IMPOSSIBLE, ALWAYS, MAYBE, KEY, KEY_SMALLER } type;
SEL_TREE(enum Type type_arg) :type(type_arg) {}
- SEL_TREE() :type(KEY) { bzero((char*) keys,sizeof(keys));}
+ SEL_TREE() :type(KEY) { keys_map.clear_all(); bzero((char*) keys,sizeof(keys));}
SEL_ARG *keys[MAX_KEY];
+ key_map keys_map; /* bitmask of non-NULL elements in keys */
+ List<SEL_IMERGE> merges; /* possible ways to read rows using index_merge */
};
@@ -302,10 +305,20 @@ static ha_rows check_quick_keys(PARAM *param,uint index,SEL_ARG *key_tree,
char *min_key,uint min_key_flag,
char *max_key, uint max_key_flag);
-static QUICK_SELECT *get_quick_select(PARAM *param,uint index,
- SEL_ARG *key_tree);
+QUICK_RANGE_SELECT *get_quick_select(PARAM *param,uint index,
+ SEL_ARG *key_tree, MEM_ROOT *alloc = NULL);
+static int get_quick_select_params(SEL_TREE *tree, PARAM& param,
+ key_map& needed_reg, TABLE *head,
+ bool index_read_can_be_used,
+ double* read_time,
+ ha_rows* records,
+ SEL_ARG*** key_to_read);
#ifndef DBUG_OFF
-static void print_quick(QUICK_SELECT *quick,const key_map* needed_reg);
+static void print_quick_sel_imerge(QUICK_INDEX_MERGE_SELECT *quick,
+ const key_map *needed_reg);
+void print_quick_sel_range(QUICK_RANGE_SELECT *quick,
+ const key_map *needed_reg);
+
#endif
static SEL_TREE *tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
static SEL_TREE *tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
@@ -313,16 +326,234 @@ static SEL_ARG *sel_add(SEL_ARG *key1,SEL_ARG *key2);
static SEL_ARG *key_or(SEL_ARG *key1,SEL_ARG *key2);
static SEL_ARG *key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag);
static bool get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1);
-static bool get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
+bool get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
SEL_ARG *key_tree,char *min_key,uint min_key_flag,
char *max_key,uint max_key_flag);
static bool eq_tree(SEL_ARG* a,SEL_ARG *b);
static SEL_ARG null_element(SEL_ARG::IMPOSSIBLE);
static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length);
+bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, PARAM* param);
+
+
+/*
+ SEL_IMERGE is a list of possible ways to do index merge, i.e. it is
+ a condition in the following form:
+ (t_1||t_2||...||t_N) && (next)
+
+ where all t_i are SEL_TREEs, next is another SEL_IMERGE and no pair
+ (t_i,t_j) contains SEL_ARGS for the same index.
+
+ SEL_TREE contained in SEL_IMERGE always has merges=NULL.
+
+ This class relies on memory manager to do the cleanup.
+*/
+
+class SEL_IMERGE : public Sql_alloc
+{
+ enum { PREALLOCED_TREES= 10};
+public:
+ SEL_TREE *trees_prealloced[PREALLOCED_TREES];
+ SEL_TREE **trees; /* trees used to do index_merge */
+ SEL_TREE **trees_next; /* last of these trees */
+ SEL_TREE **trees_end; /* end of allocated space */
+
+ SEL_ARG ***best_keys; /* best keys to read in SEL_TREEs */
+
+ SEL_IMERGE() :
+ trees(&trees_prealloced[0]),
+ trees_next(trees),
+ trees_end(trees + PREALLOCED_TREES)
+ {}
+ int or_sel_tree(PARAM *param, SEL_TREE *tree);
+ int or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree);
+ int or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge);
+};
+
+
+/*
+ Add SEL_TREE to this index_merge without any checks,
+
+ NOTES
+ This function implements the following:
+ (x_1||...||x_N) || t = (x_1||...||x_N||t), where x_i, t are SEL_TREEs
+
+ RETURN
+ 0 - OK
+ -1 - Out of memory.
+*/
+
+int SEL_IMERGE::or_sel_tree(PARAM *param, SEL_TREE *tree)
+{
+ if (trees_next == trees_end)
+ {
+ const int realloc_ratio= 2; /* Double size for next round */
+ uint old_elements= (trees_end - trees);
+ uint old_size= sizeof(SEL_TREE**) * old_elements;
+ uint new_size= old_size * realloc_ratio;
+ SEL_TREE **new_trees;
+ if (!(new_trees= (SEL_TREE**)alloc_root(param->mem_root, new_size)))
+ return -1;
+ memcpy(new_trees, trees, old_size);
+ trees= new_trees;
+ trees_next= trees + old_elements;
+ trees_end= trees + old_elements * realloc_ratio;
+ }
+ *(trees_next++)= tree;
+ return 0;
+}
+
+
+/*
+ Perform OR operation on this SEL_IMERGE and supplied SEL_TREE new_tree,
+ combining new_tree with one of the trees in this SEL_IMERGE if they both
+ have SEL_ARGs for the same key.
+
+ SYNOPSIS
+ or_sel_tree_with_checks()
+ param PARAM from SQL_SELECT::test_quick_select
+ new_tree SEL_TREE with type KEY or KEY_SMALLER.
+
+ NOTES
+ This does the following:
+ (t_1||...||t_k)||new_tree =
+ either
+ = (t_1||...||t_k||new_tree)
+ or
+ = (t_1||....||(t_j|| new_tree)||...||t_k),
+
+ where t_i, y are SEL_TREEs.
+ new_tree is combined with the first t_j it has a SEL_ARG on common
+ key with. As a consequence of this, choice of keys to do index_merge
+ read may depend on the order of conditions in WHERE part of the query.
+
+ RETURN
+ 0 OK
+ 1 One of the trees was combined with new_tree to SEL_TREE::ALWAYS,
+ and (*this) should be discarded.
+ -1 An error occurred.
+*/
+
+int SEL_IMERGE::or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree)
+{
+ for (SEL_TREE** tree = trees;
+ tree != trees_next;
+ tree++)
+ {
+ if (sel_trees_can_be_ored(*tree, new_tree, param))
+ {
+ *tree = tree_or(param, *tree, new_tree);
+ if (!*tree)
+ return 1;
+ if (((*tree)->type == SEL_TREE::MAYBE) ||
+ ((*tree)->type == SEL_TREE::ALWAYS))
+ return 1;
+ /* SEL_TREE::IMPOSSIBLE is impossible here */
+ return 0;
+ }
+ }
+
+ /* new tree cannot be combined with any of existing trees */
+ return or_sel_tree(param, new_tree);
+}
+
+
+/*
+ Perform OR operation on this index_merge and supplied index_merge list.
+
+ RETURN
+ 0 - OK
+ 1 - One of conditions in result is always TRUE and this SEL_IMERGE
+ should be discarded.
+ -1 - An error occurred
+*/
+
+int SEL_IMERGE::or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge)
+{
+ for (SEL_TREE** tree= imerge->trees;
+ tree != imerge->trees_next;
+ tree++)
+ {
+ if (or_sel_tree_with_checks(param, *tree))
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ Perform AND operation on two index_merge lists, storing result in *im1.
+
+*/
+
+inline void imerge_list_and_list(List<SEL_IMERGE> *im1, List<SEL_IMERGE> *im2)
+{
+ im1->concat(im2);
+}
+
+
+/*
+ Perform OR operation on 2 index_merge lists, storing result in first list.
+
+ NOTES
+ The following conversion is implemented:
+ (a_1 &&...&& a_N)||(b_1 &&...&& b_K) = AND_i,j(a_i || b_j) =>
+ => (a_1||b_1).
+
+ i.e. all conjuncts except the first one are currently dropped.
+ This is done to avoid producing N*K ways to do index_merge.
+
+ If (a_1||b_1) produce a condition that is always true, NULL is
+ returned and index_merge is discarded. (while it is actually
+ possible to try harder).
+
+ As a consequence of this, choice of keys to do index_merge
+ read may depend on the order of conditions in WHERE part of
+ the query.
+
+ RETURN
+ 0 OK, result is stored in *im1
+ other Error, both passed lists are unusable
+
+*/
+
+int imerge_list_or_list(PARAM *param,
+ List<SEL_IMERGE> *im1,
+ List<SEL_IMERGE> *im2)
+{
+ SEL_IMERGE *imerge= im1->head();
+ im1->empty();
+ im1->push_back(imerge);
+
+ return imerge->or_sel_imerge_with_checks(param, im2->head());
+}
+
+
+/*
+ Perform OR operation on index_merge list and key tree.
+
+ RETURN
+ 0 OK, result is stored in *im1
+ other Error
+
+*/
+
+int imerge_list_or_tree(PARAM *param,
+ List<SEL_IMERGE> *im1,
+ SEL_TREE *tree)
+{
+ SEL_IMERGE *imerge;
+ List_iterator<SEL_IMERGE> it(*im1);
+ while((imerge= it++))
+ {
+ if (imerge->or_sel_tree_with_checks(param, tree))
+ it.remove();
+ }
+ return im1->is_empty();
+}
/***************************************************************************
-** Basic functions for SQL_SELECT and QUICK_SELECT
+** Basic functions for SQL_SELECT and QUICK_RANGE_SELECT
***************************************************************************/
/* make a select from mysql info
@@ -379,11 +610,19 @@ SQL_SELECT::~SQL_SELECT()
#undef index // Fix for Unixware 7
-QUICK_SELECT::QUICK_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc)
- :dont_free(0),error(0),index(key_nr),max_used_key_length(0),
- used_key_parts(0), head(table), it(ranges),range(0)
+QUICK_SELECT_I::QUICK_SELECT_I()
+ :max_used_key_length(0),
+ used_key_parts(0)
+{}
+
+QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
+ bool no_alloc, MEM_ROOT *parent_alloc)
+ :dont_free(0),error(0),it(ranges),range(0)
{
- if (!no_alloc)
+ index= key_nr;
+ head= table;
+
+ if (!no_alloc && !parent_alloc)
{
// Allocates everything through the internal memroot
init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
@@ -391,12 +630,16 @@ QUICK_SELECT::QUICK_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc)
}
else
bzero((char*) &alloc,sizeof(alloc));
- file=head->file;
- record=head->record[0];
- init();
+ file= head->file;
+ record= head->record[0];
+}
+
+int QUICK_RANGE_SELECT::init()
+{
+ return (error= file->index_init(index));
}
-QUICK_SELECT::~QUICK_SELECT()
+QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT()
{
if (!dont_free)
{
@@ -405,6 +648,42 @@ QUICK_SELECT::~QUICK_SELECT()
}
}
+
+QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd, TABLE *table)
+ :cur_quick_it(quick_selects), index_merge(thd)
+{
+ index= MAX_KEY;
+ head= table;
+ init_sql_alloc(&alloc,1024,0);
+}
+
+int QUICK_INDEX_MERGE_SELECT::init()
+{
+ int error;
+ cur_quick_it.rewind();
+ cur_quick_select= cur_quick_it++;
+ if ((error= index_merge.init(head)))
+ return error;
+ return cur_quick_select->init();
+}
+
+void QUICK_INDEX_MERGE_SELECT::reset()
+{
+ cur_quick_select->reset();
+}
+
+bool
+QUICK_INDEX_MERGE_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick_sel_range)
+{
+ return quick_selects.push_back(quick_sel_range);
+}
+
+QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT()
+{
+ quick_selects.delete_elements();
+ free_root(&alloc,MYF(0));
+}
+
QUICK_RANGE::QUICK_RANGE()
:min_key(0),max_key(0),min_length(0),max_length(0),
flag(NO_MIN_RANGE | NO_MAX_RANGE)
@@ -598,6 +877,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
uint basflag;
uint idx;
double scan_time;
+ QUICK_INDEX_MERGE_SELECT *quick_imerge= NULL;
DBUG_ENTER("test_quick_select");
DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu",
keys_to_use.to_ulonglong(), (ulong) prev_tables,
@@ -691,70 +971,215 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
read_time= (double) HA_POS_ERROR;
}
else if (tree->type == SEL_TREE::KEY ||
- tree->type == SEL_TREE::KEY_SMALLER)
+ tree->type == SEL_TREE::KEY_SMALLER)
{
- SEL_ARG **key,**end,**best_key=0;
-
-
- for (idx=0,key=tree->keys, end=key+param.keys ;
- key != end ;
- key++,idx++)
- {
- ha_rows found_records;
- double found_read_time;
- if (*key)
- {
- uint keynr= param.real_keynr[idx];
- if ((*key)->type == SEL_ARG::MAYBE_KEY ||
- (*key)->maybe_flag)
- needed_reg.set_bit(keynr);
-
- found_records=check_quick_select(&param, idx, *key);
- if (found_records != HA_POS_ERROR && found_records > 2 &&
- head->used_keys.is_set(keynr) &&
- (head->file->index_flags(keynr) & HA_KEY_READ_ONLY))
- {
- /*
- We can resolve this by only reading through this key.
- Assume that we will read trough the whole key range
- and that all key blocks are half full (normally things are
- much better).
- */
- uint keys_per_block= (head->file->block_size/2/
- (head->key_info[keynr].key_length+
- head->file->ref_length) + 1);
- found_read_time=((double) (found_records+keys_per_block-1)/
- (double) keys_per_block);
- }
- else
- found_read_time= (head->file->read_time(keynr,
- param.range_count,
- found_records)+
- (double) found_records / TIME_FOR_COMPARE);
- if (read_time > found_read_time && found_records != HA_POS_ERROR)
- {
- read_time=found_read_time;
- records=found_records;
- best_key=key;
- }
- }
- }
- if (best_key && records)
- {
- if ((quick=get_quick_select(&param,(uint) (best_key-tree->keys),
- *best_key)))
- {
- quick->records=records;
- quick->read_time=read_time;
- }
- }
+ /*
+ It is possible to use a quick select (but maybe it would be slower
+ than 'all' table scan).
+ */
+ SEL_ARG **best_key= 0;
+ ha_rows found_records;
+ double found_read_time= read_time;
+
+ if (!get_quick_select_params(tree, param, needed_reg, head, true,
+ &found_read_time, &found_records,
+ &best_key))
+ {
+ /*
+ Ok, quick select is better than 'all' table scan and we have its
+ parameters, so construct it.
+ */
+ read_time= found_read_time;
+ records= found_records;
+
+ if ((quick= get_quick_select(&param,(uint) (best_key-tree->keys),
+ *best_key)) && (!quick->init()))
+ {
+ quick->records= records;
+ quick->read_time= read_time;
+ }
+ }
+
+ /*
+ btw, tree type SEL_TREE::INDEX_MERGE was not introduced
+ intentionally
+ */
+
+ /* if no range select could be built, try using index_merge */
+ if (!quick && !tree->merges.is_empty())
+ {
+ DBUG_PRINT("info",("No range reads possible,"
+ " trying to construct index_merge"));
+ SEL_IMERGE *imerge;
+ SEL_IMERGE *min_imerge= NULL;
+ double min_imerge_cost= DBL_MAX;
+ ha_rows min_imerge_records;
+ LINT_INIT(min_imerge_records); // Protected by min_imerge
+
+ List_iterator_fast<SEL_IMERGE> it(tree->merges);
+ while ((imerge= it++))
+ {
+ bool imerge_failed= false;
+ double imerge_cost= 0;
+ ha_rows imerge_total_records= 0;
+ double tree_read_time;
+ ha_rows tree_records;
+ imerge->best_keys=
+ (SEL_ARG***)alloc_root(&alloc,
+ (imerge->trees_next - imerge->trees)*
+ sizeof(void*));
+ for (SEL_TREE **ptree= imerge->trees;
+ ptree != imerge->trees_next;
+ ptree++)
+ {
+ tree_read_time= read_time;
+ if (get_quick_select_params(*ptree, param, needed_reg, head,
+ false,
+ &tree_read_time, &tree_records,
+ &(imerge->best_keys[ptree -
+ imerge->trees])))
+ imerge_failed= true;
+ imerge_cost += tree_read_time;
+ imerge_total_records += tree_records;
+ }
+
+ if (!imerge_failed)
+ {
+ imerge_total_records= min(imerge_total_records,
+ head->file->records);
+ imerge_cost += imerge_total_records / TIME_FOR_COMPARE;
+ if (imerge_cost < min_imerge_cost)
+ {
+ min_imerge= imerge;
+ min_imerge_cost= imerge_cost;
+ min_imerge_records= imerge_total_records;
+ }
+ }
+ }
+
+ if (!min_imerge)
+ goto end_free;
+
+ records= min_imerge_records;
+ /* ok, got minimal imerge, *min_imerge, with cost min_imerge_cost */
+
+ if (!head->used_keys.is_clear_all())
+ {
+ /* check if "ALL" +"using index" read would be faster */
+ int key_for_use= find_shortest_key(head, &head->used_keys);
+ ha_rows total_table_records= (0 == head->file->records)? 1 :
+ head->file->records;
+ uint keys_per_block= (head->file->block_size/2/
+ (head->key_info[key_for_use].key_length+
+ head->file->ref_length) + 1);
+ double all_index_scan_read_time= ((double)(total_table_records+
+ keys_per_block-1)/
+ (double) keys_per_block);
+
+ DBUG_PRINT("info",
+ ("'all' scan will be using key %d, read time %g",
+ key_for_use, all_index_scan_read_time));
+ if (all_index_scan_read_time < min_imerge_cost)
+ {
+ DBUG_PRINT("info",
+ ("index merge would be slower, "
+ "will do full 'index' scan"));
+ goto end_free;
+ }
+ }
+ else
+ {
+ /* check if "ALL" would be faster */
+ if (read_time < min_imerge_cost)
+ {
+ DBUG_PRINT("info",
+ ("index merge would be slower, "
+ "will do full table scan"));
+ goto end_free;
+ }
+ }
+
+ if (!(quick= quick_imerge= new QUICK_INDEX_MERGE_SELECT(thd, head)))
+ goto end_free;
+
+ quick->records= min_imerge_records;
+ quick->read_time= min_imerge_cost;
+
+ my_pthread_setspecific_ptr(THR_MALLOC, &quick_imerge->alloc);
+
+ QUICK_RANGE_SELECT *new_quick;
+ for (SEL_TREE **ptree = min_imerge->trees;
+ ptree != min_imerge->trees_next;
+ ptree++)
+ {
+ SEL_ARG **tree_best_key=
+ min_imerge->best_keys[ptree - min_imerge->trees];
+ if ((new_quick= get_quick_select(&param,
+ (uint)(tree_best_key-
+ (*ptree)->keys),
+ *tree_best_key,
+ &quick_imerge->alloc)))
+ {
+ new_quick->records= min_imerge_records;
+ new_quick->read_time= min_imerge_cost;
+ /*
+ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT leaves THR_MALLOC
+ pointing to its allocator, restore it back
+ */
+ quick_imerge->last_quick_select= new_quick;
+
+ if (quick_imerge->push_quick_back(new_quick))
+ {
+ delete new_quick;
+ delete quick;
+ quick= quick_imerge= NULL;
+ goto end_free;
+ }
+ }
+ else
+ {
+ delete quick;
+ quick= quick_imerge= NULL;
+ goto end_free;
+ }
+ }
+
+ free_root(&alloc,MYF(0));
+ my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ if (quick->init())
+ {
+ delete quick;
+ quick= quick_imerge= NULL;
+ DBUG_PRINT("error",
+ ("Failed to allocate index merge structures,"
+ "falling back to full scan."));
+ }
+ else
+ {
+ /* with 'using filesort' quick->reset() is not called */
+ quick->reset();
+ }
+
+ goto end;
+ }
}
}
+end_free:
free_root(&alloc,MYF(0)); // Return memory & allocator
my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+end:
thd->no_errors=0;
}
- DBUG_EXECUTE("info",print_quick(quick,&needed_reg););
+
+ DBUG_EXECUTE("info",
+ {
+ if (quick_imerge)
+ print_quick_sel_imerge(quick_imerge, &needed_reg);
+ else
+ print_quick_sel_range((QUICK_RANGE_SELECT*)quick, &needed_reg);
+ }
+ );
+
/*
Assume that if the user is using 'limit' we will only need to scan
limit rows if we are using a key
@@ -762,6 +1187,75 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
DBUG_RETURN(records ? test(quick) : -1);
}
+
+/*
+ Calculate quick select read time, # of records, and best key to use
+ without constructing QUICK_SELECT
+*/
+
+static int get_quick_select_params(SEL_TREE *tree, PARAM& param,
+ key_map& needed_reg, TABLE *head,
+ bool index_read_can_be_used,
+ double* read_time, ha_rows* records,
+ SEL_ARG*** key_to_read)
+{
+ int idx;
+ int result = 1;
+ /*
+ Note that there may be trees that have type SEL_TREE::KEY but contain
+ no key reads at all. For example, tree for expression "key1 is not null"
+ where key1 is defined as "not null".
+ */
+ SEL_ARG **key,**end;
+
+ for (idx= 0,key=tree->keys, end=key+param.keys ;
+ key != end ;
+ key++,idx++)
+ {
+ ha_rows found_records;
+ double found_read_time;
+ if (*key)
+ {
+ uint keynr= param.real_keynr[idx];
+ if ((*key)->type == SEL_ARG::MAYBE_KEY ||
+ (*key)->maybe_flag)
+ needed_reg.set_bit(keynr);
+
+ bool read_index_only= index_read_can_be_used? head->used_keys.is_set(keynr): false;
+ found_records=check_quick_select(&param, idx, *key);
+ if (found_records != HA_POS_ERROR && found_records > 2 &&
+ read_index_only &&
+ (head->file->index_flags(keynr) & HA_KEY_READ_ONLY))
+ {
+ /*
+ We can resolve this by only reading through this key.
+ Assume that we will read trough the whole key range
+ and that all key blocks are half full (normally things are
+ much better).
+ */
+ uint keys_per_block= (head->file->block_size/2/
+ (head->key_info[keynr].key_length+
+ head->file->ref_length) + 1);
+ found_read_time=((double) (found_records+keys_per_block-1)/
+ (double) keys_per_block);
+ }
+ else
+ found_read_time= (head->file->read_time(keynr,
+ param.range_count,
+ found_records)+
+ (double) found_records / TIME_FOR_COMPARE);
+ if (*read_time > found_read_time && found_records != HA_POS_ERROR)
+ {
+ *read_time= found_read_time;
+ *records= found_records;
+ *key_to_read= key;
+ result = 0;
+ }
+ }
+ }
+ return result;
+}
+
/* make a select tree of all keys in condition */
static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
@@ -948,6 +1442,7 @@ get_mm_parts(PARAM *param, Field *field, Item_func::Functype type,
}
sel_arg->part=(uchar) key_part->part;
tree->keys[key_part->key]=sel_add(tree->keys[key_part->key],sel_arg);
+ tree->keys_map.set_bit(key_part->key);
}
}
@@ -1243,6 +1738,8 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
DBUG_RETURN(tree1);
}
+ key_map result_keys;
+ result_keys.clear_all();
/* Join the trees key per key */
SEL_ARG **key1,**key2,**end;
for (key1= tree1->keys,key2= tree2->keys,end=key1+param->keys ;
@@ -1259,17 +1756,57 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
if ((*key1)->type == SEL_ARG::IMPOSSIBLE)
{
tree1->type= SEL_TREE::IMPOSSIBLE;
- break;
+ DBUG_RETURN(tree1);
}
+ result_keys.set_bit(key1 - tree1->keys);
#ifdef EXTRA_DEBUG
(*key1)->test_use_count(*key1);
#endif
}
}
+ tree1->keys_map= result_keys;
+ /* dispose index_merge if there is a "range" option */
+ if (!result_keys.is_clear_all())
+ {
+ tree1->merges.empty();
+ DBUG_RETURN(tree1);
+ }
+
+ /* ok, both trees are index_merge trees */
+ imerge_list_and_list(&tree1->merges, &tree2->merges);
DBUG_RETURN(tree1);
}
+/*
+ Check if two SEL_TREES can be combined into one without using index_merge
+*/
+
+bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, PARAM* param)
+{
+ key_map common_keys= tree1->keys_map;
+ common_keys.intersect(tree2->keys_map);
+ DBUG_ENTER("sel_trees_can_be_ored");
+
+ if (common_keys.is_clear_all())
+ DBUG_RETURN(false);
+
+ /* trees have a common key, check if they refer to same key part */
+ SEL_ARG **key1,**key2;
+ for (uint key_no=0; key_no < param->keys; key_no++)
+ {
+ if (common_keys.is_set(key_no))
+ {
+ key1= tree1->keys + key_no;
+ key2= tree2->keys + key_no;
+ if ((*key1)->part == (*key2)->part)
+ {
+ DBUG_RETURN(true);
+ }
+ }
+ }
+ DBUG_RETURN(false);
+}
static SEL_TREE *
tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
@@ -1286,19 +1823,62 @@ tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
if (tree2->type == SEL_TREE::MAYBE)
DBUG_RETURN(tree2);
- /* Join the trees key per key */
- SEL_ARG **key1,**key2,**end;
- SEL_TREE *result=0;
- for (key1= tree1->keys,key2= tree2->keys,end=key1+param->keys ;
- key1 != end ; key1++,key2++)
+ SEL_TREE *result= 0;
+ key_map result_keys;
+ result_keys.clear_all();
+ if (sel_trees_can_be_ored(tree1, tree2, param))
{
- *key1=key_or(*key1,*key2);
- if (*key1)
+ /* Join the trees key per key */
+ SEL_ARG **key1,**key2,**end;
+ for (key1= tree1->keys,key2= tree2->keys,end= key1+param->keys ;
+ key1 != end ; key1++,key2++)
{
- result=tree1; // Added to tree1
+ *key1=key_or(*key1,*key2);
+ if (*key1)
+ {
+ result=tree1; // Added to tree1
+ result_keys.set_bit(key1 - tree1->keys);
#ifdef EXTRA_DEBUG
- (*key1)->test_use_count(*key1);
+ (*key1)->test_use_count(*key1);
#endif
+ }
+ }
+ if (result)
+ result->keys_map= result_keys;
+ }
+ else
+ {
+ /* ok, two trees have KEY type but cannot be used without index merge */
+ if (tree1->merges.is_empty() && tree2->merges.is_empty())
+ {
+ SEL_IMERGE *merge;
+ /* both trees are "range" trees, produce new index merge structure */
+ if (!(result= new SEL_TREE()) || !(merge= new SEL_IMERGE()) ||
+ (result->merges.push_back(merge)) ||
+ (merge->or_sel_tree(param, tree1)) ||
+ (merge->or_sel_tree(param, tree2)))
+ result= NULL;
+ else
+ result->type= tree1->type;
+ }
+ else if (!tree1->merges.is_empty() && !tree2->merges.is_empty())
+ {
+ if (imerge_list_or_list(param, &tree1->merges, &tree2->merges))
+ result= new SEL_TREE(SEL_TREE::ALWAYS);
+ else
+ result= tree1;
+ }
+ else
+ {
+ /* one tree is index merge tree and another is range tree */
+ if (tree1->merges.is_empty())
+ swap(SEL_TREE*, tree1, tree2);
+
+ /* add tree2 to tree1->merges, checking if it collapses to ALWAYS */
+ if (imerge_list_or_tree(param, &tree1->merges, tree2))
+ result= new SEL_TREE(SEL_TREE::ALWAYS);
+ else
+ result= tree1;
}
}
DBUG_RETURN(result);
@@ -1347,7 +1927,6 @@ and_all_keys(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag)
}
-
static SEL_ARG *
key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag)
{
@@ -2292,15 +2871,17 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
/****************************************************************************
** change a tree to a structure to be used by quick_select
** This uses it's own malloc tree
+** The caller should call QUICK_SELCT::init for returned quick select
****************************************************************************/
-
-static QUICK_SELECT *
-get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
+QUICK_RANGE_SELECT *
+get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree,
+ MEM_ROOT *parent_alloc)
{
- QUICK_SELECT *quick;
+ QUICK_RANGE_SELECT *quick;
DBUG_ENTER("get_quick_select");
- if ((quick=new QUICK_SELECT(param->thd, param->table,
- param->real_keynr[idx])))
+ if ((quick=new QUICK_RANGE_SELECT(param->thd, param->table,
+ param->real_keynr[idx],test(parent_alloc),
+ parent_alloc)))
{
if (quick->error ||
get_quick_keys(param,quick,param->key[idx],key_tree,param->min_key,0,
@@ -2312,9 +2893,10 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
else
{
quick->key_parts=(KEY_PART*)
- memdup_root(&quick->alloc,(char*) param->key[idx],
- sizeof(KEY_PART)*
- param->table->key_info[param->real_keynr[idx]].key_parts);
+ memdup_root(parent_alloc? parent_alloc : &quick->alloc,
+ (char*) param->key[idx],
+ sizeof(KEY_PART)*
+ param->table->key_info[param->real_keynr[idx]].key_parts);
}
}
DBUG_RETURN(quick);
@@ -2324,9 +2906,8 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
/*
** Fix this to get all possible sub_ranges
*/
-
-static bool
-get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
+bool
+get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
SEL_ARG *key_tree,char *min_key,uint min_key_flag,
char *max_key, uint max_key_flag)
{
@@ -2436,7 +3017,7 @@ get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
Return 1 if there is only one range and this uses the whole primary key
*/
-bool QUICK_SELECT::unique_key_range()
+bool QUICK_RANGE_SELECT::unique_key_range()
{
if (ranges.elements == 1)
{
@@ -2473,10 +3054,11 @@ static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length)
** Create a QUICK RANGE based on a key
****************************************************************************/
-QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref)
+QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
+ TABLE_REF *ref)
{
table->file->index_end(); // Remove old cursor
- QUICK_SELECT *quick=new QUICK_SELECT(thd, table, ref->key, 1);
+ QUICK_RANGE_SELECT *quick=new QUICK_RANGE_SELECT(thd, table, ref->key, 1);
KEY *key_info = &table->key_info[ref->key];
KEY_PART *key_part;
QUICK_RANGE *range;
@@ -2484,6 +3066,12 @@ QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref)
if (!quick)
return 0; /* no ranges found */
+ if (quick->init())
+ {
+ delete quick;
+ return 0;
+ }
+
if (cp_buffer_from_ref(ref))
{
if (thd->is_fatal_error)
@@ -2521,11 +3109,204 @@ err:
return 0;
}
+INDEX_MERGE::INDEX_MERGE(THD *thd_arg) :
+ dont_save(false), thd(thd_arg)
+{}
+
+String *INDEX_MERGE::Item_rowid::val_str(String *str)
+{
+ str->set_quick((char*)head->file->ref, head->file->ref_length, collation.collation);
+ return str;
+}
+
+
+/*
+ Initialize index_merge operation.
+ RETURN
+ 0 - OK
+ other - error.
+*/
+
+int INDEX_MERGE::init(TABLE *table)
+{
+ DBUG_ENTER("INDEX_MERGE::init");
+
+ head= table;
+ if (!(rowid_item= new Item_rowid(table)))
+ DBUG_RETURN(1);
+
+ tmp_table_param.copy_field= 0;
+ tmp_table_param.end_write_records= HA_POS_ERROR;
+ tmp_table_param.group_length= table->file->ref_length;
+ tmp_table_param.group_parts= 1;
+ tmp_table_param.group_null_parts= 0;
+ tmp_table_param.hidden_field_count= 0;
+ tmp_table_param.field_count= 0;
+ tmp_table_param.func_count= 1;
+ tmp_table_param.sum_func_count= 0;
+ tmp_table_param.quick_group= 1;
+
+ bzero(&order, sizeof(ORDER));
+ order.item= (Item**)&rowid_item;
+ order.asc= 1;
+
+ fields.push_back(rowid_item);
+
+ temp_table= create_tmp_table(thd,
+ &tmp_table_param,
+ fields,
+ &order,
+ false,
+ 0,
+ SELECT_DISTINCT,
+ HA_POS_ERROR,
+ (char *)"");
+ DBUG_RETURN(!temp_table);
+}
+
+/*
+ Check if record with ROWID record_pos has already been processed and
+ if not - store the ROWID value.
+
+ RETURN
+ 0 - record has not been processed yet
+ 1 - record has already been processed.
+ -1 - an error occurred and query processing should be terminated.
+ Error code is stored in INDEX_MERGE::error
+*/
+
+int INDEX_MERGE::check_record_in()
+{
+ return (dont_save)?
+ check_record() :
+ put_record();
+}
+
+
+/*
+ Stop remembering records in check().
+ (this should be called just before the last key scan)
+
+ RETURN
+ 0 - OK
+ 1 - error occurred initializing table index.
+*/
+
+int INDEX_MERGE::start_last_quick_select()
+{
+ int result= 0;
+ if (!temp_table->uniques)
+ {
+ dont_save= true;
+ result= temp_table->file->index_init(0);
+ }
+ return result;
+}
+
+
+inline int INDEX_MERGE::put_record()
+{
+ DBUG_ENTER("INDEX_MERGE::put_record");
+
+ copy_funcs(tmp_table_param.items_to_copy);
+
+ if ((error= temp_table->file->write_row(temp_table->record[0])))
+ {
+ if (error == HA_ERR_FOUND_DUPP_KEY ||
+ error == HA_ERR_FOUND_DUPP_UNIQUE)
+ DBUG_RETURN(1);
+
+ DBUG_PRINT("info",
+ ("Error writing row to temp. table: %d, converting to myisam",
+ error));
+ if (create_myisam_from_heap(current_thd, temp_table, &tmp_table_param,
+ error,1))
+ {
+ DBUG_PRINT("info", ("Table conversion failed, bailing out"));
+ DBUG_RETURN(-1);
+ }
+ }
+
+ DBUG_RETURN(0);
+}
+
+inline int INDEX_MERGE::check_record()
+{
+ int result= 1;
+ DBUG_ENTER("INDEX_MERGE::check_record");
+
+ if ((error= temp_table->file->index_read(temp_table->record[0],
+ head->file->ref,
+ head->file->ref_length,
+ HA_READ_KEY_EXACT)))
+ {
+ if (error != HA_ERR_KEY_NOT_FOUND)
+ result= -1;
+ else
+ result= 0;
+ }
+
+ DBUG_RETURN(result);
+}
+
+INDEX_MERGE::~INDEX_MERGE()
+{
+ if (temp_table)
+ {
+ DBUG_PRINT("info", ("Freeing temp. table"));
+ free_tmp_table(current_thd, temp_table);
+ }
+ /* rowid_item is freed automatically */
+ list_node* node;
+ node= fields.first_node();
+ fields.remove(&node);
+}
+
+int QUICK_INDEX_MERGE_SELECT::get_next()
+{
+ int result;
+ int put_result;
+ DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::get_next");
+
+ do
+ {
+ while ((result= cur_quick_select->get_next()) == HA_ERR_END_OF_FILE)
+ {
+ cur_quick_select= cur_quick_it++;
+ if (!cur_quick_select)
+ break;
+
+ cur_quick_select->init();
+ cur_quick_select->reset();
+
+ if (last_quick_select == cur_quick_select)
+ {
+ if ((result= index_merge.start_last_quick_select()))
+ DBUG_RETURN(result);
+ }
+ }
+
+ if (result)
+ {
+ /*
+ table read error (including HA_ERR_END_OF_FILE on last quick select
+ in index_merge)
+ */
+ DBUG_RETURN(result);
+ }
+
+ cur_quick_select->file->position(cur_quick_select->record);
+ put_result= index_merge.check_record_in();
+ }while(put_result == 1); /* While record is processed */
+
+ DBUG_RETURN((put_result != -1) ? result : index_merge.error);
+}
+
/* get next possible record using quick-struct */
-int QUICK_SELECT::get_next()
+int QUICK_RANGE_SELECT::get_next()
{
- DBUG_ENTER("get_next");
+ DBUG_ENTER("QUICK_RANGE_SELECT::get_next");
for (;;)
{
@@ -2612,7 +3393,7 @@ int QUICK_SELECT::get_next()
Returns 0 if key <= range->max_key
*/
-int QUICK_SELECT::cmp_next(QUICK_RANGE *range_arg)
+int QUICK_RANGE_SELECT::cmp_next(QUICK_RANGE *range_arg)
{
if (range_arg->flag & NO_MAX_RANGE)
return 0; /* key can't be to large */
@@ -2653,8 +3434,9 @@ int QUICK_SELECT::cmp_next(QUICK_RANGE *range_arg)
for now, this seems to work right at least.
*/
-QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_SELECT *q, uint used_key_parts)
- : QUICK_SELECT(*q), rev_it(rev_ranges)
+QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q,
+ uint used_key_parts)
+ : QUICK_RANGE_SELECT(*q), rev_it(rev_ranges)
{
bool not_read_after_key = file->table_flags() & HA_NOT_READ_AFTER_KEY;
QUICK_RANGE *r;
@@ -2921,7 +3703,23 @@ print_key(KEY_PART *key_part,const char *key,uint used_length)
}
}
-static void print_quick(QUICK_SELECT *quick,const key_map* needed_reg)
+static void print_quick_sel_imerge(QUICK_INDEX_MERGE_SELECT *quick,
+ const key_map *needed_reg)
+{
+ DBUG_ENTER("print_param");
+ if (! _db_on_ || !quick)
+ DBUG_VOID_RETURN;
+
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick->quick_selects);
+ QUICK_RANGE_SELECT* quick_range_sel;
+ while ((quick_range_sel= it++))
+ {
+ print_quick_sel_range(quick_range_sel, needed_reg);
+ }
+ DBUG_VOID_RETURN;
+}
+
+void print_quick_sel_range(QUICK_RANGE_SELECT *quick,const key_map *needed_reg)
{
QUICK_RANGE *range;
char buf[MAX_KEY/8+1];
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 1c209dc7027..c5e9956ceef 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -65,41 +65,198 @@ class QUICK_RANGE :public Sql_alloc {
}
};
+class INDEX_MERGE;
-class QUICK_SELECT {
+/*
+ Quick select interface.
+ This class is parent for all QUICK_*_SELECT and FT_SELECT classes.
+*/
+
+class QUICK_SELECT_I
+{
public:
+ ha_rows records; /* estimate of # of records to be retrieved */
+ double read_time; /* time to perform this retrieval */
+ TABLE *head;
+
+ /*
+ the only index this quick select uses, or MAX_KEY for
+ QUICK_INDEX_MERGE_SELECT
+ */
+ uint index;
+ uint max_used_key_length, used_key_parts;
+
+ QUICK_SELECT_I();
+ virtual ~QUICK_SELECT_I(){};
+ virtual int init() = 0;
+ virtual void reset(void) = 0;
+ virtual int get_next() = 0; /* get next record to retrieve */
+ virtual bool reverse_sorted() = 0;
+ virtual bool unique_key_range() { return false; }
+
+ enum {
+ QS_TYPE_RANGE = 0,
+ QS_TYPE_INDEX_MERGE = 1,
+ QS_TYPE_RANGE_DESC = 2,
+ QS_TYPE_FULLTEXT = 3
+ };
+
+ /* Get type of this quick select - one of the QS_* values */
+ virtual int get_type() = 0;
+};
+
+struct st_qsel_param;
+class SEL_ARG;
+
+class QUICK_RANGE_SELECT : public QUICK_SELECT_I
+{
+protected:
bool next,dont_free;
+public:
int error;
- uint index, max_used_key_length, used_key_parts;
- TABLE *head;
handler *file;
byte *record;
+protected:
+ friend void print_quick_sel_range(QUICK_RANGE_SELECT *quick,
+ const key_map* needed_reg);
+ friend QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
+ struct st_table_ref *ref);
+ friend bool get_quick_keys(struct st_qsel_param *param,
+ QUICK_RANGE_SELECT *quick,KEY_PART *key,
+ SEL_ARG *key_tree,char *min_key,uint min_key_flag,
+ char *max_key, uint max_key_flag);
+ friend QUICK_RANGE_SELECT *get_quick_select(struct st_qsel_param*,uint idx,
+ SEL_ARG *key_tree,
+ MEM_ROOT *alloc);
+ friend class QUICK_SELECT_DESC;
+
List<QUICK_RANGE> ranges;
List_iterator<QUICK_RANGE> it;
QUICK_RANGE *range;
MEM_ROOT alloc;
-
- KEY_PART *key_parts;
- ha_rows records;
- double read_time;
-
- QUICK_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc=0);
- virtual ~QUICK_SELECT();
- void reset(void) { next=0; it.rewind(); }
- int init() { return error=file->index_init(index); }
- virtual int get_next();
- virtual bool reverse_sorted() { return 0; }
+ KEY_PART *key_parts;
int cmp_next(QUICK_RANGE *range);
+public:
+ QUICK_RANGE_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc=0,
+ MEM_ROOT *parent_alloc=NULL);
+ ~QUICK_RANGE_SELECT();
+
+ void reset(void) { next=0; it.rewind(); }
+ int init();
+ int get_next();
+ bool reverse_sorted() { return 0; }
bool unique_key_range();
+ int get_type() { return QS_TYPE_RANGE; }
+};
+
+/*
+ Helper class for keeping track of rows that have been passed to output
+ in index_merge access method.
+
+ NOTES
+ Current implementation uses a temporary table to store ROWIDs of rows that
+ have been passed to output. In the future it might be changed to use more
+ efficient mechanisms, like Unique class.
+*/
+
+class INDEX_MERGE
+{
+public:
+ INDEX_MERGE(THD *thd_arg);
+ ~INDEX_MERGE();
+
+ int init(TABLE *table);
+ int check_record_in();
+ int start_last_quick_select();
+ int error;
+private:
+ /* The only field in temporary table */
+ class Item_rowid : public Item_str_func
+ {
+ TABLE *head; /* source table */
+ public:
+ Item_rowid(TABLE *table) : head(table)
+ {
+ max_length= table->file->ref_length;
+ collation.set(&my_charset_bin);
+ };
+ const char *func_name() const { return "rowid"; }
+ bool const_item() const { return 0; }
+ String *val_str(String *);
+ void fix_length_and_dec()
+ {}
+ };
+
+ /* Check if record has been processed and save it if it wasn't */
+ inline int put_record();
+
+ /* Check if record has been processed without saving it */
+ inline int check_record();
+
+ /* If true, check_record_in does't store ROWIDs it is passed. */
+ bool dont_save;
+
+ THD *thd;
+ TABLE *head; /* source table */
+ TABLE *temp_table; /* temp. table used for values storage */
+ TMP_TABLE_PARAM tmp_table_param; /* temp. table creation parameters */
+ Item_rowid *rowid_item; /* the only field in temp. table */
+ List<Item> fields; /* temp. table fields list
+ (the only element is rowid_item) */
+ ORDER order; /* key for temp. table (rowid_item) */
};
-class QUICK_SELECT_DESC: public QUICK_SELECT
+/*
+ Index merge quick select.
+ It is implemented as a container for several QUICK_RANGE_SELECTs.
+*/
+
+class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I
+{
+public:
+ QUICK_INDEX_MERGE_SELECT(THD *thd, TABLE *table);
+ ~QUICK_INDEX_MERGE_SELECT();
+
+ int init();
+ void reset(void);
+ int get_next();
+ bool reverse_sorted() { return false; }
+ bool unique_key_range() { return false; }
+ int get_type() { return QS_TYPE_INDEX_MERGE; }
+
+ bool push_quick_back(QUICK_RANGE_SELECT *quick_sel_range);
+
+ /* range quick selects this index_merge read consists of */
+ List<QUICK_RANGE_SELECT> quick_selects;
+
+ /* quick select which is currently used for rows retrieval */
+ List_iterator_fast<QUICK_RANGE_SELECT> cur_quick_it;
+ QUICK_RANGE_SELECT* cur_quick_select;
+
+ /*
+ Last element in quick_selects list.
+ INDEX_MERGE::start_last_quick_select is called before retrieving
+ rows for it.
+ */
+ QUICK_RANGE_SELECT* last_quick_select;
+
+ /*
+ Used to keep track of what records have been already passed to output
+ when doing index_merge access (NULL means no index_merge)
+ */
+ INDEX_MERGE index_merge;
+
+ MEM_ROOT alloc;
+};
+
+class QUICK_SELECT_DESC: public QUICK_RANGE_SELECT
{
public:
- QUICK_SELECT_DESC(QUICK_SELECT *q, uint used_key_parts);
+ QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts);
int get_next();
bool reverse_sorted() { return 1; }
+ int get_type() { return QS_TYPE_RANGE_DESC; }
private:
int cmp_prev(QUICK_RANGE *range);
bool range_reads_after_key(QUICK_RANGE *range);
@@ -114,7 +271,7 @@ private:
class SQL_SELECT :public Sql_alloc {
public:
- QUICK_SELECT *quick; // If quick-select used
+ QUICK_SELECT_I *quick; // If quick-select used
COND *cond; // where condition
TABLE *head;
IO_CACHE file; // Positions to used records
@@ -134,7 +291,7 @@ class SQL_SELECT :public Sql_alloc {
ha_rows limit, bool force_quick_range=0);
};
-QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
+QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
struct st_table_ref *ref);
#endif
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 0c9fed629b4..89aa7203e17 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -24,6 +24,7 @@
#endif
#include "mysql_priv.h"
+#include "sp_rcontext.h"
#include <stdarg.h>
#ifndef EMBEDDED_LIBRARY
@@ -62,6 +63,10 @@ void send_error(THD *thd, uint sql_errno, const char *err)
err ? err : net->last_error[0] ?
net->last_error : "NULL"));
+ if (thd->spcont && thd->spcont->find_handler(sql_errno))
+ {
+ DBUG_VOID_RETURN;
+ }
#ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/
query_cache_abort(net);
#endif
@@ -122,7 +127,7 @@ void send_error(THD *thd, uint sql_errno, const char *err)
thd->net.report_error= 0;
/* Abort multi-result sets */
- thd->lex.found_colon= 0;
+ thd->lex->found_colon= 0;
thd->server_status= ~SERVER_MORE_RESULTS_EXISTS;
DBUG_VOID_RETURN;
}
@@ -145,6 +150,10 @@ void send_error(THD *thd, uint sql_errno, const char *err)
void send_warning(THD *thd, uint sql_errno, const char *err)
{
DBUG_ENTER("send_warning");
+ if (thd->spcont && thd->spcont->find_handler(sql_errno))
+ {
+ DBUG_VOID_RETURN;
+ }
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, sql_errno,
err ? err : ER(sql_errno));
send_ok(thd);
@@ -175,6 +184,10 @@ net_printf(THD *thd, uint errcode, ...)
DBUG_ENTER("net_printf");
DBUG_PRINT("enter",("message: %u",errcode));
+ if (thd->spcont && thd->spcont->find_handler(errcode))
+ {
+ DBUG_VOID_RETURN;
+ }
thd->query_error= 1; // needed to catch query errors during replication
#ifndef EMBEDDED_LIBRARY
query_cache_abort(net); // Safety
@@ -342,7 +355,7 @@ send_eof(THD *thd, bool no_flush)
static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */
NET *net= &thd->net;
DBUG_ENTER("send_eof");
- if (net->vio != 0)
+ if (net->vio != 0 && !net->no_send_eof)
{
if (!no_flush && (thd->client_capabilities & CLIENT_PROTOCOL_41))
{
@@ -1148,12 +1161,3 @@ bool Protocol_prep::store_time(TIME *tm)
buff[0]=(char) length; // Length is stored first
return packet->append(buff, length+1, PACKET_BUFFET_EXTRA_ALLOC);
}
-
-#ifdef EMBEDDED_LIBRARY
-/* Should be removed when we define the Protocol_cursor's future */
-bool Protocol_cursor::write()
-{
- return Protocol_simple::write();
-}
-#endif
-
diff --git a/sql/protocol.h b/sql/protocol.h
index 67ae4ed01b4..d2ec08c3cf4 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -47,17 +47,13 @@ public:
Protocol(THD *thd_arg) { init(thd_arg); }
virtual ~Protocol() {}
void init(THD* thd_arg);
- bool send_fields(List<Item> *list, uint flag);
+ virtual bool send_fields(List<Item> *list, uint flag);
bool send_records_num(List<Item> *list, ulonglong records);
bool store(I_List<i_string> *str_list);
bool store(const char *from, CHARSET_INFO *cs);
String *storage_packet() { return packet; }
inline void free() { packet->free(); }
-#ifndef EMBEDDED_LIBRARY
- bool write();
-#else
virtual bool write();
-#endif
inline bool store(uint32 from)
{ return store_long((longlong) from); }
inline bool store(longlong from)
@@ -158,6 +154,7 @@ public:
Protocol_cursor(THD *thd_arg, MEM_ROOT *ini_alloc) :Protocol_simple(thd_arg), alloc(ini_alloc) {}
bool prepare_for_send(List<Item> *item_list)
{
+ row_count= 0;
fields= NULL;
data= NULL;
prev_record= &data;
@@ -165,6 +162,7 @@ public:
}
bool send_fields(List<Item> *list, uint flag);
bool write();
+ uint get_field_count() { return field_count; }
};
void send_warning(THD *thd, uint sql_errno, const char *err=0);
diff --git a/sql/protocol_cursor.cc b/sql/protocol_cursor.cc
index 5f35552c562..749b66785d4 100644
--- a/sql/protocol_cursor.cc
+++ b/sql/protocol_cursor.cc
@@ -51,6 +51,7 @@ bool Protocol_cursor::send_fields(List<Item> *list, uint flag)
client_field->name= strdup_root(alloc, server_field.col_name);
client_field->org_table= strdup_root(alloc, server_field.org_table_name);
client_field->org_name= strdup_root(alloc, server_field.org_col_name);
+ client_field->catalog= strdup_root(alloc, "");
client_field->length= server_field.length;
client_field->type= server_field.type;
client_field->flags= server_field.flags;
@@ -60,6 +61,7 @@ bool Protocol_cursor::send_fields(List<Item> *list, uint flag)
client_field->name_length= strlen(client_field->name);
client_field->org_name_length= strlen(client_field->org_name);
client_field->org_table_length= strlen(client_field->org_table);
+ client_field->catalog_length= 0;
client_field->charsetnr= server_field.charsetnr;
if (INTERNAL_NUM_FIELD(client_field))
@@ -100,17 +102,17 @@ bool Protocol_cursor::write()
byte *to;
new_record= (MYSQL_ROWS *)alloc_root(alloc,
- sizeof(MYSQL_ROWS) + (field_count + 1)*sizeof(char *) + packet->length());
+ sizeof(MYSQL_ROWS) + (field_count + 1)*sizeof(char *) + packet->length());
if (!new_record)
goto err;
data_tmp= (byte **)(new_record + 1);
new_record->data= (char **)data_tmp;
- to= (byte *)(fields + field_count + 1);
+ to= (byte *)data_tmp + (field_count + 1)*sizeof(char *);
for (; cur_field < fields_end; ++cur_field, ++data_tmp)
{
- if ((len=net_field_length((uchar **)&cp)))
+ if ((len= net_field_length((uchar **)&cp)) == 0)
{
*data_tmp= 0;
}
@@ -121,6 +123,7 @@ bool Protocol_cursor::write()
// TODO error signal send_error(thd, CR_MALFORMED_PACKET);
return TRUE;
}
+ *data_tmp= to;
memcpy(to,(char*) cp,len);
to[len]=0;
to+=len+1;
@@ -129,6 +132,7 @@ bool Protocol_cursor::write()
cur_field->max_length=len;
}
}
+ *data_tmp= 0;
*prev_record= new_record;
prev_record= &new_record->next;
@@ -139,5 +143,3 @@ bool Protocol_cursor::write()
// TODO error signal send_error(thd, ER_OUT_OF_RESOURCES);
return TRUE;
}
-
-
diff --git a/sql/records.cc b/sql/records.cc
index 0feb873a6af..cd1de5af6aa 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -175,7 +175,7 @@ static int rr_sequential(READ_RECORD *info)
{
if (info->thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ info->thd->send_kill_message();
return 1;
}
if (tmp != HA_ERR_RECORD_DELETED)
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 18456af8048..49c29ee1e9f 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -441,7 +441,7 @@ int show_new_master(THD* thd)
DBUG_ENTER("show_new_master");
List<Item> field_list;
char errmsg[SLAVE_ERRMSG_SIZE];
- LEX_MASTER_INFO* lex_mi = &thd->lex.mi;
+ LEX_MASTER_INFO* lex_mi = &thd->lex->mi;
errmsg[0]=0; // Safety
if (translate_master(thd, lex_mi, errmsg))
diff --git a/sql/set_var.cc b/sql/set_var.cc
index c8b11eb0f58..4cf9f07b4ba 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -83,6 +83,7 @@ static void sys_default_init_slave(THD*, enum_var_type type);
static bool set_option_bit(THD *thd, set_var *var);
static bool set_option_autocommit(THD *thd, set_var *var);
static bool set_log_update(THD *thd, set_var *var);
+static bool set_log_bin(THD *thd, set_var *var);
static void fix_low_priority_updates(THD *thd, enum_var_type type);
static void fix_tx_isolation(THD *thd, enum_var_type type);
static void fix_net_read_timeout(THD *thd, enum_var_type type);
@@ -350,7 +351,7 @@ static sys_var_thd_bit sys_log_update("sql_log_update",
set_log_update,
OPTION_UPDATE_LOG);
static sys_var_thd_bit sys_log_binlog("sql_log_bin",
- set_log_update,
+ set_log_bin,
OPTION_BIN_LOG);
static sys_var_thd_bit sys_sql_warnings("sql_warnings",
set_option_bit,
@@ -2197,13 +2198,38 @@ static bool set_option_autocommit(THD *thd, set_var *var)
static bool set_log_update(THD *thd, set_var *var)
{
+ /*
+ The update log is not supported anymore since 5.0.
+ See sql/mysqld.cc/, comments in function init_server_components() for an
+ explaination of the different warnings we send below
+ */
+
if (opt_sql_bin_update)
+ {
((sys_var_thd_bit*) var->var)->bit_flag|= (OPTION_BIN_LOG |
OPTION_UPDATE_LOG);
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_UPDATE_LOG_DEPRECATED_TRANSLATED,
+ ER(ER_UPDATE_LOG_DEPRECATED_TRANSLATED));
+ }
+ else
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_UPDATE_LOG_DEPRECATED_IGNORED,
+ ER(ER_UPDATE_LOG_DEPRECATED_IGNORED));
set_option_bit(thd, var);
return 0;
}
+static bool set_log_bin(THD *thd, set_var *var)
+{
+ if (opt_sql_bin_update)
+ ((sys_var_thd_bit*) var->var)->bit_flag|= (OPTION_BIN_LOG |
+ OPTION_UPDATE_LOG);
+ set_option_bit(thd, var);
+ return 0;
+}
+
+
static byte *get_warning_count(THD *thd)
{
thd->sys_var_tmp.long_value=
diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt
index 85606a060e7..ae040fd92ac 100644
--- a/sql/share/czech/errmsg.txt
+++ b/sql/share/czech/errmsg.txt
@@ -299,3 +299,36 @@ character-set=latin2
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt
index d005150cb89..b7f947d346e 100644
--- a/sql/share/danish/errmsg.txt
+++ b/sql/share/danish/errmsg.txt
@@ -293,3 +293,36 @@ character-set=latin1
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt
index 3fd44f9ec69..9e2bb859a45 100644
--- a/sql/share/dutch/errmsg.txt
+++ b/sql/share/dutch/errmsg.txt
@@ -301,3 +301,36 @@ character-set=latin1
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt
index afce57a9a74..cffbdeeeca2 100644
--- a/sql/share/english/errmsg.txt
+++ b/sql/share/english/errmsg.txt
@@ -290,3 +290,36 @@ character-set=latin1
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt
index dcdc74f5ab8..34158b491b2 100644
--- a/sql/share/estonian/errmsg.txt
+++ b/sql/share/estonian/errmsg.txt
@@ -295,3 +295,36 @@ character-set=latin7
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
index 94922ca40f4..9a2b3a5ec6a 100644
--- a/sql/share/french/errmsg.txt
+++ b/sql/share/french/errmsg.txt
@@ -290,3 +290,36 @@ character-set=latin1
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt
index 74f50289d86..36b8d4b98d6 100644
--- a/sql/share/german/errmsg.txt
+++ b/sql/share/german/errmsg.txt
@@ -302,3 +302,36 @@ character-set=latin1
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
index 5813717f57a..95c00ec2dd9 100644
--- a/sql/share/greek/errmsg.txt
+++ b/sql/share/greek/errmsg.txt
@@ -290,3 +290,36 @@ character-set=greek
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt
index 77ce0b8d4d2..4a10797e1d1 100644
--- a/sql/share/hungarian/errmsg.txt
+++ b/sql/share/hungarian/errmsg.txt
@@ -292,3 +292,36 @@ character-set=latin2
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt
index 204e1f980cc..2b8a7ea5632 100644
--- a/sql/share/italian/errmsg.txt
+++ b/sql/share/italian/errmsg.txt
@@ -290,3 +290,36 @@ character-set=latin1
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt
index af22a2eb49a..752ed402190 100644
--- a/sql/share/japanese/errmsg.txt
+++ b/sql/share/japanese/errmsg.txt
@@ -292,3 +292,36 @@ character-set=ujis
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt
index 6e348736698..8d98d5cb610 100644
--- a/sql/share/korean/errmsg.txt
+++ b/sql/share/korean/errmsg.txt
@@ -290,3 +290,36 @@ character-set=euckr
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt
index 248d88bd1c9..f1dd2cb8371 100644
--- a/sql/share/norwegian-ny/errmsg.txt
+++ b/sql/share/norwegian-ny/errmsg.txt
@@ -292,3 +292,36 @@ character-set=latin1
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt
index 9055b84f5e9..44705db0af5 100644
--- a/sql/share/norwegian/errmsg.txt
+++ b/sql/share/norwegian/errmsg.txt
@@ -292,3 +292,36 @@ character-set=latin1
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt
index ec9f3782b08..622e127dc5e 100644
--- a/sql/share/polish/errmsg.txt
+++ b/sql/share/polish/errmsg.txt
@@ -294,3 +294,36 @@ character-set=latin2
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
index ffb70632bd4..cb9c0e745f9 100644
--- a/sql/share/portuguese/errmsg.txt
+++ b/sql/share/portuguese/errmsg.txt
@@ -291,3 +291,36 @@ character-set=latin1
"MySQL foi inicializado em modo --skip-name-resolve. Você necesita reincializá-lo sem esta opção para este grant funcionar",
"Motor de tabela desconhecido '%s'",
"'%s' é desatualizado. Use '%s' em seu lugar.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt
index e3640fb7a7a..a37f29b8a33 100644
--- a/sql/share/romanian/errmsg.txt
+++ b/sql/share/romanian/errmsg.txt
@@ -294,3 +294,36 @@ character-set=latin2
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt
index 5224f19b001..2dbe6b9184c 100644
--- a/sql/share/russian/errmsg.txt
+++ b/sql/share/russian/errmsg.txt
@@ -292,3 +292,36 @@ character-set=koi8r
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt
index 904c8f0c8b9..97bef4e1714 100644
--- a/sql/share/serbian/errmsg.txt
+++ b/sql/share/serbian/errmsg.txt
@@ -285,3 +285,36 @@ character-set=cp1250
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt
index c7e543a5497..e841214ba1c 100644
--- a/sql/share/slovak/errmsg.txt
+++ b/sql/share/slovak/errmsg.txt
@@ -298,3 +298,36 @@ character-set=latin2
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
index 845a7da36eb..cdabd339e1e 100644
--- a/sql/share/spanish/errmsg.txt
+++ b/sql/share/spanish/errmsg.txt
@@ -292,3 +292,36 @@ character-set=latin1
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
index 9da0a511ddb..aab70294ec8 100644
--- a/sql/share/swedish/errmsg.txt
+++ b/sql/share/swedish/errmsg.txt
@@ -290,3 +290,36 @@ character-set=latin1
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt
index 7186e0550b2..828c0a46cad 100644
--- a/sql/share/ukrainian/errmsg.txt
+++ b/sql/share/ukrainian/errmsg.txt
@@ -295,3 +295,36 @@ character-set=koi8u
"MySQL is started in --skip-name-resolve mode. You need to restart it without this switch for this grant to work",
"Unknown table engine '%s'",
"'%s' is deprecated. Use '%s' instead.",
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
+"Duplicate parameter: %s"
+"Duplicate variable: %s"
+"Duplicate condition: %s"
+"Duplicate cursor: %s"
+"Failed to ALTER %s %s"
+"Subselect value not supported"
diff --git a/sql/slave.cc b/sql/slave.cc
index a715be5bc8b..42a1e4e9061 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -583,7 +583,7 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock,
if (thd->killed)
{
pthread_mutex_unlock(cond_lock);
- DBUG_RETURN(ER_SERVER_SHUTDOWN);
+ DBUG_RETURN(thd->killed_errno());
}
}
}
@@ -1103,12 +1103,11 @@ static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi)
BINLOG_FORMAT_323_GEQ_57 ;
break;
case '4':
+ case '5':
mi->old_format = BINLOG_FORMAT_CURRENT;
break;
default:
- /* 5.0 is not supported */
- errmsg = "Master reported an unrecognized MySQL version. Note that 4.1 \
-slaves can't replicate a 5.0 or newer master.";
+ errmsg = "Master reported unrecognized MySQL version";
break;
}
@@ -2332,7 +2331,7 @@ err:
pthread_mutex_unlock(&data_lock);
DBUG_PRINT("exit",("killed: %d abort: %d slave_running: %d \
improper_arguments: %d timed_out: %d",
- (int) thd->killed,
+ thd->killed_errno(),
(int) (init_abort_pos_wait != abort_pos_wait),
(int) slave_running,
(int) (error == -2),
@@ -2739,7 +2738,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
thd->server_id = ev->server_id; // use the original server id for logging
thd->set_time(); // time the query
- thd->lex.current_select= 0;
+ thd->lex->current_select= 0;
if (!ev->when)
ev->when = time(NULL);
ev->thd = thd;
@@ -3111,7 +3110,6 @@ slave_begin:
sql_print_error("Failed during slave thread initialization");
goto err;
}
- thd->init_for_queries();
rli->sql_thd= thd;
thd->temporary_tables = rli->save_temporary_tables; // restore temp tables
pthread_mutex_lock(&LOCK_thread_count);
diff --git a/sql/sp.cc b/sql/sp.cc
new file mode 100644
index 00000000000..6143c31176c
--- /dev/null
+++ b/sql/sp.cc
@@ -0,0 +1,853 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#include "mysql_priv.h"
+#include "sp.h"
+#include "sp_head.h"
+#include "sp_cache.h"
+
+static char *
+create_string(THD *thd, ulong *lenp,
+ int sp_type,
+ char *name, ulong namelen,
+ const char *params, ulong paramslen,
+ const char *returns, ulong returnslen,
+ const char *body, ulong bodylen,
+ st_sp_chistics *chistics);
+
+/*
+ *
+ * DB storage of Stored PROCEDUREs and FUNCTIONs
+ *
+ */
+
+enum
+{
+ MYSQL_PROC_FIELD_DB = 0,
+ MYSQL_PROC_FIELD_NAME,
+ MYSQL_PROC_FIELD_TYPE,
+ MYSQL_PROC_FIELD_SPECIFIC_NAME,
+ MYSQL_PROC_FIELD_LANGUAGE,
+ MYSQL_PROC_FIELD_ACCESS,
+ MYSQL_PROC_FIELD_DETERMINISTIC,
+ MYSQL_PROC_FIELD_SECURITY_TYPE,
+ MYSQL_PROC_FIELD_PARAM_LIST,
+ MYSQL_PROC_FIELD_RETURNS,
+ MYSQL_PROC_FIELD_BODY,
+ MYSQL_PROC_FIELD_DEFINER,
+ MYSQL_PROC_FIELD_CREATED,
+ MYSQL_PROC_FIELD_MODIFIED,
+ MYSQL_PROC_FIELD_SQL_MODE,
+ MYSQL_PROC_FIELD_COMMENT,
+ MYSQL_PROC_FIELD_COUNT
+};
+
+/* *opened=true means we opened ourselves */
+static int
+db_find_routine_aux(THD *thd, int type, char *name, uint namelen,
+ enum thr_lock_type ltype, TABLE **tablep, bool *opened)
+{
+ DBUG_ENTER("db_find_routine_aux");
+ DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
+ TABLE *table;
+ byte key[64+64+1]; // db, name, type
+ uint keylen;
+ int ret;
+
+ // Put the key together
+ memset(key, (int)' ', 64); // QQ Empty db for now
+ keylen= namelen;
+ if (keylen > 64)
+ keylen= 64;
+ memcpy(key+64, name, keylen);
+ memset(key+64+keylen, (int)' ', 64-keylen); // Pad with space
+ key[128]= type;
+ keylen= sizeof(key);
+
+ for (table= thd->open_tables ; table ; table= table->next)
+ if (strcmp(table->table_cache_key, "mysql") == 0 &&
+ strcmp(table->real_name, "proc") == 0)
+ break;
+ if (table)
+ *opened= FALSE;
+ else
+ {
+ TABLE_LIST tables;
+
+ memset(&tables, 0, sizeof(tables));
+ tables.db= (char*)"mysql";
+ tables.real_name= tables.alias= (char*)"proc";
+ if (! (table= open_ltable(thd, &tables, ltype)))
+ {
+ *tablep= NULL;
+ DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+ }
+ *opened= TRUE;
+ }
+
+ if (table->file->index_read_idx(table->record[0], 0,
+ key, keylen,
+ HA_READ_KEY_EXACT))
+ {
+ *tablep= NULL;
+ DBUG_RETURN(SP_KEY_NOT_FOUND);
+ }
+ *tablep= table;
+
+ DBUG_RETURN(SP_OK);
+}
+
+static int
+db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
+{
+ DBUG_ENTER("db_find_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
+ extern int yyparse(void *thd);
+ TABLE *table;
+ const char *params, *returns, *body;
+ int ret;
+ bool opened;
+ const char *definer;
+ longlong created;
+ longlong modified;
+ st_sp_chistics chistics;
+ char *ptr;
+ uint length;
+ char buff[65];
+ String str(buff, sizeof(buff), &my_charset_bin);
+ ulong sql_mode;
+
+ ret= db_find_routine_aux(thd, type, name, namelen, TL_READ, &table, &opened);
+ if (ret != SP_OK)
+ goto done;
+
+ if (table->fields != MYSQL_PROC_FIELD_COUNT)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+
+ if ((ptr= get_field(&thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_DETERMINISTIC])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+ bzero((char *)&chistics, sizeof(chistics));
+ chistics.detistic= (ptr[0] == 'N' ? FALSE : TRUE);
+
+ if ((ptr= get_field(&thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_SECURITY_TYPE])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+ chistics.suid= (ptr[0] == 'I' ? IS_NOT_SUID : IS_SUID);
+
+ if ((params= get_field(&thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_PARAM_LIST])) == NULL)
+ {
+ params= "";
+ }
+
+ if (type == TYPE_ENUM_PROCEDURE)
+ returns= "";
+ else if ((returns= get_field(&thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_RETURNS])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+
+ if ((body= get_field(&thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_BODY])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+
+ // Get additional information
+ if ((definer= get_field(&thd->mem_root,
+ table->field[MYSQL_PROC_FIELD_DEFINER])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+
+ modified= table->field[MYSQL_PROC_FIELD_MODIFIED]->val_int();
+ created= table->field[MYSQL_PROC_FIELD_CREATED]->val_int();
+
+ sql_mode= table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int();
+
+ table->field[MYSQL_PROC_FIELD_COMMENT]->val_str(&str, &str);
+
+ ptr= 0;
+ if ((length= str.length()))
+ ptr= thd->strmake(str.ptr(), length);
+ chistics.comment.str= ptr;
+ chistics.comment.length= length;
+
+ if (opened)
+ {
+ close_thread_tables(thd, 0, 1);
+ opened= FALSE;
+ }
+
+ {
+ char *defstr;
+ ulong deflen;
+ LEX *oldlex= thd->lex;
+ enum enum_sql_command oldcmd= thd->lex->sql_command;
+ ulong old_sql_mode= thd->variables.sql_mode;
+ ha_rows select_limit= thd->variables.select_limit;
+
+ thd->variables.sql_mode= sql_mode;
+ thd->variables.select_limit= HA_POS_ERROR;
+
+ defstr= create_string(thd, &deflen,
+ type,
+ name, namelen,
+ params, strlen(params),
+ returns, strlen(returns),
+ body, strlen(body),
+ &chistics);
+ lex_start(thd, (uchar*)defstr, deflen);
+ if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL)
+ {
+ LEX *newlex= thd->lex;
+ sp_head *sp= newlex->sphead;
+
+ if (sp)
+ {
+ if (oldlex != newlex)
+ sp->restore_lex(thd);
+ delete sp;
+ newlex->sphead= NULL;
+ }
+ ret= SP_PARSE_ERROR;
+ }
+ else
+ {
+ *sphp= thd->lex->sphead;
+ (*sphp)->set_info((char *)definer, (uint)strlen(definer),
+ created, modified, &chistics);
+ }
+ thd->lex->sql_command= oldcmd;
+ thd->variables.sql_mode= old_sql_mode;
+ thd->variables.select_limit= select_limit;
+ }
+
+ done:
+ if (opened)
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+static int
+db_create_routine(THD *thd, int type, sp_head *sp)
+{
+ DBUG_ENTER("db_create_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s",type,sp->m_name.length,sp->m_name.str));
+ int ret;
+ TABLE *table;
+ TABLE_LIST tables;
+ char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
+
+ memset(&tables, 0, sizeof(tables));
+ tables.db= (char*)"mysql";
+ tables.real_name= tables.alias= (char*)"proc";
+
+ if (! (table= open_ltable(thd, &tables, TL_WRITE)))
+ ret= SP_OPEN_TABLE_FAILED;
+ else
+ {
+ restore_record(table, default_values); // Get default values for fields
+ strxmov(definer, thd->priv_user, "@", thd->priv_host, NullS);
+
+ if (table->fields != MYSQL_PROC_FIELD_COUNT)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+ table->field[MYSQL_PROC_FIELD_NAME]->
+ store(sp->m_name.str, sp->m_name.length, system_charset_info);
+ table->field[MYSQL_PROC_FIELD_TYPE]->
+ store((longlong)type);
+ table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]->
+ store(sp->m_name.str, sp->m_name.length, system_charset_info);
+ table->field[MYSQL_PROC_FIELD_DETERMINISTIC]->
+ store((longlong)(sp->m_chistics->detistic ? 1 : 2));
+ if (sp->m_chistics->suid != IS_DEFAULT_SUID)
+ table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->
+ store((longlong)sp->m_chistics->suid);
+ table->field[MYSQL_PROC_FIELD_PARAM_LIST]->
+ store(sp->m_params.str, sp->m_params.length, system_charset_info);
+ if (sp->m_retstr.str)
+ table->field[MYSQL_PROC_FIELD_RETURNS]->
+ store(sp->m_retstr.str, sp->m_retstr.length, system_charset_info);
+ table->field[MYSQL_PROC_FIELD_BODY]->
+ store(sp->m_body.str, sp->m_body.length, system_charset_info);
+ table->field[MYSQL_PROC_FIELD_DEFINER]->
+ store(definer, (uint)strlen(definer), system_charset_info);
+ ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time();
+ table->field[MYSQL_PROC_FIELD_SQL_MODE]->
+ store((longlong)thd->variables.sql_mode);
+ if (sp->m_chistics->comment.str)
+ table->field[MYSQL_PROC_FIELD_COMMENT]->
+ store(sp->m_chistics->comment.str, sp->m_chistics->comment.length,
+ system_charset_info);
+
+ if (table->file->write_row(table->record[0]))
+ ret= SP_WRITE_ROW_FAILED;
+ else
+ ret= SP_OK;
+ }
+
+done:
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+static int
+db_drop_routine(THD *thd, int type, char *name, uint namelen)
+{
+ DBUG_ENTER("db_drop_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
+ TABLE *table;
+ int ret;
+ bool opened;
+
+ ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened);
+ if (ret == SP_OK)
+ {
+ if (table->file->delete_row(table->record[0]))
+ ret= SP_DELETE_ROW_FAILED;
+ }
+
+ if (opened)
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+static int
+db_update_routine(THD *thd, int type, char *name, uint namelen,
+ char *newname, uint newnamelen,
+ st_sp_chistics *chistics)
+{
+ DBUG_ENTER("db_update_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
+ TABLE *table;
+ int ret;
+ bool opened;
+
+ ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened);
+ if (ret == SP_OK)
+ {
+ store_record(table,record[1]);
+ ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
+ if (chistics->suid != IS_DEFAULT_SUID)
+ table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->store((longlong)chistics->suid);
+ if (newname)
+ table->field[MYSQL_PROC_FIELD_NAME]->store(newname,
+ newnamelen,
+ system_charset_info);
+ if (chistics->comment.str)
+ table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment.str,
+ chistics->comment.length,
+ system_charset_info);
+ if ((table->file->update_row(table->record[1],table->record[0])))
+ ret= SP_WRITE_ROW_FAILED;
+ }
+ if (opened)
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+struct st_used_field
+{
+ const char *field_name;
+ uint field_length;
+ enum enum_field_types field_type;
+ Field *field;
+};
+
+static struct st_used_field init_fields[]=
+{
+ { "Name", NAME_LEN, MYSQL_TYPE_STRING, 0},
+ { "Type", 9, MYSQL_TYPE_STRING, 0},
+ { "Definer", 77, MYSQL_TYPE_STRING, 0},
+ { "Modified", 0, MYSQL_TYPE_TIMESTAMP, 0},
+ { "Created", 0, MYSQL_TYPE_TIMESTAMP, 0},
+ { "Security_type", 1, MYSQL_TYPE_STRING, 0},
+ { "Comment", NAME_LEN, MYSQL_TYPE_STRING, 0},
+ { 0, 0, MYSQL_TYPE_STRING, 0}
+};
+
+static int
+print_field_values(THD *thd, TABLE *table,
+ struct st_used_field *used_fields,
+ int type, const char *wild)
+{
+ Protocol *protocol= thd->protocol;
+
+ if (table->field[MYSQL_PROC_FIELD_TYPE]->val_int() == type)
+ {
+ String *tmp_string= new String();
+ struct st_used_field *used_field= used_fields;
+
+ get_field(&thd->mem_root, used_field->field, tmp_string);
+ if (!wild || !wild[0] || !wild_compare(tmp_string->ptr(), wild, 0))
+ {
+ protocol->prepare_for_resend();
+ protocol->store(tmp_string);
+ for (used_field++;
+ used_field->field_name;
+ used_field++)
+ {
+ switch (used_field->field_type) {
+ case MYSQL_TYPE_TIMESTAMP:
+ {
+ TIME tmp_time;
+
+ bzero((char *)&tmp_time, sizeof(tmp_time));
+ ((Field_timestamp *) used_field->field)->get_time(&tmp_time);
+ protocol->store(&tmp_time);
+ }
+ break;
+ default:
+ {
+ String *tmp_string1= new String();
+
+ get_field(&thd->mem_root, used_field->field, tmp_string1);
+ protocol->store(tmp_string1);
+ }
+ break;
+ }
+ }
+ if (protocol->write())
+ return SP_INTERNAL_ERROR;
+ }
+ }
+ return SP_OK;
+}
+
+static int
+db_show_routine_status(THD *thd, int type, const char *wild)
+{
+ DBUG_ENTER("db_show_routine_status");
+
+ TABLE *table;
+ TABLE_LIST tables;
+ int res;
+
+ memset(&tables, 0, sizeof(tables));
+ tables.db= (char*)"mysql";
+ tables.real_name= tables.alias= (char*)"proc";
+
+ if (! (table= open_ltable(thd, &tables, TL_READ)))
+ {
+ res= SP_OPEN_TABLE_FAILED;
+ goto done;
+ }
+ else
+ {
+ Item *item;
+ List<Item> field_list;
+ struct st_used_field *used_field;
+ st_used_field used_fields[array_elements(init_fields)];
+
+ memcpy((char*) used_fields, (char*) init_fields, sizeof(used_fields));
+ /* Init header */
+ for (used_field= &used_fields[0];
+ used_field->field_name;
+ used_field++)
+ {
+ switch (used_field->field_type) {
+ case MYSQL_TYPE_TIMESTAMP:
+ field_list.push_back(item=new Item_datetime(used_field->field_name));
+ break;
+ default:
+ field_list.push_back(item=new Item_empty_string(used_field->field_name,
+ used_field->
+ field_length));
+ break;
+ }
+ }
+ /* Print header */
+ if (thd->protocol->send_fields(&field_list,1))
+ {
+ res= SP_INTERNAL_ERROR;
+ goto err_case;
+ }
+
+ /* Init fields */
+ setup_tables(&tables);
+ for (used_field= &used_fields[0];
+ used_field->field_name;
+ used_field++)
+ {
+ TABLE_LIST *not_used;
+ Item_field *field= new Item_field("mysql", "proc",
+ used_field->field_name);
+ if (!(used_field->field= find_field_in_tables(thd, field, &tables,
+ &not_used, TRUE)))
+ {
+ res= SP_INTERNAL_ERROR;
+ goto err_case1;
+ }
+ }
+
+ table->file->index_init(0);
+ if ((res= table->file->index_first(table->record[0])))
+ {
+ if (res == HA_ERR_END_OF_FILE)
+ res= 0;
+ else
+ res= SP_INTERNAL_ERROR;
+ goto err_case1;
+ }
+ if ((res= print_field_values(thd, table, used_fields, type, wild)))
+ goto err_case1;
+ while (!table->file->index_next(table->record[0]))
+ {
+ if ((res= print_field_values(thd, table, used_fields, type, wild)))
+ goto err_case1;
+ }
+ res= SP_OK;
+ }
+
+ err_case1:
+ send_eof(thd);
+ err_case:
+ close_thread_tables(thd);
+ done:
+ DBUG_RETURN(res);
+}
+
+
+/*
+ *
+ * PROCEDURE
+ *
+ */
+
+sp_head *
+sp_find_procedure(THD *thd, LEX_STRING *name)
+{
+ DBUG_ENTER("sp_find_procedure");
+ sp_head *sp;
+
+ DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
+
+ sp= sp_cache_lookup(&thd->sp_proc_cache, name->str, name->length);
+ if (! sp)
+ {
+ if (db_find_routine(thd, TYPE_ENUM_PROCEDURE,
+ name->str, name->length, &sp) == SP_OK)
+ {
+ sp_cache_insert(&thd->sp_proc_cache, sp);
+ }
+ }
+
+ DBUG_RETURN(sp);
+}
+
+int
+sp_create_procedure(THD *thd, sp_head *sp)
+{
+ DBUG_ENTER("sp_create_procedure");
+ DBUG_PRINT("enter", ("name: %*s", sp->m_name.length, sp->m_name.str));
+ int ret;
+
+ ret= db_create_routine(thd, TYPE_ENUM_PROCEDURE, sp);
+
+ DBUG_RETURN(ret);
+}
+
+int
+sp_drop_procedure(THD *thd, char *name, uint namelen)
+{
+ DBUG_ENTER("sp_drop_procedure");
+ DBUG_PRINT("enter", ("name: %*s", namelen, name));
+ int ret;
+
+ sp_cache_remove(&thd->sp_proc_cache, name, namelen);
+ ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen);
+
+ DBUG_RETURN(ret);
+}
+
+int
+sp_update_procedure(THD *thd, char *name, uint namelen,
+ char *newname, uint newnamelen,
+ st_sp_chistics *chistics)
+{
+ DBUG_ENTER("sp_update_procedure");
+ DBUG_PRINT("enter", ("name: %*s", namelen, name));
+ int ret;
+
+ sp_cache_remove(&thd->sp_proc_cache, name, namelen);
+ ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen,
+ newname, newnamelen,
+ chistics);
+
+ DBUG_RETURN(ret);
+}
+
+int
+sp_show_create_procedure(THD *thd, LEX_STRING *name)
+{
+ DBUG_ENTER("sp_show_create_procedure");
+ DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
+ sp_head *sp;
+
+ sp= sp_find_procedure(thd, name);
+ if (sp)
+ DBUG_RETURN(sp->show_create_procedure(thd));
+
+ DBUG_RETURN(SP_KEY_NOT_FOUND);
+}
+
+int
+sp_show_status_procedure(THD *thd, const char *wild)
+{
+ DBUG_ENTER("sp_show_status_procedure");
+ DBUG_RETURN(db_show_routine_status(thd, TYPE_ENUM_PROCEDURE, wild));
+}
+
+/*
+ *
+ * FUNCTION
+ *
+ */
+
+sp_head *
+sp_find_function(THD *thd, LEX_STRING *name)
+{
+ DBUG_ENTER("sp_find_function");
+ sp_head *sp;
+
+ DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
+
+ sp= sp_cache_lookup(&thd->sp_func_cache, name->str, name->length);
+ if (! sp)
+ {
+ if (db_find_routine(thd, TYPE_ENUM_FUNCTION,
+ name->str, name->length, &sp) != SP_OK)
+ sp= NULL;
+ }
+ DBUG_RETURN(sp);
+}
+
+int
+sp_create_function(THD *thd, sp_head *sp)
+{
+ DBUG_ENTER("sp_create_function");
+ DBUG_PRINT("enter", ("name: %*s", sp->m_name.length, sp->m_name.str));
+ int ret;
+
+ ret= db_create_routine(thd, TYPE_ENUM_FUNCTION, sp);
+
+ DBUG_RETURN(ret);
+}
+
+int
+sp_drop_function(THD *thd, char *name, uint namelen)
+{
+ DBUG_ENTER("sp_drop_function");
+ DBUG_PRINT("enter", ("name: %*s", namelen, name));
+ int ret;
+
+ sp_cache_remove(&thd->sp_func_cache, name, namelen);
+ ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name, namelen);
+
+ DBUG_RETURN(ret);
+}
+
+int
+sp_update_function(THD *thd, char *name, uint namelen,
+ char *newname, uint newnamelen,
+ st_sp_chistics *chistics)
+{
+ DBUG_ENTER("sp_update_procedure");
+ DBUG_PRINT("enter", ("name: %*s", namelen, name));
+ int ret;
+
+ sp_cache_remove(&thd->sp_func_cache, name, namelen);
+ ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name, namelen,
+ newname, newnamelen,
+ chistics);
+
+ DBUG_RETURN(ret);
+}
+
+int
+sp_show_create_function(THD *thd, LEX_STRING *name)
+{
+ DBUG_ENTER("sp_show_create_function");
+ DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
+ sp_head *sp;
+
+ sp= sp_find_function(thd, name);
+ if (sp)
+ DBUG_RETURN(sp->show_create_function(thd));
+
+ DBUG_RETURN(SP_KEY_NOT_FOUND);
+}
+
+int
+sp_show_status_function(THD *thd, const char *wild)
+{
+ DBUG_ENTER("sp_show_status_function");
+ DBUG_RETURN(db_show_routine_status(thd, TYPE_ENUM_FUNCTION, wild));
+}
+
+// QQ Temporary until the function call detection in sql_lex has been reworked.
+bool
+sp_function_exists(THD *thd, LEX_STRING *name)
+{
+ TABLE *table;
+ bool ret= FALSE;
+ bool opened= FALSE;
+
+ if (sp_cache_lookup(&thd->sp_func_cache, name->str, name->length) ||
+ db_find_routine_aux(thd, TYPE_ENUM_FUNCTION,
+ name->str, name->length, TL_READ,
+ &table, &opened) == SP_OK)
+ {
+ ret= TRUE;
+ }
+ if (opened)
+ close_thread_tables(thd, 0, 1);
+ return ret;
+}
+
+
+byte *
+sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first)
+{
+ LEX_STRING *lsp= (LEX_STRING *)ptr;
+ *plen= lsp->length;
+ return (byte *)lsp->str;
+}
+
+void
+sp_add_fun_to_lex(LEX *lex, LEX_STRING fun)
+{
+ if (! hash_search(&lex->spfuns, (byte *)fun.str, fun.length))
+ {
+ LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING));
+ ls->str= sql_strmake(fun.str, fun.length);
+ ls->length= fun.length;
+
+ my_hash_insert(&lex->spfuns, (byte *)ls);
+ }
+}
+
+void
+sp_merge_funs(LEX *dst, LEX *src)
+{
+ for (uint i=0 ; i < src->spfuns.records ; i++)
+ {
+ LEX_STRING *ls= (LEX_STRING *)hash_element(&src->spfuns, i);
+
+ if (! hash_search(&dst->spfuns, (byte *)ls->str, ls->length))
+ my_hash_insert(&dst->spfuns, (byte *)ls);
+ }
+}
+
+int
+sp_cache_functions(THD *thd, LEX *lex)
+{
+ HASH *h= &lex->spfuns;
+ int ret= 0;
+
+ for (uint i=0 ; i < h->records ; i++)
+ {
+ LEX_STRING *ls= (LEX_STRING *)hash_element(h, i);
+
+ if (! sp_cache_lookup(&thd->sp_func_cache, ls->str, ls->length))
+ {
+ sp_head *sp;
+ LEX *oldlex= thd->lex;
+ LEX *newlex= new st_lex;
+
+ thd->lex= newlex;
+ if (db_find_routine(thd, TYPE_ENUM_FUNCTION, ls->str, ls->length, &sp)
+ == SP_OK)
+ {
+ ret= sp_cache_functions(thd, newlex);
+ delete newlex;
+ thd->lex= oldlex;
+ if (ret)
+ break;
+ sp_cache_insert(&thd->sp_func_cache, sp);
+ }
+ else
+ {
+ delete newlex;
+ thd->lex= oldlex;
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, "FUNCTION", ls->str);
+ ret= 1;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+static char *
+create_string(THD *thd, ulong *lenp,
+ int type,
+ char *name, ulong namelen,
+ const char *params, ulong paramslen,
+ const char *returns, ulong returnslen,
+ const char *body, ulong bodylen,
+ st_sp_chistics *chistics)
+{
+ char *buf, *ptr;
+ ulong buflen, pos;
+
+ buflen= 100 + namelen + paramslen + returnslen + bodylen +
+ chistics->comment.length;
+ ptr= buf= thd->alloc(buflen);
+ if (type == TYPE_ENUM_FUNCTION)
+ {
+ ptr+= my_sprintf(buf,
+ (buf, (char *)
+ "CREATE FUNCTION %s(%s) RETURNS %s\n",
+ name, params, returns));
+ }
+ else
+ {
+ ptr+= my_sprintf(buf,
+ (buf, (char *)
+ "CREATE PROCEDURE %s(%s)\n",
+ name, params));
+ }
+ if (chistics->detistic)
+ ptr+= my_sprintf(ptr, (ptr, (char *)" DETERMINISTIC\n"));
+ if (chistics->suid == IS_NOT_SUID)
+ ptr+= my_sprintf(ptr, (ptr, (char *)" SQL SECURITY INVOKER\n"));
+ if (chistics->comment.length)
+ ptr+= my_sprintf(ptr, (ptr, (char *)" COMMENT '%*s'\n",
+ chistics->comment.length,
+ chistics->comment.str));
+ strcpy(ptr, body);
+ *lenp= (ptr-buf)+bodylen;
+ return buf;
+}
diff --git a/sql/sp.h b/sql/sp.h
new file mode 100644
index 00000000000..b9f29138de2
--- /dev/null
+++ b/sql/sp.h
@@ -0,0 +1,86 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_H_
+#define _SP_H_
+
+// Return codes from sp_create_*, sp_drop_*, and sp_show_*:
+#define SP_OK 0
+#define SP_KEY_NOT_FOUND -1
+#define SP_OPEN_TABLE_FAILED -2
+#define SP_WRITE_ROW_FAILED -3
+#define SP_DELETE_ROW_FAILED -4
+#define SP_GET_FIELD_FAILED -5
+#define SP_PARSE_ERROR -6
+#define SP_INTERNAL_ERROR -7
+
+sp_head *
+sp_find_procedure(THD *thd, LEX_STRING *name);
+
+int
+sp_create_procedure(THD *thd, sp_head *sp);
+
+int
+sp_drop_procedure(THD *thd, char *name, uint namelen);
+
+
+int
+sp_update_procedure(THD *thd, char *name, uint namelen,
+ char *newname, uint newnamelen,
+ st_sp_chistics *chistics);
+
+int
+sp_show_create_procedure(THD *thd, LEX_STRING *name);
+
+int
+sp_show_status_procedure(THD *thd, const char *wild);
+
+sp_head *
+sp_find_function(THD *thd, LEX_STRING *name);
+
+int
+sp_create_function(THD *thd, sp_head *sp);
+
+int
+sp_drop_function(THD *thd, char *name, uint namelen);
+
+int
+sp_update_function(THD *thd, char *name, uint namelen,
+ char *newname, uint newnamelen,
+ st_sp_chistics *chistics);
+
+int
+sp_show_create_function(THD *thd, LEX_STRING *name);
+
+int
+sp_show_status_function(THD *thd, const char *wild);
+
+// QQ Temporary until the function call detection in sql_lex has been reworked.
+bool
+sp_function_exists(THD *thd, LEX_STRING *name);
+
+
+// This is needed since we have to read the functions before we
+// do anything else.
+void
+sp_add_fun_to_lex(LEX *lex, LEX_STRING fun);
+void
+sp_merge_funs(LEX *dst, LEX *src);
+int
+sp_cache_functions(THD *thd, LEX *lex);
+
+#endif /* _SP_H_ */
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
new file mode 100644
index 00000000000..4fee49c2d81
--- /dev/null
+++ b/sql/sp_cache.cc
@@ -0,0 +1,156 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#include "mysql_priv.h"
+#include "sp_cache.h"
+#include "sp_head.h"
+
+static pthread_mutex_t Cversion_lock;
+static ulong Cversion = 0;
+
+void
+sp_cache_init()
+{
+ pthread_mutex_init(&Cversion_lock, MY_MUTEX_INIT_FAST);
+}
+
+void
+sp_cache_clear(sp_cache **cp)
+{
+ sp_cache *c= *cp;
+
+ if (c)
+ {
+ delete c;
+ *cp= NULL;
+ }
+}
+
+void
+sp_cache_insert(sp_cache **cp, sp_head *sp)
+{
+ sp_cache *c= *cp;
+
+ if (! c)
+ c= new sp_cache();
+ if (c)
+ {
+ ulong v;
+
+ pthread_mutex_lock(&Cversion_lock); // LOCK
+ v= Cversion;
+ pthread_mutex_unlock(&Cversion_lock); // UNLOCK
+
+ if (c->version < v)
+ {
+ if (*cp)
+ c->remove_all();
+ c->version= v;
+ }
+ c->insert(sp);
+ if (*cp == NULL)
+ *cp= c;
+ }
+}
+
+sp_head *
+sp_cache_lookup(sp_cache **cp, char *name, uint namelen)
+{
+ ulong v;
+ sp_cache *c= *cp;
+
+ if (! c)
+ return NULL;
+
+ pthread_mutex_lock(&Cversion_lock); // LOCK
+ v= Cversion;
+ pthread_mutex_unlock(&Cversion_lock); // UNLOCK
+
+ if (c->version < v)
+ {
+ c->remove_all();
+ c->version= v;
+ return NULL;
+ }
+ return c->lookup(name, namelen);
+}
+
+bool
+sp_cache_remove(sp_cache **cp, char *name, uint namelen)
+{
+ sp_cache *c= *cp;
+ bool found= FALSE;
+
+ if (c)
+ {
+ ulong v;
+
+ pthread_mutex_lock(&Cversion_lock); // LOCK
+ v= Cversion++;
+ pthread_mutex_unlock(&Cversion_lock); // UNLOCK
+
+ if (c->version < v)
+ c->remove_all();
+ else
+ found= c->remove(name, namelen);
+ c->version= v+1;
+ }
+ return found;
+}
+
+
+static byte *
+hash_get_key_for_sp_head(const byte *ptr, uint *plen,
+ my_bool first)
+{
+ return ((sp_head*)ptr)->name(plen);
+}
+
+static void
+hash_free_sp_head(void *p)
+{
+ sp_head *sp= (sp_head *)p;
+
+ delete sp;
+}
+
+sp_cache::sp_cache()
+{
+ init();
+}
+
+sp_cache::~sp_cache()
+{
+ hash_free(&m_hashtable);
+}
+
+void
+sp_cache::init()
+{
+ hash_init(&m_hashtable, system_charset_info, 0, 0, 0,
+ hash_get_key_for_sp_head, hash_free_sp_head, 0);
+ version= 0;
+}
+
+void
+sp_cache::cleanup()
+{
+ hash_free(&m_hashtable);
+}
diff --git a/sql/sp_cache.h b/sql/sp_cache.h
new file mode 100644
index 00000000000..da25227303b
--- /dev/null
+++ b/sql/sp_cache.h
@@ -0,0 +1,104 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_CACHE_H_
+#define _SP_CACHE_H_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+class sp_head;
+class sp_cache;
+
+/* Initialize the SP caching once at startup */
+void sp_cache_init();
+
+/* Clear the cache *cp and set *cp to NULL */
+void sp_cache_clear(sp_cache **cp);
+
+/* Insert an SP to cache. If 'cp' points to NULL, it's set to a new cache */
+void sp_cache_insert(sp_cache **cp, sp_head *sp);
+
+/* Lookup an SP in cache */
+sp_head *sp_cache_lookup(sp_cache **cp, char *name, uint namelen);
+
+/* Remove an SP from cache. Returns true if something was removed */
+bool sp_cache_remove(sp_cache **cp, char *name, uint namelen);
+
+
+/*
+ *
+ * The cache class. Don't use this directly, use the C API above
+ *
+ */
+
+class sp_cache
+{
+public:
+
+ ulong version;
+
+ sp_cache();
+
+ ~sp_cache();
+
+ void
+ init();
+
+ void
+ cleanup();
+
+ inline void
+ insert(sp_head *sp)
+ {
+ my_hash_insert(&m_hashtable, (const byte *)sp);
+ }
+
+ inline sp_head *
+ lookup(char *name, uint namelen)
+ {
+ return (sp_head *)hash_search(&m_hashtable, (const byte *)name, namelen);
+ }
+
+ inline bool
+ remove(char *name, uint namelen)
+ {
+ sp_head *sp= lookup(name, namelen);
+
+ if (sp)
+ {
+ hash_delete(&m_hashtable, (byte *)sp);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ inline void
+ remove_all()
+ {
+ cleanup();
+ init();
+ }
+
+private:
+
+ HASH m_hashtable;
+
+}; // class sp_cache
+
+#endif /* _SP_CACHE_H_ */
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
new file mode 100644
index 00000000000..f6f4b4b8dae
--- /dev/null
+++ b/sql/sp_head.cc
@@ -0,0 +1,1140 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#include "mysql_priv.h"
+#include "sql_acl.h"
+#include "sp_head.h"
+#include "sp.h"
+#include "sp_pcontext.h"
+#include "sp_rcontext.h"
+
+Item_result
+sp_map_result_type(enum enum_field_types type)
+{
+ switch (type)
+ {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ return INT_RESULT;
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ return REAL_RESULT;
+ default:
+ return STRING_RESULT;
+ }
+}
+
+/* Evaluate a (presumed) func item. Always returns an item, the parameter
+** if nothing else.
+*/
+Item *
+sp_eval_func_item(THD *thd, Item *it, enum enum_field_types type)
+{
+ DBUG_ENTER("sp_eval_func_item");
+ it= it->this_item();
+ DBUG_PRINT("info", ("type: %d", type));
+
+ if (!it->fixed && it->fix_fields(thd, 0, &it))
+ {
+ DBUG_PRINT("info", ("fix_fields() failed"));
+ DBUG_RETURN(it); // Shouldn't happen?
+ }
+
+ /* QQ How do we do this? Is there some better way? */
+ if (type == MYSQL_TYPE_NULL)
+ it= new Item_null();
+ else
+ {
+ switch (sp_map_result_type(type)) {
+ case INT_RESULT:
+ {
+ longlong i= it->val_int();
+
+ if (it->null_value)
+ {
+ DBUG_PRINT("info", ("INT_RESULT: null"));
+ it= new Item_null();
+ }
+ else
+ {
+ DBUG_PRINT("info", ("INT_RESULT: %d", i));
+ it= new Item_int(it->val_int());
+ }
+ break;
+ }
+ case REAL_RESULT:
+ {
+ double d= it->val();
+
+ if (it->null_value)
+ {
+ DBUG_PRINT("info", ("REAL_RESULT: null"));
+ it= new Item_null();
+ }
+ else
+ {
+ DBUG_PRINT("info", ("REAL_RESULT: %g", d));
+ it= new Item_real(it->val());
+ }
+ break;
+ }
+ default:
+ {
+ char buffer[MAX_FIELD_WIDTH];
+ String tmp(buffer, sizeof(buffer), it->collation.collation);
+ String *s= it->val_str(&tmp);
+
+ if (it->null_value)
+ {
+ DBUG_PRINT("info", ("default result: null"));
+ it= new Item_null();
+ }
+ else
+ {
+ DBUG_PRINT("info",("default result: %*s",s->length(),s->c_ptr_quick()));
+ it= new Item_string(thd->strmake(s->c_ptr_quick(), s->length()),
+ s->length(), it->collation.collation);
+ }
+ break;
+ }
+ }
+ }
+
+ DBUG_RETURN(it);
+}
+
+void *
+sp_head::operator new(size_t size)
+{
+ DBUG_ENTER("sp_head::operator new");
+ MEM_ROOT own_root;
+ sp_head *sp;
+
+ bzero((char *)&own_root, sizeof(own_root));
+ init_alloc_root(&own_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
+ sp= (sp_head *)alloc_root(&own_root, size);
+ sp->m_mem_root= own_root;
+
+ DBUG_RETURN(sp);
+}
+
+void
+sp_head::operator delete(void *ptr, size_t size)
+{
+ DBUG_ENTER("sp_head::operator delete");
+ MEM_ROOT own_root;
+ sp_head *sp= (sp_head *)ptr;
+
+ DBUG_PRINT("info", ("root: %lx", &sp->m_mem_root));
+ memcpy(&own_root, (const void *)&sp->m_mem_root, sizeof(MEM_ROOT));
+ free_root(&own_root, MYF(0));
+
+ DBUG_VOID_RETURN;
+}
+
+sp_head::sp_head()
+ : Sql_alloc(), m_has_return(FALSE), m_simple_case(FALSE),
+ m_multi_results(FALSE), m_free_list(NULL)
+{
+ DBUG_ENTER("sp_head::sp_head");
+
+ m_backpatch.empty();
+ m_lex.empty();
+ DBUG_VOID_RETURN;
+}
+
+void
+sp_head::init(LEX *lex)
+{
+ DBUG_ENTER("sp_head::init");
+
+ lex->spcont= m_pcont= new sp_pcontext();
+ my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
+ m_param_begin= m_param_end= m_returns_begin= m_returns_end= m_body_begin= 0;
+ m_name.str= m_params.str= m_retstr.str= m_body.str= m_defstr.str= 0;
+ m_name.length= m_params.length= m_retstr.length= m_body.length=
+ m_defstr.length= 0;
+ DBUG_VOID_RETURN;
+}
+
+void
+sp_head::init_strings(THD *thd, LEX *lex, LEX_STRING *name)
+{
+ DBUG_ENTER("sp_head::init_strings");
+ /* During parsing, we must use thd->mem_root */
+ MEM_ROOT *root= &thd->mem_root;
+
+ DBUG_PRINT("info", ("name: %*s", name->length, name->str));
+ m_name.length= name->length;
+ m_name.str= strmake_root(root, name->str, name->length);
+ m_params.length= m_param_end- m_param_begin;
+ m_params.str= strmake_root(root,
+ (char *)m_param_begin, m_params.length);
+ if (m_returns_begin && m_returns_end)
+ {
+ /* QQ KLUDGE: We can't seem to cut out just the type in the parser
+ (without the RETURNS), so we'll have to do it here. :-( */
+ char *p= (char *)m_returns_begin+strspn((char *)m_returns_begin,"\t\n\r ");
+ p+= strcspn(p, "\t\n\r ");
+ p+= strspn(p, "\t\n\r ");
+ if (p < (char *)m_returns_end)
+ m_returns_begin= (uchar *)p;
+ /* While we're at it, trim the end too. */
+ p= (char *)m_returns_end-1;
+ while (p > (char *)m_returns_begin &&
+ (*p == '\t' || *p == '\n' || *p == '\r' || *p == ' '))
+ p-= 1;
+ m_returns_end= (uchar *)p+1;
+ m_retstr.length= m_returns_end - m_returns_begin;
+ m_retstr.str= strmake_root(root,
+ (char *)m_returns_begin, m_retstr.length);
+ }
+ m_body.length= lex->end_of_query - m_body_begin;
+ m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length);
+ m_defstr.length= lex->end_of_query - lex->buf;
+ m_defstr.str= strmake_root(root, (char *)lex->buf, m_defstr.length);
+ DBUG_VOID_RETURN;
+}
+
+int
+sp_head::create(THD *thd)
+{
+ DBUG_ENTER("sp_head::create");
+ int ret;
+
+ DBUG_PRINT("info", ("type: %d name: %s params: %s body: %s",
+ m_type, m_name.str, m_params.str, m_body.str));
+ if (m_type == TYPE_ENUM_FUNCTION)
+ ret= sp_create_function(thd, this);
+ else
+ ret= sp_create_procedure(thd, this);
+
+ DBUG_RETURN(ret);
+}
+
+sp_head::~sp_head()
+{
+ destroy();
+ if (m_thd)
+ restore_thd_mem_root(m_thd);
+}
+
+void
+sp_head::destroy()
+{
+ DBUG_ENTER("sp_head::destroy");
+ DBUG_PRINT("info", ("name: %s", m_name.str));
+ sp_instr *i;
+ LEX *lex;
+
+ for (uint ip = 0 ; (i = get_instr(ip)) ; ip++)
+ delete i;
+ delete_dynamic(&m_instr);
+ m_pcont->destroy();
+ free_items(m_free_list);
+ while ((lex= (LEX *)m_lex.pop()))
+ {
+ if (lex != &m_thd->main_lex) // We got interrupted and have lex'es left
+ delete lex;
+ }
+ DBUG_VOID_RETURN;
+}
+
+int
+sp_head::execute(THD *thd)
+{
+ DBUG_ENTER("sp_head::execute");
+ char olddbname[128];
+ char *olddbptr= thd->db;
+ sp_rcontext *ctx= thd->spcont;
+ int ret= 0;
+ uint ip= 0;
+
+ if (olddbptr)
+ {
+ uint i= 0;
+ char *p= olddbptr;
+
+ /* Fast inline strncpy without padding... */
+ while (*p && i < sizeof(olddbname))
+ olddbname[i++]= *p++;
+ if (i == sizeof(olddbname))
+ i-= 1; // QQ Error or warning for truncate?
+ olddbname[i]= '\0';
+ }
+
+ if (ctx)
+ ctx->clear_handler();
+ do
+ {
+ sp_instr *i;
+ uint hip; // Handler ip
+
+ i = get_instr(ip); // Returns NULL when we're done.
+ if (i == NULL)
+ break;
+ DBUG_PRINT("execute", ("Instruction %u", ip));
+ ret= i->execute(thd, &ip);
+ // Check if an exception has occurred and a handler has been found
+ // Note: We havo to check even if ret==0, since warnings (and some
+ // errors don't return a non-zero value.
+ if (!thd->killed && ctx)
+ {
+ uint hf;
+
+ switch (ctx->found_handler(&hip, &hf))
+ {
+ case SP_HANDLER_NONE:
+ break;
+ case SP_HANDLER_CONTINUE:
+ ctx->save_variables(hf);
+ ctx->push_hstack(ip);
+ // Fall through
+ default:
+ ip= hip;
+ ret= 0;
+ ctx->clear_handler();
+ continue;
+ }
+ }
+ } while (ret == 0 && !thd->killed);
+
+ DBUG_PRINT("info", ("ret=%d killed=%d", ret, thd->killed));
+ if (thd->killed)
+ ret= -1;
+ /* If the DB has changed, the pointer has changed too, but the
+ original thd->db will then have been freed */
+ if (olddbptr && olddbptr != thd->db)
+ {
+ /* QQ Maybe we should issue some special error message or warning here,
+ if this fails?? */
+ if (! thd->killed)
+ ret= mysql_change_db(thd, olddbname);
+ }
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
+{
+ DBUG_ENTER("sp_head::execute_function");
+ DBUG_PRINT("info", ("function %s", m_name.str));
+ uint csize = m_pcont->max_framesize();
+ uint params = m_pcont->params();
+ uint hmax = m_pcont->handlers();
+ uint cmax = m_pcont->cursors();
+ sp_rcontext *octx = thd->spcont;
+ sp_rcontext *nctx = NULL;
+ uint i;
+ int ret;
+
+ if (argcount != params)
+ {
+ // Need to use my_printf_error here, or it will not terminate the
+ // invoking query properly.
+ my_printf_error(ER_SP_WRONG_NO_OF_ARGS, ER(ER_SP_WRONG_NO_OF_ARGS), MYF(0),
+ "FUNCTION", m_name.str, params, argcount);
+ DBUG_RETURN(-1);
+ }
+
+ // QQ Should have some error checking here? (types, etc...)
+ nctx= new sp_rcontext(csize, hmax, cmax);
+ for (i= 0 ; i < params && i < argcount ; i++)
+ {
+ sp_pvar_t *pvar = m_pcont->find_pvar(i);
+
+ nctx->push_item(sp_eval_func_item(thd, *argp++, pvar->type));
+ }
+ // Close tables opened for subselect in argument list
+ close_thread_tables(thd);
+
+ // The rest of the frame are local variables which are all IN.
+ // Default all variables to null (those with default clauses will
+ // be set by an set instruction).
+ {
+ Item_null *nit= NULL; // Re-use this, and only create if needed
+ for (; i < csize ; i++)
+ {
+ if (! nit)
+ nit= new Item_null();
+ nctx->push_item(nit);
+ }
+ }
+ thd->spcont= nctx;
+
+ ret= execute(thd);
+ if (ret == 0)
+ {
+ Item *it= nctx->get_result();
+
+ if (it)
+ *resp= it;
+ else
+ {
+ my_printf_error(ER_SP_NORETURNEND, ER(ER_SP_NORETURNEND), MYF(0),
+ m_name.str);
+ ret= -1;
+ }
+ }
+
+ nctx->pop_all_cursors(); // To avoid memory leaks after an error
+ thd->spcont= octx;
+ DBUG_RETURN(ret);
+}
+
+int
+sp_head::execute_procedure(THD *thd, List<Item> *args)
+{
+ DBUG_ENTER("sp_head::execute_procedure");
+ DBUG_PRINT("info", ("procedure %s", m_name.str));
+ int ret;
+ sp_instr *p;
+ uint csize = m_pcont->max_framesize();
+ uint params = m_pcont->params();
+ uint hmax = m_pcont->handlers();
+ uint cmax = m_pcont->cursors();
+ sp_rcontext *octx = thd->spcont;
+ sp_rcontext *nctx = NULL;
+ my_bool tmp_octx = FALSE; // True if we have allocated a temporary octx
+
+ if (args->elements != params)
+ {
+ net_printf(thd, ER_SP_WRONG_NO_OF_ARGS, "PROCEDURE", m_name.str,
+ params, args->elements);
+ DBUG_RETURN(-1);
+ }
+
+ if (csize > 0 || hmax > 0 || cmax > 0)
+ {
+ Item_null *nit= NULL; // Re-use this, and only create if needed
+ uint i;
+ List_iterator_fast<Item> li(*args);
+ Item *it;
+
+ nctx= new sp_rcontext(csize, hmax, cmax);
+ if (! octx)
+ { // Create a temporary old context
+ octx= new sp_rcontext(csize, hmax, cmax);
+ tmp_octx= TRUE;
+ }
+ // QQ: Should do type checking?
+ for (i = 0 ; (it= li++) && i < params ; i++)
+ {
+ sp_pvar_t *pvar = m_pcont->find_pvar(i);
+
+ if (! pvar)
+ nctx->set_oindex(i, -1); // Shouldn't happen
+ else
+ {
+ if (pvar->mode == sp_param_out)
+ {
+ if (! nit)
+ nit= new Item_null();
+ nctx->push_item(nit); // OUT
+ }
+ else
+ nctx->push_item(sp_eval_func_item(thd, it,pvar->type)); // IN or INOUT
+ // Note: If it's OUT or INOUT, it must be a variable.
+ // QQ: We can check for global variables here, or should we do it
+ // while parsing?
+ if (pvar->mode == sp_param_in)
+ nctx->set_oindex(i, -1); // IN
+ else // OUT or INOUT
+ nctx->set_oindex(i, static_cast<Item_splocal *>(it)->get_offset());
+ }
+ }
+ // Close tables opened for subselect in argument list
+ close_thread_tables(thd);
+
+ // The rest of the frame are local variables which are all IN.
+ // Default all variables to null (those with default clauses will
+ // be set by an set instruction).
+ for (; i < csize ; i++)
+ {
+ if (! nit)
+ nit= new Item_null();
+ nctx->push_item(nit);
+ }
+ thd->spcont= nctx;
+ }
+
+ ret= execute(thd);
+
+ // Don't copy back OUT values if we got an error
+ if (ret == 0 && csize > 0)
+ {
+ List_iterator_fast<Item> li(*args);
+ Item *it;
+
+ // Copy back all OUT or INOUT values to the previous frame, or
+ // set global user variables
+ for (uint i = 0 ; (it= li++) && i < params ; i++)
+ {
+ int oi = nctx->get_oindex(i);
+
+ if (oi >= 0)
+ {
+ if (! tmp_octx)
+ octx->set_item(nctx->get_oindex(i), nctx->get_item(i));
+ else
+ {
+ // QQ Currently we just silently ignore non-user-variable arguments.
+ // We should check this during parsing, when setting up the call
+ // above
+ if (it->type() == Item::FUNC_ITEM)
+ {
+ Item_func *fi= static_cast<Item_func*>(it);
+
+ if (fi->functype() == Item_func::GUSERVAR_FUNC)
+ { // A global user variable
+ Item *item= nctx->get_item(i);
+ Item_func_set_user_var *suv;
+ Item_func_get_user_var *guv=
+ static_cast<Item_func_get_user_var*>(fi);
+
+ suv= new Item_func_set_user_var(guv->get_name(), item);
+ suv->fix_fields(thd, NULL, &item);
+ suv->fix_length_and_dec();
+ suv->check();
+ suv->update();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (tmp_octx)
+ octx= NULL;
+ if (nctx)
+ nctx->pop_all_cursors(); // To avoid memory leaks after an error
+ thd->spcont= octx;
+
+ DBUG_RETURN(ret);
+}
+
+
+// Reset lex during parsing, before we parse a sub statement.
+void
+sp_head::reset_lex(THD *thd)
+{
+ DBUG_ENTER("sp_head::reset_lex");
+ LEX *sublex;
+ LEX *oldlex= thd->lex;
+
+ (void)m_lex.push_front(oldlex);
+ thd->lex= sublex= new st_lex;
+ sublex->yylineno= oldlex->yylineno;
+ /* Reset most stuff. The length arguments doesn't matter here. */
+ lex_start(thd, oldlex->buf, oldlex->end_of_query - oldlex->ptr);
+ /* We must reset ptr and end_of_query again */
+ sublex->ptr= oldlex->ptr;
+ sublex->end_of_query= oldlex->end_of_query;
+ sublex->tok_start= oldlex->tok_start;
+ /* And keep the SP stuff too */
+ sublex->sphead= oldlex->sphead;
+ sublex->spcont= oldlex->spcont;
+ mysql_init_query(thd, true); // Only init lex
+ sublex->sp_lex_in_use= FALSE;
+ DBUG_VOID_RETURN;
+}
+
+// Restore lex during parsing, after we have parsed a sub statement.
+void
+sp_head::restore_lex(THD *thd)
+{
+ DBUG_ENTER("sp_head::restore_lex");
+ LEX *sublex= thd->lex;
+ LEX *oldlex= (LEX *)m_lex.pop();
+ SELECT_LEX *sl;
+
+ if (! oldlex)
+ return; // Nothing to restore
+
+ // Update some state in the old one first
+ oldlex->ptr= sublex->ptr;
+ oldlex->next_state= sublex->next_state;
+ for (sl= sublex->all_selects_list ;
+ sl ;
+ sl= sl->next_select_in_list())
+ {
+ // Save WHERE clause pointers to avoid damaging by optimisation
+ sl->prep_where= sl->where;
+ if (sl->with_wild)
+ {
+ // Copy item_list. We will restore it before calling the
+ // sub-statement, so it's ok to pop them.
+ sl->item_list_copy.empty();
+ while (Item *it= sl->item_list.pop())
+ sl->item_list_copy.push_back(it);
+ }
+ }
+
+ // Collect some data from the sub statement lex.
+ sp_merge_funs(oldlex, sublex);
+#ifdef NOT_USED_NOW
+ // QQ We're not using this at the moment.
+ if (sublex.sql_command == SQLCOM_CALL)
+ {
+ // It would be slightly faster to keep the list sorted, but we need
+ // an "insert before" method to do that.
+ char *proc= sublex.udf.name.str;
+
+ List_iterator_fast<char *> li(m_calls);
+ char **it;
+
+ while ((it= li++))
+ if (my_strcasecmp(system_charset_info, proc, *it) == 0)
+ break;
+ if (! it)
+ m_calls.push_back(&proc);
+
+ }
+ // Merge used tables
+ // QQ ...or just open tables in thd->open_tables?
+ // This is not entirerly clear at the moment, but for now, we collect
+ // tables here.
+ for (sl= sublex.all_selects_list ;
+ sl ;
+ sl= sl->next_select())
+ {
+ for (TABLE_LIST *tables= sl->get_table_list() ;
+ tables ;
+ tables= tables->next)
+ {
+ List_iterator_fast<char *> li(m_tables);
+ char **tb;
+
+ while ((tb= li++))
+ if (my_strcasecmp(system_charset_info, tables->real_name, *tb) == 0)
+ break;
+ if (! tb)
+ m_tables.push_back(&tables->real_name);
+ }
+ }
+#endif
+ if (! sublex->sp_lex_in_use)
+ delete sublex;
+ thd->lex= oldlex;
+ DBUG_VOID_RETURN;
+}
+
+void
+sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
+{
+ bp_t *bp= (bp_t *)sql_alloc(sizeof(bp_t));
+
+ if (bp)
+ {
+ bp->lab= lab;
+ bp->instr= i;
+ (void)m_backpatch.push_front(bp);
+ }
+}
+
+void
+sp_head::backpatch(sp_label_t *lab)
+{
+ bp_t *bp;
+ uint dest= instructions();
+ List_iterator_fast<bp_t> li(m_backpatch);
+
+ while ((bp= li++))
+ if (bp->lab == lab)
+ {
+ sp_instr_jump *i= static_cast<sp_instr_jump *>(bp->instr);
+
+ i->set_destination(dest);
+ }
+}
+
+void
+sp_head::set_info(char *definer, uint definerlen,
+ longlong created, longlong modified,
+ st_sp_chistics *chistics)
+{
+ char *p= strchr(definer, '@');
+ uint len;
+
+ if (! p)
+ p= definer; // Weird...
+ len= p-definer;
+ m_definer_user.str= strmake_root(&m_mem_root, definer, len);
+ m_definer_user.length= len;
+ len= definerlen-len-1;
+ m_definer_host.str= strmake_root(&m_mem_root, p+1, len);
+ m_definer_host.length= len;
+ m_created= created;
+ m_modified= modified;
+ m_chistics= (st_sp_chistics *)alloc_root(&m_mem_root, sizeof(st_sp_chistics));
+ memcpy(m_chistics, chistics, sizeof(st_sp_chistics));
+ if (m_chistics->comment.length == 0)
+ m_chistics->comment.str= 0;
+ else
+ m_chistics->comment.str= strmake_root(&m_mem_root,
+ m_chistics->comment.str,
+ m_chistics->comment.length);
+}
+
+int
+sp_head::show_create_procedure(THD *thd)
+{
+ Protocol *protocol= thd->protocol;
+ char buff[2048];
+ String buffer(buff, sizeof(buff), system_charset_info);
+ int res;
+ List<Item> field_list;
+
+ DBUG_ENTER("sp_head::show_create_procedure");
+ DBUG_PRINT("info", ("procedure %s", m_name.str));
+
+ field_list.push_back(new Item_empty_string("Procedure",NAME_LEN));
+ // 1024 is for not to confuse old clients
+ field_list.push_back(new Item_empty_string("Create Procedure",
+ max(buffer.length(),1024)));
+ if (protocol->send_fields(&field_list, 1))
+ DBUG_RETURN(1);
+ protocol->prepare_for_resend();
+ protocol->store(m_name.str, m_name.length, system_charset_info);
+ protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
+ res= protocol->write();
+ send_eof(thd);
+ DBUG_RETURN(res);
+}
+
+int
+sp_head::show_create_function(THD *thd)
+{
+ Protocol *protocol= thd->protocol;
+ char buff[2048];
+ String buffer(buff, sizeof(buff), system_charset_info);
+ int res;
+ List<Item> field_list;
+
+ DBUG_ENTER("sp_head::show_create_function");
+ DBUG_PRINT("info", ("procedure %s", m_name.str));
+
+ field_list.push_back(new Item_empty_string("Function",NAME_LEN));
+ field_list.push_back(new Item_empty_string("Create Function",
+ max(buffer.length(),1024)));
+ if (protocol->send_fields(&field_list, 1))
+ DBUG_RETURN(1);
+ protocol->prepare_for_resend();
+ protocol->store(m_name.str, m_name.length, system_charset_info);
+ protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
+ res= protocol->write();
+ send_eof(thd);
+ DBUG_RETURN(res);
+}
+// ------------------------------------------------------------------
+
+//
+// sp_instr_stmt
+//
+sp_instr_stmt::~sp_instr_stmt()
+{
+ if (m_lex)
+ delete m_lex;
+}
+
+int
+sp_instr_stmt::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_stmt::execute");
+ DBUG_PRINT("info", ("command: %d", m_lex->sql_command));
+ int res= exec_stmt(thd, m_lex);
+ *nextp = m_ip+1;
+ DBUG_RETURN(res);
+}
+
+int
+sp_instr_stmt::exec_stmt(THD *thd, LEX *lex)
+{
+ LEX *olex; // The other lex
+ Item *freelist;
+ SELECT_LEX *sl;
+ int res;
+
+ olex= thd->lex; // Save the other lex
+ thd->lex= lex; // Use my own lex
+ thd->lex->thd = thd; // QQ Not reentrant!
+ thd->lex->unit.thd= thd; // QQ Not reentrant
+ freelist= thd->free_list;
+ thd->free_list= NULL;
+ thd->query_id= query_id++;
+
+ // Copy WHERE clause pointers to avoid damaging by optimisation
+ // Also clear ref_pointer_arrays.
+ for (sl= lex->all_selects_list ;
+ sl ;
+ sl= sl->next_select_in_list())
+ {
+ if (lex->sql_command == SQLCOM_CREATE_TABLE ||
+ lex->sql_command == SQLCOM_INSERT_SELECT)
+ { // Destroys sl->table_list.first
+ sl->table_list_first_copy= sl->table_list.first;
+ }
+ if (sl->with_wild)
+ {
+ // Restore item_list
+ // Note: We have to do this before executing the sub-statement,
+ // to make sure that the list nodes are in the right
+ // memroot.
+ List_iterator_fast<Item> li(sl->item_list_copy);
+
+ sl->item_list.empty();
+ while (Item *it= li++)
+ sl->item_list.push_back(it);
+ }
+ sl->ref_pointer_array= 0;
+ if (sl->prep_where)
+ sl->where= sl->prep_where->copy_andor_structure(thd);
+ for (ORDER *order= (ORDER *)sl->order_list.first ;
+ order ;
+ order= order->next)
+ {
+ order->item_copy= order->item;
+ }
+ for (ORDER *group= (ORDER *)sl->group_list.first ;
+ group ;
+ group= group->next)
+ {
+ group->item_copy= group->item;
+ }
+ }
+
+ res= mysql_execute_command(thd);
+
+ if (thd->lock || thd->open_tables || thd->derived_tables)
+ {
+ thd->proc_info="closing tables";
+ close_thread_tables(thd); /* Free tables */
+ }
+
+ for (sl= lex->all_selects_list ;
+ sl ;
+ sl= sl->next_select_in_list())
+ {
+ TABLE_LIST *tabs;
+
+ // We have closed all tables, get rid of pointers to them
+ for (tabs=(TABLE_LIST *)sl->table_list.first ;
+ tabs ;
+ tabs= tabs->next)
+ {
+ tabs->table= NULL;
+ }
+ if (lex->sql_command == SQLCOM_CREATE_TABLE ||
+ lex->sql_command == SQLCOM_INSERT_SELECT)
+ { // Restore sl->table_list.first
+ sl->table_list.first= sl->table_list_first_copy;
+ }
+ for (ORDER *order= (ORDER *)sl->order_list.first ;
+ order ;
+ order= order->next)
+ {
+ order->item= order->item_copy;
+ }
+ for (ORDER *group= (ORDER *)sl->group_list.first ;
+ group ;
+ group= group->next)
+ {
+ group->item= group->item_copy;
+ }
+ }
+ thd->lex= olex; // Restore the other lex
+ thd->free_list= freelist;
+
+ return res;
+}
+
+//
+// sp_instr_set
+//
+int
+sp_instr_set::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_set::execute");
+ DBUG_PRINT("info", ("offset: %u", m_offset));
+ thd->spcont->set_item(m_offset, sp_eval_func_item(thd, m_value, m_type));
+ *nextp = m_ip+1;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_jump
+//
+int
+sp_instr_jump::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_jump::execute");
+ DBUG_PRINT("info", ("destination: %u", m_dest));
+
+ *nextp= m_dest;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_jump_if
+//
+int
+sp_instr_jump_if::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_jump_if::execute");
+ DBUG_PRINT("info", ("destination: %u", m_dest));
+ Item *it= sp_eval_func_item(thd, m_expr, MYSQL_TYPE_TINY);
+
+ if (it->val_int())
+ *nextp = m_dest;
+ else
+ *nextp = m_ip+1;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_jump_if_not
+//
+int
+sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_jump_if_not::execute");
+ DBUG_PRINT("info", ("destination: %u", m_dest));
+ Item *it= sp_eval_func_item(thd, m_expr, MYSQL_TYPE_TINY);
+
+ if (! it->val_int())
+ *nextp = m_dest;
+ else
+ *nextp = m_ip+1;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_freturn
+//
+int
+sp_instr_freturn::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_freturn::execute");
+ thd->spcont->set_result(sp_eval_func_item(thd, m_value, m_type));
+ *nextp= UINT_MAX;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_hpush_jump
+//
+int
+sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_hpush_jump::execute");
+ List_iterator_fast<sp_cond_type_t> li(m_cond);
+ sp_cond_type_t *p;
+
+ while ((p= li++))
+ thd->spcont->push_handler(p, m_handler, m_type, m_frame);
+
+ *nextp= m_dest;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_hpop
+//
+int
+sp_instr_hpop::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_hpop::execute");
+ thd->spcont->pop_handlers(m_count);
+ *nextp= m_ip+1;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_hreturn
+//
+int
+sp_instr_hreturn::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_hreturn::execute");
+ thd->spcont->restore_variables(m_frame);
+ *nextp= thd->spcont->pop_hstack();
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_cpush
+//
+int
+sp_instr_cpush::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cpush::execute");
+ thd->spcont->push_cursor(m_lex);
+ *nextp= m_ip+1;
+ DBUG_RETURN(0);
+}
+
+sp_instr_cpush::~sp_instr_cpush()
+{
+ if (m_lex)
+ delete m_lex;
+}
+
+//
+// sp_instr_cpop
+//
+int
+sp_instr_cpop::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cpop::execute");
+ thd->spcont->pop_cursors(m_count);
+ *nextp= m_ip+1;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_copen
+//
+int
+sp_instr_copen::execute(THD *thd, uint *nextp)
+{
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res;
+ DBUG_ENTER("sp_instr_copen::execute");
+
+ if (! c)
+ res= -1;
+ else
+ {
+ LEX *lex= c->pre_open(thd);
+
+ if (! lex)
+ res= -1;
+ else
+ res= exec_stmt(thd, lex);
+ c->post_open(thd, (res == 0 ? TRUE : FALSE));
+ }
+
+ *nextp= m_ip+1;
+ DBUG_RETURN(res);
+}
+
+//
+// sp_instr_cclose
+//
+int
+sp_instr_cclose::execute(THD *thd, uint *nextp)
+{
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res;
+ DBUG_ENTER("sp_instr_cclose::execute");
+
+ if (! c)
+ res= -1;
+ else
+ res= c->close(thd);
+ *nextp= m_ip+1;
+ DBUG_RETURN(res);
+}
+
+//
+// sp_instr_cfetch
+//
+int
+sp_instr_cfetch::execute(THD *thd, uint *nextp)
+{
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res;
+ DBUG_ENTER("sp_instr_cfetch::execute");
+
+ if (! c)
+ res= -1;
+ else
+ res= c->fetch(thd, &m_varlist);
+ *nextp= m_ip+1;
+ DBUG_RETURN(res);
+}
+
+
+//
+// Security context swapping
+//
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+void
+sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp)
+{
+ ctxp->changed= (sp->m_chistics->suid != IS_NOT_SUID &&
+ (strcmp(sp->m_definer_user.str, thd->priv_user) ||
+ strcmp(sp->m_definer_host.str, thd->priv_host)));
+
+ if (ctxp->changed)
+ {
+ ctxp->master_access= thd->master_access;
+ ctxp->db_access= thd->db_access;
+ ctxp->db= thd->db;
+ ctxp->db_length= thd->db_length;
+ ctxp->priv_user= thd->priv_user;
+ strncpy(ctxp->priv_host, thd->priv_host, sizeof(ctxp->priv_host));
+ ctxp->user= thd->user;
+ ctxp->host= thd->host;
+ ctxp->ip= thd->ip;
+
+ /* Change thise just to do the acl_getroot_no_password */
+ thd->user= sp->m_definer_user.str;
+ thd->host= thd->ip = sp->m_definer_host.str;
+
+ if (acl_getroot_no_password(thd))
+ { // Failed, run as invoker for now
+ ctxp->changed= FALSE;
+ thd->master_access= ctxp->master_access;
+ thd->db_access= ctxp->db_access;
+ thd->db= ctxp->db;
+ thd->db_length= ctxp->db_length;
+ thd->priv_user= ctxp->priv_user;
+ strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host));
+ }
+
+ /* Restore these immiediately */
+ thd->user= ctxp->user;
+ thd->host= ctxp->host;
+ thd->ip= ctxp->ip;
+ }
+}
+
+void
+sp_restore_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp)
+{
+ if (ctxp->changed)
+ {
+ ctxp->changed= FALSE;
+ thd->master_access= ctxp->master_access;
+ thd->db_access= ctxp->db_access;
+ thd->db= ctxp->db;
+ thd->db_length= ctxp->db_length;
+ thd->priv_user= ctxp->priv_user;
+ strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host));
+ }
+}
+
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
diff --git a/sql/sp_head.h b/sql/sp_head.h
new file mode 100644
index 00000000000..b2dee5204bb
--- /dev/null
+++ b/sql/sp_head.h
@@ -0,0 +1,659 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_HEAD_H_
+#define _SP_HEAD_H_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+#include <stddef.h>
+
+// Values for the type enum. This reflects the order of the enum declaration
+// in the CREATE TABLE command.
+#define TYPE_ENUM_FUNCTION 1
+#define TYPE_ENUM_PROCEDURE 2
+
+Item_result
+sp_map_result_type(enum enum_field_types type);
+
+struct sp_label;
+class sp_instr;
+struct sp_cond_type;
+struct sp_pvar;
+
+class sp_head : public Sql_alloc
+{
+ sp_head(const sp_head &); /* Prevent use of these */
+ void operator=(sp_head &);
+
+public:
+
+ int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE
+ enum enum_field_types m_returns; // For FUNCTIONs only
+ my_bool m_has_return; // For FUNCTIONs only
+ my_bool m_simple_case; // TRUE if parsing simple case, FALSE otherwise
+ my_bool m_multi_results; // TRUE if a procedure with SELECT(s)
+ uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value
+ st_sp_chistics *m_chistics;
+#if NOT_USED_NOW
+ // QQ We're not using this at the moment.
+ List<char *> m_calls; // Called procedures.
+ List<char *> m_tables; // Used tables.
+#endif
+ LEX_STRING m_name;
+ LEX_STRING m_params;
+ LEX_STRING m_retstr; // For FUNCTIONs only
+ LEX_STRING m_body;
+ LEX_STRING m_defstr;
+ LEX_STRING m_definer_user;
+ LEX_STRING m_definer_host;
+ longlong m_created;
+ longlong m_modified;
+ // Pointers set during parsing
+ uchar *m_param_begin, *m_param_end, *m_returns_begin, *m_returns_end,
+ *m_body_begin;
+
+ static void *
+ operator new(size_t size);
+
+ static void
+ operator delete(void *ptr, size_t size);
+
+ sp_head();
+
+ // Initialize after we have reset mem_root
+ void
+ init(LEX *lex);
+
+ // Initialize strings after parsing header
+ void
+ init_strings(THD *thd, LEX *lex, LEX_STRING *name);
+
+ int
+ create(THD *thd);
+
+ virtual ~sp_head();
+
+ // Free memory
+ void
+ destroy();
+
+ int
+ execute_function(THD *thd, Item **args, uint argcount, Item **resp);
+
+ int
+ execute_procedure(THD *thd, List<Item> *args);
+
+ int
+ show_create_procedure(THD *thd);
+
+ int
+ show_create_function(THD *thd);
+
+ inline void
+ add_instr(sp_instr *i)
+ {
+ insert_dynamic(&m_instr, (gptr)&i);
+ }
+
+ inline uint
+ instructions()
+ {
+ return m_instr.elements;
+ }
+
+ inline sp_instr *
+ last_instruction()
+ {
+ sp_instr *i;
+
+ get_dynamic(&m_instr, (gptr)&i, m_instr.elements-1);
+ return i;
+ }
+
+ // Resets lex in 'thd' and keeps a copy of the old one.
+ void
+ reset_lex(THD *thd);
+
+ // Restores lex in 'thd' from our copy, but keeps some status from the
+ // one in 'thd', like ptr, tables, fields, etc.
+ void
+ restore_lex(THD *thd);
+
+ // Put the instruction on the backpatch list, associated with the label.
+ void
+ push_backpatch(sp_instr *, struct sp_label *);
+
+ // Update all instruction with this label in the backpatch list to
+ // the current position.
+ void
+ backpatch(struct sp_label *);
+
+ char *name(uint *lenp = 0) const
+ {
+ if (lenp)
+ *lenp= m_name.length;
+ return m_name.str;
+ }
+
+ char *create_string(THD *thd, ulong *lenp);
+
+ inline Item_result result()
+ {
+ return sp_map_result_type(m_returns);
+ }
+
+ void set_info(char *definer, uint definerlen,
+ longlong created, longlong modified,
+ st_sp_chistics *chistics);
+
+ inline void reset_thd_mem_root(THD *thd)
+ {
+ m_thd_root= thd->mem_root;
+ thd->mem_root= m_mem_root;
+ m_free_list= thd->free_list; // Keep the old list
+ thd->free_list= NULL; // Start a new one
+ m_thd= thd;
+ }
+
+ inline void restore_thd_mem_root(THD *thd)
+ {
+ Item *flist= m_free_list; // The old list
+ m_free_list= thd->free_list; // Get the new one
+ thd->free_list= flist; // Restore the old one
+ m_mem_root= thd->mem_root;
+ thd->mem_root= m_thd_root;
+ m_thd= NULL;
+ }
+
+private:
+
+ MEM_ROOT m_mem_root; // My own mem_root
+ MEM_ROOT m_thd_root; // Temp. store for thd's mem_root
+ Item *m_free_list; // Where the items go
+ THD *m_thd; // Set if we have reset mem_root
+
+ sp_pcontext *m_pcont; // Parse context
+ List<LEX> m_lex; // Temp. store for the other lex
+ DYNAMIC_ARRAY m_instr; // The "instructions"
+ typedef struct
+ {
+ struct sp_label *lab;
+ sp_instr *instr;
+ } bp_t;
+ List<bp_t> m_backpatch; // Instructions needing backpatching
+
+ inline sp_instr *
+ get_instr(uint i)
+ {
+ sp_instr *ip;
+
+ if (i < m_instr.elements)
+ get_dynamic(&m_instr, (gptr)&ip, i);
+ else
+ ip= NULL;
+ return ip;
+ }
+
+ int
+ execute(THD *thd);
+
+}; // class sp_head : public Sql_alloc
+
+
+//
+// "Instructions"...
+//
+
+class sp_instr : public Sql_alloc
+{
+ sp_instr(const sp_instr &); /* Prevent use of these */
+ void operator=(sp_instr &);
+
+public:
+
+ // Should give each a name or type code for debugging purposes?
+ sp_instr(uint ip)
+ : Sql_alloc(), m_ip(ip)
+ {}
+
+ virtual ~sp_instr()
+ {}
+
+ // Execute this instrution. '*nextp' will be set to the index of the next
+ // instruction to execute. (For most instruction this will be the
+ // instruction following this one.)
+ // Returns 0 on success, non-zero if some error occured.
+ virtual int
+ execute(THD *thd, uint *nextp)
+ { // Default is a no-op.
+ *nextp = m_ip+1; // Next instruction
+ return 0;
+ }
+
+protected:
+
+ uint m_ip; // My index
+
+}; // class sp_instr : public Sql_alloc
+
+
+//
+// Call out to some prepared SQL statement.
+//
+class sp_instr_stmt : public sp_instr
+{
+ sp_instr_stmt(const sp_instr_stmt &); /* Prevent use of these */
+ void operator=(sp_instr_stmt &);
+
+public:
+
+ sp_instr_stmt(uint ip)
+ : sp_instr(ip), m_lex(NULL)
+ {}
+
+ virtual ~sp_instr_stmt();
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ inline void
+ set_lex(LEX *lex)
+ {
+ m_lex= lex;
+ }
+
+ inline LEX *
+ get_lex()
+ {
+ return m_lex;
+ }
+
+protected:
+
+ int exec_stmt(THD *thd, LEX *lex); // Execute a statement
+
+private:
+
+ LEX *m_lex; // My own lex
+
+}; // class sp_instr_stmt : public sp_instr
+
+
+class sp_instr_set : public sp_instr
+{
+ sp_instr_set(const sp_instr_set &); /* Prevent use of these */
+ void operator=(sp_instr_set &);
+
+public:
+
+ sp_instr_set(uint ip, uint offset, Item *val, enum enum_field_types type)
+ : sp_instr(ip), m_offset(offset), m_value(val), m_type(type)
+ {}
+
+ virtual ~sp_instr_set()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ uint m_offset; // Frame offset
+ Item *m_value;
+ enum enum_field_types m_type; // The declared type
+
+}; // class sp_instr_set : public sp_instr
+
+
+class sp_instr_jump : public sp_instr
+{
+ sp_instr_jump(const sp_instr_jump &); /* Prevent use of these */
+ void operator=(sp_instr_jump &);
+
+public:
+
+ sp_instr_jump(uint ip)
+ : sp_instr(ip)
+ {}
+
+ sp_instr_jump(uint ip, uint dest)
+ : sp_instr(ip), m_dest(dest)
+ {}
+
+ virtual ~sp_instr_jump()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void
+ set_destination(uint dest)
+ {
+ m_dest= dest;
+ }
+
+protected:
+
+ int m_dest; // Where we will go
+
+}; // class sp_instr_jump : public sp_instr
+
+
+class sp_instr_jump_if : public sp_instr_jump
+{
+ sp_instr_jump_if(const sp_instr_jump_if &); /* Prevent use of these */
+ void operator=(sp_instr_jump_if &);
+
+public:
+
+ sp_instr_jump_if(uint ip, Item *i)
+ : sp_instr_jump(ip), m_expr(i)
+ {}
+
+ sp_instr_jump_if(uint ip, Item *i, uint dest)
+ : sp_instr_jump(ip, dest), m_expr(i)
+ {}
+
+ virtual ~sp_instr_jump_if()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ Item *m_expr; // The condition
+
+}; // class sp_instr_jump_if : public sp_instr_jump
+
+
+class sp_instr_jump_if_not : public sp_instr_jump
+{
+ sp_instr_jump_if_not(const sp_instr_jump_if_not &); /* Prevent use of these */
+ void operator=(sp_instr_jump_if_not &);
+
+public:
+
+ sp_instr_jump_if_not(uint ip, Item *i)
+ : sp_instr_jump(ip), m_expr(i)
+ {}
+
+ sp_instr_jump_if_not(uint ip, Item *i, uint dest)
+ : sp_instr_jump(ip, dest), m_expr(i)
+ {}
+
+ virtual ~sp_instr_jump_if_not()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ Item *m_expr; // The condition
+
+}; // class sp_instr_jump_if_not : public sp_instr_jump
+
+
+class sp_instr_freturn : public sp_instr
+{
+ sp_instr_freturn(const sp_instr_freturn &); /* Prevent use of these */
+ void operator=(sp_instr_freturn &);
+
+public:
+
+ sp_instr_freturn(uint ip, Item *val, enum enum_field_types type)
+ : sp_instr(ip), m_value(val), m_type(type)
+ {}
+
+ virtual ~sp_instr_freturn()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+protected:
+
+ Item *m_value;
+ enum enum_field_types m_type;
+
+}; // class sp_instr_freturn : public sp_instr
+
+
+class sp_instr_hpush_jump : public sp_instr_jump
+{
+ sp_instr_hpush_jump(const sp_instr_hpush_jump &); /* Prevent use of these */
+ void operator=(sp_instr_hpush_jump &);
+
+public:
+
+ sp_instr_hpush_jump(uint ip, int htype, uint fp)
+ : sp_instr_jump(ip), m_type(htype), m_frame(fp)
+ {
+ m_handler= ip+1;
+ m_cond.empty();
+ }
+
+ virtual ~sp_instr_hpush_jump()
+ {
+ m_cond.empty();
+ }
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ inline void add_condition(struct sp_cond_type *cond)
+ {
+ m_cond.push_front(cond);
+ }
+
+private:
+
+ int m_type; // Handler type
+ uint m_frame;
+ uint m_handler; // Location of handler
+ List<struct sp_cond_type> m_cond;
+
+}; // class sp_instr_hpush_jump : public sp_instr_jump
+
+
+class sp_instr_hpop : public sp_instr
+{
+ sp_instr_hpop(const sp_instr_hpop &); /* Prevent use of these */
+ void operator=(sp_instr_hpop &);
+
+public:
+
+ sp_instr_hpop(uint ip, uint count)
+ : sp_instr(ip), m_count(count)
+ {}
+
+ virtual ~sp_instr_hpop()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ uint m_count;
+
+}; // class sp_instr_hpop : public sp_instr
+
+
+class sp_instr_hreturn : public sp_instr
+{
+ sp_instr_hreturn(const sp_instr_hreturn &); /* Prevent use of these */
+ void operator=(sp_instr_hreturn &);
+
+public:
+
+ sp_instr_hreturn(uint ip, uint fp)
+ : sp_instr(ip), m_frame(fp)
+ {}
+
+ virtual ~sp_instr_hreturn()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ uint m_frame;
+
+}; // class sp_instr_hreturn : public sp_instr
+
+
+class sp_instr_cpush : public sp_instr
+{
+ sp_instr_cpush(const sp_instr_cpush &); /* Prevent use of these */
+ void operator=(sp_instr_cpush &);
+
+public:
+
+ sp_instr_cpush(uint ip, LEX *lex)
+ : sp_instr(ip), m_lex(lex)
+ {}
+
+ virtual ~sp_instr_cpush();
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ LEX *m_lex;
+
+}; // class sp_instr_cpush : public sp_instr
+
+
+class sp_instr_cpop : public sp_instr
+{
+ sp_instr_cpop(const sp_instr_cpop &); /* Prevent use of these */
+ void operator=(sp_instr_cpop &);
+
+public:
+
+ sp_instr_cpop(uint ip, uint count)
+ : sp_instr(ip), m_count(count)
+ {}
+
+ virtual ~sp_instr_cpop()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ uint m_count;
+
+}; // class sp_instr_cpop : public sp_instr
+
+
+class sp_instr_copen : public sp_instr_stmt
+{
+ sp_instr_copen(const sp_instr_copen &); /* Prevent use of these */
+ void operator=(sp_instr_copen &);
+
+public:
+
+ sp_instr_copen(uint ip, uint c)
+ : sp_instr_stmt(ip), m_cursor(c)
+ {}
+
+ virtual ~sp_instr_copen()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ uint m_cursor; // Stack index
+
+}; // class sp_instr_copen : public sp_instr_stmt
+
+
+class sp_instr_cclose : public sp_instr
+{
+ sp_instr_cclose(const sp_instr_cclose &); /* Prevent use of these */
+ void operator=(sp_instr_cclose &);
+
+public:
+
+ sp_instr_cclose(uint ip, uint c)
+ : sp_instr(ip), m_cursor(c)
+ {}
+
+ virtual ~sp_instr_cclose()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ uint m_cursor;
+
+}; // class sp_instr_cclose : public sp_instr
+
+
+class sp_instr_cfetch : public sp_instr
+{
+ sp_instr_cfetch(const sp_instr_cfetch &); /* Prevent use of these */
+ void operator=(sp_instr_cfetch &);
+
+public:
+
+ sp_instr_cfetch(uint ip, uint c)
+ : sp_instr(ip), m_cursor(c)
+ {
+ m_varlist.empty();
+ }
+
+ virtual ~sp_instr_cfetch()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ void add_to_varlist(struct sp_pvar *var)
+ {
+ m_varlist.push_back(var);
+ }
+
+private:
+
+ uint m_cursor;
+ List<struct sp_pvar> m_varlist;
+
+}; // class sp_instr_cfetch : public sp_instr
+
+
+struct st_sp_security_context
+{
+ bool changed;
+ uint master_access;
+ uint db_access;
+ char *db;
+ uint db_length;
+ char *priv_user;
+ char priv_host[MAX_HOSTNAME];
+ char *user;
+ char *host;
+ char *ip;
+};
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+void
+sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp);
+void
+sp_restore_security_context(THD *thd, sp_head *sp,st_sp_security_context *ctxp);
+#endif /* NO_EMBEDDED_ACCESS_CHECKS */
+
+#endif /* _SP_HEAD_H_ */
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
new file mode 100644
index 00000000000..b7e23c9f5ad
--- /dev/null
+++ b/sql/sp_pcontext.cc
@@ -0,0 +1,246 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#if defined(WIN32) || defined(__WIN__)
+#undef SAFEMALLOC /* Problems with threads */
+#endif
+
+#include "mysql_priv.h"
+#include "sp_pcontext.h"
+#include "sp_head.h"
+
+sp_pcontext::sp_pcontext()
+ : Sql_alloc(), m_params(0), m_framesize(0), m_handlers(0), m_cursmax(0)
+{
+ VOID(my_init_dynamic_array(&m_pvar, sizeof(sp_pvar_t *), 16, 8));
+ VOID(my_init_dynamic_array(&m_cond, sizeof(sp_cond_type_t *), 16, 8));
+ VOID(my_init_dynamic_array(&m_cursor, sizeof(LEX_STRING), 16, 8));
+ VOID(my_init_dynamic_array(&m_scopes, sizeof(sp_scope_t), 16, 8));
+ m_label.empty();
+}
+
+void
+sp_pcontext::destroy()
+{
+ delete_dynamic(&m_pvar);
+ delete_dynamic(&m_cond);
+ delete_dynamic(&m_cursor);
+ delete_dynamic(&m_scopes);
+ m_label.empty();
+}
+
+void
+sp_pcontext::push_scope()
+{
+ sp_scope_t s;
+
+ s.vars= m_pvar.elements;
+ s.conds= m_cond.elements;
+ s.curs= m_cursor.elements;
+ insert_dynamic(&m_scopes, (gptr)&s);
+}
+
+void
+sp_pcontext::pop_scope()
+{
+ (void)pop_dynamic(&m_scopes);
+}
+
+
+/* This does a linear search (from newer to older variables, in case
+** we have shadowed names).
+** It's possible to have a more efficient allocation and search method,
+** but it might not be worth it. The typical number of parameters and
+** variables will in most cases be low (a handfull).
+** ...and, this is only called during parsing.
+*/
+sp_pvar_t *
+sp_pcontext::find_pvar(LEX_STRING *name, my_bool scoped)
+{
+ uint i = m_pvar.elements;
+ uint limit;
+
+ if (! scoped || m_scopes.elements == 0)
+ limit= 0;
+ else
+ {
+ sp_scope_t s;
+
+ get_dynamic(&m_scopes, (gptr)&s, m_scopes.elements-1);
+ limit= s.vars;
+ }
+
+ while (i-- > limit)
+ {
+ sp_pvar_t *p;
+
+ get_dynamic(&m_pvar, (gptr)&p, i);
+ if (my_strnncoll(system_charset_info,
+ (const uchar *)name->str, name->length,
+ (const uchar *)p->name.str, p->name.length) == 0)
+ {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+void
+sp_pcontext::push_pvar(LEX_STRING *name, enum enum_field_types type,
+ sp_param_mode_t mode)
+{
+ sp_pvar_t *p= (sp_pvar_t *)sql_alloc(sizeof(sp_pvar_t));
+
+ if (p)
+ {
+ if (m_pvar.elements == m_framesize)
+ m_framesize += 1;
+ p->name.str= name->str;
+ p->name.length= name->length;
+ p->type= type;
+ p->mode= mode;
+ p->offset= m_pvar.elements;
+ p->isset= (mode == sp_param_out ? FALSE : TRUE);
+ insert_dynamic(&m_pvar, (gptr)&p);
+ }
+}
+
+sp_label_t *
+sp_pcontext::push_label(char *name, uint ip)
+{
+ sp_label_t *lab = (sp_label_t *)sql_alloc(sizeof(sp_label_t));
+
+ if (lab)
+ {
+ lab->name= name;
+ lab->ip= ip;
+ lab->isbegin= FALSE;
+ m_label.push_front(lab);
+ }
+ return lab;
+}
+
+sp_label_t *
+sp_pcontext::find_label(char *name)
+{
+ List_iterator_fast<sp_label_t> li(m_label);
+ sp_label_t *lab;
+
+ while ((lab= li++))
+ if (my_strcasecmp(system_charset_info, name, lab->name) == 0)
+ return lab;
+
+ return NULL;
+}
+
+void
+sp_pcontext::push_cond(LEX_STRING *name, sp_cond_type_t *val)
+{
+ sp_cond_t *p= (sp_cond_t *)sql_alloc(sizeof(sp_cond_t));
+
+ if (p)
+ {
+ p->name.str= name->str;
+ p->name.length= name->length;
+ p->val= val;
+ insert_dynamic(&m_cond, (gptr)&p);
+ }
+}
+
+/*
+ * See comment for find_pvar() above
+ */
+sp_cond_type_t *
+sp_pcontext::find_cond(LEX_STRING *name, my_bool scoped)
+{
+ uint i = m_cond.elements;
+ uint limit;
+
+ if (! scoped || m_scopes.elements == 0)
+ limit= 0;
+ else
+ {
+ sp_scope_t s;
+
+ get_dynamic(&m_scopes, (gptr)&s, m_scopes.elements-1);
+ limit= s.conds;
+ }
+
+ while (i-- > limit)
+ {
+ sp_cond_t *p;
+
+ get_dynamic(&m_cond, (gptr)&p, i);
+ if (my_strnncoll(system_charset_info,
+ (const uchar *)name->str, name->length,
+ (const uchar *)p->name.str, p->name.length) == 0)
+ {
+ return p->val;
+ }
+ }
+ return NULL;
+}
+
+void
+sp_pcontext::push_cursor(LEX_STRING *name)
+{
+ LEX_STRING n;
+
+ n.str= name->str;
+ n.length= name->length;
+ insert_dynamic(&m_cursor, (gptr)&n);
+ if (m_cursor.elements > m_cursmax)
+ m_cursmax= m_cursor.elements;
+}
+
+/*
+ * See comment for find_pvar() above
+ */
+my_bool
+sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped)
+{
+ uint i = m_cursor.elements;
+ uint limit;
+
+ if (! scoped || m_scopes.elements == 0)
+ limit= 0;
+ else
+ {
+ sp_scope_t s;
+
+ get_dynamic(&m_scopes, (gptr)&s, m_scopes.elements-1);
+ limit= s.curs;
+ }
+
+ while (i-- > limit)
+ {
+ LEX_STRING n;
+
+ get_dynamic(&m_cursor, (gptr)&n, i);
+ if (my_strnncoll(system_charset_info,
+ (const uchar *)name->str, name->length,
+ (const uchar *)n.str, n.length) == 0)
+ {
+ *poff= i;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
new file mode 100644
index 00000000000..02134e3604f
--- /dev/null
+++ b/sql/sp_pcontext.h
@@ -0,0 +1,256 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_PCONTEXT_H_
+#define _SP_PCONTEXT_H_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+typedef enum
+{
+ sp_param_in,
+ sp_param_out,
+ sp_param_inout
+} sp_param_mode_t;
+
+typedef struct sp_pvar
+{
+ LEX_STRING name;
+ enum enum_field_types type;
+ sp_param_mode_t mode;
+ uint offset; // Offset in current frame
+ my_bool isset;
+} sp_pvar_t;
+
+typedef struct sp_label
+{
+ char *name;
+ uint ip; // Instruction index
+ my_bool isbegin; // For ITERATE error checking
+} sp_label_t;
+
+typedef struct sp_cond_type
+{
+ enum { number, state, warning, notfound, exception } type;
+ char sqlstate[6];
+ uint mysqlerr;
+} sp_cond_type_t;
+
+typedef struct sp_cond
+{
+ LEX_STRING name;
+ sp_cond_type_t *val;
+} sp_cond_t;
+
+typedef struct sp_scope
+{
+ uint vars, conds, curs;
+} sp_scope_t;
+
+class sp_pcontext : public Sql_alloc
+{
+ sp_pcontext(const sp_pcontext &); /* Prevent use of these */
+ void operator=(sp_pcontext &);
+
+ public:
+
+ sp_pcontext();
+
+ // Free memory
+ void
+ destroy();
+
+ // For error checking of duplicate things
+ void
+ push_scope();
+
+ void
+ pop_scope();
+
+ //
+ // Parameters and variables
+ //
+
+ inline uint
+ max_framesize()
+ {
+ return m_framesize;
+ }
+
+ inline uint
+ current_framesize()
+ {
+ return m_pvar.elements;
+ }
+
+ inline uint
+ params()
+ {
+ return m_params;
+ }
+
+ // Set the number of parameters to the current esize
+ inline void
+ set_params()
+ {
+ m_params= m_pvar.elements;
+ }
+
+ inline void
+ set_type(uint i, enum enum_field_types type)
+ {
+ sp_pvar_t *p= find_pvar(i);
+
+ if (p)
+ p->type= type;
+ }
+
+ inline void
+ set_isset(uint i, my_bool val)
+ {
+ sp_pvar_t *p= find_pvar(i);
+
+ if (p)
+ p->isset= val;
+ }
+
+ void
+ push_pvar(LEX_STRING *name, enum enum_field_types type, sp_param_mode_t mode);
+
+ // Pop the last 'num' slots of the frame
+ inline void
+ pop_pvar(uint num = 1)
+ {
+ while (num--)
+ pop_dynamic(&m_pvar);
+ }
+
+ // Find by name
+ sp_pvar_t *
+ find_pvar(LEX_STRING *name, my_bool scoped=0);
+
+ // Find by index
+ sp_pvar_t *
+ find_pvar(uint i)
+ {
+ sp_pvar_t *p;
+
+ if (i < m_pvar.elements)
+ get_dynamic(&m_pvar, (gptr)&p, i);
+ else
+ p= NULL;
+ return p;
+ }
+
+ //
+ // Labels
+ //
+
+ sp_label_t *
+ push_label(char *name, uint ip);
+
+ sp_label_t *
+ find_label(char *name);
+
+ inline sp_label_t *
+ last_label()
+ {
+ return m_label.head();
+ }
+
+ inline sp_label_t *
+ pop_label()
+ {
+ return m_label.pop();
+ }
+
+ //
+ // Conditions
+ //
+
+ void
+ push_cond(LEX_STRING *name, sp_cond_type_t *val);
+
+ inline void
+ pop_cond(uint num)
+ {
+ while (num--)
+ pop_dynamic(&m_cond);
+ }
+
+ sp_cond_type_t *
+ find_cond(LEX_STRING *name, my_bool scoped=0);
+
+ //
+ // Handlers
+ //
+
+ inline void
+ add_handler()
+ {
+ m_handlers+= 1;
+ }
+
+ inline uint
+ handlers()
+ {
+ return m_handlers;
+ }
+
+ //
+ // Cursors
+ //
+
+ void
+ push_cursor(LEX_STRING *name);
+
+ my_bool
+ find_cursor(LEX_STRING *name, uint *poff, my_bool scoped=0);
+
+ inline void
+ pop_cursor(uint num)
+ {
+ while (num--)
+ pop_dynamic(&m_cursor);
+ }
+
+ inline uint
+ cursors()
+ {
+ return m_cursmax;
+ }
+
+private:
+
+ uint m_params; // The number of parameters
+ uint m_framesize; // The maximum framesize
+ uint m_handlers; // The total number of handlers
+ uint m_cursmax; // The maximum number of cursors
+
+ DYNAMIC_ARRAY m_pvar; // Parameters/variables
+ DYNAMIC_ARRAY m_cond; // Conditions
+ DYNAMIC_ARRAY m_cursor; // Cursors
+ DYNAMIC_ARRAY m_scopes; // For error checking
+
+ List<sp_label_t> m_label; // The label list
+
+}; // class sp_pcontext : public Sql_alloc
+
+
+#endif /* _SP_PCONTEXT_H_ */
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
new file mode 100644
index 00000000000..a25cd9ee63e
--- /dev/null
+++ b/sql/sp_rcontext.cc
@@ -0,0 +1,245 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#if defined(WIN32) || defined(__WIN__)
+#undef SAFEMALLOC /* Problems with threads */
+#endif
+
+#include "mysql_priv.h"
+#include "mysql.h"
+#include "sp_head.h"
+#include "sp_rcontext.h"
+#include "sp_pcontext.h"
+
+sp_rcontext::sp_rcontext(uint fsize, uint hmax, uint cmax)
+ : m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0),
+ m_hfound(-1), m_ccount(0)
+{
+ m_frame= (Item **)sql_alloc(fsize * sizeof(Item*));
+ m_outs= (int *)sql_alloc(fsize * sizeof(int));
+ m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t));
+ m_hstack= (uint *)sql_alloc(hmax * sizeof(uint));
+ m_cstack= (sp_cursor **)sql_alloc(cmax * sizeof(sp_cursor *));
+ m_saved.empty();
+}
+
+void
+sp_rcontext::set_item_eval(uint idx, Item *i, enum_field_types type)
+{
+ extern Item *sp_eval_func_item(THD *thd, Item *it, enum_field_types type);
+
+ set_item(idx, sp_eval_func_item(current_thd, i, type));
+}
+
+int
+sp_rcontext::find_handler(uint sql_errno)
+{
+ if (m_hfound >= 0)
+ return 1; // Already got one
+
+ const char *sqlstate= mysql_errno_to_sqlstate(sql_errno);
+ int i= m_hcount, found= 0;
+
+ while (!found && i--)
+ {
+ sp_cond_type_t *cond= m_handler[i].cond;
+
+ switch (cond->type)
+ {
+ case sp_cond_type_t::number:
+ if (sql_errno == cond->mysqlerr)
+ found= 1;
+ break;
+ case sp_cond_type_t::state:
+ if (strcmp(sqlstate, cond->sqlstate) == 0)
+ found= 1;
+ break;
+ case sp_cond_type_t::warning:
+ if (sqlstate[0] == '0' && sqlstate[0] == '1')
+ found= 1;
+ break;
+ case sp_cond_type_t::notfound:
+ if (sqlstate[0] == '0' && sqlstate[0] == '2')
+ found= 1;
+ break;
+ case sp_cond_type_t::exception:
+ if (sqlstate[0] != '0' || sqlstate[0] > '2')
+ found= 1;
+ break;
+ }
+ }
+ if (found)
+ m_hfound= i;
+ return found;
+}
+
+void
+sp_rcontext::save_variables(uint fp)
+{
+ while (fp < m_count)
+ m_saved.push_front(m_frame[fp++]);
+}
+
+void
+sp_rcontext::restore_variables(uint fp)
+{
+ uint i= m_count;
+
+ while (i-- > fp)
+ m_frame[i]= m_saved.pop();
+}
+
+void
+sp_rcontext::push_cursor(LEX *lex)
+{
+ m_cstack[m_ccount++]= new sp_cursor(lex);
+}
+
+void
+sp_rcontext::pop_cursors(uint count)
+{
+ while (count--)
+ {
+ delete m_cstack[--m_ccount];
+ }
+}
+
+
+/*
+ *
+ * sp_cursor
+ *
+ */
+
+// We have split this in two to make it easy for sp_instr_copen
+// to reuse the sp_instr::exec_stmt() code.
+LEX *
+sp_cursor::pre_open(THD *thd)
+{
+ int res;
+
+ if (m_isopen)
+ {
+ send_error(thd, ER_SP_CURSOR_ALREADY_OPEN);
+ return NULL;
+ }
+
+ bzero((char *)&m_mem_root, sizeof(m_mem_root));
+ init_alloc_root(&m_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
+ if ((m_prot= new Protocol_cursor(thd, &m_mem_root)) == NULL)
+ return NULL;
+
+ m_oprot= thd->protocol; // Save the original protocol
+ thd->protocol= m_prot;
+
+ m_nseof= thd->net.no_send_eof;
+ thd->net.no_send_eof= TRUE;
+ return m_lex;
+}
+
+void
+sp_cursor::post_open(THD *thd, my_bool isopen)
+{
+ thd->net.no_send_eof= m_nseof; // Restore the originals
+ thd->protocol= m_oprot;
+ m_isopen= isopen;
+ m_current_row= m_prot->data;
+}
+
+int
+sp_cursor::close(THD *thd)
+{
+ if (! m_isopen)
+ {
+ send_error(thd, ER_SP_CURSOR_NOT_OPEN);
+ return -1;
+ }
+ destroy();
+ return 0;
+}
+
+void
+sp_cursor::destroy()
+{
+ delete m_prot;
+ m_prot= NULL;
+ free_root(&m_mem_root, MYF(0));
+ bzero((char *)&m_mem_root, sizeof(m_mem_root));
+ m_isopen= FALSE;
+}
+
+int
+sp_cursor::fetch(THD *thd, List<struct sp_pvar> *vars)
+{
+ List_iterator_fast<struct sp_pvar> li(*vars);
+ sp_pvar_t *pv;
+ MYSQL_ROW row;
+ uint fldcount;
+ MYSQL_FIELD *fields= m_prot->fields;
+
+ if (! m_isopen)
+ {
+ send_error(thd, ER_SP_CURSOR_NOT_OPEN);
+ return -1;
+ }
+
+ if (m_current_row == NULL)
+ {
+ send_error(thd, ER_SP_FETCH_NO_DATA);
+ return -1;
+ }
+
+ row= m_current_row->data;
+ for (fldcount= 0 ; (pv= li++) ; fldcount++)
+ {
+ Item *it;
+ const char *s;
+
+ if (fldcount >= m_prot->get_field_count())
+ {
+ send_error(thd, ER_SP_WRONG_NO_OF_FETCH_ARGS);
+ return -1;
+ }
+ s= row[fldcount];
+ switch (sp_map_result_type(pv->type))
+ {
+ case INT_RESULT:
+ it= new Item_int(s);
+ break;
+ case REAL_RESULT:
+ it= new Item_real(s, strlen(s));
+ break;
+ default:
+ {
+ uint len= strlen(s);
+ it= new Item_string(thd->strmake(s, len), len, thd->db_charset);
+ break;
+ }
+ }
+ thd->spcont->set_item(pv->offset, it);
+ }
+ if (fldcount < m_prot->get_field_count())
+ {
+ send_error(thd, ER_SP_WRONG_NO_OF_FETCH_ARGS);
+ return -1;
+ }
+ m_current_row= m_current_row->next;
+ return 0;
+}
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
new file mode 100644
index 00000000000..6526ed58575
--- /dev/null
+++ b/sql/sp_rcontext.h
@@ -0,0 +1,252 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_RCONTEXT_H_
+#define _SP_RCONTEXT_H_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+struct sp_cond_type;
+struct sp_cursor;
+struct sp_pvar;
+
+#define SP_HANDLER_NONE 0
+#define SP_HANDLER_EXIT 1
+#define SP_HANDLER_CONTINUE 2
+#define SP_HANDLER_UNDO 3
+
+typedef struct
+{
+ struct sp_cond_type *cond;
+ uint handler; // Location of handler
+ int type;
+ uint foffset; // Frame offset for the handlers declare level
+} sp_handler_t;
+
+class sp_rcontext : public Sql_alloc
+{
+ sp_rcontext(const sp_rcontext &); /* Prevent use of these */
+ void operator=(sp_rcontext &);
+
+ public:
+
+ sp_rcontext(uint fsize, uint hmax, uint cmax);
+
+ ~sp_rcontext()
+ {
+ // Not needed?
+ //sql_element_free(m_frame);
+ //m_saved.empty();
+ }
+
+ inline void
+ push_item(Item *i)
+ {
+ if (m_count < m_fsize)
+ m_frame[m_count++] = i;
+ }
+
+ inline void
+ set_item(uint idx, Item *i)
+ {
+ if (idx < m_count)
+ m_frame[idx] = i;
+ }
+
+ void
+ set_item_eval(uint idx, Item *i, enum_field_types type);
+
+ inline Item *
+ get_item(uint idx)
+ {
+ return m_frame[idx];
+ }
+
+ inline void
+ set_oindex(uint idx, int oidx)
+ {
+ m_outs[idx] = oidx;
+ }
+
+ inline int
+ get_oindex(uint idx)
+ {
+ return m_outs[idx];
+ }
+
+ inline void
+ set_result(Item *it)
+ {
+ m_result= it;
+ }
+
+ inline Item *
+ get_result()
+ {
+ return m_result;
+ }
+
+ inline void
+ push_handler(struct sp_cond_type *cond, uint h, int type, uint f)
+ {
+ m_handler[m_hcount].cond= cond;
+ m_handler[m_hcount].handler= h;
+ m_handler[m_hcount].type= type;
+ m_handler[m_hcount].foffset= f;
+ m_hcount+= 1;
+ }
+
+ inline void
+ pop_handlers(uint count)
+ {
+ m_hcount-= count;
+ }
+
+ // Returns 1 if a handler was found, 0 otherwise.
+ int
+ find_handler(uint sql_errno);
+
+ // Returns handler type and sets *ip to location if one was found
+ inline int
+ found_handler(uint *ip, uint *fp)
+ {
+ if (m_hfound < 0)
+ return SP_HANDLER_NONE;
+ *ip= m_handler[m_hfound].handler;
+ *fp= m_handler[m_hfound].foffset;
+ return m_handler[m_hfound].type;
+ }
+
+ // Clears the handler find state
+ inline void
+ clear_handler()
+ {
+ m_hfound= -1;
+ }
+
+ inline void
+ push_hstack(uint h)
+ {
+ m_hstack[m_hsp++]= h;
+ }
+
+ inline uint
+ pop_hstack()
+ {
+ return m_hstack[--m_hsp];
+ }
+
+ // Save variables starting at fp and up
+ void
+ save_variables(uint fp);
+
+ // Restore variables down to fp
+ void
+ restore_variables(uint fp);
+
+ void
+ push_cursor(LEX *lex);
+
+ void
+ pop_cursors(uint count);
+
+ void
+ pop_all_cursors()
+ {
+ pop_cursors(m_ccount);
+ }
+
+ inline sp_cursor *
+ get_cursor(uint i)
+ {
+ return m_cstack[i];
+ }
+
+private:
+
+ uint m_count;
+ uint m_fsize;
+ Item **m_frame;
+ int *m_outs;
+
+ Item *m_result; // For FUNCTIONs
+
+ sp_handler_t *m_handler;
+ uint m_hcount;
+ uint *m_hstack;
+ uint m_hsp;
+ int m_hfound; // Set by find_handler; -1 if not found
+ List<Item> m_saved; // Saved variables
+
+ sp_cursor **m_cstack;
+ uint m_ccount;
+
+}; // class sp_rcontext : public Sql_alloc
+
+
+class sp_cursor : public Sql_alloc
+{
+public:
+
+ sp_cursor(LEX *lex)
+ : m_lex(lex), m_isopen(0), m_current_row(NULL)
+ {
+ /* Empty */
+ }
+
+ virtual ~sp_cursor()
+ {
+ destroy();
+ }
+
+ // We have split this in two to make it easy for sp_instr_copen
+ // to reuse the sp_instr::exec_stmt() code.
+ LEX *
+ pre_open(THD *thd);
+ void
+ post_open(THD *thd, my_bool isopen);
+
+ int
+ close(THD *thd);
+
+ inline my_bool
+ is_open()
+ {
+ return m_isopen;
+ }
+
+ int
+ fetch(THD *, List<struct sp_pvar> *vars);
+
+private:
+
+ MEM_ROOT m_mem_root; // My own mem_root
+ LEX *m_lex;
+ Protocol_cursor *m_prot;
+ my_bool m_isopen;
+ my_bool m_nseof; // Original no_send_eof
+ Protocol *m_oprot; // Original protcol
+ MYSQL_ROWS *m_current_row;
+
+ void
+ destroy();
+
+}; // class sp_cursor : public Sql_alloc
+
+#endif /* _SP_RCONTEXT_H_ */
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index c0f36f202f3..486d9df5393 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -783,6 +783,66 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh,
}
+/*
+ * This is like acl_getroot() above, but it doesn't check password,
+ * and we don't care about the user resources.
+ * Used to get access rights for SQL SECURITY DEFINER invokation of
+ * stored procedures.
+ */
+int acl_getroot_no_password(THD *thd)
+{
+ ulong user_access= NO_ACCESS;
+ int res= 1;
+ ACL_USER *acl_user= 0;
+ DBUG_ENTER("acl_getroot_no_password");
+
+ if (!initialized)
+ {
+ /*
+ here if mysqld's been started with --skip-grant-tables option.
+ */
+ thd->priv_user= (char *) ""; // privileges for
+ *thd->priv_host= '\0'; // the user are unknown
+ thd->master_access= ~NO_ACCESS; // everything is allowed
+ DBUG_RETURN(0);
+ }
+
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ /*
+ Find acl entry in user database.
+ This is specially tailored to suit the check we do for CALL of
+ a stored procedure; thd->user is set to what is actually a
+ priv_user, which can be ''.
+ */
+ for (uint i=0 ; i < acl_users.elements ; i++)
+ {
+ acl_user= dynamic_element(&acl_users,i,ACL_USER*);
+ if ((!acl_user->user && (!thd->user || !thd->user[0])) ||
+ (acl_user->user && strcmp(thd->user, acl_user->user) == 0))
+ {
+ if (compare_hostname(&acl_user->host, thd->host, thd->ip))
+ {
+ res= 0;
+ break;
+ }
+ }
+ }
+
+ if (acl_user)
+ {
+ thd->master_access= acl_user->access;
+ thd->priv_user= acl_user->user ? thd->user : (char *) "";
+
+ if (acl_user->host.hostname)
+ strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
+ else
+ *thd->priv_host= 0;
+ }
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ DBUG_RETURN(res);
+}
+
static byte* check_get_key(ACL_USER *buff,uint *length,
my_bool not_used __attribute__((unused)))
{
@@ -1192,7 +1252,6 @@ bool change_password(THD *thd, const char *host, const char *user,
acl_user->host.hostname ? acl_user->host.hostname : "",
new_password));
thd->clear_error();
- mysql_update_log.write(thd, buff, query_length);
Query_log_event qinfo(thd, buff, query_length, 0);
mysql_bin_log.write(&qinfo);
DBUG_RETURN(0);
@@ -1476,7 +1535,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
if (table->fields >= 31) /* From 4.0.0 we have more fields */
{
/* We write down SSL related ACL stuff */
- switch (thd->lex.ssl_type) {
+ switch (thd->lex->ssl_type) {
case SSL_TYPE_ANY:
table->field[24]->store("ANY",3, &my_charset_latin1);
table->field[25]->store("", 0, &my_charset_latin1);
@@ -1494,15 +1553,15 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
table->field[25]->store("", 0, &my_charset_latin1);
table->field[26]->store("", 0, &my_charset_latin1);
table->field[27]->store("", 0, &my_charset_latin1);
- if (thd->lex.ssl_cipher)
- table->field[25]->store(thd->lex.ssl_cipher,
- strlen(thd->lex.ssl_cipher), &my_charset_latin1);
- if (thd->lex.x509_issuer)
- table->field[26]->store(thd->lex.x509_issuer,
- strlen(thd->lex.x509_issuer), &my_charset_latin1);
- if (thd->lex.x509_subject)
- table->field[27]->store(thd->lex.x509_subject,
- strlen(thd->lex.x509_subject), &my_charset_latin1);
+ if (thd->lex->ssl_cipher)
+ table->field[25]->store(thd->lex->ssl_cipher,
+ strlen(thd->lex->ssl_cipher), &my_charset_latin1);
+ if (thd->lex->x509_issuer)
+ table->field[26]->store(thd->lex->x509_issuer,
+ strlen(thd->lex->x509_issuer), &my_charset_latin1);
+ if (thd->lex->x509_subject)
+ table->field[27]->store(thd->lex->x509_subject,
+ strlen(thd->lex->x509_subject), &my_charset_latin1);
break;
case SSL_TYPE_NOT_SPECIFIED:
break;
@@ -1514,7 +1573,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
break;
}
- USER_RESOURCES mqh = thd->lex.mqh;
+ USER_RESOURCES mqh = thd->lex->mqh;
if (mqh.bits & 1)
table->field[28]->store((longlong) mqh.questions);
if (mqh.bits & 2)
@@ -1555,19 +1614,19 @@ end:
acl_cache->clear(1); // Clear privilege cache
if (old_row_exists)
acl_update_user(combo.user.str, combo.host.str, password, password_len,
- thd->lex.ssl_type,
- thd->lex.ssl_cipher,
- thd->lex.x509_issuer,
- thd->lex.x509_subject,
- &thd->lex.mqh,
+ thd->lex->ssl_type,
+ thd->lex->ssl_cipher,
+ thd->lex->x509_issuer,
+ thd->lex->x509_subject,
+ &thd->lex->mqh,
rights);
else
acl_insert_user(combo.user.str, combo.host.str, password, password_len,
- thd->lex.ssl_type,
- thd->lex.ssl_cipher,
- thd->lex.x509_issuer,
- thd->lex.x509_subject,
- &thd->lex.mqh,
+ thd->lex->ssl_type,
+ thd->lex->ssl_cipher,
+ thd->lex->x509_issuer,
+ thd->lex->x509_subject,
+ &thd->lex->mqh,
rights);
}
table->file->index_end();
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 7b66a851d33..f17c9781e8c 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -135,6 +135,7 @@ ulong acl_get(const char *host, const char *ip,
const char *user, const char *db, my_bool db_is_pattern);
int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd,
uint passwd_len);
+int acl_getroot_no_password(THD *thd);
bool acl_check_host(const char *host, const char *ip);
bool check_change_password(THD *thd, const char *host, const char *user);
bool change_password(THD *thd, const char *host, const char *user,
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 0101037da85..82e84f8ce49 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -2198,7 +2198,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
DBUG_ENTER("setup_conds");
thd->set_query_id=1;
- thd->lex.current_select->cond_count= 0;
+ thd->lex->current_select->cond_count= 0;
if (*conds)
{
thd->where="where clause";
@@ -2217,7 +2217,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
if (table->on_expr->fix_fields(thd, tables, &table->on_expr) ||
table->on_expr->check_cols(1))
DBUG_RETURN(1);
- thd->lex.current_select->cond_count++;
+ thd->lex->current_select->cond_count++;
/*
If it's a normal join or a LEFT JOIN which can be optimized away
@@ -2269,7 +2269,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
}
}
cond_and->used_tables_cache= t1->map | t2->map;
- thd->lex.current_select->cond_count+=cond_and->list.elements;
+ thd->lex->current_select->cond_count+=cond_and->list.elements;
if (!table->outer_join) // Not left join
{
if (!(*conds=and_conds(*conds, cond_and)))
@@ -2477,7 +2477,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
/* Kill delayed insert threads */
if (in_use->system_thread && ! in_use->killed)
{
- in_use->killed=1;
+ in_use->killed= THD::KILL_CONNECTION;
pthread_mutex_lock(&in_use->mysys_var->mutex);
if (in_use->mysys_var->current_cond)
{
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 71dd7123d98..4a517a04495 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -761,7 +761,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
uint8 tables_type= 0;
if ((local_tables= is_cacheable(thd, thd->query_length,
- thd->query, &thd->lex, tables_used,
+ thd->query, thd->lex, tables_used,
&tables_type)))
{
NET *net= &thd->net;
@@ -913,7 +913,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
/* Check that we haven't forgot to reset the query cache variables */
DBUG_ASSERT(thd->net.query_cache_query == 0);
- if (!thd->lex.safe_to_cache_query)
+ if (!thd->lex->safe_to_cache_query)
{
DBUG_PRINT("qcache", ("SELECT is non-cacheable"));
goto err;
@@ -921,11 +921,16 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
/*
Test if the query is a SELECT
- (pre-space is removed in dispatch_command)
+ (pre-space is removed in dispatch_command).
+
+ First '/' looks like comment before command it is not
+ frequently appeared in real lihe, consequently we can
+ check all such queries, too.
*/
- if (my_toupper(system_charset_info, sql[0]) != 'S' ||
- my_toupper(system_charset_info, sql[1]) != 'E' ||
- my_toupper(system_charset_info,sql[2]) !='L')
+ if ((my_toupper(system_charset_info, sql[0]) != 'S' ||
+ my_toupper(system_charset_info, sql[1]) != 'E' ||
+ my_toupper(system_charset_info,sql[2]) !='L') &&
+ sql[0] != '/')
{
DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached"));
goto err;
@@ -1018,7 +1023,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
table_list.db, table_list.alias));
refused++; // This is actually a hit
STRUCT_UNLOCK(&structure_guard_mutex);
- thd->lex.safe_to_cache_query=0; // Don't try to cache this
+ thd->lex->safe_to_cache_query=0; // Don't try to cache this
BLOCK_UNLOCK_RD(query_block);
DBUG_RETURN(-1); // Privilege error
}
@@ -1027,7 +1032,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
DBUG_PRINT("qcache", ("Need to check column privileges for %s.%s",
table_list.db, table_list.alias));
BLOCK_UNLOCK_RD(query_block);
- thd->lex.safe_to_cache_query= 0; // Don't try to cache this
+ thd->lex->safe_to_cache_query= 0; // Don't try to cache this
goto err_unlock; // Parse query
}
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
@@ -1038,7 +1043,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
table_list.db, table_list.alias));
BLOCK_UNLOCK_RD(query_block);
- thd->lex.safe_to_cache_query= 0; // Don't try to cache this
+ thd->lex->safe_to_cache_query= 0; // Don't try to cache this
goto err_unlock; // Parse query
}
else
@@ -2615,7 +2620,7 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
{
DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
tables_used->db, tables_used->alias));
- thd->lex.safe_to_cache_query= 0; // Don't try to cache this
+ thd->lex->safe_to_cache_query= 0; // Don't try to cache this
DBUG_RETURN(1);
}
}
@@ -3055,7 +3060,7 @@ void Query_cache::wreck(uint line, const char *message)
DBUG_PRINT("warning", ("%5d QUERY CACHE WRECK => DISABLED",line));
DBUG_PRINT("warning", ("=================================="));
if (thd)
- thd->killed = 1;
+ thd->killed= THD::KILL_CONNECTION;
cache_dump();
/* check_integrity(0); */ /* Can't call it here because of locks */
bins_dump();
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 5dee75a5670..d1ebcdbd15e 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -36,6 +36,9 @@
#endif
#include <mysys_err.h>
+#include <sp_rcontext.h>
+#include <sp_cache.h>
+
/*
The following is used to initialise Table_ident with a internal
table name
@@ -86,24 +89,25 @@ extern "C" void free_user_var(user_var_entry *entry)
THD::THD():user_time(0), is_fatal_error(0),
last_insert_id_used(0),
insert_id_used(0), rand_used(0), in_lock_tables(0),
- global_read_lock(0), bootstrap(0)
+ global_read_lock(0), bootstrap(0), spcont(NULL)
{
- host=user=priv_user=db=query=ip=0;
+ host= user= priv_user= db= ip= 0;
host_or_ip= "connecting host";
- locked=killed=some_tables_deleted=no_errors=password= 0;
+ locked=some_tables_deleted=no_errors=password= 0;
query_start_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
- db_length=query_length=col_access=0;
+ killed= NOT_KILLED;
+ db_length= col_access=0;
query_error= tmp_table_used= 0;
next_insert_id=last_insert_id=0;
open_tables= temporary_tables= handler_tables= derived_tables= 0;
- handler_items=0;
tmp_table=0;
lock=locked_tables=0;
used_tables=0;
- cuted_fields= sent_row_count= current_stmt_id= 0L;
+ cuted_fields= sent_row_count= 0L;
+ statement_id_counter= 0UL;
// Must be reset to handle error with THD's created for init of mysqld
- lex.current_select= 0;
+ lex->current_select= 0;
start_time=(time_t) 0;
current_linfo = 0;
slave_thread = 0;
@@ -137,7 +141,6 @@ THD::THD():user_time(0), is_fatal_error(0),
server_id = ::server_id;
slave_net = 0;
command=COM_CONNECT;
- set_query_id=1;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
db_access=NO_ACCESS;
#endif
@@ -145,16 +148,18 @@ THD::THD():user_time(0), is_fatal_error(0),
*scramble= '\0';
init();
+ init_sql_alloc(&mem_root, // must be after init()
+ variables.query_alloc_block_size,
+ variables.query_prealloc_size);
/* Initialize sub structures */
- bzero((char*) &mem_root,sizeof(mem_root));
- bzero((char*) &transaction.mem_root,sizeof(transaction.mem_root));
- bzero((char*) &con_root,sizeof(con_root));
- bzero((char*) &warn_root,sizeof(warn_root));
init_alloc_root(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
user_connect=(USER_CONN *)0;
- hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
+ hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_var_key,
- (hash_free_key) free_user_var,0);
+ (hash_free_key) free_user_var, 0);
+
+ sp_proc_cache= NULL;
+ sp_func_cache= NULL;
/* For user vars replication*/
if (opt_bin_log)
@@ -184,11 +189,13 @@ THD::THD():user_time(0), is_fatal_error(0),
if (open_cached_file(&transaction.trans_log,
mysql_tmpdir, LOG_PREFIX, binlog_cache_size,
MYF(MY_WME)))
- killed=1;
+ killed= KILL_CONNECTION;
transaction.trans_log.end_of_file= max_binlog_cache_size;
}
#endif
-
+ init_sql_alloc(&transaction.mem_root,
+ variables.trans_alloc_block_size,
+ variables.trans_prealloc_size);
/*
We need good random number initialization for new thread
Just coping global one will not work
@@ -232,22 +239,6 @@ void THD::init(void)
/*
- Init THD for query processing
-
- This has to be called once before we call mysql_parse()
-*/
-
-void THD::init_for_queries()
-{
- init_sql_alloc(&mem_root, variables.query_alloc_block_size,
- variables.query_prealloc_size);
- init_sql_alloc(&transaction.mem_root,
- variables.trans_alloc_block_size,
- variables.trans_prealloc_size);
-}
-
-
-/*
Do what's needed when one invokes change user
SYNOPSIS
@@ -263,9 +254,11 @@ void THD::change_user(void)
cleanup();
cleanup_done= 0;
init();
- hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
+ hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_var_key,
(hash_free_key) free_user_var, 0);
+ sp_cache_clear(&sp_proc_cache);
+ sp_cache_clear(&sp_func_cache);
}
@@ -292,6 +285,8 @@ void THD::cleanup(void)
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)
@@ -301,6 +296,7 @@ void THD::cleanup(void)
pthread_mutex_unlock(&LOCK_user_locks);
ull= 0;
}
+
cleanup_done=1;
DBUG_VOID_RETURN;
}
@@ -332,6 +328,9 @@ THD::~THD()
}
#endif
+ 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);
@@ -339,27 +338,25 @@ THD::~THD()
safeFree(user);
safeFree(db);
safeFree(ip);
- free_root(&mem_root,MYF(0));
- free_root(&con_root,MYF(0));
- free_root(&warn_root,MYF(0));
- free_root(&transaction.mem_root,MYF(0));
- mysys_var=0; // Safety (shouldn't be needed)
+ free_root(&warn_root, MYF(0));
+ free_root(&transaction.mem_root, MYF(0));
+ mysys_var= 0; // Safety (shouldn't be needed)
pthread_mutex_destroy(&LOCK_delete);
#ifndef DBUG_OFF
- dbug_sentry = THD_SENTRY_GONE;
+ dbug_sentry= THD_SENTRY_GONE;
#endif
DBUG_VOID_RETURN;
}
-void THD::awake(bool prepare_to_die)
+void THD::awake(THD::killed_state state_to_set)
{
THD_CHECK_SENTRY(this);
safe_mutex_assert_owner(&LOCK_delete);
- if (prepare_to_die)
- killed = 1;
- thr_alarm_kill(real_id);
+ killed= state_to_set;
+ if (state_to_set != THD::KILL_QUERY)
+ thr_alarm_kill(real_id);
#ifdef SIGNAL_WITH_VIO_CLOSE
close_active_vio();
#endif
@@ -534,7 +531,7 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
{
my_error(EE_OUTOFMEMORY, MYF(ME_BELL),
ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1);
- killed= 1;
+ killed= KILL_CONNECTION;
return 0;
}
@@ -559,8 +556,8 @@ int THD::send_explain_fields(select_result *result)
item->maybe_null=1;
field_list.push_back(item=new Item_empty_string("key",NAME_LEN));
item->maybe_null=1;
- field_list.push_back(item=new Item_return_int("key_len",3,
- MYSQL_TYPE_LONGLONG));
+ field_list.push_back(item=new Item_empty_string("key_len",
+ NAME_LEN*MAX_KEY));
item->maybe_null=1;
field_list.push_back(item=new Item_empty_string("ref",
NAME_LEN*MAX_REF_PARTS));
@@ -1173,30 +1170,52 @@ bool select_exists_subselect::send_data(List<Item> &items)
int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
List_iterator_fast<Item> li(list);
- List_iterator_fast<LEX_STRING> gl(var_list);
+ List_iterator_fast<my_var> gl(var_list);
Item *item;
- LEX_STRING *ls;
+
+ local_vars.empty(); // Clear list if SP
+ unit= u;
+ row_count= 0;
+
if (var_list.elements != list.elements)
{
my_error(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, MYF(0));
return 1;
}
- unit=u;
while ((item=li++))
{
- ls= gl++;
- Item_func_set_user_var *xx = new Item_func_set_user_var(*ls,item);
- xx->fix_fields(thd,(TABLE_LIST*) thd->lex.select_lex.table_list.first,&item);
- xx->fix_length_and_dec();
- vars.push_back(xx);
+ my_var *mv= gl++;
+ if (mv->local)
+ (void)local_vars.push_back(new Item_splocal(mv->s, mv->offset));
+ else
+ {
+ Item_func_set_user_var *xx = new Item_func_set_user_var(mv->s, item);
+ xx->fix_fields(thd, (TABLE_LIST*) thd->lex->select_lex.table_list.first,
+ &item);
+ xx->fix_length_and_dec();
+ vars.push_back(xx);
+ }
}
return 0;
}
+
+
bool select_dumpvar::send_data(List<Item> &items)
{
List_iterator_fast<Item_func_set_user_var> li(vars);
+ List_iterator_fast<Item_splocal> var_li(local_vars);
+ List_iterator_fast<my_var> my_li(var_list);
+ List_iterator_fast<Item> it(items);
Item_func_set_user_var *xx;
+ Item_splocal *yy;
+ Item *item;
+ my_var *zz;
DBUG_ENTER("send_data");
+ if (unit->offset_limit_cnt)
+ { // using limit offset,count
+ unit->offset_limit_cnt--;
+ DBUG_RETURN(0);
+ }
if (unit->offset_limit_cnt)
{ // Using limit offset,count
@@ -1208,26 +1227,33 @@ bool select_dumpvar::send_data(List<Item> &items)
my_error(ER_TOO_MANY_ROWS, MYF(0));
DBUG_RETURN(1);
}
- while ((xx=li++))
+ while ((zz=my_li++) && (item=it++))
{
- xx->check();
- xx->update();
+ if (zz->local)
+ {
+ if ((yy=var_li++))
+ {
+ thd->spcont->set_item_eval(yy->get_offset(), item, zz->type);
+ }
+ }
+ else
+ {
+ if ((xx=li++))
+ {
+ xx->check();
+ xx->update();
+ }
+ }
}
DBUG_RETURN(0);
}
bool select_dumpvar::send_eof()
{
- if (row_count)
- {
- ::send_ok(thd,row_count);
- return 0;
- }
- else
- {
- my_error(ER_EMPTY_QUERY,MYF(0));
- return 1;
- }
+ if (! row_count)
+ send_warning(thd, ER_SP_FETCH_NO_DATA);
+ ::send_ok(thd,row_count);
+ return 0;
}
/****************************************************************************
@@ -1240,3 +1266,67 @@ void TMP_TABLE_PARAM::init()
group_parts= group_length= group_null_parts= 0;
quick_group= 1;
}
+
+/****************************************************************************
+ Statement functions
+****************************************************************************/
+
+Statement::Statement(THD *thd)
+ :id(++thd->statement_id_counter),
+ query_id(0), /* initialized later */
+ set_query_id(1),
+ allow_sum_func(0), /* initialized later */
+ command(COM_SLEEP), /* initialized later */
+ lex(&main_lex),
+ query(0), /* these two are set */
+ query_length(0), /* in alloc_query() */
+ free_list(0)
+{
+ init_sql_alloc(&mem_root,
+ thd->variables.query_alloc_block_size,
+ thd->variables.query_prealloc_size);
+}
+
+
+Statement::Statement()
+ :id(0),
+ query_id(0),
+ set_query_id(1),
+ allow_sum_func(0),
+ command(COM_SLEEP),
+ lex(&main_lex),
+ query(0),
+ query_length(0),
+ free_list(0)
+{
+ bzero((char *) &mem_root, sizeof(mem_root));
+}
+
+
+Statement::~Statement()
+{
+ free_root(&mem_root, MYF(0));
+}
+
+
+C_MODE_START
+
+static byte *
+get_statement_id_as_hash_key(const byte *record, uint *key_length,
+ my_bool not_used __attribute__((unused)))
+{
+ const Statement *statement= (const Statement *) record;
+ *key_length= sizeof(statement->id);
+ return (byte *) &((const Statement *) statement)->id;
+}
+
+C_MODE_END
+
+
+Statement_map::Statement_map()
+{
+ enum { START_HASH_SIZE = 16 };
+ hash_init(&st_hash, default_charset_info, START_HASH_SIZE, 0, 0,
+ get_statement_id_as_hash_key, (hash_free_key) 0, MYF(0));
+}
+
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 5479a3cecb6..2333a9ab369 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -26,6 +26,8 @@
class Query_log_event;
class Load_log_event;
class Slave_log_event;
+class sp_rcontext;
+class sp_cache;
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY };
@@ -428,12 +430,130 @@ struct system_variables
};
void free_tmp_table(THD *thd, TABLE *entry);
+
+
+/*
+ State of a single command executed against this connection.
+ One connection can contain a lot of simultaneously running statements,
+ some of which could be:
+ - prepared, that is, contain placeholders,
+ - opened as cursors. We maintain 1 to 1 relationship between
+ statement and cursor - if user wants to create another cursor for his
+ query, we create another statement for it.
+ To perform some action with statement we reset THD part to the state of
+ that statement, do the action, and then save back modified state from THD
+ to the statement. It will be changed in near future, and Statement will
+ be used explicitly.
+*/
+
+class Statement
+{
+public:
+ /* FIXME: must be private */
+ LEX main_lex;
+public:
+ /*
+ Uniquely identifies each statement object in thread scope; change during
+ statement lifetime.
+ */
+ ulong id;
+
+ /*
+ Id of current query. Statement can be reused to execute several queries.
+ query_id is global in context of the whole MySQL server.
+ Id is automatically generated from mutex-protected counter.
+ It's used in handler code for various purposes: to check which columns
+ from table are necessary for this select, to check if it's necessary to
+ update auto-updatable fields (like auto_increment and timestamp).
+ */
+ ulong query_id;
+ /*
+ - if set_query_id == 1, we set field->query_id for all fields. In that case
+ field list can not contain duplicates.
+ */
+ bool set_query_id;
+ /*
+ This variable is used in post-parse stage to declare that sum-functions,
+ or functions which have sense only if GROUP BY is present, are allowed.
+ For example in queries
+ SELECT MIN(i) FROM foo
+ SELECT GROUP_CONCAT(a, b, MIN(i)) FROM ... GROUP BY ...
+ MIN(i) have no sense.
+ Though it's grammar-related issue, it's hard to catch it out during the
+ parse stage because GROUP BY clause goes in the end of query. This
+ variable is mainly used in setup_fields/fix_fields.
+ See item_sum.cc for details.
+ */
+ bool allow_sum_func;
+ /*
+ Type of current query: COM_PREPARE, COM_QUERY, etc. Set from the
+ first byte of the incoming packet in do_command()
+ */
+ enum enum_server_command command;
+
+ LEX *lex; // parse tree descriptor
+ /*
+ Points to the query associated with this statement. It's const, but
+ we need to declare it char * because all table handlers are written
+ in C and need to point to it.
+ */
+ char *query;
+ uint32 query_length; // current query length
+ /*
+ List of items created in the parser for this query. Every item puts
+ itself to the list on creation (see Item::Item() for details))
+ */
+ Item *free_list;
+ MEM_ROOT mem_root;
+
+protected:
+ /*
+ This constructor is called when statement is a subobject of THD:
+ some variables are initialized in THD::init due to locking problems
+ */
+ Statement();
+public:
+ Statement(THD *thd);
+ virtual ~Statement();
+};
+
+
+/*
+ Used to seek all existing statements in the connection
+ Not responsible for statements memory.
+*/
+
+class Statement_map
+{
+public:
+ Statement_map();
+
+ int insert(Statement *statement)
+ {
+ return my_hash_insert(&st_hash, (byte *) statement);
+ }
+ Statement *seek(ulong id)
+ {
+ return (Statement *) hash_search(&st_hash, (byte *) &id, sizeof(id));
+ }
+ void erase(Statement *statement)
+ {
+ hash_delete(&st_hash, (byte *) statement);
+ }
+
+ ~Statement_map() { hash_free(&st_hash); }
+private:
+ HASH st_hash;
+};
+
+
/*
For each client connection we create a separate thread with THD serving as
a thread/connection descriptor
*/
-class THD :public ilink
+class THD :public ilink,
+ public Statement
{
public:
#ifdef EMBEDDED_LIBRARY
@@ -446,9 +566,6 @@ public:
ulong extra_length;
#endif
NET net; // client connection descriptor
- LEX lex; // parse tree descriptor
- MEM_ROOT mem_root; // 1 command-life memory pool
- MEM_ROOT con_root; // connection-life memory
MEM_ROOT warn_root; // For warnings and errors
Protocol *protocol; // Current protocol
Protocol_simple protocol_simple; // Normal protocol
@@ -461,7 +578,6 @@ public:
struct system_variables variables; // Changeable local variables
pthread_mutex_t LOCK_delete; // Locked before thd is deleted
- char *query; // Points to the current query,
/*
A pointer to the stack frame of handle_one_connection(),
which is called first in the thread for handling a client
@@ -515,7 +631,6 @@ public:
uint dbug_sentry; // watch out for memory corruption
#endif
struct st_my_thread_var *mysys_var;
- enum enum_server_command command;
uint32 server_id;
uint32 file_id; // for LOAD DATA INFILE
/*
@@ -548,7 +663,6 @@ public:
free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
}
} transaction;
- Item *free_list, *handler_items;
Field *dupp_field;
#ifndef __WIN__
sigset_t signals,block_signals;
@@ -579,18 +693,25 @@ public:
USER_CONN *user_connect;
CHARSET_INFO *db_charset;
List<TABLE> temporary_tables_should_be_free; // list of temporary tables
+ /*
+ FIXME: this, and some other variables like 'count_cuted_fields'
+ maybe should be statement/cursor local, that is, moved to Statement
+ class. With current implementation warnings produced in each prepared
+ statement/ cursor settle here.
+ */
List <MYSQL_ERROR> warn_list;
uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
uint total_warn_count;
- ulong query_id, warn_id, version, options, thread_id, col_access;
- ulong current_stmt_id;
+ ulong warn_id, version, options, thread_id, col_access;
+
+ /* Statement id is thread-wide. This counter is used to generate ids */
+ ulong statement_id_counter;
ulong rand_saved_seed1, rand_saved_seed2;
ulong row_count; // Row counter, mainly for errors and warnings
long dbug_thread_id;
pthread_t real_id;
uint current_tablenr,tmp_table;
uint server_status,open_options,system_thread;
- uint32 query_length;
uint32 db_length;
uint select_number; //number of select (used for EXPLAIN)
/* variables.transaction_isolation is reset to this after each commit */
@@ -603,17 +724,32 @@ public:
char scramble[SCRAMBLE_LENGTH+1];
bool slave_thread;
- bool set_query_id,locked,some_tables_deleted;
+ bool locked, some_tables_deleted;
bool last_cuted_field;
- bool no_errors, allow_sum_func, password, is_fatal_error;
+ bool no_errors, password, is_fatal_error;
bool query_start_used,last_insert_id_used,insert_id_used,rand_used;
bool in_lock_tables,global_read_lock;
bool query_error, bootstrap, cleanup_done;
- bool volatile killed;
+
+ enum killed_state { NOT_KILLED=0, KILL_CONNECTION=ER_SERVER_SHUTDOWN, KILL_QUERY=ER_QUERY_INTERRUPTED };
+ killed_state volatile killed;
+ inline int killed_errno() const
+ {
+ return killed;
+ }
+ inline void send_kill_message() const
+ {
+ my_error(killed_errno(), MYF(0));
+ }
+
bool tmp_table_used;
bool charset_is_system_charset, charset_is_collation_connection;
bool slow_command;
+ sp_rcontext *spcont; // SP runtime context
+ sp_cache *sp_proc_cache;
+ sp_cache *sp_func_cache;
+
/*
If we do a purge of binary logs, log index info of the threads
that are currently reading it needs to be adjusted. To do that
@@ -633,7 +769,6 @@ public:
void init(void);
void change_user(void);
- void init_for_queries();
void cleanup(void);
bool store_globals();
#ifdef SIGNAL_WITH_VIO_CLOSE
@@ -651,7 +786,7 @@ public:
}
void close_active_vio();
#endif
- void awake(bool prepare_to_die);
+ void awake(THD::killed_state state_to_set);
inline const char* enter_cond(pthread_cond_t *cond, pthread_mutex_t* mutex,
const char* msg)
{
@@ -1077,8 +1212,13 @@ class user_var_entry
DTCollation collation;
};
-
-/* Class for unique (removing of duplicates) */
+/*
+ Unique -- class for unique (removing of duplicates).
+ Puts all values to the TREE. If the tree becomes too big,
+ it's dumped to the file. User can request sorted values, or
+ just iterate through them. In the last case tree merging is performed in
+ memory simultaneously with iteration, so it should be ~2-3x faster.
+ */
class Unique :public Sql_alloc
{
@@ -1092,10 +1232,10 @@ class Unique :public Sql_alloc
public:
ulong elements;
- Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
+ Unique(qsort_cmp2 comp_func, void *comp_func_fixed_arg,
uint size_arg, ulong max_in_memory_size_arg);
~Unique();
- inline bool unique_add(gptr ptr)
+ inline bool unique_add(void *ptr)
{
if (tree.elements_in_tree > max_elements && flush())
return 1;
@@ -1104,6 +1244,9 @@ public:
bool get(TABLE *table);
+ void reset();
+ bool walk(tree_walk_action action, void *walk_action_arg);
+
friend int unique_write_to_file(gptr key, element_count count, Unique *unique);
friend int unique_write_to_ptrs(gptr key, element_count count, Unique *unique);
};
@@ -1159,13 +1302,25 @@ public:
bool send_eof();
};
+class my_var : public Sql_alloc {
+public:
+ LEX_STRING s;
+ bool local;
+ uint offset;
+ enum_field_types type;
+ my_var (LEX_STRING& j, bool i, uint o, enum_field_types t)
+ :s(j), local(i), offset(o), type(t)
+ {}
+ ~my_var() {}
+};
class select_dumpvar :public select_result {
ha_rows row_count;
public:
- List<LEX_STRING> var_list;
+ List<my_var> var_list;
List<Item_func_set_user_var> vars;
- select_dumpvar(void) { var_list.empty(); vars.empty(); row_count=0;}
+ List<Item_splocal> local_vars;
+ select_dumpvar(void) { var_list.empty(); local_vars.empty(); vars.empty(); row_count=0;}
~select_dumpvar() {}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_fields(List<Item> &list, uint flag) {return 0;}
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 70b1d1d0d3a..2d636c51dd4 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -241,7 +241,6 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
query= thd->query;
query_length= thd->query_length;
}
- mysql_update_log.write(thd, query, query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, query, query_length, 0);
@@ -294,7 +293,6 @@ int mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
thd->variables.collation_database= thd->db_charset;
}
- mysql_update_log.write(thd,thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -381,7 +379,6 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
query=thd->query;
query_length=thd->query_length;
}
- mysql_update_log.write(thd, query, query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, query, query_length, 0);
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 4ab0cfa20c1..03f952f3ff5 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -38,18 +38,18 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
bool transactional_table, log_delayed, safe_update, const_cond;
ha_rows deleted;
TABLE_LIST *delete_table_list= (TABLE_LIST*)
- thd->lex.select_lex.table_list.first;
+ thd->lex->select_lex.table_list.first;
DBUG_ENTER("mysql_delete");
if ((open_and_lock_tables(thd, table_list)))
DBUG_RETURN(-1);
- fix_tables_pointers(thd->lex.all_selects_list);
+ fix_tables_pointers(thd->lex->all_selects_list);
table= table_list->table;
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
thd->proc_info="init";
table->map=1;
if (setup_conds(thd, delete_table_list, &conds) ||
- setup_ftfuncs(&thd->lex.select_lex))
+ setup_ftfuncs(&thd->lex->select_lex))
DBUG_RETURN(-1);
if (find_real_table_in_list(table_list->next,
table_list->db, table_list->real_name))
@@ -66,8 +66,8 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
DBUG_RETURN(1);
}
- if (thd->lex.duplicates == DUP_IGNORE)
- thd->lex.select_lex.no_error= 1;
+ if (thd->lex->duplicates == DUP_IGNORE)
+ thd->lex->select_lex.no_error= 1;
/* Test if the user wants to delete all rows */
if (!using_limit && const_cond && (!conds || conds->val_int()) &&
@@ -96,7 +96,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
if ((select && select->check_quick(thd, safe_update, limit)) || !limit)
{
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
send_ok(thd,0L);
DBUG_RETURN(0); // Nothing to delete
}
@@ -108,7 +108,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
if (safe_update && !using_limit)
{
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
send_error(thd,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
DBUG_RETURN(1);
}
@@ -130,8 +130,8 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
MYF(MY_FAE | MY_ZEROFILL));
- if (thd->lex.select_lex.setup_ref_array(thd, 0) ||
- setup_order(thd, thd->lex.select_lex.ref_pointer_array, &tables,
+ if (thd->lex->select_lex.setup_ref_array(thd, 0) ||
+ setup_order(thd, thd->lex->select_lex.ref_pointer_array, &tables,
fields, all_fields, order) ||
!(sortorder=make_unireg_sortorder(order, &length)) ||
(table->sort.found_records = filesort(thd, table, sortorder, length,
@@ -140,7 +140,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
== HA_POS_ERROR)
{
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
DBUG_RETURN(-1); // This will force out message
}
/*
@@ -153,7 +153,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
init_read_record(&info,thd,table,select,1,1);
deleted=0L;
- init_ftfuncs(thd, &thd->lex.select_lex, 1);
+ init_ftfuncs(thd, &thd->lex->select_lex, 1);
thd->proc_info="updating";
while (!(error=info.read_record(&info)) && !thd->killed &&
!thd->net.report_error)
@@ -208,7 +208,6 @@ cleanup:
log_delayed= (transactional_table || table->tmp_table);
if (deleted && (error <= 0 || !transactional_table))
{
- mysql_update_log.write(thd,thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
if (error <= 0)
@@ -233,9 +232,9 @@ cleanup:
thd->lock=0;
}
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
if (error >= 0 || thd->net.report_error)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN: 0);
+ send_error(thd,thd->killed_errno());
else
{
send_ok(thd,deleted);
@@ -323,7 +322,7 @@ multi_delete::initialize_tables(JOIN *join)
table->file->ref_length,
MEM_STRIP_BUF_SIZE);
}
- init_ftfuncs(thd, thd->lex.current_select, 1);
+ init_ftfuncs(thd, thd->lex->current_select, 1);
DBUG_RETURN(thd->is_fatal_error != 0);
}
@@ -518,7 +517,6 @@ bool multi_delete::send_eof()
*/
if (deleted && (error <= 0 || normal_tables))
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
if (error <= 0)
@@ -631,7 +629,6 @@ end:
{
if (!error)
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index 81b98358486..c94d5e52bcf 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -185,7 +185,7 @@ my_bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
DBUG_RETURN(1);
MYSQL_ERROR *err;
- SELECT_LEX *sel= &thd->lex.select_lex;
+ SELECT_LEX *sel= &thd->lex->select_lex;
ha_rows offset= sel->offset_limit, limit= sel->select_limit;
Protocol *protocol=thd->protocol;
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 46ff9770893..88585db34ce 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -23,7 +23,7 @@
static int check_null_fields(THD *thd,TABLE *entry);
static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list);
static int write_delayed(THD *thd,TABLE *table, enum_duplicates dup,
- char *query, uint query_length, int log_on);
+ char *query, uint query_length, bool log_on);
static void end_delayed_insert(THD *thd);
extern "C" pthread_handler_decl(handle_delayed_insert,arg);
static void unlink_blobs(register TABLE *table);
@@ -38,9 +38,6 @@ static void unlink_blobs(register TABLE *table);
#define my_safe_afree(ptr, size, min_length) if (size > min_length) my_free(ptr,MYF(0))
#endif
-#define DELAYED_LOG_UPDATE 1
-#define DELAYED_LOG_BIN 2
-
/*
Check if insert fields are correct
Updates table->time_stamp to point to timestamp field or 0, depending on
@@ -118,8 +115,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
By default, both logs are enabled (this won't cause problems if the server
runs without --log-update or --log-bin).
*/
- int log_on= DELAYED_LOG_UPDATE | DELAYED_LOG_BIN ;
-
+ bool log_on= (thd->options & OPTION_BIN_LOG) || (!(thd->master_access & SUPER_ACL));
bool transactional_table, log_delayed, bulk_insert;
uint value_count;
ulong counter = 1;
@@ -131,18 +127,9 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
char *query=thd->query;
thr_lock_type lock_type = table_list->lock_type;
TABLE_LIST *insert_table_list= (TABLE_LIST*)
- thd->lex.select_lex.table_list.first;
+ thd->lex->select_lex.table_list.first;
DBUG_ENTER("mysql_insert");
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (thd->master_access & SUPER_ACL)
-#endif
- {
- if (!(thd->options & OPTION_UPDATE_LOG))
- log_on&= ~(int) DELAYED_LOG_UPDATE;
- if (!(thd->options & OPTION_BIN_LOG))
- log_on&= ~(int) DELAYED_LOG_BIN;
- }
/*
in safe mode or with skip-new change delayed insert to be regular
if we are told to replace duplicates, the insert cannot be concurrent
@@ -188,7 +175,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
res= open_and_lock_tables(thd, table_list);
if (res)
DBUG_RETURN(-1);
- fix_tables_pointers(thd->lex.all_selects_list);
+ fix_tables_pointers(thd->lex->all_selects_list);
table= table_list->table;
thd->proc_info="init";
@@ -377,7 +364,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
log_delayed= (transactional_table || table->tmp_table);
if ((info.copied || info.deleted) && (error <= 0 || !transactional_table))
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
if (error <= 0)
@@ -429,14 +415,14 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
(ulong) info.deleted, (ulong) thd->cuted_fields);
::send_ok(thd,info.copied+info.deleted,(ulonglong)id,buff);
}
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
table->insert_values=0;
DBUG_RETURN(0);
abort:
if (lock_type == TL_WRITE_DELAYED)
end_delayed_insert(thd);
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
table->insert_values=0;
DBUG_RETURN(-1);
}
@@ -605,13 +591,12 @@ public:
char *record,*query;
enum_duplicates dup;
time_t start_time;
- bool query_start_used,last_insert_id_used,insert_id_used;
- int log_query;
+ bool query_start_used,last_insert_id_used,insert_id_used, log_query;
ulonglong last_insert_id;
ulong time_stamp;
uint query_length;
- delayed_row(enum_duplicates dup_arg, int log_query_arg)
+ delayed_row(enum_duplicates dup_arg, bool log_query_arg)
:record(0),query(0),dup(dup_arg),log_query(log_query_arg) {}
~delayed_row()
{
@@ -644,7 +629,7 @@ public:
thd.current_tablenr=0;
thd.version=refresh_version;
thd.command=COM_DELAYED_INSERT;
- thd.lex.current_select= 0; /* for my_message_sql */
+ thd.lex->current_select= 0; /* for my_message_sql */
bzero((char*) &thd.net,sizeof(thd.net)); // Safety
thd.system_thread= SYSTEM_THREAD_DELAYED_INSERT;
@@ -918,7 +903,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd)
/* Put a question in queue */
static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic,
- char *query, uint query_length, int log_on)
+ char *query, uint query_length, bool log_on)
{
delayed_row *row=0;
delayed_insert *di=thd->di;
@@ -998,7 +983,7 @@ void kill_delayed_threads(void)
{
/* Ensure that the thread doesn't kill itself while we are looking at it */
pthread_mutex_lock(&tmp->mutex);
- tmp->thd.killed=1;
+ tmp->thd.killed= THD::KILL_CONNECTION;
if (tmp->thd.mysys_var)
{
pthread_mutex_lock(&tmp->thd.mysys_var->mutex);
@@ -1037,7 +1022,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
thd->thread_id=thread_id++;
thd->end_time();
threads.append(thd);
- thd->killed=abort_loop;
+ thd->killed=abort_loop ? THD::KILL_CONNECTION : THD::NOT_KILLED;
pthread_mutex_unlock(&LOCK_thread_count);
pthread_mutex_lock(&di->mutex);
@@ -1090,7 +1075,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
for (;;)
{
- if (thd->killed)
+ if (thd->killed == THD::KILL_CONNECTION)
{
uint lock_count;
/*
@@ -1138,7 +1123,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
break;
if (error == ETIME || error == ETIMEDOUT)
{
- thd->killed=1;
+ thd->killed= THD::KILL_CONNECTION;
break;
}
}
@@ -1157,7 +1142,9 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
/* request for new delayed insert */
if (!(thd->lock=mysql_lock_tables(thd,&di->table,1)))
{
- di->dead=thd->killed=1; // Fatal error
+ /* Fatal error */
+ di->dead= 1;
+ thd->killed= THD::KILL_CONNECTION;
}
pthread_cond_broadcast(&di->cond_client);
}
@@ -1165,7 +1152,9 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
{
if (di->handle_inserts())
{
- di->dead=thd->killed=1; // Some fatal error
+ /* Some fatal error */
+ di->dead= 1;
+ thd->killed= THD::KILL_CONNECTION;
}
}
di->status=0;
@@ -1192,7 +1181,8 @@ end:
close_thread_tables(thd); // Free the table
di->table=0;
- di->dead=thd->killed=1; // If error
+ di->dead= 1; // If error
+ thd->killed= THD::KILL_CONNECTION; // If error
pthread_cond_broadcast(&di->cond_client); // Safety
pthread_mutex_unlock(&di->mutex);
@@ -1261,7 +1251,7 @@ bool delayed_insert::handle_inserts(void)
max_rows=delayed_insert_limit;
if (thd.killed || table->version != refresh_version)
{
- thd.killed=1;
+ thd.killed= THD::KILL_CONNECTION;
max_rows= ~0; // Do as much as possible
}
@@ -1305,15 +1295,10 @@ bool delayed_insert::handle_inserts(void)
using_ignore=0;
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
}
- if (row->query)
+ if (row->query && row->log_query && using_bin_log)
{
- if (row->log_query & DELAYED_LOG_UPDATE)
- mysql_update_log.write(&thd,row->query, row->query_length);
- if (row->log_query & DELAYED_LOG_BIN && using_bin_log)
- {
- Query_log_event qinfo(&thd, row->query, row->query_length,0);
- mysql_bin_log.write(&qinfo);
- }
+ Query_log_event qinfo(&thd, row->query, row->query_length,0);
+ mysql_bin_log.write(&qinfo);
}
if (table->blob_fields)
free_delayed_insert_blobs(table);
@@ -1470,7 +1455,6 @@ void select_insert::send_error(uint errcode,const char *err)
{
if (last_insert_id)
thd->insert_id(last_insert_id); // For binary log
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
@@ -1511,7 +1495,6 @@ bool select_insert::send_eof()
if (last_insert_id)
thd->insert_id(last_insert_id); // For binary log
/* Write to binlog before commiting transaction */
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
if (!error)
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 4257df494bc..a67ee2385b9 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -21,6 +21,8 @@
#include "item_create.h"
#include <m_ctype.h>
#include <hash.h>
+#include "sp.h"
+#include "sp_head.h"
LEX_STRING tmp_table_alias= {(char*) "tmp-table",8};
@@ -115,9 +117,10 @@ void lex_free(void)
LEX *lex_start(THD *thd, uchar *buf,uint length)
{
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
lex->thd= thd;
lex->next_state=MY_LEX_START;
+ lex->buf= buf;
lex->end_of_query=(lex->ptr=buf)+length;
lex->yylineno = 1;
lex->select_lex.parsing_place= SELECT_LEX_NODE::NO_MATTER;
@@ -132,6 +135,14 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE);
lex->sql_command=SQLCOM_END;
lex->duplicates= DUP_ERROR;
+ lex->sphead= NULL;
+ lex->spcont= NULL;
+
+ extern byte *sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first);
+ hash_free(&lex->spfuns);
+ hash_init(&lex->spfuns, system_charset_info, 0, 0, 0,
+ sp_lex_spfuns_key, 0, 0);
+
return lex;
}
@@ -155,6 +166,17 @@ static int find_keyword(LEX *lex, uint len, bool function)
lex->yylval->symbol.length=len;
return symbol->tok;
}
+
+ LEX_STRING ls;
+ ls.str = (char *)tok; ls.length= len;
+ if (function && sp_function_exists(current_thd, &ls)) // QQ temp fix
+ {
+ lex->safe_to_cache_query= 0;
+ lex->yylval->lex_str.str= lex->thd->strmake((char*)lex->tok_start, len);
+ lex->yylval->lex_str.length= len;
+ return SP_FUNC;
+ }
+
#ifdef HAVE_DLOPEN
udf_func *udf;
if (function && using_udf_functions && (udf=find_udf((char*) tok, len)))
@@ -433,7 +455,7 @@ int yylex(void *arg, void *yythd)
int tokval, result_state;
uint length;
enum my_lex_states state;
- LEX *lex= &(((THD *)yythd)->lex);
+ LEX *lex= (((THD *)yythd)->lex);
YYSTYPE *yylval=(YYSTYPE*) arg;
CHARSET_INFO *cs= ((THD *) yythd)->charset();
uchar *state_map= cs->state_map;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index a3403ff5edf..fb7d8415e91 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -21,6 +21,9 @@
class Table_ident;
class sql_exchange;
class LEX_COLUMN;
+class sp_head;
+class sp_instr;
+class sp_pcontext;
/*
The following hack is needed because mysql_yacc.cc does not define
@@ -75,6 +78,10 @@ enum enum_sql_command {
SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS,
SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
+ SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL,
+ SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION,
+ SQLCOM_SHOW_CREATE_PROC, SQLCOM_SHOW_CREATE_FUNC,
+ SQLCOM_SHOW_STATUS_PROC, SQLCOM_SHOW_STATUS_FUNC,
/* This should be the last !!! */
SQLCOM_END
@@ -84,6 +91,10 @@ enum enum_sql_command {
#define DESCRIBE_NORMAL 1
#define DESCRIBE_EXTENDED 2
+enum suid_behaviour
+{
+ IS_DEFAULT_SUID= 0, IS_NOT_SUID, IS_SUID
+};
typedef List<Item> List_item;
@@ -357,7 +368,7 @@ public:
void print(String *str);
- friend void mysql_init_query(THD *thd);
+ friend void mysql_init_query(THD *thd, bool lexonly);
friend int subselect_union_engine::exec();
private:
bool create_total_list_n_last_return(THD *thd, st_lex *lex,
@@ -378,6 +389,8 @@ public:
enum olap_type olap;
SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */
List<Item> item_list; /* list of fields & expressions */
+ List<Item> item_list_copy; /* For SPs */
+ byte *table_list_first_copy; /* For SPs */
List<String> interval_list, use_index, *use_index_ptr,
ignore_index, *ignore_index_ptr;
/*
@@ -489,7 +502,7 @@ public:
bool test_limit();
- friend void mysql_init_query(THD *thd);
+ friend void mysql_init_query(THD *thd, bool lexonly);
st_select_lex() {}
void make_empty_select()
{
@@ -504,6 +517,13 @@ public:
typedef class st_select_lex SELECT_LEX;
+struct st_sp_chistics
+{
+ LEX_STRING comment;
+ enum suid_behaviour suid;
+ bool detistic;
+};
+
/* The state of the lex parsing. This is saved in the THD struct */
typedef struct st_lex
@@ -516,6 +536,7 @@ typedef struct st_lex
SELECT_LEX *current_select;
/* list of all SELECT_LEX */
SELECT_LEX *all_selects_list;
+ uchar *buf; /* The beginning of string, used by SPs */
uchar *ptr,*tok_start,*tok_end,*end_of_query;
char *length,*dec,*change,*name;
char *help_arg;
@@ -579,7 +600,23 @@ typedef struct st_lex
bool in_comment, ignore_space, verbose, simple_alter, no_write_to_binlog;
bool derived_tables;
bool safe_to_cache_query;
- st_lex() {}
+ sp_head *sphead;
+ bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
+ sp_pcontext *spcont;
+ HASH spfuns; /* Called functions */
+ st_sp_chistics sp_chistics;
+
+ st_lex()
+ {
+ bzero((char *)&spfuns, sizeof(spfuns));
+ }
+
+ ~st_lex()
+ {
+ if (spfuns.array.buffer)
+ hash_free(&spfuns);
+ }
+
inline void uncacheable(uint8 cause)
{
safe_to_cache_query= 0;
@@ -611,4 +648,4 @@ extern pthread_key(LEX*,THR_LEX);
extern LEX_STRING tmp_table_alias;
-#define current_lex (&current_thd->lex)
+#define current_lex (current_thd->lex)
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 0972d0341f6..592173c36da 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -135,6 +135,12 @@ public:
last= &first;
return tmp->info;
}
+ inline void concat(base_list *list)
+ {
+ *last= list->first;
+ last= list->last;
+ elements+= list->elements;
+ }
inline list_node* last_node() { return *last; }
inline list_node* first_node() { return first;}
inline void *head() { return first->info; }
@@ -255,6 +261,7 @@ public:
}
empty();
}
+ inline void concat(List<T> *list) { base_list::concat(list); }
};
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 0c35e99ed08..2f8a979b5ee 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -351,9 +351,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted,
(ulong) (info.records - info.copied), (ulong) thd->cuted_fields);
send_ok(thd,info.copied+info.deleted,0L,name);
- // on the slave thd->query is never initialized
- if (!thd->slave_thread)
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (!log_delayed)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -419,7 +416,7 @@ read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List<Item> &fields,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
DBUG_RETURN(1);
}
if (skip_lines)
@@ -518,7 +515,7 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
DBUG_RETURN(1);
}
while ((sql_field=(Item_field*) it++))
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 50f2a03b448..2232b7816ed 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -26,6 +26,9 @@
#include "ha_innodb.h"
#endif
+#include "sp_head.h"
+#include "sp.h"
+
#ifdef HAVE_OPENSSL
/*
Without SSL the handshake consists of one packet. This packet
@@ -44,6 +47,15 @@
#define MIN_HANDSHAKE_SIZE 6
#endif /* HAVE_OPENSSL */
+/* Used in error handling only */
+#define SP_TYPE_STRING(LP) \
+ ((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
+#define SP_COM_STRING(LP) \
+ ((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \
+ (LP)->sql_command == SQLCOM_ALTER_FUNCTION || \
+ (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
+ "FUNCTION" : "PROCEDURE")
+
#ifdef SOLARIS
extern "C" int gethostname(char *name, int namelen);
#endif
@@ -969,20 +981,18 @@ pthread_handler_decl(handle_one_connection,arg)
{
execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
if (thd->query_error)
- thd->killed= 1;
+ thd->killed= THD::KILL_CONNECTION;
}
thd->proc_info=0;
thd->set_time();
- thd->init_for_queries();
- while (!net->error && net->vio != 0 && !thd->killed)
+ while (!net->error && net->vio != 0 && !(thd->killed == THD::KILL_CONNECTION))
{
if (do_command(thd))
break;
}
if (thd->user_connect)
decrease_user_connections(thd->user_connect);
- free_root(&thd->mem_root,MYF(0));
if (net->error && net->vio != 0 && net->report_error)
{
if (!thd->killed && thd->variables.log_warnings)
@@ -1055,7 +1065,6 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg)
thd->priv_user=thd->user=(char*) my_strdup("boot", MYF(MY_WME));
buff= (char*) thd->net.buff;
- thd->init_for_queries();
while (fgets(buff, thd->net.max_packet, file))
{
uint length=(uint) strlen(buff);
@@ -1176,7 +1185,7 @@ bool do_command(THD *thd)
indicator of uninitialized lex => normal flow of errors handling
(see my_message_sql)
*/
- thd->lex.current_select= 0;
+ thd->lex->current_select= 0;
packet=0;
old_timeout=net->read_timeout;
@@ -1202,6 +1211,9 @@ bool do_command(THD *thd)
}
else
{
+ if (thd->killed == THD::KILL_QUERY)
+ thd->killed= THD::NOT_KILLED;
+
packet=(char*) net->read_pos;
command = (enum enum_server_command) (uchar) packet[0];
if (command >= COM_END)
@@ -1221,13 +1233,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
NET *net= &thd->net;
bool error= 0;
+ DBUG_ENTER("dispatch_command");
+
+ thd->command=command;
/*
Commands which will always take a long time should be marked with
this so that they will not get logged to the slow query log
*/
- DBUG_ENTER("dispatch_command");
-
- thd->command=command;
thd->slow_command=FALSE;
thd->set_time();
VOID(pthread_mutex_lock(&LOCK_thread_count));
@@ -1383,16 +1395,16 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
DBUG_PRINT("query",("%-.4096s",thd->query));
mysql_parse(thd,thd->query, thd->query_length);
- while (!thd->killed && !thd->is_fatal_error && thd->lex.found_colon)
+ while (!thd->killed && !thd->is_fatal_error && thd->lex->found_colon)
{
- char *packet= thd->lex.found_colon;
+ char *packet= thd->lex->found_colon;
/*
Multiple queries exits, execute them individually
*/
if (thd->lock || thd->open_tables || thd->derived_tables)
close_thread_tables(thd);
- ulong length= thd->query_length-(ulong)(thd->lex.found_colon-thd->query);
+ ulong length= thd->query_length-(ulong)(thd->lex->found_colon-thd->query);
/* Remove garbage at start of query */
while (my_isspace(thd->charset(), *packet) && length > 0)
@@ -1554,8 +1566,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
close_connection(thd, 0, 1);
close_thread_tables(thd); // Free before kill
- free_root(&thd->mem_root,MYF(0));
- free_root(&thd->transaction.mem_root,MYF(0));
kill_mysql();
error=TRUE;
break;
@@ -1605,7 +1615,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status);
ulong id=(ulong) uint4korr(packet);
- kill_one_thread(thd,id);
+ kill_one_thread(thd,id,false);
break;
}
case COM_SET_OPTION:
@@ -1677,6 +1687,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thread_running--;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
+
free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_RETURN(error);
}
@@ -1732,16 +1743,23 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
** Execute command saved in thd and current_lex->sql_command
****************************************************************************/
-void
+int
mysql_execute_command(THD *thd)
{
int res= 0;
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
TABLE_LIST *tables= (TABLE_LIST*) lex->select_lex.table_list.first;
SELECT_LEX *select_lex= &lex->select_lex;
SELECT_LEX_UNIT *unit= &lex->unit;
DBUG_ENTER("mysql_execute_command");
+ if (lex->sql_command != SQLCOM_CREATE_PROCEDURE &&
+ lex->sql_command != SQLCOM_CREATE_SPFUNCTION)
+ {
+ if (sp_cache_functions(thd, lex))
+ DBUG_RETURN(-1);
+ }
+
/*
Reset warning count for each query that uses tables
A better approach would be to reset this for any commands
@@ -1762,7 +1780,7 @@ mysql_execute_command(THD *thd)
{
/* we warn the slave SQL thread */
my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
}
#ifndef TO_BE_DELETED
/*
@@ -1798,7 +1816,7 @@ mysql_execute_command(THD *thd)
{
if (res < 0 || thd->net.report_error)
send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(res);
}
}
}
@@ -1806,7 +1824,7 @@ mysql_execute_command(THD *thd)
if (&lex->select_lex != lex->all_selects_list &&
lex->sql_command != SQLCOM_CREATE_TABLE &&
lex->unit.create_total_list(thd, lex, &tables, 0))
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
/*
When option readonly is set deny operations which change tables.
@@ -1817,7 +1835,7 @@ mysql_execute_command(THD *thd)
(uc_update_queries[lex->sql_command] > 0))
{
send_error(thd, ER_CANT_UPDATE_WITH_READLOCK);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(-1);
}
statistic_increment(com_stat[lex->sql_command],&LOCK_status);
@@ -1860,12 +1878,12 @@ mysql_execute_command(THD *thd)
if (!(result= new select_send()))
{
send_error(thd, ER_OUT_OF_RESOURCES);
- DBUG_VOID_RETURN;
+ goto error;
}
else
thd->send_explain_fields(result);
fix_tables_pointers(lex->all_selects_list);
- res= mysql_explain_union(thd, &thd->lex.unit, result);
+ res= mysql_explain_union(thd, &thd->lex->unit, result);
MYSQL_LOCK *save_lock= thd->lock;
thd->lock= (MYSQL_LOCK *)0;
if (lex->describe & DESCRIBE_EXTENDED)
@@ -1873,7 +1891,7 @@ mysql_execute_command(THD *thd)
char buff[1024];
String str(buff,(uint32) sizeof(buff), system_charset_info);
str.length(0);
- thd->lex.unit.print(&str);
+ thd->lex->unit.print(&str);
str.append('\0');
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_YES, str.ptr());
@@ -2165,7 +2183,7 @@ mysql_execute_command(THD *thd)
create_table->real_name))
{
net_printf(thd,ER_UPDATE_TABLE_USED, create_table->real_name);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(-1);
}
if (tables && check_table_access(thd, SELECT_ACL, tables,0))
goto error; // Error message is given
@@ -2244,7 +2262,7 @@ mysql_execute_command(THD *thd)
if (thd->locked_tables || thd->active_transaction())
{
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
- break;
+ goto error;
}
{
LOCK_ACTIVE_MI;
@@ -2257,7 +2275,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_ALTER_TABLE:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- break;
+ goto error;
#else
{
ulong priv=0;
@@ -2352,7 +2370,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (check_global_access(thd, SUPER_ACL))
@@ -2365,7 +2383,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_CREATE:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (check_db_used(thd, tables) ||
@@ -2394,7 +2412,6 @@ mysql_execute_command(THD *thd)
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2422,7 +2439,6 @@ mysql_execute_command(THD *thd)
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2462,7 +2478,6 @@ mysql_execute_command(THD *thd)
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2480,7 +2495,7 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
- DBUG_VOID_RETURN;
+ goto error;
}
res= mysql_update(thd,tables,
select_lex->item_list,
@@ -2501,7 +2516,7 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
- DBUG_VOID_RETURN;
+ goto error;
}
{
const char *msg= 0;
@@ -2536,7 +2551,7 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
- DBUG_VOID_RETURN;
+ goto error;
}
res = mysql_insert(thd,tables,lex->field_list,lex->many_values,
select_lex->item_list, lex->value_list,
@@ -2634,7 +2649,7 @@ mysql_execute_command(THD *thd)
}
case SQLCOM_DELETE_MULTI:
{
- TABLE_LIST *aux_tables=(TABLE_LIST *)thd->lex.auxilliary_table_list.first;
+ TABLE_LIST *aux_tables=(TABLE_LIST *)thd->lex->auxilliary_table_list.first;
TABLE_LIST *auxi;
uint table_count=0;
multi_delete *result;
@@ -2758,7 +2773,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_DATABASES:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
check_global_access(thd, SHOW_DB_ACL))
@@ -2794,7 +2809,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_LOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (grant_option && check_access(thd, FILE_ACL, any_db,0,0,0))
@@ -2807,7 +2822,7 @@ mysql_execute_command(THD *thd)
/* FALL THROUGH */
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
char *db=select_lex->db ? select_lex->db : thd->db;
@@ -2854,7 +2869,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_FIELDS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
char *db=tables->db;
@@ -2879,7 +2894,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_KEYS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
char *db=tables->db;
@@ -3068,26 +3083,24 @@ mysql_execute_command(THD *thd)
res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
break;
}
- case SQLCOM_CREATE_FUNCTION:
- if (check_access(thd,INSERT_ACL,"mysql",0,1,0))
- break;
-#ifdef HAVE_DLOPEN
- if (!(res = mysql_create_function(thd,&lex->udf)))
- send_ok(thd);
-#else
- res= -1;
-#endif
- break;
- case SQLCOM_DROP_FUNCTION:
- if (check_access(thd,DELETE_ACL,"mysql",0,1,0))
- break;
+ case SQLCOM_CREATE_FUNCTION: // UDF function
+ {
+ if (check_access(thd,INSERT_ACL,"mysql",0,1,0))
+ break;
#ifdef HAVE_DLOPEN
- if (!(res = mysql_drop_function(thd,&lex->udf.name)))
- send_ok(thd);
+ sp_head *sph= sp_find_function(thd, &lex->udf.name);
+ if (sph)
+ {
+ net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str);
+ goto error;
+ }
+ if (!(res = mysql_create_function(thd,&lex->udf)))
+ send_ok(thd);
#else
- res= -1;
+ res= -1;
#endif
break;
+ }
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_DROP_USER:
{
@@ -3095,7 +3108,6 @@ mysql_execute_command(THD *thd)
break;
if (!(res= mysql_drop_user(thd, lex->users_list)))
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -3111,7 +3123,6 @@ mysql_execute_command(THD *thd)
break;
if (!(res = mysql_revoke_all(thd, lex->users_list)))
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -3175,15 +3186,12 @@ mysql_execute_command(THD *thd)
goto error;
if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
lex->grant,
- lex->sql_command == SQLCOM_REVOKE)))
+ lex->sql_command == SQLCOM_REVOKE)) &&
+ mysql_bin_log.is_open())
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
- mysql_bin_log.write(&qinfo);
- }
+ thd->clear_error();
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ mysql_bin_log.write(&qinfo);
}
}
else
@@ -3198,7 +3206,6 @@ mysql_execute_command(THD *thd)
lex->sql_command == SQLCOM_REVOKE);
if (!res)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
@@ -3242,7 +3249,6 @@ mysql_execute_command(THD *thd)
*/
if (!lex->no_write_to_binlog && write_to_binlog)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -3254,7 +3260,7 @@ mysql_execute_command(THD *thd)
break;
}
case SQLCOM_KILL:
- kill_one_thread(thd,lex->thread_id);
+ kill_one_thread(thd,lex->thread_id, lex->type & ONLY_KILL_QUERY);
break;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_SHOW_GRANTS:
@@ -3366,16 +3372,274 @@ mysql_execute_command(THD *thd)
else
res= -1;
break;
+ case SQLCOM_CREATE_PROCEDURE:
+ case SQLCOM_CREATE_SPFUNCTION:
+ if (!lex->sphead)
+ {
+ res= -1; // Shouldn't happen
+ break;
+ }
+ else
+ {
+ uint namelen;
+ char *name= lex->sphead->name(&namelen);
+#ifdef HAVE_DLOPEN
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
+ {
+ udf_func *udf = find_udf(name, namelen);
+
+ if (udf)
+ {
+ net_printf(thd, ER_UDF_EXISTS, name);
+ goto error;
+ }
+ }
+#endif
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
+ !lex->sphead->m_has_return)
+ {
+ net_printf(thd, ER_SP_NORETURN, name);
+ goto error;
+ }
+
+ res= lex->sphead->create(thd);
+
+ switch (res)
+ {
+ case SP_OK:
+ send_ok(thd);
+ delete lex->sphead;
+ lex->sphead= 0;
+ break;
+ case SP_WRITE_ROW_FAILED:
+ net_printf(thd, ER_SP_ALREADY_EXISTS, SP_TYPE_STRING(lex), name);
+ delete lex->sphead;
+ lex->sphead= 0;
+ goto error;
+ default:
+ net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name);
+ delete lex->sphead;
+ lex->sphead= 0;
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_CALL:
+ {
+ sp_head *sp;
+
+ sp= sp_find_procedure(thd, &lex->udf.name);
+ if (! sp)
+ {
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, "PROCEDURE", lex->udf.name);
+ goto error;
+ }
+ else
+ {
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ st_sp_security_context save_ctx;
+#endif
+ uint smrx;
+ LINT_INIT(smrx);
+
+ // In case the arguments are subselects...
+ if (tables && ((res= check_table_access(thd, SELECT_ACL, tables, 0)) ||
+ (res= open_and_lock_tables(thd, tables))))
+ {
+ break;
+ }
+ fix_tables_pointers(lex->all_selects_list);
+
+#ifndef EMBEDDED_LIBRARY
+ // When executing substatements, they're assumed to send_error when
+ // it happens, but not to send_ok.
+ my_bool nsok= thd->net.no_send_ok;
+ thd->net.no_send_ok= TRUE;
+#endif
+ if (sp->m_multi_results)
+ {
+ if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
+ {
+ send_error(thd, ER_SP_BADSELECT);
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ goto error;
+ }
+ smrx= thd->server_status & SERVER_MORE_RESULTS_EXISTS;
+ thd->server_status |= SERVER_MORE_RESULTS_EXISTS;
+ }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_change_security_context(thd, sp, &save_ctx);
+#endif
+
+ res= sp->execute_procedure(thd, &lex->value_list);
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ sp_restore_security_context(thd, sp, &save_ctx);
+#endif
+
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ if (sp->m_multi_results)
+ {
+ if (! smrx)
+ thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ }
+
+ if (res == 0)
+ send_ok(thd);
+ else
+ goto error; // Substatement should already have sent error
+ }
+ break;
+ }
+ case SQLCOM_ALTER_PROCEDURE:
+ case SQLCOM_ALTER_FUNCTION:
+ {
+ res= -1;
+ uint newname_len= 0;
+ if (lex->name)
+ newname_len= strlen(lex->name);
+ if (newname_len > NAME_LEN)
+ {
+ net_printf(thd, ER_TOO_LONG_IDENT, lex->name);
+ goto error;
+ }
+ if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
+ res= sp_update_procedure(thd, lex->udf.name.str, lex->udf.name.length,
+ lex->name, newname_len, &lex->sp_chistics);
+ else
+ res= sp_update_function(thd, lex->udf.name.str, lex->udf.name.length,
+ lex->name, newname_len, &lex->sp_chistics);
+ switch (res)
+ {
+ case SP_OK:
+ send_ok(thd);
+ break;
+ case SP_KEY_NOT_FOUND:
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),lex->udf.name);
+ goto error;
+ default:
+ net_printf(thd, ER_SP_CANT_ALTER, SP_COM_STRING(lex),lex->udf.name);
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_DROP_PROCEDURE:
+ case SQLCOM_DROP_FUNCTION:
+ {
+ if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
+ res= sp_drop_procedure(thd, lex->udf.name.str, lex->udf.name.length);
+ else
+ {
+ res= sp_drop_function(thd, lex->udf.name.str, lex->udf.name.length);
+#ifdef HAVE_DLOPEN
+ if (res == SP_KEY_NOT_FOUND)
+ {
+ udf_func *udf = find_udf(lex->udf.name.str, lex->udf.name.length);
+ if (udf)
+ {
+ if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0))
+ goto error;
+ if (!(res = mysql_drop_function(thd,&lex->udf.name)))
+ {
+ send_ok(thd);
+ break;
+ }
+ }
+ }
+#endif
+ }
+ switch (res)
+ {
+ case SP_OK:
+ send_ok(thd);
+ break;
+ case SP_KEY_NOT_FOUND:
+ if (lex->drop_if_exists)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
+ SP_COM_STRING(lex), lex->udf.name.str);
+ res= 0;
+ send_ok(thd);
+ break;
+ }
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),
+ lex->udf.name.str);
+ goto error;
+ default:
+ net_printf(thd, ER_SP_DROP_FAILED, SP_COM_STRING(lex),
+ lex->udf.name.str);
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_SHOW_CREATE_PROC:
+ {
+ res= -1;
+ if (lex->udf.name.length > NAME_LEN)
+ {
+ net_printf(thd, ER_TOO_LONG_IDENT, lex->udf.name.str);
+ goto error;
+ }
+ res= sp_show_create_procedure(thd, &lex->udf.name);
+ if (res != SP_OK)
+ { /* We don't distinguish between errors for now */
+ net_printf(thd, ER_SP_DOES_NOT_EXIST,
+ SP_COM_STRING(lex), lex->udf.name.str);
+ res= 0;
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_SHOW_CREATE_FUNC:
+ {
+ if (lex->udf.name.length > NAME_LEN)
+ {
+ net_printf(thd, ER_TOO_LONG_IDENT, lex->udf.name.str);
+ goto error;
+ }
+ res= sp_show_create_function(thd, &lex->udf.name);
+ if (res != SP_OK)
+ { /* We don't distinguish between errors for now */
+ net_printf(thd, ER_SP_DOES_NOT_EXIST,
+ SP_COM_STRING(lex), lex->udf.name.str);
+ res= 0;
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_SHOW_STATUS_PROC:
+ {
+ res= sp_show_status_procedure(thd, (lex->wild ?
+ lex->wild->ptr() : NullS));
+ break;
+ }
+ case SQLCOM_SHOW_STATUS_FUNC:
+ {
+ res= sp_show_status_function(thd, (lex->wild ?
+ lex->wild->ptr() : NullS));
+ break;
+ }
default: /* Impossible */
send_ok(thd);
break;
}
thd->proc_info="query end"; // QQ
+
+ // We end up here if res == 0 and send_ok() has been done,
+ // or res != 0 and no send_error() has yet been done.
if (res < 0)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
+ send_error(thd,thd->killed_errno());
+ DBUG_RETURN(res);
error:
- DBUG_VOID_RETURN;
+ // We end up here if send_error() has already been done.
+ DBUG_RETURN(-1);
}
@@ -3690,10 +3954,10 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize)
****************************************************************************/
void
-mysql_init_query(THD *thd)
+mysql_init_query(THD *thd, bool lexonly)
{
DBUG_ENTER("mysql_init_query");
- LEX *lex=&thd->lex;
+ LEX *lex=thd->lex;
lex->unit.init_query();
lex->unit.init_select();
lex->unit.thd= thd;
@@ -3710,24 +3974,29 @@ mysql_init_query(THD *thd)
lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0;
lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list);
lex->select_lex.options=0;
+ lex->select_lex.init_order();
+ lex->select_lex.group_list.empty();
lex->describe= 0;
lex->derived_tables= FALSE;
lex->lock_option= TL_READ;
lex->found_colon= 0;
lex->safe_to_cache_query= 1;
- thd->select_number= lex->select_lex.select_number= 1;
- thd->free_list= 0;
- thd->total_warn_count=0; // Warnings for this query
- thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
- thd->sent_row_count= thd->examined_row_count= 0;
- thd->is_fatal_error= thd->rand_used= 0;
- thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS |
- SERVER_QUERY_NO_INDEX_USED |
- SERVER_QUERY_NO_GOOD_INDEX_USED);
- thd->tmp_table_used= 0;
- if (opt_bin_log)
- reset_dynamic(&thd->user_var_events);
- thd->clear_error();
+ if (! lexonly)
+ {
+ thd->select_number= lex->select_lex.select_number= 1;
+ thd->free_list= 0;
+ thd->total_warn_count=0; // Warnings for this query
+ thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
+ thd->sent_row_count= thd->examined_row_count= 0;
+ thd->is_fatal_error= thd->rand_used= 0;
+ thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS |
+ SERVER_QUERY_NO_INDEX_USED |
+ SERVER_QUERY_NO_GOOD_INDEX_USED);
+ thd->tmp_table_used= 0;
+ if (opt_bin_log)
+ reset_dynamic(&thd->user_var_events);
+ thd->clear_error();
+ }
DBUG_VOID_RETURN;
}
@@ -3821,7 +4090,7 @@ void create_select_for_variable(const char *var_name)
DBUG_ENTER("create_select_for_variable");
thd= current_thd;
- lex= &thd->lex;
+ lex= thd->lex;
mysql_init_select(lex);
lex->sql_command= SQLCOM_SELECT;
tmp.str= (char*) var_name;
@@ -3864,7 +4133,16 @@ mysql_parse(THD *thd, char *inBuf, uint length)
#endif
{
if (thd->net.report_error)
+ {
send_error(thd, 0, NullS);
+ if (thd->lex->sphead)
+ {
+ if (lex != thd->lex)
+ thd->lex->sphead->restore_lex(thd);
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
+ }
else
{
mysql_execute_command(thd);
@@ -3877,6 +4155,13 @@ mysql_parse(THD *thd, char *inBuf, uint length)
DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
thd->is_fatal_error));
query_cache_abort(&thd->net);
+ if (thd->lex->sphead)
+ {
+ if (lex != thd->lex)
+ thd->lex->sphead->restore_lex(thd);
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
}
thd->proc_info="freeing items";
free_items(thd->free_list); /* Free strings used by items */
@@ -3899,7 +4184,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
uint uint_geom_type)
{
register create_field *new_field;
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
uint allowed_type_modifier=0;
char warn_buff[MYSQL_ERRMSG_SIZE];
DBUG_ENTER("add_field_to_list");
@@ -4233,7 +4518,7 @@ add_proc_to_list(THD* thd, Item *item)
*item_ptr= item;
order->item=item_ptr;
order->free_me=0;
- thd->lex.proc_list.link_in_list((byte*) order,(byte**) &order->next);
+ thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next);
return 0;
}
@@ -4518,7 +4803,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
*/
tmp_write_to_binlog= 0;
mysql_log.new_file(1);
- mysql_update_log.new_file(1);
mysql_bin_log.new_file(1);
mysql_slow_log.new_file(1);
#ifdef HAVE_REPLICATION
@@ -4616,7 +4900,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
This is written such that we have a short lock on LOCK_thread_count
*/
-void kill_one_thread(THD *thd, ulong id)
+void kill_one_thread(THD *thd, ulong id, bool only_kill_query)
{
THD *tmp;
uint error=ER_NO_SUCH_THREAD;
@@ -4636,7 +4920,7 @@ void kill_one_thread(THD *thd, ulong id)
if ((thd->master_access & SUPER_ACL) ||
!strcmp(thd->user,tmp->user))
{
- tmp->awake(1 /*prepare to die*/);
+ tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
error=0;
}
else
@@ -4715,11 +4999,11 @@ static bool append_file_to_dir(THD *thd, char **filename_ptr, char *table_name)
bool check_simple_select()
{
THD *thd= current_thd;
- if (thd->lex.current_select != &thd->lex.select_lex)
+ if (thd->lex->current_select != &thd->lex->select_lex)
{
char command[80];
- strmake(command, thd->lex.yylval->symbol.str,
- min(thd->lex.yylval->symbol.length, sizeof(command)-1));
+ strmake(command, thd->lex->yylval->symbol.str,
+ min(thd->lex->yylval->symbol.length, sizeof(command)-1));
net_printf(thd, ER_CANT_USE_OPTION_HERE, command);
return 1;
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 58e6dbff1ed..2897af0b87f 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -71,6 +71,7 @@ Long data handling:
#include "sql_acl.h"
#include "sql_select.h" // for JOIN
#include <m_ctype.h> // for isspace()
+#include "sp_head.h"
#define IS_PARAM_NULL(pos, param_no) (pos[param_no/8] & (1 << (param_no & 7)))
@@ -173,7 +174,7 @@ static bool send_prep_stmt(PREP_STMT *stmt, uint columns __attribute__((unused))
return 0;
}
-#endif /*!EMBEDDED_LIBRAYR*/
+#endif /*!EMBEDDED_LIBRARY*/
/*
Send information about all item parameters
@@ -421,7 +422,7 @@ void setup_param_functions(Item_param *param, uchar param_type)
static bool insert_params_withlog(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
{
THD *thd= stmt->thd;
- List<Item> &params= thd->lex.param_list;
+ List<Item> &params= thd->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
DBUG_ENTER("insert_params_withlog");
@@ -467,7 +468,7 @@ static bool insert_params_withlog(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
static bool insert_params(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
{
THD *thd= stmt->thd;
- List<Item> &params= thd->lex.param_list;
+ List<Item> &params= thd->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
DBUG_ENTER("insert_params");
@@ -493,7 +494,7 @@ static bool insert_params(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
static bool setup_params_data(PREP_STMT *stmt)
{
THD *thd= stmt->thd;
- List<Item> &params= thd->lex.param_list;
+ List<Item> &params= thd->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
DBUG_ENTER("setup_params_data");
@@ -538,8 +539,8 @@ static bool mysql_test_insert_fields(PREP_STMT *stmt,
DBUG_ENTER("mysql_test_insert_fields");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- my_bool update=(thd->lex.value_list.elements ? UPDATE_ACL : 0);
- ulong privilege= (thd->lex.duplicates == DUP_REPLACE ?
+ my_bool update=(thd->lex->value_list.elements ? UPDATE_ACL : 0);
+ ulong privilege= (thd->lex->duplicates == DUP_REPLACE ?
INSERT_ACL | DELETE_ACL : INSERT_ACL | update);
if (check_access(thd,privilege,table_list->db,
@@ -640,8 +641,8 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
SELECT_LEX *select_lex)
{
THD *thd= stmt->thd;
- LEX *lex= &thd->lex;
- select_result *result= thd->lex.result;
+ LEX *lex= thd->lex;
+ select_result *result= thd->lex->result;
DBUG_ENTER("mysql_test_select_fields");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -668,7 +669,7 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
}
else
{
- fix_tables_pointers(thd->lex.all_selects_list);
+ fix_tables_pointers(thd->lex->all_selects_list);
if (!result && !(result= new select_send()))
{
send_error(thd, ER_OUT_OF_RESOURCES);
@@ -702,8 +703,8 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
static bool send_prepare_results(PREP_STMT *stmt)
{
THD *thd= stmt->thd;
- LEX *lex= &thd->lex;
- enum enum_sql_command sql_command= thd->lex.sql_command;
+ LEX *lex= thd->lex;
+ enum enum_sql_command sql_command= thd->lex->sql_command;
DBUG_ENTER("send_prepare_results");
DBUG_PRINT("enter",("command: %d, param_count: %ld",
sql_command, lex->param_count));
@@ -764,7 +765,7 @@ static bool send_prepare_results(PREP_STMT *stmt)
DBUG_RETURN(0);
abort:
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
+ send_error(thd,thd->killed_errno());
DBUG_RETURN(1);
}
@@ -783,9 +784,19 @@ static bool parse_prepare_query(PREP_STMT *stmt,
mysql_init_query(thd);
LEX *lex=lex_start(thd, (uchar*) packet, length);
lex->safe_to_cache_query= 0;
- thd->lex.param_count= 0;
+ thd->lex->param_count= 0;
if (!yyparse((void *)thd) && !thd->is_fatal_error)
error= send_prepare_results(stmt);
+ else
+ {
+ if (thd->lex->sphead)
+ {
+ if (lex != thd->lex)
+ thd->lex->sphead->restore_lex(thd);
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
+ }
lex_end(lex);
DBUG_RETURN(error);
}
@@ -797,13 +808,13 @@ static bool parse_prepare_query(PREP_STMT *stmt,
static bool init_param_items(PREP_STMT *stmt)
{
THD *thd= stmt->thd;
- List<Item> &params= thd->lex.param_list;
+ List<Item> &params= thd->lex->param_list;
Item_param **to;
uint32 length= thd->query_length;
- stmt->lex= thd->lex;
+ stmt->lex= *thd->lex;
- if (mysql_bin_log.is_open() || mysql_update_log.is_open())
+ if (mysql_bin_log.is_open())
{
stmt->log_full_query= 1;
#ifndef EMBEDDED_LIBRARY
@@ -850,7 +861,7 @@ static bool init_param_items(PREP_STMT *stmt)
static void init_stmt_execute(PREP_STMT *stmt)
{
THD *thd= stmt->thd;
- TABLE_LIST *tables= (TABLE_LIST*) thd->lex.select_lex.table_list.first;
+ TABLE_LIST *tables= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
/*
TODO: When the new table structure is ready, then have a status bit
@@ -877,7 +888,7 @@ static void init_stmt_execute(PREP_STMT *stmt)
If parameter markers are found in the query, then store
the information using Item_param along with maintaining a
list in lex->param_list, so that a fast and direct
- retrieveal can be made without going through all field
+ retrieval can be made without going through all field
items.
*/
@@ -890,7 +901,7 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
bzero((char*) &stmt, sizeof(stmt));
- stmt.stmt_id= ++thd->current_stmt_id;
+ stmt.stmt_id= ++thd->statement_id_counter;
init_sql_alloc(&stmt.mem_root,
thd->variables.query_alloc_block_size,
thd->variables.query_prealloc_size);
@@ -908,7 +919,7 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
my_pthread_setprio(pthread_self(),WAIT_PRIOR);
// save WHERE clause pointers to avoid damaging they by optimisation
- for (sl= thd->lex.all_selects_list;
+ for (sl= thd->lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
{
@@ -961,8 +972,8 @@ void mysql_stmt_execute(THD *thd, char *packet)
DBUG_VOID_RETURN;
}
- LEX thd_lex= thd->lex;
- thd->lex= stmt->lex;
+ LEX *thd_lex= thd->lex;
+ thd->lex= &stmt->lex;
for (sl= stmt->lex.all_selects_list;
sl;
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index 3ab6621f35b..29b4f2f4a01 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -79,7 +79,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
/* Lets hope this doesn't fail as the result will be messy */
if (!error)
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 3c477afa300..c0ee4277128 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -678,8 +678,8 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report)
was running (as we don't wan't to touch the other thread), so set the
bit to 0 for the other thread
*/
- if (thd->lex.slave_thd_opt)
- thread_mask &= thd->lex.slave_thd_opt;
+ if (thd->lex->slave_thd_opt)
+ thread_mask &= thd->lex->slave_thd_opt;
if (thread_mask) //some threads are stopped, start them
{
if (init_master_info(mi,master_info_file,relay_log_info_file, 0))
@@ -695,22 +695,22 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report)
{
pthread_mutex_lock(&mi->rli.data_lock);
- if (thd->lex.mi.pos)
+ if (thd->lex->mi.pos)
{
mi->rli.until_condition= RELAY_LOG_INFO::UNTIL_MASTER_POS;
- mi->rli.until_log_pos= thd->lex.mi.pos;
+ mi->rli.until_log_pos= thd->lex->mi.pos;
/*
We don't check thd->lex.mi.log_file_name for NULL here
since it is checked in sql_yacc.yy
*/
- strmake(mi->rli.until_log_name, thd->lex.mi.log_file_name,
+ strmake(mi->rli.until_log_name, thd->lex->mi.log_file_name,
sizeof(mi->rli.until_log_name)-1);
}
- else if (thd->lex.mi.relay_log_pos)
+ else if (thd->lex->mi.relay_log_pos)
{
mi->rli.until_condition= RELAY_LOG_INFO::UNTIL_RELAY_POS;
- mi->rli.until_log_pos= thd->lex.mi.relay_log_pos;
- strmake(mi->rli.until_log_name, thd->lex.mi.relay_log_name,
+ mi->rli.until_log_pos= thd->lex->mi.relay_log_pos;
+ strmake(mi->rli.until_log_name, thd->lex->mi.relay_log_name,
sizeof(mi->rli.until_log_name)-1);
}
else
@@ -748,7 +748,7 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report)
pthread_mutex_unlock(&mi->rli.data_lock);
}
- else if (thd->lex.mi.pos || thd->lex.mi.relay_log_pos)
+ else if (thd->lex->mi.pos || thd->lex->mi.relay_log_pos)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNTIL_COND_IGNORED,
ER(ER_UNTIL_COND_IGNORED));
@@ -802,8 +802,8 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report )
was stopped (as we don't wan't to touch the other thread), so set the
bit to 0 for the other thread
*/
- if (thd->lex.slave_thd_opt)
- thread_mask &= thd->lex.slave_thd_opt;
+ if (thd->lex->slave_thd_opt)
+ thread_mask &= thd->lex->slave_thd_opt;
if (thread_mask)
{
@@ -952,7 +952,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
it will be slow because it will iterate through the list
again. We just to do kill the thread ourselves.
*/
- tmp->awake(1/*prepare to die*/);
+ tmp->awake(THD::KILL_QUERY);
pthread_mutex_unlock(&tmp->LOCK_delete);
}
}
@@ -975,7 +975,7 @@ int change_master(THD* thd, MASTER_INFO* mi)
}
thd->proc_info = "Changing master";
- LEX_MASTER_INFO* lex_mi = &thd->lex.mi;
+ LEX_MASTER_INFO* lex_mi = &thd->lex->mi;
// TODO: see if needs re-write
if (init_master_info(mi, master_info_file, relay_log_info_file, 0))
{
@@ -1204,7 +1204,7 @@ int show_binlog_events(THD* thd)
if (mysql_bin_log.is_open())
{
- LEX_MASTER_INFO *lex_mi = &thd->lex.mi;
+ LEX_MASTER_INFO *lex_mi = &thd->lex->mi;
ha_rows event_count, limit_start, limit_end;
my_off_t pos = max(BIN_LOG_HEADER_SIZE, lex_mi->pos); // user-friendly
char search_file_name[FN_REFLEN], *name;
@@ -1213,8 +1213,8 @@ int show_binlog_events(THD* thd)
LOG_INFO linfo;
Log_event* ev;
- limit_start = thd->lex.current_select->offset_limit;
- limit_end = thd->lex.current_select->select_limit + limit_start;
+ limit_start = thd->lex->current_select->offset_limit;
+ limit_end = thd->lex->current_select->select_limit + limit_start;
name= search_file_name;
if (log_file_name)
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index fe1b7167d4a..2ff38029b7b 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -20,7 +20,7 @@ extern I_List<i_string> binlog_do_db, binlog_ignore_db;
extern int max_binlog_dump_events;
extern my_bool opt_sporadic_binlog_dump_fail;
-#define KICK_SLAVE(thd) { pthread_mutex_lock(&(thd)->LOCK_delete); (thd)->awake(0 /* do not prepare to die*/); pthread_mutex_unlock(&(thd)->LOCK_delete); }
+#define KICK_SLAVE(thd) { pthread_mutex_lock(&(thd)->LOCK_delete); (thd)->awake(THD::NOT_KILLED); pthread_mutex_unlock(&(thd)->LOCK_delete); }
File open_binlog(IO_CACHE *log, const char *log_file_name,
const char **errmsg);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 16b9a341ee5..b2c1ef1ea8f 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -32,7 +32,8 @@
const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref",
"MAYBE_REF","ALL","range","index","fulltext",
- "ref_or_null","unique_subquery","index_subquery"
+ "ref_or_null","unique_subquery","index_subquery",
+ "index_merge"
};
const key_map key_map_empty(0);
@@ -116,7 +117,7 @@ static int join_read_next_same_or_null(READ_RECORD *info);
static COND *make_cond_for_table(COND *cond,table_map table,
table_map used_table);
static Item* part_of_refkey(TABLE *form,Field *field);
-static uint find_shortest_key(TABLE *table, const key_map *usable_keys);
+uint find_shortest_key(TABLE *table, const key_map *usable_keys);
static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,
ha_rows select_limit, bool no_changes);
static int create_sort_index(THD *thd, JOIN *join, ORDER *order,
@@ -208,7 +209,8 @@ int handle_select(THD *thd, LEX *lex, select_result *result)
send_error(thd, 0, NullS);
res= 1; // Error sent to client
}
- delete result;
+ if (result != lex->result)
+ delete result;
DBUG_RETURN(res);
}
@@ -501,8 +503,8 @@ JOIN::optimize()
optimized= 1;
// Ignore errors of execution if option IGNORE present
- if (thd->lex.duplicates == DUP_IGNORE)
- thd->lex.current_select->no_error= 1;
+ if (thd->lex->duplicates == DUP_IGNORE)
+ thd->lex->current_select->no_error= 1;
#ifdef HAVE_REF_TO_FIELDS // Not done yet
/* Add HAVING to WHERE if possible */
if (having && !group_list && !sum_func_count)
@@ -1602,7 +1604,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
goto err; // 1
}
- if (thd->lex.describe & DESCRIBE_EXTENDED)
+ if (thd->lex->describe & DESCRIBE_EXTENDED)
{
join->conds_history= join->conds;
join->having_history= (join->having?join->having:join->tmp_having);
@@ -1613,7 +1615,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
join->exec();
- if (thd->lex.describe & DESCRIBE_EXTENDED)
+ if (thd->lex->describe & DESCRIBE_EXTENDED)
{
select_lex->where= join->conds_history;
select_lex->having= join->having_history;
@@ -2459,7 +2461,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
if (!(key_fields=(KEY_FIELD*)
thd->alloc(sizeof(key_fields[0])*
- (thd->lex.current_select->cond_count+1)*2)))
+ (thd->lex->current_select->cond_count+1)*2)))
return TRUE; /* purecov: inspected */
and_level= 0;
field= end= key_fields;
@@ -3438,7 +3440,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
with key reading */
if (tab->needed_reg.is_clear_all() && tab->type != JT_EQ_REF
&& tab->type != JT_FT && (tab->type != JT_REF ||
- (uint) tab->ref.key == tab->quick->index))
+ (uint) tab->ref.key == tab->quick->index))
{
sel->quick=tab->quick; // Use value from get_quick_...
sel->quick_keys.clear_all();
@@ -3549,7 +3551,7 @@ static void
make_join_readinfo(JOIN *join, uint options)
{
uint i;
- SELECT_LEX *select_lex = &(join->thd->lex.select_lex);
+ SELECT_LEX *select_lex = &(join->thd->lex->select_lex);
DBUG_ENTER("make_join_readinfo");
for (i=join->const_tables ; i < join->tables ; i++)
@@ -5461,7 +5463,7 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
thd->proc_info="converting HEAP to MyISAM";
if (create_myisam_tmp_table(&new_table,param,
- thd->lex.select_lex.options | thd->options))
+ thd->lex->select_lex.options | thd->options))
goto err2;
if (open_tmp_table(&new_table))
goto err1;
@@ -5659,7 +5661,7 @@ sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
}
if (join->thd->killed) // If aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
return -2; /* purecov: inspected */
}
if (join_tab->use_quick != 2 || test_if_quick_select(join_tab) <= 0)
@@ -5700,7 +5702,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
if (join->thd->killed) // Aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
return -2; /* purecov: inspected */
}
join->examined_rows++;
@@ -5781,7 +5783,7 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skipp_last)
{
if (join->thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
return -2; // Aborted by user /* purecov: inspected */
}
SQL_SELECT *select=join_tab->select;
@@ -6406,7 +6408,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (join->thd->killed) // Aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
DBUG_RETURN(-2); /* purecov: inspected */
}
if (!end_of_records)
@@ -6474,7 +6476,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(0);
if (join->thd->killed) // Aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
DBUG_RETURN(-2); /* purecov: inspected */
}
@@ -6544,7 +6546,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(0);
if (join->thd->killed) // Aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
DBUG_RETURN(-2); /* purecov: inspected */
}
@@ -6591,7 +6593,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (join->thd->killed)
{ // Aborted by user
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
DBUG_RETURN(-2); /* purecov: inspected */
}
if (!join->first_record || end_of_records ||
@@ -6840,7 +6842,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
return reverse;
}
-static uint find_shortest_key(TABLE *table, const key_map *usable_keys)
+uint find_shortest_key(TABLE *table, const key_map *usable_keys)
{
uint min_length= (uint) ~0;
uint best= MAX_KEY;
@@ -6974,6 +6976,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
}
else if (select && select->quick) // Range found by opt_range
{
+ /* assume results are not ordered when index merge is used */
+ if (select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
+ DBUG_RETURN(0);
ref_key= select->quick->index;
ref_key_parts= select->quick->used_key_parts;
}
@@ -7008,6 +7013,10 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
}
else
{
+ /*
+ We have verified above that select->quick is not
+ index_merge quick select.
+ */
select->quick->index= new_ref_key;
select->quick->init();
}
@@ -7029,10 +7038,13 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
*/
if (!select->quick->reverse_sorted())
{
- if (table->file->index_flags(ref_key) & HA_NOT_READ_PREFIX_LAST)
+ if (table->file->index_flags(ref_key) & HA_NOT_READ_PREFIX_LAST ||
+ (select->quick->get_type() ==
+ QUICK_SELECT_I::QS_TYPE_INDEX_MERGE))
DBUG_RETURN(0); // Use filesort
- // ORDER BY range_key DESC
- QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC(select->quick,
+
+ // ORDER BY range_key DESC
+ QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick),
used_key_parts);
if (!tmp || tmp->error)
{
@@ -7174,8 +7186,11 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
{
select->quick=tab->quick;
tab->quick=0;
- /* We can only use 'Only index' if quick key is same as ref_key */
- if (table->key_read && (uint) tab->ref.key != select->quick->index)
+ /*
+ We can only use 'Only index' if quick key is same as ref_key
+ and in index_merge 'Only index' cannot be used
+ */
+ if (table->key_read && ((uint) tab->ref.key != select->quick->index))
{
table->key_read=0;
table->file->extra(HA_EXTRA_NO_KEYREAD);
@@ -7357,7 +7372,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
error=0;
goto err;
}
@@ -7469,7 +7484,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
error=0;
goto err;
}
@@ -8942,7 +8957,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
List<Item> field_list;
List<Item> item_list;
THD *thd=join->thd;
- SELECT_LEX *select_lex = &(join->thd->lex.select_lex);
+ SELECT_LEX *select_lex = &(join->thd->lex->select_lex);
select_result *result=join->result;
Item *item_null= new Item_null();
CHARSET_INFO *cs= &my_charset_latin1;
@@ -8973,12 +8988,15 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
JOIN_TAB *tab=join->join_tab+i;
TABLE *table=tab->table;
char buff[512],*buff_ptr=buff;
- char buff1[512], buff2[512];
+ char buff1[512], buff2[512], buff3[512];
+ char keylen_str_buf[64];
char derived_name[64];
String tmp1(buff1,sizeof(buff1),cs);
String tmp2(buff2,sizeof(buff2),cs);
+ String tmp3(buff3,sizeof(buff3),cs);
tmp1.length(0);
tmp2.length(0);
+ tmp3.length(0);
item_list.empty();
item_list.push_back(new Item_int((int32)
@@ -8987,7 +9005,13 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
strlen(join->select_lex->type),
cs));
if (tab->type == JT_ALL && tab->select && tab->select->quick)
- tab->type= JT_RANGE;
+ {
+ if (tab->select->quick->get_type() ==
+ QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
+ tab->type = JT_INDEX_MERGE;
+ else
+ tab->type = JT_RANGE;
+ }
if (table->derived_select_number)
{
/* Derived table name generation */
@@ -9023,10 +9047,14 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
if (tab->ref.key_parts)
{
KEY *key_info=table->key_info+ tab->ref.key;
+ register uint length;
item_list.push_back(new Item_string(key_info->name,
strlen(key_info->name),
system_charset_info));
- item_list.push_back(new Item_int((int32) tab->ref.key_length));
+ length= longlong2str(tab->ref.key_length, keylen_str_buf, 10) -
+ keylen_str_buf;
+ item_list.push_back(new Item_string(keylen_str_buf, length,
+ system_charset_info));
for (store_key **ref=tab->ref.key_copy ; *ref ; ref++)
{
if (tmp2.length())
@@ -9038,18 +9066,60 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
else if (tab->type == JT_NEXT)
{
KEY *key_info=table->key_info+ tab->index;
+ register uint length;
item_list.push_back(new Item_string(key_info->name,
strlen(key_info->name),cs));
- item_list.push_back(new Item_int((int32) key_info->key_length));
+ length= longlong2str(key_info->key_length, keylen_str_buf, 10) -
+ keylen_str_buf;
+ item_list.push_back(new Item_string(keylen_str_buf,
+ length,
+ system_charset_info));
item_list.push_back(item_null);
}
else if (tab->select && tab->select->quick)
{
- KEY *key_info=table->key_info+ tab->select->quick->index;
- item_list.push_back(new Item_string(key_info->name,
- strlen(key_info->name),cs));
- item_list.push_back(new Item_int((int32) tab->select->quick->
- max_used_key_length));
+ if (tab->select->quick->get_type() ==
+ QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
+ {
+ QUICK_INDEX_MERGE_SELECT *quick_imerge=
+ (QUICK_INDEX_MERGE_SELECT*)tab->select->quick;
+ QUICK_RANGE_SELECT *quick;
+
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_imerge->
+ quick_selects);
+ while ((quick= it++))
+ {
+ KEY *key_info= table->key_info + quick->index;
+ register uint length;
+ if (tmp3.length())
+ tmp3.append(',');
+
+ tmp3.append(key_info->name);
+
+ if (tmp2.length())
+ tmp2.append(',');
+
+ length= longlong2str(quick->max_used_key_length, keylen_str_buf,
+ 10) -
+ keylen_str_buf;
+
+ tmp2.append(keylen_str_buf, length);
+ }
+ }
+ else
+ {
+ KEY *key_info= table->key_info + tab->select->quick->index;
+ register uint length;
+ tmp3.append(key_info->name);
+
+ length= longlong2str(tab->select->quick->max_used_key_length,
+ keylen_str_buf, 10) -
+ keylen_str_buf;
+ tmp2.append(keylen_str_buf, length);
+ }
+
+ item_list.push_back(new Item_string(tmp3.ptr(),tmp3.length(),cs));
+ item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
item_list.push_back(item_null);
}
else
@@ -9129,8 +9199,8 @@ int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
sl= sl->next_select())
{
res= mysql_explain_select(thd, sl,
- (((&thd->lex.select_lex)==sl)?
- ((thd->lex.all_selects_list != sl)?"PRIMARY":
+ (((&thd->lex->select_lex)==sl)?
+ ((thd->lex->all_selects_list != sl)?"PRIMARY":
"SIMPLE"):
((sl == first)?
((sl->linkage == DERIVED_TABLE_TYPE) ?
@@ -9160,7 +9230,7 @@ int mysql_explain_select(THD *thd, SELECT_LEX *select_lex, char const *type,
DBUG_ENTER("mysql_explain_select");
DBUG_PRINT("info", ("Select 0x%lx, type %s", (ulong)select_lex, type))
select_lex->type= type;
- thd->lex.current_select= select_lex;
+ thd->lex->current_select= select_lex;
SELECT_LEX_UNIT *unit= select_lex->master_unit();
int res= mysql_select(thd, &select_lex->ref_pointer_array,
(TABLE_LIST*) select_lex->table_list.first,
@@ -9171,7 +9241,7 @@ int mysql_explain_select(THD *thd, SELECT_LEX *select_lex, char const *type,
(ORDER*) select_lex->order_list.first,
(ORDER*) select_lex->group_list.first,
select_lex->having,
- (ORDER*) thd->lex.proc_list.first,
+ (ORDER*) thd->lex->proc_list.first,
select_lex->options | thd->options | SELECT_DESCRIBE,
result, unit, select_lex);
DBUG_RETURN(res);
@@ -9188,8 +9258,8 @@ void st_select_lex::print(THD *thd, String *str)
//options
if (options & SELECT_STRAIGHT_JOIN)
str->append("straight_join ", 14);
- if ((thd->lex.lock_option & TL_READ_HIGH_PRIORITY) &&
- (this == &thd->lex.select_lex))
+ if ((thd->lex->lock_option & TL_READ_HIGH_PRIORITY) &&
+ (this == &thd->lex->select_lex))
str->append("high_priority ", 14);
if (options & SELECT_DISTINCT)
str->append("distinct ", 9);
@@ -9201,7 +9271,7 @@ void st_select_lex::print(THD *thd, String *str)
str->append("buffer_result ", 14);
if (options & OPTION_FOUND_ROWS)
str->append("calc_found_rows ", 16);
- if (!thd->lex.safe_to_cache_query)
+ if (!thd->lex->safe_to_cache_query)
str->append("no_cache ", 9);
if (options & OPTION_TO_QUERY_CACHE)
str->append("cache ", 6);
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 7cc71117914..a4672a22384 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -76,7 +76,7 @@ typedef struct st_join_cache {
enum join_type { JT_UNKNOWN,JT_SYSTEM,JT_CONST,JT_EQ_REF,JT_REF,JT_MAYBE_REF,
JT_ALL, JT_RANGE, JT_NEXT, JT_FT, JT_REF_OR_NULL,
- JT_UNIQUE_SUBQUERY, JT_INDEX_SUBQUERY};
+ JT_UNIQUE_SUBQUERY, JT_INDEX_SUBQUERY, JT_INDEX_MERGE};
class JOIN;
@@ -85,7 +85,7 @@ typedef struct st_join_table {
KEYUSE *keyuse; /* pointer to first used key */
SQL_SELECT *select;
COND *select_cond;
- QUICK_SELECT *quick;
+ QUICK_SELECT_I *quick;
Item *on_expr;
const char *info;
byte *null_ref_key;
@@ -323,6 +323,7 @@ void copy_fields(TMP_TABLE_PARAM *param);
void copy_funcs(Item **func_ptr);
bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
int error, bool ignore_last_dupp_error);
+uint find_shortest_key(TABLE *table, const key_map *usable_keys);
/* functions from opt_sum.cc */
int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds);
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 52f7e6cb9ed..3dfef4b7189 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1388,7 +1388,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
thd_info->command=(int) tmp->command;
if ((mysys_var= tmp->mysys_var))
pthread_mutex_lock(&mysys_var->mutex);
- thd_info->proc_info= (char*) (tmp->killed ? "Killed" : 0);
+ thd_info->proc_info= (char*) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0);
#ifndef EMBEDDED_LIBRARY
thd_info->state_info= (char*) (tmp->locked ? "Locked" :
tmp->net.reading_or_writing ?
diff --git a/sql/sql_sort.h b/sql/sql_sort.h
index 9f95ffa4884..1831c4a2f3d 100644
--- a/sql/sql_sort.h
+++ b/sql/sql_sort.h
@@ -78,3 +78,4 @@ int merge_buffers(SORTPARAM *param,IO_CACHE *from_file,
IO_CACHE *to_file, uchar *sort_buffer,
BUFFPEK *lastbuff,BUFFPEK *Fb,
BUFFPEK *Tb,int flag);
+void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length);
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index d61a52003ec..0b48ae9bc2e 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -255,16 +255,12 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
if (some_tables_deleted || tmp_table_deleted)
{
query_cache_invalidate3(thd, tables, 0);
- if (!dont_log_query)
+ if (!dont_log_query && mysql_bin_log.is_open())
{
- mysql_update_log.write(thd, thd->query,thd->query_length);
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- tmp_table_deleted && !some_tables_deleted);
- mysql_bin_log.write(&qinfo);
- }
+ thd->clear_error();
+ Query_log_event qinfo(thd, thd->query, thd->query_length,
+ tmp_table_deleted && !some_tables_deleted);
+ mysql_bin_log.write(&qinfo);
}
}
@@ -988,18 +984,13 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
}
thd->tmp_table_used= 1;
}
- if (!tmp_table && !no_log)
+ if (!tmp_table && !no_log && mysql_bin_log.is_open())
{
- // Must be written before unlock
- mysql_update_log.write(thd,thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- test(create_info->options &
- HA_LEX_CREATE_TMP_TABLE));
- mysql_bin_log.write(&qinfo);
- }
+ thd->clear_error();
+ Query_log_event qinfo(thd, thd->query, thd->query_length,
+ test(create_info->options &
+ HA_LEX_CREATE_TMP_TABLE));
+ mysql_bin_log.write(&qinfo);
}
error=0;
end:
@@ -1246,7 +1237,7 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
}
else
{
- char* backup_dir = thd->lex.backup_dir;
+ char* backup_dir = thd->lex->backup_dir;
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
char* table_name = table->real_name;
char* db = thd->db ? thd->db : table->db;
@@ -1917,7 +1908,6 @@ int mysql_discard_or_import_tablespace(THD *thd,
error=1;
if (error)
goto err;
- mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2081,7 +2071,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
if (!error)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
@@ -2465,7 +2454,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
my_free((gptr) new_table,MYF(0));
goto err;
}
- mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
@@ -2597,7 +2585,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
goto err;
}
thd->proc_info="end";
- mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
thd->clear_error();
@@ -2696,8 +2683,8 @@ copy_data_between_tables(TABLE *from,TABLE *to,
tables.db = from->table_cache_key;
error=1;
- if (thd->lex.select_lex.setup_ref_array(thd, order_num) ||
- setup_order(thd, thd->lex.select_lex.ref_pointer_array,
+ if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
+ setup_order(thd, thd->lex->select_lex.ref_pointer_array,
&tables, fields, all_fields, order) ||
!(sortorder=make_unireg_sortorder(order, &length)) ||
(from->sort.found_records = filesort(thd, from, sortorder, length,
@@ -2727,7 +2714,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
error= 1;
break;
}
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 6763181ce4a..cd493fcac30 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -181,9 +181,39 @@ TEST_join(JOIN *join)
" quick select checked for each record (keys: %s)\n",
tab->select->quick_keys.print(buf));
else if (tab->select->quick)
- fprintf(DBUG_FILE," quick select used on key %s, length: %d\n",
+ {
+ int quick_type= tab->select->quick->get_type();
+ if ((quick_type == QUICK_SELECT_I::QS_TYPE_RANGE) ||
+ (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC))
+ {
+ fprintf(DBUG_FILE,
+ " quick select used on key %s, length: %d\n",
form->key_info[tab->select->quick->index].name,
tab->select->quick->max_used_key_length);
+ }
+ else if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
+ {
+ QUICK_INDEX_MERGE_SELECT *quick_imerge=
+ (QUICK_INDEX_MERGE_SELECT*)tab->select->quick;
+ QUICK_RANGE_SELECT *quick;
+ fprintf(DBUG_FILE,
+ " index_merge quick select used\n");
+
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_imerge->quick_selects);
+ while ((quick = it++))
+ {
+ fprintf(DBUG_FILE,
+ " range quick select: key %s, length: %d\n",
+ form->key_info[quick->index].name,
+ quick->max_used_key_length);
+ }
+ }
+ else
+ {
+ fprintf(DBUG_FILE,
+ " quick select of unknown nature used\n");
+ }
+ }
else
VOID(fputs(" select used\n",DBUG_FILE));
}
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index f2470a59944..25620229844 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -108,7 +108,7 @@ bool select_union::flush()
int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result)
{
- SELECT_LEX *lex_select_save= thd_arg->lex.current_select;
+ SELECT_LEX *lex_select_save= thd_arg->lex->current_select;
SELECT_LEX *sl, *first_select;
select_result *tmp_result;
DBUG_ENTER("st_select_lex_unit::prepare");
@@ -124,7 +124,7 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result)
prepared= 1;
res= 0;
- thd_arg->lex.current_select= sl= first_select= first_select_in_union();
+ thd_arg->lex->current_select= sl= first_select= first_select_in_union();
found_rows_for_union= first_select->options & OPTION_FOUND_ROWS;
/* Global option */
@@ -148,7 +148,7 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result)
JOIN *join= new JOIN(thd_arg, sl->item_list,
sl->options | thd_arg->options | SELECT_NO_UNLOCK,
tmp_result);
- thd_arg->lex.current_select= sl;
+ thd_arg->lex->current_select= sl;
offset_limit_cnt= sl->offset_limit;
select_limit_cnt= sl->select_limit+sl->offset_limit;
if (select_limit_cnt < sl->select_limit)
@@ -221,7 +221,7 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result)
union_result->set_table(table);
item_list.empty();
- thd_arg->lex.current_select= lex_select_save;
+ thd_arg->lex->current_select= lex_select_save;
{
Field **field;
for (field= table->field; *field; field++)
@@ -234,19 +234,19 @@ int st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result)
else
first_select->braces= 0; // remove our changes
- thd_arg->lex.current_select= lex_select_save;
+ thd_arg->lex->current_select= lex_select_save;
DBUG_RETURN(res || thd_arg->is_fatal_error ? 1 : 0);
err:
- thd_arg->lex.current_select= lex_select_save;
+ thd_arg->lex->current_select= lex_select_save;
DBUG_RETURN(-1);
}
int st_select_lex_unit::exec()
{
- SELECT_LEX *lex_select_save= thd->lex.current_select;
+ SELECT_LEX *lex_select_save= thd->lex->current_select;
SELECT_LEX *select_cursor=first_select_in_union();
ulonglong add_rows=0;
DBUG_ENTER("st_select_lex_unit::exec");
@@ -266,7 +266,7 @@ int st_select_lex_unit::exec()
for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select())
{
ha_rows records_at_start= 0;
- thd->lex.current_select= sl;
+ thd->lex->current_select= sl;
if (optimized)
res= sl->join->reinit();
@@ -338,13 +338,13 @@ int st_select_lex_unit::exec()
offset_limit_cnt= sl->offset_limit;
if (!res && union_result->flush())
{
- thd->lex.current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
DBUG_RETURN(1);
}
}
if (res)
{
- thd->lex.current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
DBUG_RETURN(res);
}
/* Needed for the following test and for records_at_start in next loop */
@@ -375,7 +375,7 @@ int st_select_lex_unit::exec()
if (!thd->is_fatal_error) // Check if EOM
{
ulong options_tmp= thd->options;
- thd->lex.current_select= fake_select_lex;
+ thd->lex->current_select= fake_select_lex;
offset_limit_cnt= global_parameters->offset_limit;
select_limit_cnt= global_parameters->select_limit +
global_parameters->offset_limit;
@@ -384,7 +384,7 @@ int st_select_lex_unit::exec()
select_limit_cnt= HA_POS_ERROR; // no limit
if (select_limit_cnt == HA_POS_ERROR)
options_tmp&= ~OPTION_FOUND_ROWS;
- else if (found_rows_for_union && !thd->lex.describe)
+ else if (found_rows_for_union && !thd->lex->describe)
options_tmp|= OPTION_FOUND_ROWS;
fake_select_lex->ftfunc_list= &empty_list;
fake_select_lex->table_list.link_in_list((byte *)&result_table_list,
@@ -430,7 +430,7 @@ int st_select_lex_unit::exec()
*/
}
}
- thd->lex.current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
DBUG_RETURN(res);
}
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 7dc537768f3..2f4c6b55963 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -68,7 +68,7 @@ int mysql_update(THD *thd,
SQL_SELECT *select;
READ_RECORD info;
TABLE_LIST *update_table_list= ((TABLE_LIST*)
- thd->lex.select_lex.table_list.first);
+ thd->lex->select_lex.table_list.first);
TABLE_LIST tables;
List<Item> all_fields;
DBUG_ENTER("mysql_update");
@@ -79,7 +79,7 @@ int mysql_update(THD *thd,
if ((open_and_lock_tables(thd, table_list)))
DBUG_RETURN(-1);
thd->proc_info="init";
- fix_tables_pointers(thd->lex.all_selects_list);
+ fix_tables_pointers(thd->lex->all_selects_list);
table= table_list->table;
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
@@ -97,10 +97,10 @@ int mysql_update(THD *thd,
if (setup_tables(update_table_list) ||
setup_conds(thd,update_table_list,&conds) ||
- thd->lex.select_lex.setup_ref_array(thd, order_num) ||
- setup_order(thd, thd->lex.select_lex.ref_pointer_array,
+ thd->lex->select_lex.setup_ref_array(thd, order_num) ||
+ setup_order(thd, thd->lex->select_lex.ref_pointer_array,
&tables, all_fields, all_fields, order) ||
- setup_ftfuncs(&thd->lex.select_lex))
+ setup_ftfuncs(&thd->lex->select_lex))
DBUG_RETURN(-1); /* purecov: inspected */
/* Check that we are not using table that we are updating in a sub select */
@@ -144,7 +144,7 @@ int mysql_update(THD *thd,
#endif
if (setup_fields(thd, 0, update_table_list, values, 0, 0, 0))
{
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
DBUG_RETURN(-1); /* purecov: inspected */
}
@@ -155,7 +155,7 @@ int mysql_update(THD *thd,
(select && select->check_quick(thd, safe_update, limit)) || !limit)
{
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
if (error)
{
DBUG_RETURN(-1); // Error in where
@@ -174,13 +174,21 @@ int mysql_update(THD *thd,
goto err;
}
}
- init_ftfuncs(thd, &thd->lex.select_lex, 1);
+ init_ftfuncs(thd, &thd->lex->select_lex, 1);
/* Check if we are modifying a key that we are used to search with */
if (select && select->quick)
- used_key_is_modified= (!select->quick->unique_key_range() &&
- check_if_key_used(table,
- (used_index=select->quick->index),
- fields));
+ {
+ if (select->quick->get_type() != QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
+ {
+ used_index= select->quick->index;
+ used_key_is_modified= (!select->quick->unique_key_range() &&
+ check_if_key_used(table,used_index,fields));
+ }
+ else
+ {
+ used_key_is_modified= true;
+ }
+ }
else if ((used_index=table->file->key_used_on_scan) < MAX_KEY)
used_key_is_modified=check_if_key_used(table, used_index, fields);
else
@@ -349,7 +357,6 @@ int mysql_update(THD *thd,
log_delayed= (transactional_table || table->tmp_table);
if (updated && (error <= 0 || !transactional_table))
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
if (error <= 0)
@@ -375,9 +382,9 @@ int mysql_update(THD *thd,
}
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
if (error >= 0)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0); /* purecov: inspected */
+ send_error(thd,thd->killed_errno()); /* purecov: inspected */
else
{
char buff[80];
@@ -394,7 +401,7 @@ int mysql_update(THD *thd,
err:
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
if (table->key_read)
{
table->key_read=0;
@@ -431,7 +438,7 @@ int mysql_multi_update(THD *thd,
#endif
if ((res=open_and_lock_tables(thd,table_list)))
DBUG_RETURN(res);
- fix_tables_pointers(thd->lex.all_selects_list);
+ fix_tables_pointers(thd->lex->all_selects_list);
select_lex->select_limit= HA_POS_ERROR;
if (setup_fields(thd, 0, table_list, *fields, 1, 0, 0))
@@ -723,8 +730,26 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields)
case JT_ALL:
/* If range search on index */
if (join_tab->quick)
- return !check_if_key_used(table, join_tab->quick->index,
- *fields);
+ {
+ if (join_tab->quick->get_type() != QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
+ {
+ return !check_if_key_used(table,join_tab->quick->index,*fields);
+ }
+ else
+ {
+ QUICK_INDEX_MERGE_SELECT *qsel_imerge=
+ (QUICK_INDEX_MERGE_SELECT*)(join_tab->quick);
+ List_iterator_fast<QUICK_RANGE_SELECT> it(qsel_imerge->quick_selects);
+ QUICK_RANGE_SELECT *quick;
+ while ((quick= it++))
+ {
+ if (check_if_key_used(table, quick->index, *fields))
+ return 0;
+ }
+ return 1;
+ }
+ }
+
/* If scanning in clustered key */
if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
table->primary_key < MAX_KEY)
@@ -1004,7 +1029,6 @@ bool multi_update::send_eof()
if (updated && (local_error <= 0 || !trans_safe))
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
if (local_error <= 0)
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index a1e3c566826..bd73762c5a7 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -28,13 +28,17 @@
#define MYSQL_YACC
#define YYINITDEPTH 100
#define YYMAXDEPTH 3200 /* Because of 64K stack */
-#define Lex (&(YYTHD->lex))
+#define Lex ((YYTHD->lex))
#define Select Lex->current_select
#include "mysql_priv.h"
#include "slave.h"
#include "sql_acl.h"
#include "lex_symbol.h"
#include "item_create.h"
+#include "sp_head.h"
+#include "sp_pcontext.h"
+#include "sp_rcontext.h"
+#include "sp.h"
#include <myisam.h>
#include <myisammrg.h>
@@ -82,10 +86,13 @@ inline Item *or_or_concat(THD *thd, Item* A, Item* B)
enum Item_udftype udf_type;
CHARSET_INFO *charset;
thr_lock_type lock_type;
- interval_type interval;
+ interval_type interval, interval_time_st;
timestamp_type date_time_type;
st_select_lex *select_lex;
chooser_compare_func_creator boolfunc2creator;
+ struct sp_cond_type *spcondtype;
+ struct { int vars, conds, hndlrs, curs; } spblock;
+ struct st_lex *lex;
}
%{
@@ -126,6 +133,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token AVG_SYM
%token BEGIN_SYM
%token BINLOG_SYM
+%token CALL_SYM
%token CHANGE
%token CLIENT_SYM
%token COMMENT_SYM
@@ -134,7 +142,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token CREATE
%token CROSS
%token CUBE_SYM
+%token DEFINER_SYM
%token DELETE_SYM
+%token DETERMINISTIC_SYM
%token DUAL_SYM
%token DO_SYM
%token DROP
@@ -164,6 +174,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SELECT_SYM
%token SHOW
%token SLAVE
+%token SQL_SYM
%token SQL_THREAD
%token START_SYM
%token STD_SYM
@@ -207,10 +218,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token COLUMNS
%token COLUMN_SYM
%token CONCURRENT
+%token CONDITION_SYM
+%token CONNECTION_SYM
%token CONSTRAINT
+%token CONTINUE_SYM
%token CONVERT_SYM
%token DATABASES
%token DATA_SYM
+%token DECLARE_SYM
%token DEFAULT
%token DELAYED_SYM
%token DELAY_KEY_WRITE_SYM
@@ -228,14 +243,17 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token DIRECTORY_SYM
%token ESCAPE_SYM
%token EXISTS
+%token EXIT_SYM
%token EXTENDED_SYM
%token FALSE_SYM
+%token FETCH_SYM
%token FILE_SYM
%token FIRST_SYM
%token FIXED_SYM
%token FLOAT_NUM
%token FORCE_SYM
%token FOREIGN
+%token FOUND_SYM
%token FROM
%token FULL
%token FULLTEXT_SYM
@@ -258,8 +276,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token INFILE
%token INNER_SYM
%token INNOBASE_SYM
+%token INOUT_SYM
%token INTO
%token IN_SYM
+%token INVOKER_SYM
%token ISOLATION
%token JOIN_SYM
%token KEYS
@@ -269,9 +289,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token LEAVES
%token LEVEL_SYM
%token LEX_HOSTNAME
+%token LANGUAGE_SYM
%token LIKE
%token LINES
%token LOCAL_SYM
+%token LOCATOR_SYM
%token LOG_SYM
%token LOGS_SYM
%token LONG_NUM
@@ -301,6 +323,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token MEDIUM_SYM
%token MIN_ROWS
%token NAMES_SYM
+%token NAME_SYM
%token NATIONAL_SYM
%token NATURAL
%token NEW_SYM
@@ -319,6 +342,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token OR
%token OR_OR_CONCAT
%token ORDER_SYM
+%token OUT_SYM
%token OUTER
%token OUTFILE
%token DUMPFILE
@@ -350,6 +374,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token ROW_FORMAT_SYM
%token ROW_SYM
%token RTREE_SYM
+%token SECURITY_SYM
%token SET
%token SEPARATOR_SYM
%token SERIAL_SYM
@@ -358,6 +383,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SIMPLE_SYM
%token SHUTDOWN
%token SPATIAL_SYM
+%token SPECIFIC_SYM
+%token SQLEXCEPTION_SYM
+%token SQLSTATE_SYM
+%token SQLWARNING_SYM
%token SSL_SYM
%token STARTING
%token STATUS_SYM
@@ -380,11 +409,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token FUNC_ARG1
%token FUNC_ARG2
%token FUNC_ARG3
-%token UDF_RETURNS_SYM
+%token RETURN_SYM
+%token RETURNS_SYM
%token UDF_SONAME_SYM
-%token UDF_SYM
+%token FUNCTION_SYM
%token UNCOMMITTED_SYM
%token UNDERSCORE_CHARSET
+%token UNDO_SYM
%token UNICODE_SYM
%token UNION_SYM
%token UNIQUE_SYM
@@ -436,6 +467,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token STRING_SYM
%token TEXT_SYM
%token TIMESTAMP
+%token TIMESTAMP_ADD
+%token TIMESTAMP_DIFF
%token TIME_SYM
%token TINYBLOB
%token TINYINT
@@ -481,6 +514,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token FIELD_FUNC
%token FORMAT_SYM
%token FOR_SYM
+%token FRAC_SECOND_SYM
%token FROM_UNIXTIME
%token GEOMCOLLFROMTEXT
%token GEOMFROMTEXT
@@ -526,6 +560,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token POLYGON
%token POSITION_SYM
%token PROCEDURE
+%token QUARTER_SYM
%token RAND
%token REPLACE
%token RIGHT
@@ -533,6 +568,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SECOND_SYM
%token SECOND_MICROSECOND_SYM
%token SHARE_SYM
+%token SP_FUNC
%token SUBDATE_SYM
%token SUBSTRING
%token SUBSTRING_INDEX
@@ -566,6 +602,18 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SQL_SMALL_RESULT
%token SQL_BUFFER_RESULT
+%token CURSOR_SYM
+%token ELSEIF_SYM
+%token ITERATE_SYM
+%token LEAVE_SYM
+%token LOOP_SYM
+%token REPEAT_SYM
+%token UNTIL_SYM
+%token WHILE_SYM
+%token ASENSITIVE_SYM
+%token INSENSITIVE_SYM
+%token SENSITIVE_SYM
+
%token ISSUER_SYM
%token SUBJECT_SYM
%token CIPHER_SYM
@@ -592,6 +640,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text
UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
NCHAR_STRING opt_component key_cache_name
+ SP_FUNC ident_or_spfunc sp_opt_label
%type <lex_str_ptr>
opt_table_alias
@@ -625,14 +674,15 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%type <item>
literal text_literal insert_ident order_ident
simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr
- table_wild no_in_expr expr_expr simple_expr no_and_expr
+ table_wild no_in_expr expr_expr simple_expr no_and_expr udf_expr
using_list expr_or_default set_expr_or_default interval_expr
param_marker singlerow_subselect singlerow_subselect_init
- exists_subselect exists_subselect_init
signed_literal NUM_literal
+ exists_subselect exists_subselect_init sp_opt_default
%type <item_list>
- expr_list udf_expr_list when_list ident_list ident_list_arg
+ expr_list sp_expr_list udf_expr_list udf_expr_list2 when_list
+ ident_list ident_list_arg
%type <key_type>
key_type opt_unique_or_fulltext constraint_key_type
@@ -656,6 +706,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%type <date_time_type> date_time_type;
%type <interval> interval
+%type <interval_time_st> interval_time_st
+
%type <db_type> storage_engines
%type <row_type> row_types
@@ -699,7 +751,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *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 handler
+ when_list2 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
@@ -719,8 +771,16 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
precision subselect_start opt_and charset
subselect_end select_var_list select_var_list_init help opt_len
opt_extended_describe
+ statement sp_suid
+ sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic sp_a_chistic
END_OF_INPUT
+%type <NONE> call sp_proc_stmts sp_proc_stmt
+%type <num> sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list
+%type <spcondtype> sp_cond sp_hcond
+%type <spblock> sp_decls sp_decl
+%type <lex> sp_cursor_stmt
+
%type <NONE>
'-' '+' '*' '/' '%' '(' ')'
',' '!' '{' '}' '&' '|' AND OR OR_OR_CONCAT BETWEEN_SYM CASE_SYM
@@ -733,23 +793,29 @@ query:
{
THD *thd= YYTHD;
if (!thd->bootstrap &&
- (!(thd->lex.select_lex.options & OPTION_FOUND_COMMENT)))
+ (!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT)))
{
send_error(thd,ER_EMPTY_QUERY);
YYABORT;
}
else
{
- thd->lex.sql_command = SQLCOM_EMPTY_QUERY;
+ thd->lex->sql_command = SQLCOM_EMPTY_QUERY;
}
}
| verb_clause END_OF_INPUT {};
verb_clause:
+ statement
+ | begin
+ ;
+
+/* Verb clauses, except begin */
+statement:
alter
| analyze
| backup
- | begin
+ | call
| change
| check
| checksum
@@ -962,20 +1028,983 @@ create:
lex->name=$4.str;
lex->create_info.options=$3;
}
- | CREATE udf_func_type UDF_SYM IDENT_sys
+ | CREATE udf_func_type FUNCTION_SYM ident_or_spfunc
{
LEX *lex=Lex;
- lex->sql_command = SQLCOM_CREATE_FUNCTION;
lex->udf.name = $4;
lex->udf.type= $2;
}
- UDF_RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
+ create_function_tail
+ {}
+ | CREATE PROCEDURE ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp;
+
+ if (lex->sphead)
+ {
+ net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "PROCEDURE");
+ YYABORT;
+ }
+ /* Order is important here: new - reset - init */
+ sp= new sp_head();
+ sp->reset_thd_mem_root(YYTHD);
+ sp->init(lex);
+
+ sp->m_type= TYPE_ENUM_PROCEDURE;
+ lex->sphead= sp;
+ /*
+ * We have to turn of CLIENT_MULTI_QUERIES while parsing a
+ * stored procedure, otherwise yylex will chop it into pieces
+ * at each ';'.
+ */
+ sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
+ YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
+ }
+ '('
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->m_param_begin= lex->tok_start+1;
+ }
+ sp_pdparam_list
+ ')'
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->m_param_end= lex->tok_start;
+ lex->spcont->set_params();
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ }
+ sp_c_chistics
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->m_chistics= &lex->sp_chistics;
+ lex->sphead->m_body_begin= lex->tok_start;
+ }
+ sp_proc_stmt
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->init_strings(YYTHD, lex, &$3);
+ lex->sql_command= SQLCOM_CREATE_PROCEDURE;
+ /* Restore flag if it was cleared above */
+ if (lex->sphead->m_old_cmq)
+ YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
+ lex->sphead->restore_thd_mem_root(YYTHD);
+ }
+ ;
+
+ident_or_spfunc:
+ IDENT_sys { $$= $1; }
+ | SP_FUNC { $$= $1; }
+ ;
+
+create_function_tail:
+ RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
{
LEX *lex=Lex;
- lex->udf.returns=(Item_result) $7;
- lex->udf.dl=$9.str;
+ lex->sql_command = SQLCOM_CREATE_FUNCTION;
+ lex->udf.returns=(Item_result) $2;
+ lex->udf.dl=$4.str;
}
- ;
+ | '('
+ {
+ LEX *lex= Lex;
+ sp_head *sp;
+
+ if (lex->sphead)
+ {
+ net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "FUNCTION");
+ YYABORT;
+ }
+ /* Order is important here: new - reset - init */
+ sp= new sp_head();
+ sp->reset_thd_mem_root(YYTHD);
+ sp->init(lex);
+
+ sp->m_type= TYPE_ENUM_FUNCTION;
+ lex->sphead= sp;
+ /*
+ * We have to turn of CLIENT_MULTI_QUERIES while parsing a
+ * stored procedure, otherwise yylex will chop it into pieces
+ * at each ';'.
+ */
+ sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
+ YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
+ lex->sphead->m_param_begin= lex->tok_start+1;
+ }
+ sp_fdparam_list ')'
+ {
+ LEX *lex= Lex;
+
+ lex->spcont->set_params();
+ lex->sphead->m_param_end= lex->tok_start;
+ }
+ RETURNS_SYM
+ {
+ Lex->sphead->m_returns_begin= Lex->tok_start;
+ }
+ type
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->m_returns_end= lex->tok_start;
+ lex->sphead->m_returns= (enum enum_field_types)$8;
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ }
+ sp_c_chistics
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->m_chistics= &lex->sp_chistics;
+ lex->sphead->m_body_begin= lex->tok_start;
+ }
+ sp_proc_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+
+ lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
+ sp->init_strings(YYTHD, lex, &lex->udf.name);
+ /* Restore flag if it was cleared above */
+ if (sp->m_old_cmq)
+ YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
+ sp->restore_thd_mem_root(YYTHD);
+ }
+ ;
+
+sp_a_chistics:
+ /* Empty */ {}
+ | sp_a_chistics sp_a_chistic {}
+ ;
+
+sp_c_chistics:
+ /* Empty */ {}
+ | sp_c_chistics sp_c_chistic {}
+ ;
+
+/* Characteristics for both create and alter */
+sp_chistic:
+ COMMENT_SYM TEXT_STRING_sys { Lex->sp_chistics.comment= $2; }
+ | sp_suid { }
+ ;
+
+/* Alter characteristics */
+sp_a_chistic:
+ sp_chistic { }
+ | NAME_SYM ident { Lex->name= $2.str; }
+ ;
+
+/* Create characteristics */
+sp_c_chistic:
+ sp_chistic { }
+ | LANGUAGE_SYM SQL_SYM { }
+ | DETERMINISTIC_SYM { Lex->sp_chistics.detistic= TRUE; }
+ | NOT DETERMINISTIC_SYM { Lex->sp_chistics.detistic= FALSE; }
+ ;
+
+sp_suid:
+ SQL_SYM SECURITY_SYM DEFINER_SYM
+ {
+ Lex->sp_chistics.suid= IS_SUID;
+ }
+ | SQL_SYM SECURITY_SYM INVOKER_SYM
+ {
+ Lex->sp_chistics.suid= IS_NOT_SUID;
+ }
+ ;
+
+call:
+ CALL_SYM ident_or_spfunc
+ {
+ LEX *lex = Lex;
+
+ lex->sql_command= SQLCOM_CALL;
+ lex->udf.name= $2;
+ lex->value_list.empty();
+ }
+ '(' sp_cparam_list ')' {}
+ ;
+
+/* CALL parameters */
+sp_cparam_list:
+ /* Empty */
+ | sp_cparams
+ ;
+
+sp_cparams:
+ sp_cparams ',' expr
+ {
+ Lex->value_list.push_back($3);
+ }
+ | expr
+ {
+ Lex->value_list.push_back($1);
+ }
+ ;
+
+/* Stored FUNCTION parameter declaration list */
+sp_fdparam_list:
+ /* Empty */
+ | sp_fdparams
+ ;
+
+sp_fdparams:
+ sp_fdparams ',' sp_fdparam
+ | sp_fdparam
+ ;
+
+sp_fdparam:
+ ident type sp_opt_locator
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_pvar(&$1, TRUE))
+ {
+ net_printf(YYTHD, ER_SP_DUP_PARAM, $1.str);
+ YYABORT;
+ }
+ spc->push_pvar(&$1, (enum enum_field_types)$2, sp_param_in);
+ }
+ ;
+
+/* Stored PROCEDURE parameter declaration list */
+sp_pdparam_list:
+ /* Empty */
+ | sp_pdparams
+ ;
+
+sp_pdparams:
+ sp_pdparams ',' sp_pdparam
+ | sp_pdparam
+ ;
+
+sp_pdparam:
+ sp_opt_inout ident type sp_opt_locator
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_pvar(&$2, TRUE))
+ {
+ net_printf(YYTHD, ER_SP_DUP_PARAM, $2.str);
+ YYABORT;
+ }
+ spc->push_pvar(&$2, (enum enum_field_types)$3,
+ (sp_param_mode_t)$1);
+ }
+ ;
+
+sp_opt_inout:
+ /* Empty */ { $$= sp_param_in; }
+ | IN_SYM { $$= sp_param_in; }
+ | OUT_SYM { $$= sp_param_out; }
+ | INOUT_SYM { $$= sp_param_inout; }
+ ;
+
+sp_opt_locator:
+ /* Empty */
+ | AS LOCATOR_SYM
+ ;
+
+sp_proc_stmts:
+ /* Empty */ {}
+ | sp_proc_stmts sp_proc_stmt ';'
+ ;
+
+sp_decls:
+ /* Empty */
+ {
+ $$.vars= $$.conds= $$.hndlrs= $$.curs= 0;
+ }
+ | sp_decls sp_decl ';'
+ {
+ $$.vars= $1.vars + $2.vars;
+ $$.conds= $1.conds + $2.conds;
+ $$.hndlrs= $1.hndlrs + $2.hndlrs;
+ $$.curs= $1.curs + $2.curs;
+ }
+ ;
+
+sp_decl:
+ DECLARE_SYM sp_decl_idents type sp_opt_default
+ {
+ LEX *lex= Lex;
+ uint max= lex->spcont->current_framesize();
+ enum enum_field_types type= (enum enum_field_types)$3;
+ Item *it= $4;
+
+ for (uint i = max-$2 ; i < max ; i++)
+ {
+ lex->spcont->set_type(i, type);
+ if (! it)
+ lex->spcont->set_isset(i, FALSE);
+ else
+ {
+ sp_instr_set *in= new sp_instr_set(lex->sphead->instructions(),
+ i, it, type);
+
+ lex->sphead->add_instr(in);
+ lex->spcont->set_isset(i, TRUE);
+ }
+ }
+ $$.vars= $2;
+ $$.conds= $$.hndlrs= $$.curs= 0;
+ }
+ | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_cond(&$2, TRUE))
+ {
+ net_printf(YYTHD, ER_SP_DUP_COND, $2.str);
+ YYABORT;
+ }
+ YYTHD->lex->spcont->push_cond(&$2, $5);
+ $$.vars= $$.hndlrs= $$.curs= 0;
+ $$.conds= 1;
+ }
+ | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ sp_instr_hpush_jump *i=
+ new sp_instr_hpush_jump(sp->instructions(), $2,
+ ctx->current_framesize());
+
+ sp->add_instr(i);
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ ctx->add_handler();
+ }
+ sp_hcond_list sp_proc_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */
+
+ if ($2 == SP_HANDLER_CONTINUE)
+ sp->add_instr(new sp_instr_hreturn(sp->instructions(),
+ lex->spcont->current_framesize()));
+ else
+ { /* EXIT or UNDO handler, just jump to the end of the block */
+ sp_instr_jump *i= new sp_instr_jump(sp->instructions());
+
+ sp->add_instr(i);
+ sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */
+ }
+ lex->sphead->backpatch(hlab);
+ $$.vars= $$.conds= $$.curs= 0;
+ $$.hndlrs= $6;
+ }
+ | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *spc= lex->spcont;
+ uint offp;
+ sp_instr_cpush *i;
+
+ if (spc->find_cursor(&$2, &offp, TRUE))
+ {
+ net_printf(YYTHD, ER_SP_DUP_CURS, $2.str);
+ delete $5;
+ YYABORT;
+ }
+ i= new sp_instr_cpush(sp->instructions(), $5);
+ sp->add_instr(i);
+ lex->spcont->push_cursor(&$2);
+ $$.vars= $$.conds= $$.hndlrs= 0;
+ $$.curs= 1;
+ }
+ ;
+
+sp_cursor_stmt:
+ {
+ Lex->sphead->reset_lex(YYTHD);
+
+ /* We use statement here just be able to get a better
+ error message. Using 'select' works too, but will then
+ result in a generic "syntax error" if a non-select
+ statement is given. */
+ }
+ statement
+ {
+ LEX *lex= Lex;
+
+ if (lex->sql_command != SQLCOM_SELECT)
+ {
+ send_error(YYTHD, ER_SP_BAD_CURSOR_QUERY);
+ YYABORT;
+ }
+ if (lex->result)
+ {
+ send_error(YYTHD, ER_SP_BAD_CURSOR_SELECT);
+ YYABORT;
+ }
+ lex->sp_lex_in_use= TRUE;
+ $$= lex;
+ lex->sphead->restore_lex(YYTHD);
+ }
+ ;
+
+sp_handler_type:
+ EXIT_SYM { $$= SP_HANDLER_EXIT; }
+ | CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; }
+/* | UNDO_SYM { QQ No yet } */
+ ;
+
+sp_hcond_list:
+ sp_hcond
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction();
+
+ i->add_condition($1);
+ $$= 1;
+ }
+ | sp_hcond_list ',' sp_hcond
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction();
+
+ i->add_condition($3);
+ $$= $1 + 1;
+ }
+ ;
+
+sp_cond:
+ ULONG_NUM
+ { /* mysql errno */
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::number;
+ $$->mysqlerr= $1;
+ }
+ | SQLSTATE_SYM opt_value TEXT_STRING_literal
+ { /* SQLSTATE */
+ uint len= ($3.length < sizeof($$->sqlstate)-1 ?
+ $3.length : sizeof($$->sqlstate)-1);
+
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::state;
+ memcpy($$->sqlstate, $3.str, len);
+ $$->sqlstate[len]= '\0';
+ }
+ ;
+
+opt_value:
+ /* Empty */ {}
+ | VALUE_SYM {}
+ ;
+
+sp_hcond:
+ sp_cond
+ {
+ $$= $1;
+ }
+ | ident /* CONDITION name */
+ {
+ $$= Lex->spcont->find_cond(&$1);
+ if ($$ == NULL)
+ {
+ net_printf(YYTHD, ER_SP_COND_MISMATCH, $1.str);
+ YYABORT;
+ }
+ }
+ | SQLWARNING_SYM /* SQLSTATEs 01??? */
+ {
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::warning;
+ }
+ | NOT FOUND_SYM /* SQLSTATEs 02??? */
+ {
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::notfound;
+ }
+ | SQLEXCEPTION_SYM /* All other SQLSTATEs */
+ {
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::exception;
+ }
+ ;
+
+sp_decl_idents:
+ ident
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_pvar(&$1, TRUE))
+ {
+ net_printf(YYTHD, ER_SP_DUP_VAR, $1.str);
+ YYABORT;
+ }
+ spc->push_pvar(&$1, (enum_field_types)0, sp_param_in);
+ $$= 1;
+ }
+ | sp_decl_idents ',' ident
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+
+ if (spc->find_pvar(&$3, TRUE))
+ {
+ net_printf(YYTHD, ER_SP_DUP_VAR, $3.str);
+ YYABORT;
+ }
+ spc->push_pvar(&$3, (enum_field_types)0, sp_param_in);
+ $$= $1 + 1;
+ }
+ ;
+
+sp_opt_default:
+ /* Empty */ { $$ = NULL; }
+ | DEFAULT expr { $$ = $2; }
+ ;
+
+sp_proc_stmt:
+ {
+ Lex->sphead->reset_lex(YYTHD);
+ }
+ statement
+ {
+ LEX *lex= Lex;
+
+ if (lex->sql_command == SQLCOM_SELECT && !lex->result)
+ {
+ /* We maybe have one or more SELECT without INTO */
+ lex->sphead->m_multi_results= TRUE;
+ }
+ /* Don't add an instruction for empty SET statements.
+ ** (This happens if the SET only contained local variables,
+ ** which get their set instructions generated separately.)
+ */
+ if (lex->sql_command != SQLCOM_SET_OPTION ||
+ ! lex->var_list.is_empty())
+ {
+ /* Currently we can't handle queries inside a FUNCTION,
+ ** because of the way table locking works.
+ ** This is unfortunate, and limits the usefulness of functions
+ ** a great deal, but it's nothing we can do about this at the
+ ** moment.
+ */
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
+ lex->sql_command != SQLCOM_SET_OPTION)
+ {
+ send_error(YYTHD, ER_SP_BADSTATEMENT);
+ YYABORT;
+ }
+ else
+ {
+ sp_instr_stmt *i=new sp_instr_stmt(lex->sphead->instructions());
+
+ i->set_lex(lex);
+ lex->sphead->add_instr(i);
+ lex->sp_lex_in_use= TRUE;
+ }
+ }
+ lex->sphead->restore_lex(YYTHD);
+ }
+ | RETURN_SYM expr
+ {
+ LEX *lex= Lex;
+
+ if (lex->sphead->m_type == TYPE_ENUM_PROCEDURE)
+ {
+ send_error(YYTHD, ER_SP_BADRETURN);
+ YYABORT;
+ }
+ else
+ {
+ sp_instr_freturn *i;
+
+ if ($2->type() == Item::SUBSELECT_ITEM)
+ { /* QQ For now, just disallow subselects as values */
+ send_error(lex->thd, ER_SP_BADSTATEMENT);
+ YYABORT;
+ }
+ i= new sp_instr_freturn(lex->sphead->instructions(),
+ $2, lex->sphead->m_returns);
+ lex->sphead->add_instr(i);
+ lex->sphead->m_has_return= TRUE;
+ }
+ }
+ | IF sp_if END IF {}
+ | CASE_SYM WHEN_SYM
+ {
+ Lex->sphead->m_simple_case= FALSE;
+ }
+ sp_case END CASE_SYM {}
+ | CASE_SYM expr WHEN_SYM
+ {
+ /* We "fake" this by using an anonymous variable which we
+ set to the expression. Note that all WHENs are evaluate
+ at the same frame level, so we then know that it's the
+ top-most variable in the frame. */
+ LEX *lex= Lex;
+ uint offset= lex->spcont->current_framesize();
+ sp_instr_set *i = new sp_instr_set(lex->sphead->instructions(),
+ offset, $2, MYSQL_TYPE_STRING);
+ LEX_STRING dummy;
+
+ dummy.str= (char *)"";
+ dummy.length= 0;
+ lex->spcont->push_pvar(&dummy, MYSQL_TYPE_STRING, sp_param_in);
+ lex->sphead->add_instr(i);
+ lex->sphead->m_simple_case= TRUE;
+ }
+ sp_case END CASE_SYM
+ {
+ Lex->spcont->pop_pvar();
+ }
+ | sp_labeled_control
+ {}
+ | { /* Unlabeled controls get a secret label. */
+ LEX *lex= Lex;
+
+ lex->spcont->push_label((char *)"", lex->sphead->instructions());
+ }
+ sp_unlabeled_control
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ | LEAVE_SYM IDENT
+ {
+ LEX *lex= Lex;
+ sp_head *sp = lex->sphead;
+ sp_label_t *lab= lex->spcont->find_label($2.str);
+
+ if (! lab)
+ {
+ net_printf(YYTHD, ER_SP_LILABEL_MISMATCH, "LEAVE", $2.str);
+ YYABORT;
+ }
+ else
+ {
+ sp_instr_jump *i= new sp_instr_jump(sp->instructions());
+
+ sp->push_backpatch(i, lab); /* Jumping forward */
+ sp->add_instr(i);
+ }
+ }
+ | ITERATE_SYM IDENT
+ {
+ LEX *lex= Lex;
+ sp_label_t *lab= lex->spcont->find_label($2.str);
+
+ if (! lab || lab->isbegin)
+ {
+ net_printf(YYTHD, ER_SP_LILABEL_MISMATCH, "ITERATE", $2.str);
+ YYABORT;
+ }
+ else
+ {
+ uint ip= lex->sphead->instructions();
+ sp_instr_jump *i= new sp_instr_jump(ip, lab->ip); /* Jump back */
+
+ lex->sphead->add_instr(i);
+ }
+ }
+ | OPEN_SYM ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint offset;
+ sp_instr_copen *i;
+
+ if (! lex->spcont->find_cursor(&$2, &offset))
+ {
+ net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str);
+ YYABORT;
+ }
+ i= new sp_instr_copen(sp->instructions(), offset);
+ sp->add_instr(i);
+ }
+ | FETCH_SYM ident INTO
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint offset;
+ sp_instr_cfetch *i;
+
+ if (! lex->spcont->find_cursor(&$2, &offset))
+ {
+ net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str);
+ YYABORT;
+ }
+ i= new sp_instr_cfetch(sp->instructions(), offset);
+ sp->add_instr(i);
+ }
+ sp_fetch_list
+ { }
+ | CLOSE_SYM ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint offset;
+ sp_instr_cclose *i;
+
+ if (! lex->spcont->find_cursor(&$2, &offset))
+ {
+ net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str);
+ YYABORT;
+ }
+ i= new sp_instr_cclose(sp->instructions(), offset);
+ sp->add_instr(i);
+ }
+ ;
+
+sp_fetch_list:
+ ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ if (!spc || !(spv = spc->find_pvar(&$1)))
+ {
+ net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $1.str);
+ YYABORT;
+ }
+ else
+ { /* An SP local variable */
+ sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
+
+ i->add_to_varlist(spv);
+ spv->isset= TRUE;
+ }
+ }
+ |
+ sp_fetch_list ',' ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ if (!spc || !(spv = spc->find_pvar(&$3)))
+ {
+ net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $3.str);
+ YYABORT;
+ }
+ else
+ { /* An SP local variable */
+ sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
+
+ i->add_to_varlist(spv);
+ spv->isset= TRUE;
+ }
+ }
+ ;
+
+sp_if:
+ expr THEN_SYM
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $1);
+
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ sp->add_instr(i);
+ }
+ sp_proc_stmts
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump *i = new sp_instr_jump(ip);
+
+ sp->add_instr(i);
+ sp->backpatch(ctx->pop_label());
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ }
+ sp_elseifs
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_elseifs:
+ /* Empty */
+ | ELSEIF_SYM sp_if
+ | ELSE sp_proc_stmts
+ ;
+
+sp_case:
+ expr THEN_SYM
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i;
+
+ if (! sp->m_simple_case)
+ i= new sp_instr_jump_if_not(ip, $1);
+ else
+ { /* Simple case: <caseval> = <whenval> */
+ LEX_STRING ivar;
+
+ ivar.str= (char *)"_tmp_";
+ ivar.length= 5;
+ Item *var= (Item*) new Item_splocal(ivar,
+ ctx->current_framesize()-1);
+ Item *expr= new Item_func_eq(var, $1);
+
+ i= new sp_instr_jump_if_not(ip, expr);
+ }
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ sp->add_instr(i);
+ }
+ sp_proc_stmts
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump *i = new sp_instr_jump(ip);
+
+ sp->add_instr(i);
+ sp->backpatch(ctx->pop_label());
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ }
+ sp_whens
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_whens:
+ /* Empty */ {}
+ | WHEN_SYM sp_case {}
+ | ELSE sp_proc_stmts {}
+ ;
+
+sp_labeled_control:
+ IDENT ':'
+ {
+ LEX *lex= Lex;
+ sp_label_t *lab= lex->spcont->find_label($1.str);
+
+ if (lab)
+ {
+ net_printf(YYTHD, ER_SP_LABEL_REDEFINE, $1.str);
+ YYABORT;
+ }
+ else
+ {
+ lex->spcont->push_label($1.str,
+ lex->sphead->instructions());
+ }
+ }
+ sp_unlabeled_control sp_opt_label
+ {
+ LEX *lex= Lex;
+
+ if ($5.str)
+ {
+ sp_label_t *lab= lex->spcont->find_label($5.str);
+
+ if (!lab ||
+ my_strcasecmp(system_charset_info, $5.str, lab->name) != 0)
+ {
+ net_printf(YYTHD, ER_SP_LABEL_MISMATCH, $5.str);
+ YYABORT;
+ }
+ }
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_opt_label:
+ /* Empty */
+ { $$.str= NULL; $$.length= 0; }
+ | IDENT
+ { $$= $1; }
+ ;
+
+sp_unlabeled_control:
+ BEGIN_SYM
+ { /* QQ This is just a dummy for grouping declarations and statements
+ together. No [[NOT] ATOMIC] yet, and we need to figure out how
+ make it coexist with the existing BEGIN COMMIT/ROLLBACK. */
+ LEX *lex= Lex;
+ sp_label_t *lab= lex->spcont->last_label();
+
+ lab->isbegin= TRUE;
+ /* Scope duplicate checking */
+ lex->spcont->push_scope();
+ }
+ sp_decls
+ sp_proc_stmts
+ END
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+
+ sp->backpatch(ctx->last_label()); /* We always has a label */
+ ctx->pop_pvar($3.vars);
+ ctx->pop_cond($3.conds);
+ ctx->pop_cursor($3.curs);
+ if ($3.hndlrs)
+ sp->add_instr(new sp_instr_hpop(sp->instructions(),$3.hndlrs));
+ if ($3.curs)
+ sp->add_instr(new sp_instr_cpop(sp->instructions(), $3.curs));
+ ctx->pop_scope();
+ }
+ | LOOP_SYM
+ sp_proc_stmts END LOOP_SYM
+ {
+ LEX *lex= Lex;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_instr_jump *i = new sp_instr_jump(ip, lab->ip);
+
+ lex->sphead->add_instr(i);
+ }
+ | WHILE_SYM expr DO_SYM
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $2);
+
+ /* Jumping forward */
+ sp->push_backpatch(i, lex->spcont->last_label());
+ sp->add_instr(i);
+ }
+ sp_proc_stmts END WHILE_SYM
+ {
+ LEX *lex= Lex;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_instr_jump *i = new sp_instr_jump(ip, lab->ip);
+
+ lex->sphead->add_instr(i);
+ }
+ | REPEAT_SYM sp_proc_stmts UNTIL_SYM expr END REPEAT_SYM
+ {
+ LEX *lex= Lex;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $4, lab->ip);
+
+ lex->sphead->add_instr(i);
+ }
+ ;
create2:
'(' create2a {}
@@ -1658,7 +2687,7 @@ alter:
ALTER opt_ignore TABLE_SYM table_ident
{
THD *thd= YYTHD;
- LEX *lex=&thd->lex;
+ LEX *lex= thd->lex;
lex->sql_command = SQLCOM_ALTER_TABLE;
lex->name=0;
if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
@@ -1687,8 +2716,38 @@ alter:
LEX *lex=Lex;
lex->sql_command=SQLCOM_ALTER_DB;
lex->name=$3.str;
- };
+ }
+ | ALTER PROCEDURE ident
+ {
+ LEX *lex= Lex;
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ lex->name= 0;
+ }
+ sp_a_chistics
+ {
+ THD *thd= YYTHD;
+ LEX *lex=Lex;
+
+ lex->sql_command= SQLCOM_ALTER_PROCEDURE;
+ lex->udf.name= $3;
+ }
+ | ALTER FUNCTION_SYM ident
+ {
+ LEX *lex= Lex;
+
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ lex->name= 0;
+ }
+ sp_a_chistics
+ {
+ THD *thd= YYTHD;
+ LEX *lex=Lex;
+
+ lex->sql_command= SQLCOM_ALTER_FUNCTION;
+ lex->udf.name= $3;
+ }
+ ;
alter_list:
| DISCARD TABLESPACE { Lex->tablespace_op=DISCARD_TABLESPACE; }
@@ -2255,7 +3314,7 @@ select_item_list:
THD *thd= YYTHD;
if (add_item_to_list(thd, new Item_field(NULL, NULL, "*")))
YYABORT;
- (thd->lex.current_select->with_wild)++;
+ (thd->lex->current_select->with_wild)++;
};
@@ -2567,6 +3626,8 @@ simple_expr:
{ $$= new Item_date_add_interval($3, $5, INTERVAL_DAY, 0);}
| ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
{ $$= new Item_date_add_interval($3, $6, $7, 0); }
+ | REPEAT_SYM '(' expr ',' expr ')'
+ { $$= new Item_func_repeat($3,$5); }
| ATAN '(' expr ')'
{ $$= new Item_func_atan($3); }
| ATAN '(' expr ',' expr ')'
@@ -2779,6 +3840,8 @@ simple_expr:
Geometry::wkbPolygon, Geometry::wkbLineString); }
| POSITION_SYM '(' no_in_expr IN_SYM expr ')'
{ $$ = new Item_func_locate($5,$3); }
+ | QUARTER_SYM '(' expr ')'
+ { $$ = new Item_func_quarter($3); }
| RAND '(' expr ')'
{ $$= new Item_func_rand($3); Lex->uncacheable(UNCACHEABLE_RAND);}
| RAND '(' ')'
@@ -2812,6 +3875,10 @@ simple_expr:
{ $$= new Item_datetime_typecast($3); }
| TIMESTAMP '(' expr ',' expr ')'
{ $$= new Item_func_add_time($3, $5, 1, 0); }
+ | TIMESTAMP_ADD '(' interval_time_st ',' expr ',' expr ')'
+ { $$= new Item_date_add_interval($7,$5,$3,0); }
+ | TIMESTAMP_DIFF '(' interval_time_st ',' expr ',' expr ')'
+ { $$= new Item_func_timestamp_diff($5,$7,$3); }
| TRIM '(' expr ')'
{ $$= new Item_func_trim($3); }
| TRIM '(' LEADING expr FROM expr ')'
@@ -2832,6 +3899,14 @@ simple_expr:
{ $$= new Item_func_round($3,$5,1); }
| TRUE_SYM
{ $$= new Item_int((char*) "TRUE",1,1); }
+ | SP_FUNC '(' sp_expr_list ')'
+ {
+ sp_add_fun_to_lex(Lex, $1);
+ if ($3)
+ $$= new Item_func_sp($1, *$3);
+ else
+ $$= new Item_func_sp($1);
+ }
| UDA_CHAR_SUM '(' udf_expr_list ')'
{
if ($3 != NULL)
@@ -2920,10 +3995,43 @@ fulltext_options:
| IN_SYM BOOLEAN_SYM MODE_SYM { $$= FT_BOOL; }
;
-udf_expr_list:
+sp_expr_list:
/* empty */ { $$= NULL; }
| expr_list { $$= $1;};
+udf_expr_list:
+ /* empty */ { $$= NULL; }
+ | udf_expr_list2 { $$= $1;}
+ ;
+
+udf_expr_list2:
+ { Select->expr_list.push_front(new List<Item>); }
+ udf_expr_list3
+ { $$= Select->expr_list.pop(); }
+ ;
+
+udf_expr_list3:
+ udf_expr
+ {
+ Select->expr_list.head()->push_back($1);
+ }
+ | udf_expr_list3 ',' udf_expr
+ {
+ Select->expr_list.head()->push_back($3);
+ }
+ ;
+
+udf_expr:
+ remember_name expr remember_end select_alias
+ {
+ if ($4.str)
+ $2->set_name($4.str,$4.length,system_charset_info);
+ else
+ $2->set_name($1,(uint) ($3 - $1), YYTHD->charset());
+ $$= $2;
+ }
+ ;
+
sum_expr:
AVG_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_avg($3); }
@@ -2947,14 +4055,25 @@ sum_expr:
{ $$= new Item_sum_unique_users($3,atoi($5.str),atoi($7.str),$9); }
| MIN_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_min($3); }
+/*
+ According to ANSI SQL, DISTINCT is allowed and has
+ no sence inside MIN and MAX grouping functions; so MIN|MAX(DISTINCT ...)
+ is processed like an ordinary MIN | MAX()
+ */
+ | MIN_SYM '(' DISTINCT in_sum_expr ')'
+ { $$=new Item_sum_min($4); }
| MAX_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_max($3); }
+ | MAX_SYM '(' DISTINCT in_sum_expr ')'
+ { $$=new Item_sum_max($4); }
| STD_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_std($3); }
| VARIANCE_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_variance($3); }
| SUM_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_sum($3); }
+ | SUM_SYM '(' DISTINCT in_sum_expr ')'
+ { $$=new Item_sum_sum_distinct($4); }
| GROUP_CONCAT_SYM '(' opt_distinct expr_list opt_gorder_clause
opt_gconcat_separator ')'
{
@@ -3261,23 +4380,29 @@ using_list:
};
interval:
- DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; }
+ interval_time_st {}
+ | DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; }
| DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; }
| DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; }
| DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; }
- | DAY_SYM { $$=INTERVAL_DAY; }
| HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; }
| HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; }
| HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; }
- | HOUR_SYM { $$=INTERVAL_HOUR; }
| MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; }
| MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; }
| MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; }
+ | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; }
+ | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; };
+
+interval_time_st:
+ DAY_SYM { $$=INTERVAL_DAY; }
+ | WEEK_SYM { $$=INTERVAL_WEEK; }
+ | HOUR_SYM { $$=INTERVAL_HOUR; }
+ | FRAC_SECOND_SYM { $$=INTERVAL_MICROSECOND; }
| MINUTE_SYM { $$=INTERVAL_MINUTE; }
| MONTH_SYM { $$=INTERVAL_MONTH; }
- | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; }
+ | QUARTER_SYM { $$=INTERVAL_QUARTER; }
| SECOND_SYM { $$=INTERVAL_SECOND; }
- | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; }
| YEAR_SYM { $$=INTERVAL_YEAR; };
date_time_type:
@@ -3529,11 +4654,32 @@ select_var_list:
| select_var_ident {}
;
-select_var_ident: '@' ident_or_text
+select_var_ident:
+ '@' ident_or_text
+ {
+ LEX *lex=Lex;
+ if (lex->result)
+ ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0,(enum_field_types)0));
+ else
+ YYABORT;
+ }
+ | ident_or_text
{
LEX *lex=Lex;
- if (lex->result && ((select_dumpvar *)lex->result)->var_list.push_back((LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING))))
+ sp_pvar_t *t;
+
+ if (!lex->spcont || !(t=lex->spcont->find_pvar(&$1)))
+ {
+ net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $1.str);
YYABORT;
+ }
+ if (! lex->result)
+ YYABORT;
+ else
+ {
+ ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($1,1,t->offset,t->type));
+ t->isset= TRUE;
+ }
}
;
@@ -3614,11 +4760,19 @@ drop:
lex->drop_if_exists=$3;
lex->name=$4.str;
}
- | DROP UDF_SYM IDENT_sys
+ | DROP FUNCTION_SYM if_exists IDENT_sys opt_restrict
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_DROP_FUNCTION;
- lex->udf.name = $3;
+ lex->drop_if_exists= $3;
+ lex->udf.name= $4;
+ }
+ | DROP PROCEDURE if_exists IDENT_sys opt_restrict
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_DROP_PROCEDURE;
+ lex->drop_if_exists= $3;
+ lex->udf.name= $4;
}
| DROP USER
{
@@ -3630,7 +4784,6 @@ drop:
{}
;
-
table_list:
table_name
| table_list ',' table_name;
@@ -3689,7 +4842,6 @@ replace:
}
insert_field_spec
{}
- {}
;
insert_lock_option:
@@ -4053,8 +5205,8 @@ show_param:
| opt_var_type VARIABLES wild
{
THD *thd= YYTHD;
- thd->lex.sql_command= SQLCOM_SHOW_VARIABLES;
- thd->lex.option_type= (enum_var_type) $1;
+ thd->lex->sql_command= SQLCOM_SHOW_VARIABLES;
+ thd->lex->option_type= (enum_var_type) $1;
}
| charset wild
{ Lex->sql_command= SQLCOM_SHOW_CHARSETS; }
@@ -4090,7 +5242,25 @@ show_param:
| SLAVE STATUS_SYM
{
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
- };
+ }
+ | CREATE PROCEDURE ident
+ {
+ Lex->sql_command = SQLCOM_SHOW_CREATE_PROC;
+ Lex->udf.name= $3;
+ }
+ | CREATE FUNCTION_SYM ident
+ {
+ Lex->sql_command = SQLCOM_SHOW_CREATE_FUNC;
+ Lex->udf.name= $3;
+ }
+ | PROCEDURE STATUS_SYM wild
+ {
+ Lex->sql_command = SQLCOM_SHOW_STATUS_PROC;
+ }
+ | FUNCTION_SYM STATUS_SYM wild
+ {
+ Lex->sql_command = SQLCOM_SHOW_STATUS_FUNC;
+ };
show_engine_param:
STATUS_SYM
@@ -4273,18 +5443,23 @@ purge_option:
/* kill threads */
kill:
- KILL_SYM expr
+ KILL_SYM kill_option expr
{
LEX *lex=Lex;
- if ($2->fix_fields(lex->thd, 0, &$2) || $2->check_cols(1))
+ if ($3->fix_fields(lex->thd, 0, &$3) || $3->check_cols(1))
{
send_error(lex->thd, ER_SET_CONSTANTS_ONLY);
YYABORT;
}
lex->sql_command=SQLCOM_KILL;
- lex->thread_id= (ulong) $2->val_int();
+ lex->thread_id= (ulong) $3->val_int();
};
+kill_option:
+ /* empty */ { Lex->type= 0; }
+ | CONNECTION_SYM { Lex->type= 0; }
+ | QUERY_SYM { Lex->type= ONLY_KILL_QUERY; };
+
/* change database */
use: USE_SYM ident
@@ -4482,16 +5657,33 @@ order_ident:
simple_ident:
ident
{
- SELECT_LEX *sel=Select;
- $$= (sel->parsing_place != SELECT_LEX_NODE::IN_HAVING ||
- sel->get_in_sum_expr() > 0) ?
- (Item*) new Item_field(NullS,NullS,$1.str) :
- (Item*) new Item_ref(NullS,NullS,$1.str);
+ sp_pvar_t *spv;
+ LEX *lex = Lex;
+ sp_pcontext *spc = lex->spcont;
+
+ if (spc && (spv = spc->find_pvar(&$1)))
+ { /* We're compiling a stored procedure and found a variable */
+ if (lex->sql_command != SQLCOM_CALL && ! spv->isset)
+ {
+ push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_SP_UNINIT_VAR, ER(ER_SP_UNINIT_VAR),
+ $1.str);
+ }
+ $$ = (Item*) new Item_splocal($1, spv->offset);
+ }
+ else
+ {
+ SELECT_LEX *sel=Select;
+ $$= (sel->parsing_place != SELECT_LEX_NODE::IN_HAVING ||
+ sel->get_in_sum_expr() > 0) ?
+ (Item*) new Item_field(NullS,NullS,$1.str) :
+ (Item*) new Item_ref(NullS,NullS,$1.str);
+ }
}
| ident '.' ident
{
THD *thd= YYTHD;
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
SELECT_LEX *sel= lex->current_select;
if (sel->no_table_names_allowed)
{
@@ -4507,7 +5699,7 @@ simple_ident:
| '.' ident '.' ident
{
THD *thd= YYTHD;
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
SELECT_LEX *sel= lex->current_select;
if (sel->no_table_names_allowed)
{
@@ -4523,7 +5715,7 @@ simple_ident:
| ident '.' ident '.' ident
{
THD *thd= YYTHD;
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
SELECT_LEX *sel= lex->current_select;
if (sel->no_table_names_allowed)
{
@@ -4664,6 +5856,7 @@ keyword:
| DATETIME {}
| DATE_SYM {}
| DAY_SYM {}
+ | DEFINER_SYM {}
| DELAY_KEY_WRITE_SYM {}
| DES_KEY_FILE {}
| DIRECTORY_SYM {}
@@ -4702,6 +5895,7 @@ keyword:
| HOSTS_SYM {}
| HOUR_SYM {}
| IDENTIFIED_SYM {}
+ | INVOKER_SYM {}
| IMPORT {}
| INDEXES {}
| ISOLATION {}
@@ -4709,6 +5903,7 @@ keyword:
| INNOBASE_SYM {}
| INSERT_METHOD {}
| RELAY_THREAD {}
+ | LANGUAGE_SYM {}
| LAST_SYM {}
| LEAVES {}
| LEVEL_SYM {}
@@ -4744,6 +5939,7 @@ keyword:
| MULTILINESTRING {}
| MULTIPOINT {}
| MULTIPOLYGON {}
+ | NAME_SYM {}
| NAMES_SYM {}
| NATIONAL_SYM {}
| NCHAR_SYM {}
@@ -4763,6 +5959,7 @@ keyword:
| PREV_SYM {}
| PROCESS {}
| PROCESSLIST_SYM {}
+ | QUARTER_SYM {}
| QUERY_SYM {}
| QUICK {}
| RAID_0_SYM {}
@@ -4779,6 +5976,7 @@ keyword:
| RESET_SYM {}
| RESOURCES {}
| RESTORE_SYM {}
+ | RETURNS_SYM {}
| ROLLBACK_SYM {}
| ROLLUP_SYM {}
| ROWS_SYM {}
@@ -4787,6 +5985,7 @@ keyword:
| RTREE_SYM {}
| SAVEPOINT_SYM {}
| SECOND_SYM {}
+ | SECURITY_SYM {}
| SERIAL_SYM {}
| SERIALIZABLE_SYM {}
| SESSION_SYM {}
@@ -4814,10 +6013,12 @@ keyword:
| TRANSACTION_SYM {}
| TRUNCATE_SYM {}
| TIMESTAMP {}
+ | TIMESTAMP_ADD {}
+ | TIMESTAMP_DIFF {}
| TIME_SYM {}
| TYPE_SYM {}
| TYPES_SYM {}
- | UDF_SYM {}
+ | FUNCTION_SYM {}
| UNCOMMITTED_SYM {}
| UNICODE_SYM {}
| UNTIL_SYM {}
@@ -4826,6 +6027,7 @@ keyword:
| VARIABLES {}
| VALUE_SYM {}
| WARNINGS {}
+ | WEEK_SYM {}
| WORK_SYM {}
| X509_SYM {}
| YEAR_SYM {}
@@ -4875,15 +6077,33 @@ opt_var_ident_type:
;
option_value:
- '@' ident_or_text equal expr
- {
- Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
- }
+ '@' ident_or_text equal expr
+ {
+ Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
+ }
| internal_variable_name equal set_expr_or_default
{
LEX *lex=Lex;
- lex->var_list.push_back(new set_var(lex->option_type, $1.var,
- &$1.base_name, $3));
+
+ if ($1.var)
+ { /* System variable */
+ lex->var_list.push_back(new set_var(lex->option_type, $1.var,
+ &$1.base_name, $3));
+ }
+ else
+ { /* An SP local variable */
+ if ($3 && $3->type() == Item::SUBSELECT_ITEM)
+ { /* QQ For now, just disallow subselects as values */
+ send_error(lex->thd, ER_SP_SUBSELECT_NYI);
+ YYABORT;
+ }
+ sp_pvar_t *spv= lex->spcont->find_pvar(&$1.base_name);
+ sp_instr_set *i= new sp_instr_set(lex->sphead->instructions(),
+ spv->offset, $3, spv->type);
+
+ lex->sphead->add_instr(i);
+ spv->isset= TRUE;
+ }
}
| '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default
{
@@ -4930,7 +6150,7 @@ option_value:
YYABORT;
user->host.str=0;
user->user.str=thd->priv_user;
- thd->lex.var_list.push_back(new set_var_password(user, $3));
+ thd->lex->var_list.push_back(new set_var_password(user, $3));
}
| PASSWORD FOR_SYM user equal text_or_password
{
@@ -4941,12 +6161,25 @@ option_value:
internal_variable_name:
ident
{
- sys_var *tmp=find_sys_var($1.str, $1.length);
- if (!tmp)
- YYABORT;
- $$.var= tmp;
- $$.base_name.str=0;
- $$.base_name.length=0;
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ /* We have to lookup here since local vars can shadow sysvars */
+ if (!spc || !(spv = spc->find_pvar(&$1)))
+ { /* Not an SP local variable */
+ sys_var *tmp=find_sys_var($1.str, $1.length);
+ if (!tmp)
+ YYABORT;
+ $$.var= tmp;
+ $$.base_name.str=0;
+ $$.base_name.length=0;
+ }
+ else
+ { /* An SP local variable */
+ $$.var= NULL;
+ $$.base_name= $1;
+ }
}
| ident '.' ident
{
@@ -5478,7 +6711,7 @@ optional_order_or_limit:
|
{
THD *thd= YYTHD;
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE);
SELECT_LEX *sel= lex->current_select;
SELECT_LEX_UNIT *unit= sel->master_unit();
@@ -5494,7 +6727,7 @@ optional_order_or_limit:
order_or_limit
{
THD *thd= YYTHD;
- thd->lex.current_select->no_table_names_allowed= 0;
+ thd->lex->current_select->no_table_names_allowed= 0;
thd->where= "";
}
;
diff --git a/sql/table.h b/sql/table.h
index 00fe803cde2..ba7349d33fc 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -26,6 +26,7 @@ class st_select_lex_unit;
typedef struct st_order {
struct st_order *next;
Item **item; /* Point at item in select fields */
+ Item **item_copy; /* For SPs; the original item ptr */
bool asc; /* true if ascending */
bool free_me; /* true if item isn't shared */
bool in_field_list; /* true if in select field list */
diff --git a/sql/udf_example.cc b/sql/udf_example.cc
index 7e2ee9113b2..f0f33ed6fd7 100644
--- a/sql/udf_example.cc
+++ b/sql/udf_example.cc
@@ -56,7 +56,9 @@
**
** Function 'myfunc_int' returns summary length of all its arguments.
**
-** Function 'sequence' returns an sequence starting from a certain number
+** Function 'sequence' returns an sequence starting from a certain number.
+**
+** Function 'myfunc_argument_name' returns name of argument.
**
** On the end is a couple of functions that converts hostnames to ip and
** vice versa.
@@ -82,6 +84,7 @@
** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
** CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so";
+** CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so";
**
** After this the functions will work exactly like native MySQL functions.
** Functions should be created only once.
@@ -94,6 +97,7 @@
** DROP FUNCTION lookup;
** DROP FUNCTION reverse_lookup;
** DROP FUNCTION avgcost;
+** DROP FUNCTION myfunc_argument_name;
**
** The CREATE FUNCTION and DROP FUNCTION update the func@mysql table. All
** Active function will be reloaded on every restart of server
@@ -984,4 +988,43 @@ avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* error )
return data->totalprice/double(data->totalquantity);
}
+extern "C" {
+my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args,
+ char *message);
+char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *null_value,
+ char *error);
+}
+
+my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args,
+ char *message)
+{
+ if (args->arg_count != 1)
+ {
+ strmov(message,"myfunc_argument_name_init accepts only one argument");
+ return 1;
+ }
+ initid->max_length= args->attribute_lengths[0];
+ initid->maybe_null= 1;
+ initid->const_item= 1;
+ return 0;
+}
+
+char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *null_value,
+ char *error)
+{
+ if (!args->attributes[0])
+ {
+ null_value= 0;
+ return 0;
+ }
+ (*length)--; // space for ending \0 (for debugging purposes)
+ if (*length > args->attribute_lengths[0])
+ *length= args->attribute_lengths[0];
+ memcpy(result, args->attributes[0], *length);
+ result[*length]= 0;
+ return result;
+}
+
#endif /* HAVE_DLOPEN */
diff --git a/sql/uniques.cc b/sql/uniques.cc
index d060965aa66..a4a5250192d 100644
--- a/sql/uniques.cc
+++ b/sql/uniques.cc
@@ -84,6 +84,7 @@ bool Unique::flush()
elements+= tree.elements_in_tree;
file_ptr.count=tree.elements_in_tree;
file_ptr.file_pos=my_b_tell(&file);
+
if (tree_walk(&tree, (tree_walk_action) unique_write_to_file,
(void*) this, left_root_right) ||
insert_dynamic(&file_ptrs, (gptr) &file_ptr))
@@ -94,6 +95,237 @@ bool Unique::flush()
/*
+ Clear the tree and the file.
+ You must call reset() if you want to reuse Unique after walk().
+*/
+
+void
+Unique::reset()
+{
+ reset_tree(&tree);
+ /*
+ If elements != 0, some trees were stored in the file (see how
+ flush() works). Note, that we can not count on my_b_tell(&file) == 0
+ here, because it can return 0 right after walk(), and walk() does not
+ reset any Unique member.
+ */
+ if (elements)
+ {
+ reset_dynamic(&file_ptrs);
+ reinit_io_cache(&file, WRITE_CACHE, 0L, 0, 1);
+ }
+ elements= 0;
+}
+
+/*
+ The comparison function, passed to queue_init() in merge_walk() must
+ use comparison function of Uniques::tree, but compare members of struct
+ BUFFPEK.
+*/
+
+struct BUFFPEK_COMPARE_CONTEXT
+{
+ qsort_cmp2 key_compare;
+ void *key_compare_arg;
+};
+
+C_MODE_START
+
+static int buffpek_compare(void *arg, byte *key_ptr1, byte *key_ptr2)
+{
+ BUFFPEK_COMPARE_CONTEXT *ctx= (BUFFPEK_COMPARE_CONTEXT *) arg;
+ return ctx->key_compare(ctx->key_compare_arg,
+ *((byte **) key_ptr1), *((byte **)key_ptr2));
+}
+
+C_MODE_END
+
+
+/*
+ DESCRIPTION
+ Function is very similar to merge_buffers, but instead of writing sorted
+ unique keys to the output file, it invokes walk_action for each key.
+ This saves I/O if you need to pass through all unique keys only once.
+ SYNOPSIS
+ merge_walk()
+ All params are 'IN' (but see comment for begin, end):
+ merge_buffer buffer to perform cached piece-by-piece loading
+ of trees; initially the buffer is empty
+ merge_buffer_size size of merge_buffer. Must be aligned with
+ key_length
+ key_length size of tree element; key_length * (end - begin)
+ must be less or equal than merge_buffer_size.
+ begin pointer to BUFFPEK struct for the first tree.
+ end pointer to BUFFPEK struct for the last tree;
+ end > begin and [begin, end) form a consecutive
+ range. BUFFPEKs structs in that range are used and
+ overwritten in merge_walk().
+ walk_action element visitor. Action is called for each unique
+ key.
+ walk_action_arg argument to walk action. Passed to it on each call.
+ compare elements comparison function
+ compare_arg comparison function argument
+ file file with all trees dumped. Trees in the file
+ must contain sorted unique values. Cache must be
+ initialized in read mode.
+ RETURN VALUE
+ 0 ok
+ <> 0 error
+*/
+
+static bool merge_walk(uchar *merge_buffer, uint merge_buffer_size,
+ uint key_length, BUFFPEK *begin, BUFFPEK *end,
+ tree_walk_action walk_action, void *walk_action_arg,
+ qsort_cmp2 compare, void *compare_arg,
+ IO_CACHE *file)
+{
+ BUFFPEK_COMPARE_CONTEXT compare_context = { compare, compare_arg };
+ QUEUE queue;
+ if (end <= begin ||
+ merge_buffer_size < key_length * (end - begin + 1) ||
+ init_queue(&queue, end - begin, offsetof(BUFFPEK, key), 0,
+ buffpek_compare, &compare_context))
+ return 1;
+ /* we need space for one key when a piece of merge buffer is re-read */
+ merge_buffer_size-= key_length;
+ uchar *save_key_buff= merge_buffer + merge_buffer_size;
+ uint max_key_count_per_piece= merge_buffer_size/(end-begin)/key_length;
+ /* if piece_size is aligned reuse_freed_buffer will always hit */
+ uint piece_size= max_key_count_per_piece * key_length;
+ uint bytes_read; /* to hold return value of read_to_buffer */
+ BUFFPEK *top;
+ int res= 1;
+ /*
+ Invariant: queue must contain top element from each tree, until a tree
+ is not completely walked through.
+ Here we're forcing the invariant, inserting one element from each tree
+ to the queue.
+ */
+ for (top= begin; top != end; ++top)
+ {
+ top->base= merge_buffer + (top - begin) * piece_size;
+ top->max_keys= max_key_count_per_piece;
+ bytes_read= read_to_buffer(file, top, key_length);
+ if (bytes_read == (uint) (-1))
+ goto end;
+ DBUG_ASSERT(bytes_read);
+ queue_insert(&queue, (byte *) top);
+ }
+ top= (BUFFPEK *) queue_top(&queue);
+ while (queue.elements > 1)
+ {
+ /*
+ Every iteration one element is removed from the queue, and one is
+ inserted by the rules of the invariant. If two adjacent elements on
+ the top of the queue are not equal, biggest one is unique, because all
+ elements in each tree are unique. Action is applied only to unique
+ elements.
+ */
+ void *old_key= top->key;
+ /*
+ read next key from the cache or from the file and push it to the
+ queue; this gives new top.
+ */
+ top->key+= key_length;
+ if (--top->mem_count)
+ queue_replaced(&queue);
+ else /* next piece should be read */
+ {
+ /* save old_key not to overwrite it in read_to_buffer */
+ memcpy(save_key_buff, old_key, key_length);
+ old_key= save_key_buff;
+ bytes_read= read_to_buffer(file, top, key_length);
+ if (bytes_read == (uint) (-1))
+ goto end;
+ else if (bytes_read > 0) /* top->key, top->mem_count are reset */
+ queue_replaced(&queue); /* in read_to_buffer */
+ else
+ {
+ /*
+ Tree for old 'top' element is empty: remove it from the queue and
+ give all its memory to the nearest tree.
+ */
+ queue_remove(&queue, 0);
+ reuse_freed_buff(&queue, top, key_length);
+ }
+ }
+ top= (BUFFPEK *) queue_top(&queue);
+ /* new top has been obtained; if old top is unique, apply the action */
+ if (compare(compare_arg, old_key, top->key))
+ {
+ if (walk_action(old_key, 1, walk_action_arg))
+ goto end;
+ }
+ }
+ /*
+ Applying walk_action to the tail of the last tree: this is safe because
+ either we had only one tree in the beginning, either we work with the
+ last tree in the queue.
+ */
+ do
+ {
+ do
+ {
+ if (walk_action(top->key, 1, walk_action_arg))
+ goto end;
+ top->key+= key_length;
+ }
+ while (--top->mem_count);
+ bytes_read= read_to_buffer(file, top, key_length);
+ if (bytes_read == (uint) (-1))
+ goto end;
+ }
+ while (bytes_read);
+ res= 0;
+end:
+ delete_queue(&queue);
+ return res;
+}
+
+
+/*
+ DESCRIPTION
+ Walks consecutively through all unique elements:
+ if all elements are in memory, then it simply invokes 'tree_walk', else
+ all flushed trees are loaded to memory piece-by-piece, pieces are
+ sorted, and action is called for each unique value.
+ Note: so as merging resets file_ptrs state, this method can change
+ internal Unique state to undefined: if you want to reuse Unique after
+ walk() you must call reset() first!
+ SYNOPSIS
+ Unique:walk()
+ All params are 'IN':
+ action function-visitor, typed in include/my_tree.h
+ function is called for each unique element
+ arg argument for visitor, which is passed to it on each call
+ RETURN VALUE
+ 0 OK
+ <> 0 error
+ */
+
+bool Unique::walk(tree_walk_action action, void *walk_action_arg)
+{
+ if (elements == 0) /* the whole tree is in memory */
+ return tree_walk(&tree, action, walk_action_arg, left_root_right);
+
+ /* flush current tree to the file to have some memory for merge buffer */
+ if (flush())
+ return 1;
+ if (flush_io_cache(&file) || reinit_io_cache(&file, READ_CACHE, 0L, 0, 0))
+ return 1;
+ uchar *merge_buffer= (uchar *) my_malloc(max_in_memory_size, MYF(0));
+ if (merge_buffer == 0)
+ return 1;
+ int res= merge_walk(merge_buffer, max_in_memory_size, size,
+ (BUFFPEK *) file_ptrs.buffer,
+ (BUFFPEK *) file_ptrs.buffer + file_ptrs.elements,
+ action, walk_action_arg,
+ tree.compare, tree.custom_arg, &file);
+ x_free(merge_buffer);
+ return res;
+}
+
+/*
Modify the TABLE element so that when one calls init_records()
the rows will be read in priority order.
*/
@@ -114,7 +346,7 @@ bool Unique::get(TABLE *table)
return 0;
}
}
- /* Not enough memory; Save the result to file */
+ /* Not enough memory; Save the result to file && free memory used by tree */
if (flush())
return 1;