diff options
author | unknown <brian@zim.(none)> | 2005-08-30 14:40:02 -0700 |
---|---|---|
committer | unknown <brian@zim.(none)> | 2005-08-30 14:40:02 -0700 |
commit | 9ea1d5e293a0a12b1be1d305d1a88e2aae18a4ba (patch) | |
tree | fb387d75c6bc8131e0d735a74cc6ab053e56c186 /sql | |
parent | 172392f272b65ce630e3c56790ed76e666af3903 (diff) | |
parent | 8d17360181f9b27295fe18e6f4137b8af1fc2d06 (diff) | |
download | mariadb-git-9ea1d5e293a0a12b1be1d305d1a88e2aae18a4ba.tar.gz |
Merge zim.(none):/home/brian/mysql/mysql-5.0
into zim.(none):/home/brian/mysql/mysql-5.1
Resolved to minor issues.
BitKeeper/etc/config:
Auto merged
Makefile.am:
Auto merged
VC++Files/sql/mysqld.vcproj:
Auto merged
extra/perror.c:
Auto merged
include/my_global.h:
Auto merged
mysql-test/mysql-test-run.pl:
Auto merged
mysql-test/mysql-test-run.sh:
Auto merged
mysql-test/t/alter_table.test:
Auto merged
mysql-test/t/disabled.def:
Auto merged
mysys/Makefile.am:
Auto merged
scripts/mysql_fix_privilege_tables.sql:
Auto merged
sql/Makefile.am:
Auto merged
sql/ha_innodb.cc:
Auto merged
sql/ha_innodb.h:
Auto merged
sql/handler.h:
Auto merged
sql/item.cc:
Auto merged
sql/lex.h:
Auto merged
sql/log.cc:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/mysqld.cc:
Auto merged
sql/opt_range.cc:
Auto merged
sql/set_var.cc:
Auto merged
sql/sp.cc:
Auto merged
sql/sql_class.h:
Auto merged
sql/sql_delete.cc:
Auto merged
sql/sql_insert.cc:
Auto merged
sql/sql_lex.cc:
Auto merged
sql/sql_lex.h:
Auto merged
sql/sql_parse.cc:
Auto merged
sql/sql_select.cc:
Auto merged
sql/sql_show.cc:
Auto merged
sql/sql_table.cc:
Auto merged
sql/sql_update.cc:
Auto merged
sql/sql_yacc.yy:
Auto merged
sql/examples/ha_tina.cc:
Auto merged
sql/table.h:
Auto merged
sql/unireg.h:
Auto merged
storage/innobase/lock/lock0lock.c:
Auto merged
storage/myisam/mi_check.c:
Auto merged
storage/myisam/mi_search.c:
Auto merged
storage/myisam/myisamchk.c:
Auto merged
storage/myisam/myisamlog.c:
Auto merged
storage/myisam/myisampack.c:
Auto merged
storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp:
Auto merged
configure.in:
Fixed the resolve of versions.
mysql-test/r/alter_table.result:
Fixed results.
Diffstat (limited to 'sql')
41 files changed, 1017 insertions, 194 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am index 2eab9052ba7..e90be7630fa 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -61,6 +61,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ tztime.h my_decimal.h\ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \ parse_file.h sql_view.h sql_trigger.h \ + sql_array.h \ examples/ha_example.h examples/ha_archive.h \ examples/ha_tina.h ha_blackhole.h \ ha_federated.h ha_partition.h diff --git a/sql/examples/ha_archive.cc b/sql/examples/ha_archive.cc index fd47b45ce52..d5cf713aa44 100644 --- a/sql/examples/ha_archive.cc +++ b/sql/examples/ha_archive.cc @@ -1028,4 +1028,15 @@ int ha_archive::end_bulk_insert() share->dirty= TRUE; DBUG_RETURN(0); } + +/* + We cancel a truncate command. The only way to delete an archive table is to drop it. + This is done for security reasons. In a later version we will enable this by + allowing the user to select a different row format. +*/ +int ha_archive::delete_all_rows() +{ + DBUG_ENTER("ha_archive::delete_all_rows"); + DBUG_RETURN(0); +} #endif /* HAVE_ARCHIVE_DB */ diff --git a/sql/examples/ha_archive.h b/sql/examples/ha_archive.h index 41835c5fb6f..e2d8aa49add 100644 --- a/sql/examples/ha_archive.h +++ b/sql/examples/ha_archive.h @@ -78,6 +78,7 @@ public: int close(void); int write_row(byte * buf); int real_write_row(byte *buf, gzFile writer); + int delete_all_rows(); int rnd_init(bool scan=1); int rnd_next(byte *buf); int rnd_pos(byte * buf, byte *pos); diff --git a/sql/examples/ha_tina.cc b/sql/examples/ha_tina.cc index 8a31d9c0b22..c5cefeae125 100644 --- a/sql/examples/ha_tina.cc +++ b/sql/examples/ha_tina.cc @@ -651,7 +651,9 @@ int ha_tina::rnd_init(bool scan) current_position= next_position= 0; records= 0; chain_ptr= chain; +#ifdef MADV_SEQUENTIAL (void)madvise(share->mapped_file,share->file_stat.st_size,MADV_SEQUENTIAL); +#endif DBUG_RETURN(0); } diff --git a/sql/ha_blackhole.cc b/sql/ha_blackhole.cc index 43a286a541f..a287d6e446b 100644 --- a/sql/ha_blackhole.cc +++ b/sql/ha_blackhole.cc @@ -158,14 +158,18 @@ int ha_blackhole::external_lock(THD *thd, int lock_type) } +uint ha_blackhole::lock_count(void) const +{ + DBUG_ENTER("ha_blackhole::lock_count"); + DBUG_RETURN(0); +} + THR_LOCK_DATA **ha_blackhole::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) { - if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) - lock.type=lock_type; - *to++= &lock; - return to; + DBUG_ENTER("ha_blackhole::store_lock"); + DBUG_RETURN(to); } diff --git a/sql/ha_blackhole.h b/sql/ha_blackhole.h index 2dccabf17cc..7238147a06a 100644 --- a/sql/ha_blackhole.h +++ b/sql/ha_blackhole.h @@ -78,6 +78,7 @@ public: void position(const byte *record); void info(uint flag); int external_lock(THD *thd, int lock_type); + uint lock_count(void) const; int create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info); THR_LOCK_DATA **store_lock(THD *thd, diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index df58a722e9a..122f0c1323a 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -5428,7 +5428,7 @@ ha_innobase::info( is an accurate estimate if it is zero. Of course, it is not, since we do not have any locks on the rows yet at this phase. Since SHOW TABLE STATUS seems to call this function with the - HA_STATUS_TIME flag set, while the left join optizer does not + HA_STATUS_TIME flag set, while the left join optimizer does not set that flag, we add one to a zero value if the flag is not set. That way SHOW TABLE STATUS will show the best estimate, while the optimizer never sees the table empty. */ @@ -6890,6 +6890,28 @@ ha_innobase::get_auto_increment() return((ulonglong) nr); } +/* See comment in handler.h */ +int +ha_innobase::reset_auto_increment(ulonglong value) +{ + DBUG_ENTER("ha_innobase::reset_auto_increment"); + + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + int error; + + error = row_lock_table_autoinc_for_mysql(prebuilt); + + if (error != DB_SUCCESS) { + error = convert_error_code_to_mysql(error, user_thd); + + DBUG_RETURN(error); + } + + dict_table_autoinc_initialize(prebuilt->table, value); + + DBUG_RETURN(0); +} + /*********************************************************************** Compares two 'refs'. A 'ref' is the (internal) primary key value of the row. If there is no explicitly declared non-null unique key or a primary key, then diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 3ff925e30de..595ab5ccde2 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -183,6 +183,8 @@ class ha_innobase: public handler enum thr_lock_type lock_type); void init_table_handle_for_HANDLER(); ulonglong get_auto_increment(); + int reset_auto_increment(ulonglong value); + uint8 table_cache_type() { return HA_CACHE_TBL_ASKTRANSACT; } /* ask handler about permission to cache table during query registration diff --git a/sql/handler.h b/sql/handler.h index 38bb79dbdbc..fca0717011f 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1129,6 +1129,16 @@ public: { return (my_errno=HA_ERR_WRONG_COMMAND); } virtual ulonglong get_auto_increment(); virtual void restore_auto_increment(); + + /* + Reset the auto-increment counter to the given value, i.e. the next row + inserted will get the given value. This is called e.g. after TRUNCATE + is emulated by doing a 'DELETE FROM t'. HA_ERR_WRONG_COMMAND is + returned by storage engines that don't support this operation. + */ + virtual int reset_auto_increment(ulonglong value) + { return HA_ERR_WRONG_COMMAND; } + virtual void update_create_info(HA_CREATE_INFO *create_info) {} /* admin commands - called from mysql_admin_table */ @@ -1339,6 +1349,8 @@ extern ulong total_ha, total_ha_2pc; #define ha_supports_generate(T) (T != DB_TYPE_INNODB && \ T != DB_TYPE_BERKELEY_DB && \ + T != DB_TYPE_ARCHIVE_DB && \ + T != DB_TYPE_FEDERATED_DB && \ T != DB_TYPE_NDBCLUSTER) /* lookups */ diff --git a/sql/item.cc b/sql/item.cc index f25734fb549..bec58a2add5 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -303,6 +303,7 @@ void *Item::operator new(size_t size, Item *reuse, uint *rsize) if (rsize) (*rsize)= reuse->rsize; reuse->cleanup(); + delete reuse; TRASH((void *)reuse, size); return (void *)reuse; } @@ -789,12 +790,15 @@ int Item::save_in_field_no_warnings(Field *field, bool no_conversions) } +/***************************************************************************** + Item_splocal methods +*****************************************************************************/ double Item_splocal::val_real() { DBUG_ASSERT(fixed); Item *it= this_item(); double ret= it->val_real(); - Item::null_value= it->null_value; + null_value= it->null_value; return ret; } @@ -804,7 +808,7 @@ longlong Item_splocal::val_int() DBUG_ASSERT(fixed); Item *it= this_item(); longlong ret= it->val_int(); - Item::null_value= it->null_value; + null_value= it->null_value; return ret; } @@ -814,7 +818,7 @@ String *Item_splocal::val_str(String *sp) DBUG_ASSERT(fixed); Item *it= this_item(); String *ret= it->val_str(sp); - Item::null_value= it->null_value; + null_value= it->null_value; return ret; } @@ -824,7 +828,7 @@ my_decimal *Item_splocal::val_decimal(my_decimal *decimal_value) DBUG_ASSERT(fixed); Item *it= this_item(); my_decimal *val= it->val_decimal(decimal_value); - Item::null_value= it->null_value; + null_value= it->null_value; return val; } @@ -833,7 +837,7 @@ bool Item_splocal::is_null() { Item *it= this_item(); bool ret= it->is_null(); - Item::null_value= it->null_value; + null_value= it->null_value; return ret; } @@ -898,6 +902,97 @@ void Item_splocal::print(String *str) } +/***************************************************************************** + Item_name_const methods +*****************************************************************************/ +double Item_name_const::val_real() +{ + DBUG_ASSERT(fixed); + double ret= value_item->val_real(); + null_value= value_item->null_value; + return ret; +} + + +longlong Item_name_const::val_int() +{ + DBUG_ASSERT(fixed); + longlong ret= value_item->val_int(); + null_value= value_item->null_value; + return ret; +} + + +String *Item_name_const::val_str(String *sp) +{ + DBUG_ASSERT(fixed); + String *ret= value_item->val_str(sp); + null_value= value_item->null_value; + return ret; +} + + +my_decimal *Item_name_const::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed); + my_decimal *val= value_item->val_decimal(decimal_value); + null_value= value_item->null_value; + return val; +} + + +bool Item_name_const::is_null() +{ + bool ret= value_item->is_null(); + null_value= value_item->null_value; + return ret; +} + +Item::Type Item_name_const::type() const +{ + return value_item->type(); +} + + +bool Item_name_const::fix_fields(THD *thd, Item **) +{ + char buf[128]; + String *item_name; + String s(buf, sizeof(buf), &my_charset_bin); + s.length(0); + + if (value_item->fix_fields(thd, &value_item) || + name_item->fix_fields(thd, &name_item)) + return TRUE; + if (!(value_item->const_item() && name_item->const_item())) + return TRUE; + + if (!(item_name= name_item->val_str(&s))) + return TRUE; /* Can't have a NULL name */ + + set_name(item_name->ptr(), (uint) item_name->length(), system_charset_info); + max_length= value_item->max_length; + decimals= value_item->decimals; + fixed= 1; + return FALSE; +} + + +void Item_name_const::cleanup() +{ + fixed= 0; +} + + +void Item_name_const::print(String *str) +{ + str->append("NAME_CONST("); + name_item->print(str); + str->append(','); + value_item->print(str); + str->append(')'); +} + /* Move SUM items out from item tree and replace with reference diff --git a/sql/item.h b/sql/item.h index ebcd5a9da33..b934e1f9f3f 100644 --- a/sql/item.h +++ b/sql/item.h @@ -700,20 +700,40 @@ public: }; -// A local SP variable (incl. parameters), used in runtime +/* + A reference to local SP variable (incl. reference to SP parameter), used in + runtime. + + NOTE + This item has a "value" item, defined as + this_item() = thd->spcont->get_item(m_offset) + and it delegates everything to that item (if !this_item() then this item + poses as Item_null) except for name, which is the name of SP local + variable. +*/ + class Item_splocal : public Item { -private: - uint m_offset; +public: LEX_STRING m_name; -public: + /* + Position of this reference to SP variable in the statement (the + statement itself is in sp_instr_stmt::m_query). + This is valid only for references to SP variables in statements, + excluding DECLARE CURSOR statement. It is used to replace references to SP + variables with NAME_CONST calls when putting statements into the binary + log. + Value of 0 means that this object doesn't corresponding to reference to + SP variable in query text. + */ + int pos_in_query; - Item_splocal(LEX_STRING name, uint offset) - : m_offset(offset), m_name(name) + Item_splocal(LEX_STRING name, uint offset, int pos_in_q=0) + : m_offset(offset), m_name(name), pos_in_query(pos_in_q) { - Item::maybe_null= TRUE; + maybe_null= TRUE; } /* For error printing */ @@ -750,7 +770,7 @@ public: bool is_null(); void print(String *str); - inline void make_field(Send_field *field) + void make_field(Send_field *field) { Item *it= this_item(); @@ -761,28 +781,84 @@ public: it->make_field(field); } - inline Item_result result_type() const + Item_result result_type() const { return this_const_item()->result_type(); } - inline bool const_item() const + bool const_item() const { return TRUE; } - inline int save_in_field(Field *field, bool no_conversions) + int save_in_field(Field *field, bool no_conversions) { return this_item()->save_in_field(field, no_conversions); } - inline bool send(Protocol *protocol, String *str) + bool send(Protocol *protocol, String *str) { return this_item()->send(protocol, str); } }; +/* + NAME_CONST(given_name, const_value). + This 'function' has all properties of the supplied const_value (which is + assumed to be a literal constant), and the name given_name. + + This is used to replace references to SP variables when we write PROCEDURE + statements into the binary log. + + TODO + Together with Item_splocal and Item::this_item() we can actually extract + common a base of this class and Item_splocal. Maybe it is possible to + extract a common base with class Item_ref, too. +*/ + +class Item_name_const : public Item +{ + Item *value_item; + Item *name_item; +public: + Item_name_const(Item *name, Item *val): value_item(val), name_item(name) + { + Item::maybe_null= TRUE; + } + + bool fix_fields(THD *, Item **); + void cleanup(); + + enum Type type() const; + double val_real(); + longlong val_int(); + String *val_str(String *sp); + my_decimal *val_decimal(my_decimal *); + bool is_null(); + void print(String *str); + + Item_result result_type() const + { + return value_item->result_type(); + } + + bool const_item() const + { + return TRUE; + } + + int save_in_field(Field *field, bool no_conversions) + { + return value_item->save_in_field(field, no_conversions); + } + + bool send(Protocol *protocol, String *str) + { + return value_item->send(protocol, str); + } +}; + bool agg_item_collations(DTCollation &c, const char *name, Item **items, uint nitems, uint flags= 0); bool agg_item_collations_for_comparison(DTCollation &c, const char *name, @@ -1299,6 +1375,15 @@ public: // it is constant => can be used without fix_fields (and frequently used) fixed= 1; } + /* Just create an item and do not fill string representation */ + Item_string(CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE) + { + collation.set(cs, dv); + max_length= 0; + set_name(NULL, 0, cs); + decimals= NOT_FIXED_DEC; + fixed= 1; + } Item_string(const char *name_par, const char *str, uint length, CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE) { @@ -1310,6 +1395,15 @@ public: // it is constant => can be used without fix_fields (and frequently used) fixed= 1; } + /* + This is used in stored procedures to avoid memory leaks and + does a deep copy of its argument. + */ + void set_str_with_copy(const char *str_arg, uint length_arg) + { + str_value.copy(str_arg, length_arg, collation.collation); + max_length= str_value.numchars() * collation.collation->mbmaxlen; + } enum Type type() const { return STRING_ITEM; } double val_real(); longlong val_int(); diff --git a/sql/item_create.cc b/sql/item_create.cc index 77476e41d0b..82a82873ad9 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -261,6 +261,11 @@ Item *create_func_mod(Item* a, Item *b) return new Item_func_mod(a,b); } +Item *create_func_name_const(Item *a, Item *b) +{ + return new Item_name_const(a,b); +} + Item *create_func_monthname(Item* a) { return new Item_func_monthname(a); diff --git a/sql/item_create.h b/sql/item_create.h index d757318bfc1..35db9be3c89 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -65,6 +65,7 @@ Item *create_func_ltrim(Item* a); Item *create_func_md5(Item* a); Item *create_func_mod(Item* a, Item *b); Item *create_func_monthname(Item* a); +Item *create_func_name_const(Item *a, Item *b); Item *create_func_nullif(Item* a, Item *b); Item *create_func_oct(Item *); Item *create_func_ord(Item* a); diff --git a/sql/item_func.cc b/sql/item_func.cc index d3b53db2d54..8125264ab15 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -734,11 +734,13 @@ longlong Item_func_numhybrid::val_int() case STRING_RESULT: { int err_not_used; - String *res= str_op(&str_value); + String *res; + if (!(res= str_op(&str_value))) + return 0; + char *end= (char*) res->ptr() + res->length(); CHARSET_INFO *cs= str_value.charset(); - return (res ? (*(cs->cset->strtoll10))(cs, res->ptr(), &end, - &err_not_used) : 0); + return (*(cs->cset->strtoll10))(cs, res->ptr(), &end, &err_not_used); } default: DBUG_ASSERT(0); @@ -769,7 +771,10 @@ my_decimal *Item_func_numhybrid::val_decimal(my_decimal *decimal_value) } case STRING_RESULT: { - String *res= str_op(&str_value); + String *res; + if (!(res= str_op(&str_value))) + return NULL; + str2my_decimal(E_DEC_FATAL_ERROR, (char*) res->ptr(), res->length(), res->charset(), decimal_value); break; @@ -1950,7 +1955,7 @@ void Item_func_min_max::fix_length_and_dec() int max_int_part=0; decimals=0; max_length=0; - maybe_null=1; + maybe_null=0; cmp_type=args[0]->result_type(); for (uint i=0 ; i < arg_count ; i++) @@ -1958,8 +1963,8 @@ void Item_func_min_max::fix_length_and_dec() set_if_bigger(max_length, args[i]->max_length); set_if_bigger(decimals, args[i]->decimals); set_if_bigger(max_int_part, args[i]->decimal_int_part()); - if (!args[i]->maybe_null) - maybe_null=0; + if (args[i]->maybe_null) + maybe_null=1; cmp_type=item_cmp_type(cmp_type,args[i]->result_type()); } if (cmp_type == STRING_RESULT) @@ -2005,14 +2010,11 @@ String *Item_func_min_max::val_str(String *str) { String *res; LINT_INIT(res); - null_value=1; + null_value= 0; for (uint i=0; i < arg_count ; i++) { - if (null_value) - { + if (i == 0) res=args[i]->val_str(str); - null_value=args[i]->null_value; - } else { String *res2; @@ -2023,7 +2025,11 @@ String *Item_func_min_max::val_str(String *str) if ((cmp_sign < 0 ? cmp : -cmp) < 0) res=res2; } + else + res= 0; } + if ((null_value= args[i]->null_value)) + break; } if (res) // If !NULL res->set_charset(collation.collation); @@ -2043,20 +2049,19 @@ double Item_func_min_max::val_real() { DBUG_ASSERT(fixed == 1); double value=0.0; - null_value=1; + null_value= 0; for (uint i=0; i < arg_count ; i++) { - if (null_value) - { + if (i == 0) value= args[i]->val_real(); - null_value=args[i]->null_value; - } else { double tmp= args[i]->val_real(); if (!args[i]->null_value && (tmp < value ? cmp_sign : -cmp_sign) > 0) value=tmp; } + if ((null_value= args[i]->null_value)) + break; } return value; } @@ -2066,20 +2071,19 @@ longlong Item_func_min_max::val_int() { DBUG_ASSERT(fixed == 1); longlong value=0; - null_value=1; + null_value= 0; for (uint i=0; i < arg_count ; i++) { - if (null_value) - { + if (i == 0) value=args[i]->val_int(); - null_value=args[i]->null_value; - } else { longlong tmp=args[i]->val_int(); if (!args[i]->null_value && (tmp < value ? cmp_sign : -cmp_sign) > 0) value=tmp; } + if ((null_value= args[i]->null_value)) + break; } return value; } @@ -2089,20 +2093,17 @@ my_decimal *Item_func_min_max::val_decimal(my_decimal *dec) { DBUG_ASSERT(fixed == 1); my_decimal tmp_buf, *tmp, *res= NULL; - null_value=1; + null_value= 0; for (uint i=0; i < arg_count ; i++) { - if (null_value) - { + if (i == 0) res= args[i]->val_decimal(dec); - null_value= args[i]->null_value; - } else { tmp= args[i]->val_decimal(&tmp_buf); if (args[i]->null_value) - continue; - if ((my_decimal_cmp(tmp, res) * cmp_sign) < 0) + res= 0; + else if ((my_decimal_cmp(tmp, res) * cmp_sign) < 0) { if (tmp == &tmp_buf) { @@ -2113,6 +2114,8 @@ my_decimal *Item_func_min_max::val_decimal(my_decimal *dec) res= tmp; } } + if ((null_value= args[i]->null_value)) + break; } return res; } @@ -4717,11 +4720,15 @@ Item_func_sp::execute(Item **itp) m_sp->m_db.str, m_sp->m_name.str, 0, 0)) goto error_check_ctx; #endif - + /* + Disable the binlogging if this is not a SELECT statement. If this is a + SELECT, leave binlogging on, so execute_function() code writes the + function call into binlog. + */ thd->reset_sub_statement_state(&statement_state, SUB_STMT_FUNCTION); res= m_sp->execute_function(thd, args, arg_count, itp); thd->restore_sub_statement_state(&statement_state); - + if (res && mysql_bin_log.is_open() && (m_sp->m_chistics->daccess == SP_CONTAINS_SQL || m_sp->m_chistics->daccess == SP_MODIFIES_SQL_DATA)) diff --git a/sql/item_func.h b/sql/item_func.h index 384cb486f7c..019abb0c072 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -682,7 +682,6 @@ public: my_decimal *val_decimal(my_decimal *); void fix_length_and_dec(); enum Item_result result_type () const { return cmp_type; } - table_map not_null_tables() const { return 0; } }; class Item_func_min :public Item_func_min_max diff --git a/sql/lex.h b/sql/lex.h index f0a6c0047d2..9b8f94f61bf 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -710,6 +710,7 @@ static SYMBOL sql_functions[] = { { "MULTIPOINTFROMWKB",SYM(GEOMFROMWKB)}, { "MULTIPOLYGONFROMTEXT",SYM(MPOLYFROMTEXT)}, { "MULTIPOLYGONFROMWKB",SYM(GEOMFROMWKB)}, + { "NAME_CONST", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_name_const)}, { "NOW", SYM(NOW_SYM)}, { "NULLIF", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_nullif)}, { "NUMGEOMETRIES", F_SYM(FUNC_ARG1),0,CREATE_FUNC_GEOM(create_func_numgeometries)}, diff --git a/sql/log.cc b/sql/log.cc index 374b1a7a94e..d3bb44b3083 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1554,6 +1554,20 @@ bool MYSQL_LOG::flush_and_sync() return err; } +void MYSQL_LOG::start_union_events(THD *thd) +{ + DBUG_ASSERT(!thd->binlog_evt_union.do_union); + thd->binlog_evt_union.do_union= TRUE; + thd->binlog_evt_union.unioned_events= FALSE; + thd->binlog_evt_union.unioned_events_trans= FALSE; +} + +void MYSQL_LOG::stop_union_events(THD *thd) +{ + DBUG_ASSERT(thd->binlog_evt_union.do_union); + thd->binlog_evt_union.do_union= FALSE; +} + /* Write an event to the binary log */ @@ -1564,6 +1578,17 @@ bool MYSQL_LOG::write(Log_event *event_info) bool error= 1; DBUG_ENTER("MYSQL_LOG::write(Log_event *)"); + if (thd->binlog_evt_union.do_union) + { + /* + In Stored function; Remember that function call caused an update. + We will log the function call to the binary log on function exit + */ + thd->binlog_evt_union.unioned_events= TRUE; + thd->binlog_evt_union.unioned_events_trans |= event_info->cache_stmt; + DBUG_RETURN(0); + } + pthread_mutex_lock(&LOCK_log); /* diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b3d107365c9..7cfb3b90c51 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -34,6 +34,7 @@ #include <thr_lock.h> #include <my_base.h> /* Needed by field.h */ #include "sql_bitmap.h" +#include "sql_array.h" #ifdef __EMX__ #undef write /* remove pthread.h macro definition for EMX */ @@ -762,8 +763,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields, int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, TABLE_LIST *table_list); bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds); -bool mysql_delete(THD *thd, TABLE_LIST *table, COND *conds, SQL_LIST *order, - ha_rows rows, ulonglong options); +bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, + SQL_LIST *order, ha_rows rows, ulonglong options, + bool reset_auto_increment); bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok); bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create); TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 7813e654433..9764c1ca653 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1331,7 +1331,7 @@ static void set_root(const char *path) #endif } -static void server_init(void) +static void network_init(void) { struct sockaddr_in IPaddr; #ifdef HAVE_SYS_UN_H @@ -1381,16 +1381,6 @@ static void server_init(void) } } - if ((user_info= check_user(mysqld_user))) - { -#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) - if (locked_in_memory) // getuid() == 0 here - set_effective_user(user_info); - else -#endif - set_user(mysqld_user, user_info); - } - #ifdef __NT__ /* create named pipe */ if (Service.IsNT() && mysqld_unix_port[0] && !opt_bootstrap && @@ -1814,6 +1804,7 @@ ulong neb_event_callback(struct EventBlock *eblock) if (!memcmp(&voldata->volID, &datavolid, sizeof(VolumeID_t))) { consoleprintf("MySQL data volume is deactivated, shutting down MySQL Server \n"); + event_flag= TRUE; nw_panic = TRUE; event_flag= TRUE; kill_server(0); @@ -3217,7 +3208,17 @@ int main(int argc, char **argv) mysql_data_home= mysql_data_home_buff; mysql_data_home[0]=FN_CURLIB; // all paths are relative from here mysql_data_home[1]=0; - server_init(); + + if ((user_info= check_user(mysqld_user))) + { +#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) + if (locked_in_memory) // getuid() == 0 here + set_effective_user(user_info); + else +#endif + set_user(mysqld_user, user_info); + } + if (opt_bin_log && !server_id) { @@ -3242,6 +3243,8 @@ we force server id to 2, but this MySQL server will not act as a slave."); if (init_server_components()) exit(1); + network_init(); + #ifdef __WIN__ if (!opt_console) { diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 25fcdfc51fd..31b55219636 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -6048,6 +6048,7 @@ int QUICK_RANGE_SELECT::reset() DBUG_ENTER("QUICK_RANGE_SELECT::reset"); next=0; range= NULL; + in_range= FALSE; cur_range= (QUICK_RANGE**) ranges.buffer; if (file->inited == handler::NONE && (error= file->ha_index_init(index,1))) @@ -7021,6 +7022,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) ha_rows cur_quick_prefix_records= 0; uint cur_param_idx; key_map cur_used_key_parts; + uint pk= param->table->s->primary_key; for (uint cur_index= 0 ; cur_index_info != cur_index_info_end ; cur_index_info++, cur_index++) @@ -7030,6 +7032,45 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) goto next_index; /* + If the current storage manager is such that it appends the primary key to + each index, then the above condition is insufficient to check if the + index is covering. In such cases it may happen that some fields are + covered by the PK index, but not by the current index. Since we can't + use the concatenation of both indexes for index lookup, such an index + does not qualify as covering in our case. If this is the case, below + we check that all query fields are indeed covered by 'cur_index'. + */ + if (pk < MAX_KEY && cur_index != pk && + (table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX)) + { + /* For each table field */ + for (uint i= 0; i < table->s->fields; i++) + { + Field *cur_field= table->field[i]; + /* + If the field is used in the current query, check that the + field is covered by some keypart of the current index. + */ + if (thd->query_id == cur_field->query_id) + { + bool is_covered= FALSE; + KEY_PART_INFO *key_part= cur_index_info->key_part; + KEY_PART_INFO *key_part_end= key_part + cur_index_info->key_parts; + for (; key_part != key_part_end ; key_part++) + { + if (key_part->field == cur_field) + { + is_covered= TRUE; + break; + } + } + if (!is_covered) + goto next_index; + } + } + } + + /* Check (GA1) for GROUP BY queries. */ if (join->group_list) diff --git a/sql/set_var.cc b/sql/set_var.cc index e92ea232759..d2ea7955d28 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -970,16 +970,18 @@ struct show_var_st init_vars[]= { #endif {sys_sort_buffer.name, (char*) &sys_sort_buffer, SHOW_SYS}, {sys_sql_mode.name, (char*) &sys_sql_mode, SHOW_SYS}, - {sys_storage_engine.name, (char*) &sys_storage_engine, SHOW_SYS}, {"sql_notes", (char*) &sys_sql_notes, SHOW_BOOL}, {"sql_warnings", (char*) &sys_sql_warnings, SHOW_BOOL}, + {sys_storage_engine.name, (char*) &sys_storage_engine, SHOW_SYS}, #ifdef HAVE_REPLICATION {sys_sync_binlog_period.name,(char*) &sys_sync_binlog_period, SHOW_SYS}, +#endif + {sys_sync_frm.name, (char*) &sys_sync_frm, SHOW_SYS}, +#ifdef HAVE_REPLICATION {sys_sync_replication.name, (char*) &sys_sync_replication, SHOW_SYS}, {sys_sync_replication_slave_id.name, (char*) &sys_sync_replication_slave_id,SHOW_SYS}, {sys_sync_replication_timeout.name, (char*) &sys_sync_replication_timeout,SHOW_SYS}, #endif - {sys_sync_frm.name, (char*) &sys_sync_frm, SHOW_SYS}, #ifdef HAVE_TZNAME {"system_time_zone", system_time_zone, SHOW_CHAR}, #endif diff --git a/sql/sp.cc b/sql/sp.cc index 4485019be4a..38130d01dab 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1532,8 +1532,6 @@ create_string(THD *thd, String *buf, buf->append("FUNCTION ", 9); else buf->append("PROCEDURE ", 10); - append_identifier(thd, buf, name->m_db.str, name->m_db.length); - buf->append('.'); append_identifier(thd, buf, name->m_name.str, name->m_name.length); buf->append('('); buf->append(params, paramslen); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index dfc91f5a3f4..4358a37daa6 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -245,10 +245,25 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, DBUG_PRINT("info",("STRING_RESULT: %*s", s->length(), s->c_ptr_quick())); CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) - Item_string(thd->strmake(s->ptr(), - s->length()), s->length(), - it->collation.collation), + Item_string(it->collation.collation), use_callers_arena, &backup_current_arena); + /* + We have to use special constructor and allocate string + on system heap here. This is because usual Item_string + constructor would allocate memory in the callers arena. + This would lead to the memory leak in SP loops. + See Bug #11333 "Stored Procedure: Memory blow up on + repeated SELECT ... INTO query" for sample of such SP. + TODO: Usage of the system heap gives significant overhead, + however usual "reuse" mechanism does not work here, as + Item_string has no max size. That is, if we have a loop, which + has string variable with constantly increasing size, we would have + to allocate new pieces of memory again and again on each iteration. + In future we should probably reserve some area of memory for + not-very-large strings and reuse it. But for large strings + we would have to use system heap anyway. + */ + ((Item_string*) it)->set_str_with_copy(s->ptr(), s->length()); break; } case ROW_RESULT: @@ -582,14 +597,162 @@ sp_head::make_field(uint max_length, const char *name, TABLE *dummy) field= ::make_field((char *)0, !m_returns_len ? max_length : m_returns_len, (uchar *)"", 0, m_returns_pack, m_returns, m_returns_cs, - (enum Field::geometry_type)0, Field::NONE, + m_geom_returns, Field::NONE, m_returns_typelib, name ? name : (const char *)m_name.str, dummy); DBUG_RETURN(field); } -int -sp_head::execute(THD *thd) + +int cmp_splocal_locations(Item_splocal * const *a, Item_splocal * const *b) +{ + return (int)((*a)->pos_in_query - (*b)->pos_in_query); +} + + +/* + StoredRoutinesBinlogging + Top-down overview: + + 1. Statements + + Statements that have is_update_query(stmt) == TRUE are written into the + binary log verbatim. + Examples: + UPDATE tbl SET tbl.x = spfunc_w_side_effects() + UPDATE tbl SET tbl.x=1 WHERE spfunc_w_side_effect_that_returns_false(tbl.y) + + Statements that have is_update_query(stmt) == FALSE (e.g. SELECTs) are not + written into binary log. Instead we catch function calls the statement + makes and write it into binary log separately (see #3). + + We actually can easily write SELECT statements into the binary log in the + right order (we don't have issues with const tables being unlocked early + because SELECTs that use FUNCTIONs unlock all tables at once) We don't do + it because replication slave thread currently can't execute SELECT + statements. Fixing this is on the TODO. + + 2. PROCEDURE calls + + CALL statements are not written into binary log. Instead + * Any FUNCTION invocation (in SET, IF, WHILE, OPEN CURSOR and other SP + instructions) is written into binlog separately. + + * Each statement executed in SP is binlogged separately, according to rules + in #1, with the exception that we modify query string: we replace uses + of SP local variables with NAME_CONST('spvar_name', <spvar-value>) calls. + This substitution is done in subst_spvars(). + + 3. FUNCTION calls + + In sp_head::execute_function(), we check + * If this function invocation is done from a statement that is written + into the binary log. + * If there were any attempts to write events to the binary log during + function execution. + If the answers are No and Yes, we write the function call into the binary + log as "DO spfunc(<param1value>, <param2value>, ...)" + +*/ + + +/* + Replace thd->query{_length} with a string that one can write to the binlog. + + SYNOPSIS + subst_spvars() + thd Current thread. + instr Instruction (we look for Item_splocal instances in + instr->free_list) + query_str Original query string + + DESCRIPTION + + The binlog-suitable string is produced by replacing references to SP local + variables with NAME_CONST('sp_var_name', value) calls. + + RETURN + 0 Ok, thd->query{_length} either has been appropriately replaced or + there is no need for replacements. + 1 Out of memory error. +*/ + +static bool subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) +{ + DBUG_ENTER("subst_spvars"); + if (thd->prelocked_mode == NON_PRELOCKED && mysql_bin_log.is_open()) + { + Dynamic_array<Item_splocal*> sp_vars_uses; + char *pbuf, *cur, buffer[512]; + String qbuf(buffer, sizeof(buffer), &my_charset_bin); + int prev_pos, res; + + /* Find all instances of item_splocal used in this statement */ + for (Item *item= instr->free_list; item; item= item->next) + { + if (item->is_splocal()) + { + Item_splocal *item_spl= (Item_splocal*)item; + if (item_spl->pos_in_query) + sp_vars_uses.append(item_spl); + } + } + if (!sp_vars_uses.elements()) + DBUG_RETURN(0); + + /* Sort SP var refs by their occurences in the query */ + sp_vars_uses.sort(cmp_splocal_locations); + + /* + Construct a statement string where SP local var refs are replaced + with "NAME_CONST(name, value)" + */ + qbuf.length(0); + cur= query_str->str; + prev_pos= res= 0; + for (Item_splocal **splocal= sp_vars_uses.front(); + splocal < sp_vars_uses.back(); splocal++) + { + Item *val; + /* append the text between sp ref occurences */ + res|= qbuf.append(cur + prev_pos, (*splocal)->pos_in_query - prev_pos); + prev_pos= (*splocal)->pos_in_query + (*splocal)->m_name.length; + + /* append the spvar substitute */ + res|= qbuf.append(" NAME_CONST('"); + res|= qbuf.append((*splocal)->m_name.str, (*splocal)->m_name.length); + res|= qbuf.append("',"); + val= (*splocal)->this_item(); + DBUG_PRINT("info", ("print %p", val)); + val->print(&qbuf); + res|= qbuf.append(')'); + if (res) + break; + } + res|= qbuf.append(cur + prev_pos, query_str->length - prev_pos); + if (res) + DBUG_RETURN(1); + + if (!(pbuf= thd->strmake(qbuf.ptr(), qbuf.length()))) + DBUG_RETURN(1); + + thd->query= pbuf; + thd->query_length= qbuf.length(); + } + DBUG_RETURN(0); +} + + +/* + Execute the routine. The main instruction jump loop is there + Assume the parameters already set. + + RETURN + -1 on error + +*/ + +int sp_head::execute(THD *thd) { DBUG_ENTER("sp_head::execute"); char olddb[128]; @@ -797,9 +960,31 @@ sp_head::execute(THD *thd) } +/* + Execute a function: + - evaluate parameters + - call sp_head::execute + - evaluate the return value + + SYNOPSIS + sp_head::execute_function() + thd Thread handle + argp Passed arguments (these are items from containing statement?) + argcount Number of passed arguments. We need to check if this is + correct. + resp OUT Put result item here (q: is it a constant Item always?) + + RETURN + 0 on OK + other on error +*/ + int sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) { + Item **param_values; + ulonglong binlog_save_options; + bool need_binlog_call; DBUG_ENTER("sp_head::execute_function"); DBUG_PRINT("info", ("function %s", m_name.str)); uint csize = m_pcont->max_pvars(); @@ -823,6 +1008,8 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) goto end; } + if (!(param_values= (Item**)thd->alloc(sizeof(Item*)*argcount))) + DBUG_RETURN(-1); // QQ Should have some error checking here? (types, etc...) if (!(nctx= new sp_rcontext(csize, hmax, cmax))) @@ -831,6 +1018,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) { sp_pvar_t *pvar = m_pcont->find_pvar(i); Item *it= sp_eval_func_item(thd, argp++, pvar->type, NULL, FALSE); + param_values[i]= it; if (!it) goto end; // EOM error @@ -855,7 +1043,44 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) } thd->spcont= nctx; + binlog_save_options= thd->options; + need_binlog_call= mysql_bin_log.is_open() && (thd->options & OPTION_BIN_LOG); + if (need_binlog_call) + mysql_bin_log.start_union_events(thd); + + thd->options&= ~OPTION_BIN_LOG; ret= execute(thd); + thd->options= binlog_save_options; + + if (need_binlog_call) + mysql_bin_log.stop_union_events(thd); + + if (need_binlog_call && thd->binlog_evt_union.unioned_events) + { + char buf[256]; + String bufstr(buf, sizeof(buf), &my_charset_bin); + bufstr.length(0); + bufstr.append("DO ", 3); + append_identifier(thd, &bufstr, m_name.str, m_name.length); + bufstr.append('('); + for (uint i=0; i < argcount; i++) + { + if (i) + bufstr.append(','); + param_values[i]->print(&bufstr); + } + bufstr.append(')'); + + Query_log_event qinfo(thd, bufstr.ptr(), bufstr.length(), + thd->binlog_evt_union.unioned_events_trans, FALSE); + if (mysql_bin_log.write(&qinfo) && + thd->binlog_evt_union.unioned_events_trans) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, + "Invoked ROUTINE modified a transactional table but MySQL " + "failed to reflect this change in the binary log"); + } + } if (m_type == TYPE_ENUM_FUNCTION && ret == 0) { @@ -893,6 +1118,25 @@ static Item_func_get_user_var *item_is_user_var(Item *it) } +/* + Execute a procedure. + SYNOPSIS + sp_head::execute_procedure() + thd Thread handle + args List of values passed as arguments. + + DESCRIPTION + + The function does the following steps: + - Set all parameters + - call sp_head::execute + - copy back values of INOUT and OUT parameters + + RETURN + 0 Ok + -1 Error +*/ + int sp_head::execute_procedure(THD *thd, List<Item> *args) { int ret= 0; @@ -928,7 +1172,7 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args) thd->spcont= save_spcont; DBUG_RETURN(-1); } - + if (csize > 0 || hmax > 0 || cmax > 0) { Item_null *nit= NULL; // Re-use this, and only create if needed @@ -1105,7 +1349,7 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args) nctx->pop_all_cursors(); // To avoid memory leaks after an error delete nctx; // Does nothing thd->spcont= save_spcont; - + DBUG_RETURN(ret); } @@ -1447,8 +1691,12 @@ sp_head::show_create_function(THD *thd) DBUG_RETURN(res); } -void -sp_head::optimize() + +/* + TODO: what does this do?? +*/ + +void sp_head::optimize() { List<sp_instr> bp; sp_instr *i; @@ -1636,7 +1884,6 @@ int sp_instr::exec_core(THD *thd, uint *nextp) return 0; } - /* sp_instr_stmt class functions */ @@ -1646,14 +1893,19 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) { char *query; uint32 query_length; + int res; DBUG_ENTER("sp_instr_stmt::execute"); DBUG_PRINT("info", ("command: %d", m_lex_keeper.sql_command())); - int res; query= thd->query; query_length= thd->query_length; - if (!(res= alloc_query(thd, m_query.str, m_query.length+1))) + if (!(res= alloc_query(thd, m_query.str, m_query.length+1)) && + !(res=subst_spvars(thd, this, &m_query))) { + /* + (the order of query cache and subst_spvars calls is irrelevant because + queries with SP vars can't be cached) + */ if (query_cache_send_result_to_client(thd, thd->query, thd->query_length) <= 0) { diff --git a/sql/sp_head.h b/sql/sp_head.h index 8ae7834eb2a..1d4bfb514d3 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -110,6 +110,7 @@ public: int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE enum enum_field_types m_returns; // For FUNCTIONs only + Field::geometry_type m_geom_returns; CHARSET_INFO *m_returns_cs; // For FUNCTIONs only TYPELIB *m_returns_typelib; // For FUNCTIONs only uint m_returns_len; // For FUNCTIONs only @@ -326,10 +327,22 @@ public: virtual ~sp_instr() { free_items(); } - // 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. + + /* + Execute this instruction + + SYNOPSIS + execute() + thd Thread handle + nextp OUT index of the next instruction to execute. (For most + instructions this will be the instruction following this + one). + + RETURN + 0 on success, + other if some error occured + */ + virtual int execute(THD *thd, uint *nextp) = 0; /* @@ -339,7 +352,7 @@ public: Should be implemented for instructions using expressions or whole statements (thus having to have own LEX). Used in concert with - sp_lex_keeper class and its descendants. + sp_lex_keeper class and its descendants (there are none currently). */ virtual int exec_core(THD *thd, uint *nextp); @@ -808,6 +821,7 @@ private: }; // class sp_instr_hreturn : public sp_instr +/* This is DECLARE CURSOR */ class sp_instr_cpush : public sp_instr { sp_instr_cpush(const sp_instr_cpush &); /* Prevent use of these */ diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index 0d218bc0538..196f9ccb24b 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -72,6 +72,11 @@ typedef struct sp_cond sp_cond_type_t *val; } sp_cond_t; + +/* + This seems to be an "SP parsing context" or something. +*/ + class sp_pcontext : public Sql_alloc { sp_pcontext(const sp_pcontext &); /* Prevent use of these */ diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index dedbc7bdef1..36380952e5d 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -41,6 +41,16 @@ typedef struct uint foffset; // Frame offset for the handlers declare level } sp_handler_t; + +/* + This is a run context? of one SP ? + THis is + - a stack of cursors? + - a stack of handlers? + - a stack of Items ? + - a stack of instruction locations in SP? +*/ + class sp_rcontext : public Sql_alloc { sp_rcontext(const sp_rcontext &); /* Prevent use of these */ diff --git a/sql/sql_array.h b/sql/sql_array.h new file mode 100644 index 00000000000..c68caf74b25 --- /dev/null +++ b/sql/sql_array.h @@ -0,0 +1,69 @@ +/* Copyright (C) 2003 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 <my_sys.h> + +/* + A typesafe wrapper around DYNAMIC_ARRAY +*/ + +template <class Elem> class Dynamic_array +{ + DYNAMIC_ARRAY array; +public: + Dynamic_array(uint prealloc=16, uint increment=16) + { + my_init_dynamic_array(&array, sizeof(Elem), prealloc, increment); + } + + Elem& at(int idx) + { + return *(((Elem*)array.buffer) + idx); + } + + Elem *front() + { + return (Elem*)array.buffer; + } + + Elem *back() + { + return ((Elem*)array.buffer) + array.elements; + } + + bool append(Elem &el) + { + return (insert_dynamic(&array, (gptr)&el)); + } + + int elements() + { + return array.elements; + } + + ~Dynamic_array() + { + delete_dynamic(&array); + } + + typedef int (*CMP_FUNC)(const Elem *el1, const Elem *el2); + + void sort(CMP_FUNC cmp_func) + { + qsort(array.buffer, array.elements, sizeof(Elem), (qsort_cmp)cmp_func); + } +}; + diff --git a/sql/sql_class.cc b/sql/sql_class.cc index d4f05456cad..4089042315f 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -210,6 +210,7 @@ THD::THD() db_charset= global_system_variables.collation_database; bzero(ha_data, sizeof(ha_data)); mysys_var=0; + binlog_evt_union.do_union= FALSE; #ifndef DBUG_OFF dbug_sentry=THD_SENTRY_MAGIC; #endif @@ -1888,7 +1889,8 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup, backup->cuted_fields= cuted_fields; backup->client_capabilities= client_capabilities; - options&= ~OPTION_BIN_LOG; + if (!lex->requires_prelocking() || is_update_query(lex->sql_command)) + options&= ~OPTION_BIN_LOG; /* Disable result sets */ client_capabilities &= ~CLIENT_MULTI_RESULTS; in_sub_stmt|= new_state; @@ -1945,7 +1947,7 @@ static byte *xid_get_hash_key(const byte *ptr,uint *length, static void xid_free_hash (void *ptr) { if (!((XID_STATE*)ptr)->in_thd) - my_free((byte *)ptr, MYF(0)); + my_free((gptr)ptr, MYF(0)); } bool xid_cache_init() diff --git a/sql/sql_class.h b/sql/sql_class.h index a1de3391ab4..2bba37afa04 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -311,6 +311,9 @@ public: bool write(Log_event* event_info); // binary log write bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event); + void start_union_events(THD *thd); + void stop_union_events(THD *thd); + /* v stands for vector invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0) @@ -1355,7 +1358,27 @@ public: my_bool my_bool_value; long long_value; } sys_var_tmp; - + + struct { + /* + If true, mysql_bin_log::write(Log_event) call will not write events to + binlog, and maintain 2 below variables instead (use + mysql_bin_log.start_union_events to turn this on) + */ + bool do_union; + /* + If TRUE, at least one mysql_bin_log::write(Log_event) call has been + made after last mysql_bin_log.start_union_events() call. + */ + bool unioned_events; + /* + If TRUE, at least one mysql_bin_log::write(Log_event e), where + e.cache_stmt == TRUE call has been made after last + mysql_bin_log.start_union_events() call. + */ + bool unioned_events_trans; + } binlog_evt_union; + THD(); ~THD(); @@ -1977,7 +2000,12 @@ class multi_delete :public select_result_interceptor ha_rows deleted, found; uint num_of_tables; int error; - bool do_delete, transactional_tables, normal_tables, delete_while_scanning; + bool do_delete; + /* True if at least one table we delete from is transactional */ + bool transactional_tables; + /* True if at least one table we delete from is not transactional */ + bool normal_tables; + bool delete_while_scanning; public: multi_delete(TABLE_LIST *dt, uint num_of_tables); @@ -2004,7 +2032,10 @@ class multi_update :public select_result_interceptor uint table_count; Copy_field *copy_field; enum enum_duplicates handle_duplicates; - bool do_update, trans_safe, transactional_tables, ignore; + bool do_update, trans_safe; + /* True if the update operation has made a change in a transactional table */ + bool transactional_tables; + bool ignore; public: multi_update(TABLE_LIST *ut, TABLE_LIST *leaves_list, diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 5ca3f07f0bd..f24e2cdc0ad 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -702,30 +702,28 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) if (!(query= thd->alloc(MAX_DROP_TABLE_Q_LEN))) goto exit; /* not much else we can do */ - query_pos= query_data_start= strmov(query,"drop table "); + query_pos= query_data_start= strmov(query,"drop table "); query_end= query + MAX_DROP_TABLE_Q_LEN; db_len= strlen(db); - + for (tbl= dropped_tables; tbl; tbl= tbl->next_local) { uint tbl_name_len; - if (!tbl->was_dropped) - continue; - - /* 3 for the quotes and the comma*/ - tbl_name_len= strlen(tbl->table_name) + 3; + + /* 3 for the quotes and the comma*/ + tbl_name_len= strlen(tbl->table_name) + 3; if (query_pos + tbl_name_len + 1 >= query_end) { write_to_binlog(thd, query, query_pos -1 - query, db, db_len); query_pos= query_data_start; - } - + } + *query_pos++ = '`'; query_pos= strmov(query_pos,tbl->table_name); *query_pos++ = '`'; *query_pos++ = ','; } - + if (query_pos != query_data_start) { write_to_binlog(thd, query, query_pos -1 - query, db, db_len); diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 5c339d7d64d..5accc9607fe 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -14,15 +14,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - /* Delete of records and truncate of tables. Multi-table deletes were introduced by Monty and Sinisa */ - - #include "mysql_priv.h" #include "ha_innodb.h" #include "sql_select.h" @@ -30,7 +27,8 @@ #include "sql_trigger.h" bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - SQL_LIST *order, ha_rows limit, ulonglong options) + SQL_LIST *order, ha_rows limit, ulonglong options, + bool reset_auto_increment) { bool will_batch; int error, loc_error; @@ -104,6 +102,13 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, free_underlaid_joins(thd, select_lex); thd->row_count_func= 0; send_ok(thd,0L); + + /* + We don't need to call reset_auto_increment in this case, because + mysql_truncate always gives a NULL conds argument, hence we never + get here. + */ + DBUG_RETURN(0); // Nothing to delete } @@ -234,6 +239,21 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_NORMAL); + if (reset_auto_increment && (error < 0)) + { + /* + We're really doing a truncate and need to reset the table's + auto-increment counter. + */ + int error2= table->file->reset_auto_increment(0); + + if (error2 && (error2 != HA_ERR_WRONG_COMMAND)) + { + table->file->print_error(error2, MYF(0)); + error= 1; + } + } + cleanup: /* Invalidate the table in the query cache if something changed. This must @@ -789,6 +809,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) TABLE *table= *table_ptr; table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK); db_type table_type= table->s->db_type; + if (!ha_supports_generate(table_type)) + goto trunc_by_del; strmov(path, table->s->path); *table_ptr= table->next; // Unlink table from list close_temporary(table,0); @@ -807,7 +829,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db, table_list->table_name,reg_ext); - fn_format(path,path,"","",4); + fn_format(path, path, "", "", MY_UNPACK_FILENAME); if (!dont_send_ok) { @@ -819,19 +841,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) DBUG_RETURN(TRUE); } if (!ha_supports_generate(table_type) || thd->lex->sphead) - { - /* Probably InnoDB table */ - ulong save_options= thd->options; - table_list->lock_type= TL_WRITE; - thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT); - ha_enable_transaction(thd, FALSE); - mysql_init_select(thd->lex); - error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0, - HA_POS_ERROR, LL(0)); - ha_enable_transaction(thd, TRUE); - thd->options= save_options; - DBUG_RETURN(error); - } + goto trunc_by_del; if (lock_and_wait_for_table_name(thd, table_list)) DBUG_RETURN(TRUE); } @@ -865,4 +875,17 @@ end: VOID(pthread_mutex_unlock(&LOCK_open)); } DBUG_RETURN(error); + + trunc_by_del: + /* Probably InnoDB table */ + ulong save_options= thd->options; + table_list->lock_type= TL_WRITE; + thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT); + ha_enable_transaction(thd, FALSE); + mysql_init_select(thd->lex); + error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0, + HA_POS_ERROR, LL(0), TRUE); + ha_enable_transaction(thd, TRUE); + thd->options= save_options; + DBUG_RETURN(error); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 9132d3177fc..623dd48b39d 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -518,6 +518,10 @@ int yylex(void *arg, void *yythd) uchar *ident_map= cs->ident_map; lex->yylval=yylval; // The global state + + lex->tok_end_prev= lex->tok_end; + lex->tok_start_prev= lex->tok_start; + lex->tok_start=lex->tok_end=lex->ptr; state=lex->next_state; lex->next_state=MY_LEX_OPERATOR_OR_IDENT; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 2f29ed4ebd4..5f384f8d144 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -717,6 +717,10 @@ typedef struct st_lex SELECT_LEX *all_selects_list; uchar *buf; /* The beginning of string, used by SPs */ uchar *ptr,*tok_start,*tok_end,*end_of_query; + + /* The values of tok_start/tok_end as they were one call of yylex before */ + uchar *tok_start_prev, *tok_end_prev; + char *length,*dec,*change,*name; char *help_arg; char *backup_dir; /* For RESTORE/BACKUP */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 9fbbee7d8d3..bd8f6aadcf9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -624,7 +624,7 @@ void init_update_queries(void) bool is_update_query(enum enum_sql_command command) { DBUG_ASSERT(command >= 0 && command <= SQLCOM_END); - return uc_update_queries[command]; + return uc_update_queries[command] != 0; } /* @@ -2257,6 +2257,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length) return FALSE; } + /**************************************************************************** ** mysql_execute_command ** Execute command saved in thd and current_lex->sql_command @@ -2994,7 +2995,24 @@ end_with_restore_list: goto error; } if (!select_lex->db) - select_lex->db= first_table->db; + { + /* + In the case of ALTER TABLE ... RENAME we should supply the + default database if the new name is not explicitly qualified + by a database. (Bug #11493) + */ + if (lex->alter_info.flags & ALTER_RENAME) + { + if (! thd->db) + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + goto error; + } + select_lex->db= thd->db; + } + else + select_lex->db= first_table->db; + } if (check_access(thd, ALTER_ACL, first_table->db, &first_table->grant.privilege, 0, 0) || check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0)|| @@ -3324,7 +3342,8 @@ end_with_restore_list: unit->set_limit(select_lex); res = mysql_delete(thd, all_tables, select_lex->where, &select_lex->order_list, - unit->select_limit_cnt, select_lex->options); + unit->select_limit_cnt, select_lex->options, + FALSE); break; } case SQLCOM_DELETE_MULTI: @@ -4221,28 +4240,16 @@ end_with_restore_list: thd->variables.select_limit= HA_POS_ERROR; thd->row_count_func= 0; - tmp_disable_binlog(thd); /* don't binlog the substatements */ - res= sp->execute_procedure(thd, &lex->value_list); - reenable_binlog(thd); - - /* - We write CALL to binlog; on the opposite we didn't write the - substatements. That choice is necessary because the substatements - may use local vars. - Binlogging should happen when all tables are locked. They are locked - just above, and unlocked by close_thread_tables(). All tables which - are to be updated are locked like with a table-level write lock, and - this also applies to InnoDB (I tested - note that it reduces - InnoDB's concurrency as we don't use row-level locks). So binlogging - below is safe. - Note the limitation: if the SP returned an error, but still did some - updates, we do NOT binlog it. This is because otherwise "permission - denied", "table does not exist" etc would stop the slave quite - often. There is no easy way to know if the SP updated something - (even no_trans_update is not suitable, as it may be a transactional - autocommit update which happened, and no_trans_update covers only - INSERT/UPDATE/LOAD). + + /* + We never write CALL statements int binlog: + - If the mode is non-prelocked, each statement will be logged + separately. + - If the mode is prelocked, the invoking statement will care + about writing into binlog. + So just execute the statement. */ + res= sp->execute_procedure(thd, &lex->value_list); if (mysql_bin_log.is_open() && (sp->m_chistics->daccess == SP_CONTAINS_SQL || sp->m_chistics->daccess == SP_MODIFIES_SQL_DATA)) @@ -4252,11 +4259,7 @@ end_with_restore_list: ER_FAILED_ROUTINE_BREAK_BINLOG, ER(ER_FAILED_ROUTINE_BREAK_BINLOG)); else - { thd->clear_error(); - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); - mysql_bin_log.write(&qinfo); - } } /* @@ -5409,8 +5412,10 @@ void mysql_parse(THD *thd, char *inBuf, uint length) if (query_cache_send_result_to_client(thd, inBuf, length) <= 0) { LEX *lex= thd->lex; + sp_cache_flush_obsolete(&thd->sp_proc_cache); sp_cache_flush_obsolete(&thd->sp_func_cache); + if (!yyparse((void *)thd) && ! thd->is_fatal_error) { #ifndef NO_EMBEDDED_ACCESS_CHECKS diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 593f294ae0e..2246da78765 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -10287,6 +10287,7 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), bool end_of_records) { int idx= -1; + enum_nested_loop_state ok_code= NESTED_LOOP_OK; DBUG_ENTER("end_send_group"); if (!join->first_record || end_of_records || @@ -10351,7 +10352,11 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), There is a server side cursor and all rows for this fetch request are sent. */ - DBUG_RETURN(NESTED_LOOP_CURSOR_LIMIT); + /* + Preventing code duplication. When finished with the group reset + the group functions and copy_fields. We fall through. bug #11904 + */ + ok_code= NESTED_LOOP_CURSOR_LIMIT; } } } @@ -10364,12 +10369,16 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), } if (idx < (int) join->send_group_parts) { + /* + This branch is executed also for cursors which have finished their + fetch limit - the reason for ok_code. + */ copy_fields(&join->tmp_table_param); if (init_sum_functions(join->sum_funcs, join->sum_funcs_end[idx+1])) DBUG_RETURN(NESTED_LOOP_ERROR); if (join->procedure) join->procedure->add(); - DBUG_RETURN(NESTED_LOOP_OK); + DBUG_RETURN(ok_code); } } if (update_sum_func(join->sum_funcs)) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index feeed2716fc..a5dbd8e3d13 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2248,7 +2248,7 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, if (share->tmp_table == SYSTEM_TMP_TABLE) table->field[3]->store("SYSTEM VIEW", 11, cs); else if (share->tmp_table) - table->field[3]->store("TEMPORARY", 9, cs); + table->field[3]->store("LOCAL TEMPORARY", 15, cs); else table->field[3]->store("BASE TABLE", 10, cs); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index d20908c9f1c..48fadb781fb 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -288,7 +288,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, for (table= tables; table; table= table->next_local) { char *db=table->db; - table->was_dropped= 0; mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL); if (!close_temporary_table(thd, db, table->table_name)) { @@ -360,8 +359,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, wrong_tables.append(','); wrong_tables.append(String(table->table_name,system_charset_info)); } - else - table->was_dropped= 1; } thd->tmp_table_used= tmp_table_deleted; error= 0; @@ -2955,15 +2952,15 @@ mysql_discard_or_import_tablespace(THD *thd, err: close_thread_tables(thd); thd->tablespace_op=FALSE; + if (error == 0) { send_ok(thd); DBUG_RETURN(0); } - if (error == HA_ERR_ROW_IS_REFERENCED) - my_error(ER_ROW_IS_REFERENCED, MYF(0)); - + table->file->print_error(error, MYF(0)); + DBUG_RETURN(-1); } @@ -4905,9 +4902,16 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt) protocol->store_null(); else { - while (!t->file->rnd_next(t->record[0])) + for (;;) { ha_checksum row_crc= 0; + int error= t->file->rnd_next(t->record[0]); + if (unlikely(error)) + { + if (error == HA_ERR_RECORD_DELETED) + continue; + break; + } if (t->record[0] != (byte*) t->field[0]->ptr) row_crc= my_checksum(row_crc, t->record[0], ((byte*) t->field[0]->ptr) - t->record[0]); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index dcada0c0780..8269c16916a 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -230,7 +230,10 @@ bool mysql_create_view(THD *thd, (check_access(thd, DROP_ACL, view->db, &view->grant.privilege, 0, 0) || grant_option && check_grant(thd, DROP_ACL, view, 0, 1, 0)))) - DBUG_RETURN(TRUE); + { + res= TRUE; + goto err; + } for (sl= select_lex; sl; sl= sl->next_select()) { for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local) @@ -243,7 +246,8 @@ bool mysql_create_view(THD *thd, { my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), "ANY", thd->priv_user, thd->host_or_ip, tbl->table_name); - DBUG_RETURN(TRUE); + res= TRUE; + goto err; } /* Mark this table as a table which will be checked after the prepare @@ -302,7 +306,10 @@ bool mysql_create_view(THD *thd, #endif if (open_and_lock_tables(thd, tables)) - DBUG_RETURN(TRUE); + { + res= TRUE; + goto err; + } /* check that tables are not temporary and this VIEW do not used in query @@ -372,7 +379,10 @@ bool mysql_create_view(THD *thd, } if (check_duplicate_names(select_lex->item_list, 1)) - DBUG_RETURN(TRUE); + { + res= TRUE; + goto err; + } #ifndef NO_EMBEDDED_ACCESS_CHECKS /* @@ -402,7 +412,8 @@ bool mysql_create_view(THD *thd, my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), "create view", thd->priv_user, thd->host_or_ip, item->name, view->table_name); - DBUG_RETURN(TRUE); + res= TRUE; + goto err; } } } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9cda6efca06..27f19d80325 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -89,6 +89,7 @@ inline Item *is_truth_value(Item *A, bool v1, bool v2) udf_func *udf; LEX_USER *lex_user; struct sys_var_with_base variable; + enum enum_var_type var_type; Key::Keytype key_type; enum ha_key_alg key_alg; enum db_type db_type; @@ -707,11 +708,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <num> type int_type real_type order_dir lock_option udf_type if_exists opt_local opt_table_options table_options - table_option opt_if_not_exists opt_no_write_to_binlog opt_var_type - opt_var_ident_type delete_option opt_temporary all_or_any opt_distinct + table_option opt_if_not_exists opt_no_write_to_binlog + delete_option opt_temporary all_or_any opt_distinct opt_ignore_leaves fulltext_options spatial_type union_option start_transaction_opts opt_chain opt_release - union_opt select_derived_init option_type option_type2 + union_opt select_derived_init option_type2 %type <ulong_num> ulong_num raid_types merge_insert_types @@ -747,6 +748,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); expr_list udf_expr_list udf_expr_list2 when_list ident_list ident_list_arg +%type <var_type> + option_type opt_var_type opt_var_ident_type + %type <key_type> key_type opt_unique_or_fulltext constraint_key_type @@ -1032,11 +1036,19 @@ execute_var_ident: '@' ident_or_text /* help */ help: - HELP_SYM ident_or_text + HELP_SYM + { + if (Lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "HELP"); + YYABORT; + } + } + ident_or_text { LEX *lex= Lex; lex->sql_command= SQLCOM_HELP; - lex->help_arg= $2.str; + lex->help_arg= $3.str; }; /* change master */ @@ -1460,7 +1472,7 @@ create_function_tail: sp_prepare_create_field(YYTHD, new_field); if (prepare_create_field(new_field, &unused1, &unused2, &unused2, - 0)) + HA_CAN_GEOMETRY)) YYABORT; sp->m_returns= new_field->sql_type; @@ -1468,6 +1480,7 @@ create_function_tail: sp->m_returns_len= new_field->length; sp->m_returns_pack= new_field->pack_flag; sp->m_returns_typelib= new_field->interval; + sp->m_geom_returns= new_field->geom_type; new_field->interval= NULL; bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); @@ -3152,8 +3165,27 @@ create_table_option: | PASSWORD opt_equal TEXT_STRING_sys { Lex->create_info.password=$3.str; Lex->create_info.used_fields|= HA_CREATE_USED_PASSWORD; } | COMMENT_SYM opt_equal TEXT_STRING_sys { Lex->create_info.comment=$3.str; Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT; } | AUTO_INC opt_equal ulonglong_num { Lex->create_info.auto_increment_value=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AUTO;} - | PACK_KEYS_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_PACK_KEYS : HA_OPTION_NO_PACK_KEYS; Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;} - | PACK_KEYS_SYM opt_equal DEFAULT { Lex->create_info.table_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS); Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;} + | PACK_KEYS_SYM opt_equal ulong_num + { + switch($3) { + case 0: + Lex->create_info.table_options|= HA_OPTION_NO_PACK_KEYS; + break; + case 1: + Lex->create_info.table_options|= HA_OPTION_PACK_KEYS; + break; + default: + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS; + } + | PACK_KEYS_SYM opt_equal DEFAULT + { + Lex->create_info.table_options&= + ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS); + Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS; + } | CHECKSUM_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; } | DELAY_KEY_WRITE_SYM opt_equal ulong_num { Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; Lex->create_info.used_fields|= HA_CREATE_USED_DELAY_KEY_WRITE; } | ROW_FORMAT_SYM opt_equal row_types { Lex->create_info.row_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT; } @@ -3370,10 +3402,10 @@ type: $$=FIELD_TYPE_STRING; } | char opt_binary { Lex->length=(char*) "1"; $$=FIELD_TYPE_STRING; } - | nchar '(' NUM ')' { Lex->length=$3.str; + | nchar '(' NUM ')' opt_bin_mod { Lex->length=$3.str; $$=FIELD_TYPE_STRING; Lex->charset=national_charset_info; } - | nchar { Lex->length=(char*) "1"; + | nchar opt_bin_mod { Lex->length=(char*) "1"; $$=FIELD_TYPE_STRING; Lex->charset=national_charset_info; } | BINARY '(' NUM ')' { Lex->length=$3.str; @@ -3384,7 +3416,7 @@ type: $$=FIELD_TYPE_STRING; } | varchar '(' NUM ')' opt_binary { Lex->length=$3.str; $$= MYSQL_TYPE_VARCHAR; } - | nvarchar '(' NUM ')' { Lex->length=$3.str; + | nvarchar '(' NUM ')' opt_bin_mod { Lex->length=$3.str; $$= MYSQL_TYPE_VARCHAR; Lex->charset=national_charset_info; } | VARBINARY '(' NUM ')' { Lex->length=$3.str; @@ -3573,7 +3605,6 @@ attribute: lex->alter_info.flags|= ALTER_ADD_INDEX; } | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; } - | BINARY { Lex->type|= BINCMP_FLAG; } | COLLATE_SYM collation_name { if (Lex->charset && !my_charset_same(Lex->charset,$2)) @@ -3658,8 +3689,27 @@ opt_default: opt_binary: /* empty */ { Lex->charset=NULL; } - | ASCII_SYM { Lex->charset=&my_charset_latin1; } + | ASCII_SYM opt_bin_mod { Lex->charset=&my_charset_latin1; } | BYTE_SYM { Lex->charset=&my_charset_bin; } + | UNICODE_SYM opt_bin_mod + { + if (!(Lex->charset=get_charset_by_csname("ucs2", + MY_CS_PRIMARY,MYF(0)))) + { + my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2"); + YYABORT; + } + } + | charset charset_name opt_bin_mod { Lex->charset=$2; } + | BINARY opt_bin_charset { Lex->type|= BINCMP_FLAG; }; + +opt_bin_mod: + /* empty */ { } + | BINARY { Lex->type|= BINCMP_FLAG; }; + +opt_bin_charset: + /* empty */ { } + | ASCII_SYM { Lex->charset=&my_charset_latin1; } | UNICODE_SYM { if (!(Lex->charset=get_charset_by_csname("ucs2", @@ -4949,7 +4999,7 @@ simple_expr: yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } - if (!($$= get_system_var(YYTHD, (enum_var_type) $3, $4, $5))) + if (!($$= get_system_var(YYTHD, $3, $4, $5))) YYABORT; Lex->variables_used= 1; } @@ -7042,7 +7092,7 @@ show_param: LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->orig_sql_command= SQLCOM_SHOW_STATUS; - lex->option_type= (enum_var_type) $1; + lex->option_type= $1; if (prepare_schema_table(YYTHD, lex, 0, SCH_STATUS)) YYABORT; } @@ -7057,7 +7107,7 @@ show_param: LEX *lex= Lex; lex->sql_command= SQLCOM_SELECT; lex->orig_sql_command= SQLCOM_SHOW_VARIABLES; - lex->option_type= (enum_var_type) $1; + lex->option_type= $1; if (prepare_schema_table(YYTHD, lex, 0, SCH_VARIABLES)) YYABORT; } @@ -7725,10 +7775,13 @@ simple_ident: 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 */ - $$ = (Item*) new Item_splocal($1, spv->offset); + { + /* We're compiling a stored procedure and found a variable */ + Item_splocal *splocal; + splocal= new Item_splocal($1, spv->offset, lex->tok_start_prev - + lex->sphead->m_tmp_query); + $$ = (Item*) splocal; lex->variables_used= 1; lex->safe_to_cache_query=0; } @@ -8447,7 +8500,7 @@ sys_option_value: else if ($2.var) { /* System variable */ if ($1) - lex->option_type= (enum_var_type)$1; + lex->option_type= $1; lex->var_list.push_back(new set_var(lex->option_type, $2.var, &$2.base_name, $4)); } @@ -8481,8 +8534,8 @@ sys_option_value: | option_type TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types { LEX *lex=Lex; - if (!$1) - lex->option_type= (enum_var_type)$1; + if ($1) + lex->option_type= $1; lex->var_list.push_back(new set_var(lex->option_type, find_sys_var("tx_isolation"), &null_lex_str, @@ -8498,8 +8551,7 @@ option_value: | '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default { LEX *lex=Lex; - lex->var_list.push_back(new set_var((enum_var_type) $3, $4.var, - &$4.base_name, $6)); + lex->var_list.push_back(new set_var($3, $4.var, &$4.base_name, $6)); } | charset old_or_new_charset_name_or_default { diff --git a/sql/table.h b/sql/table.h index 767acf8bdc8..b6b3821273d 100644 --- a/sql/table.h +++ b/sql/table.h @@ -577,9 +577,6 @@ typedef struct st_table_list st_table_list *embedding; /* nested join containing the table */ List<struct st_table_list> *join_list;/* join list the table belongs to */ bool cacheable_table; /* stop PS caching */ - - /* used for proper partially successful DROP DATABASE binlogging */ - bool was_dropped; /* used in multi-upd/views privilege check */ bool table_in_first_from_clause; bool skip_temporary; /* this table shouldn't be temporary */ diff --git a/sql/unireg.h b/sql/unireg.h index aafb96ef7c3..2f1d3c2082d 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -50,7 +50,11 @@ #define MAX_SYS_VAR_LENGTH 32 #define MAX_KEY 64 /* Max used keys */ #define MAX_REF_PARTS 16 /* Max parts used as ref */ -#define MAX_KEY_LENGTH 1024 /* max possible key */ +#if SIZEOF_CHARP > 4 +#define MAX_KEY_LENGTH 3072 /* max possible key, if 64 bits */ +#else +#define MAX_KEY_LENGTH 1024 /* max possible key, if 32 bits */ +#endif #if SIZEOF_OFF_T > 4 #define MAX_REFLENGTH 8 /* Max length for record ref */ #else |