diff options
Diffstat (limited to 'sql')
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= ¤t_thd->killed; + volatile THD::killed_state *killed= ¤t_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= ¤t_thd->killed; - bool not_killable; + volatile THD::killed_state *killed= ¤t_thd->killed; + THD::killed_state not_killable; DBUG_ENTER("merge_buffers"); statistic_increment(filesort_merge_passes, &LOCK_status); if (param->not_killable) { killed= ¬_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(<ime, + (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, µseconds); - 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(<ime1, 0) || + args[1]->get_date(<ime2, 0)) + goto null_date; + + if (calc_time_diff(<ime2,<ime1, 1, + &seconds, µseconds)) + 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(¶m, 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(¶m,(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(¶m,(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(¶m, + (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(¶m, 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, + ¬_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 (¤t_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> ¶ms= thd->lex.param_list; + List<Item> ¶ms= 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> ¶ms= thd->lex.param_list; + List<Item> ¶ms= 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> ¶ms= thd->lex.param_list; + List<Item> ¶ms= 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> ¶ms= thd->lex.param_list; + List<Item> ¶ms= 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; |