diff options
Diffstat (limited to 'sql')
88 files changed, 4073 insertions, 2007 deletions
diff --git a/sql/examples/ha_archive.cc b/sql/examples/ha_archive.cc index a4fe2aaa8cc..9b439087259 100644 --- a/sql/examples/ha_archive.cc +++ b/sql/examples/ha_archive.cc @@ -116,10 +116,14 @@ static ARCHIVE_SHARE *get_share(const char *table_name, TABLE *table) pthread_mutex_lock(&LOCK_mysql_create_db); if (!archive_init) { - archive_init++; VOID(pthread_mutex_init(&archive_mutex,MY_MUTEX_INIT_FAST)); - (void) hash_init(&archive_open_tables,system_charset_info,32,0,0, - (hash_get_key) archive_get_key,0,0); + if (!hash_init(&archive_open_tables,system_charset_info,32,0,0, + (hash_get_key) archive_get_key,0,0)) + { + pthread_mutex_unlock(&LOCK_mysql_create_db); + return NULL; + } + archive_init++; } pthread_mutex_unlock(&LOCK_mysql_create_db); } @@ -130,11 +134,10 @@ static ARCHIVE_SHARE *get_share(const char *table_name, TABLE *table) (byte*) table_name, length))) { - if (!(share=(ARCHIVE_SHARE *) - my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), + if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), &share, sizeof(*share), &tmp_name, length+1, - NullS))) + NullS)) { pthread_mutex_unlock(&archive_mutex); return NULL; @@ -238,11 +241,7 @@ int ha_archive::open(const char *name, int mode, uint test_if_locked) int ha_archive::close(void) { DBUG_ENTER("ha_archive::close"); - int rc= 0; - if (gzclose(archive) == Z_ERRNO) - rc =-1; - rc |= free_share(share); - DBUG_RETURN(rc); + DBUG_RETURN(((gzclose(archive) == Z_ERRNO || free_share(share)) ? -1 : 0)); } @@ -276,12 +275,7 @@ int ha_archive::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *creat } version= ARCHIVE_VERSION; written= gzwrite(archive, &version, sizeof(version)); - if (written == 0 || written != sizeof(version)) - { - delete_table(name); - DBUG_RETURN(-1); - } - if (gzclose(archive)) + if (written != sizeof(version) || gzclose(archive)) { delete_table(name); DBUG_RETURN(-1); @@ -305,7 +299,7 @@ int ha_archive::write_row(byte * buf) update_timestamp(buf+table->timestamp_default_now-1); written= gzwrite(share->archive_write, buf, table->reclength); share->dirty= true; - if (written == 0 || written != table->reclength) + if (written != table->reclength) DBUG_RETURN(-1); for (Field_blob **field=table->blob_field ; *field ; field++) @@ -315,7 +309,7 @@ int ha_archive::write_row(byte * buf) (*field)->get_ptr(&ptr); written= gzwrite(share->archive_write, ptr, (unsigned)size); - if (written == 0 || written != size) + if (written != size) DBUG_RETURN(-1); } diff --git a/sql/field.cc b/sql/field.cc index 26c84575b4d..c96a5a6d809 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -5625,8 +5625,11 @@ bool Field_enum::eq_def(Field *field) if (typelib->count < from_lib->count) return 0; for (uint i=0 ; i < from_lib->count ; i++) - if (my_strcasecmp(field_charset, - typelib->type_names[i],from_lib->type_names[i])) + if (my_strnncoll(field_charset, + (const uchar*)typelib->type_names[i], + strlen(typelib->type_names[i]), + (const uchar*)from_lib->type_names[i], + strlen(from_lib->type_names[i]))) return 0; return 1; } diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 9f14e22ce38..700b8fafe19 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -4795,8 +4795,8 @@ ha_innobase::external_lock( trx->n_mysql_tables_in_use--; prebuilt->mysql_has_locked = FALSE; auto_inc_counter_for_this_stat = 0; - if (trx->n_tables_locked) { - row_unlock_table_for_mysql(trx); + if (trx->n_lock_table_exp) { + row_unlock_tables_for_mysql(trx); } /* If the MySQL lock count drops to zero we know that the current SQL @@ -4891,7 +4891,8 @@ innodb_show_status( field_list.push_back(new Item_empty_string("Status", flen)); - if (protocol->send_fields(&field_list, 1)) { + if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | + Protocol::SEND_EOF)) { my_free(str, MYF(0)); diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index d3dc0bb58de..79e1d7b463b 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -337,7 +337,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt) HA_STATUS_CONST); } } - else if (!mi_is_crashed(file)) + else if (!mi_is_crashed(file) && !thd->killed) { mi_mark_crashed(file); file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED; @@ -379,7 +379,7 @@ int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt) error=update_state_info(¶m,file,UPDATE_STAT); pthread_mutex_unlock(&share->intern_lock); } - else if (!mi_is_crashed(file)) + else if (!mi_is_crashed(file) && !thd->killed) mi_mark_crashed(file); return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK; } diff --git a/sql/handler.cc b/sql/handler.cc index 8ca842efebe..9342d20ec24 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -62,21 +62,21 @@ static SHOW_COMP_OPTION have_yes= SHOW_OPTION_YES; struct show_table_type_st sys_table_types[]= { {"MyISAM", &have_yes, - "Default type from 3.23 with great performance", DB_TYPE_MYISAM}, + "Default engine as of MySQL 3.23 with great performance", DB_TYPE_MYISAM}, {"HEAP", &have_yes, - "Hash based, stored in memory, useful for temporary tables", DB_TYPE_HEAP}, + "Alias for MEMORY", DB_TYPE_HEAP}, {"MEMORY", &have_yes, - "Alias for HEAP", DB_TYPE_HEAP}, + "Hash based, stored in memory, useful for temporary tables", DB_TYPE_HEAP}, {"MERGE", &have_yes, "Collection of identical MyISAM tables", DB_TYPE_MRG_MYISAM}, {"MRG_MYISAM",&have_yes, "Alias for MERGE", DB_TYPE_MRG_MYISAM}, {"ISAM", &have_isam, - "Obsolete table type; Is replaced by MyISAM", DB_TYPE_ISAM}, + "Obsolete storage engine, now replaced by MyISAM", DB_TYPE_ISAM}, {"MRG_ISAM", &have_isam, - "Obsolete table type; Is replaced by MRG_MYISAM", DB_TYPE_MRG_ISAM}, + "Obsolete storage engine, now replaced by MERGE", DB_TYPE_MRG_ISAM}, {"InnoDB", &have_innodb, - "Supports transactions, row-level locking and foreign keys", DB_TYPE_INNODB}, + "Supports transactions, row-level locking, and foreign keys", DB_TYPE_INNODB}, {"INNOBASE", &have_innodb, "Alias for INNODB", DB_TYPE_INNODB}, {"BDB", &have_berkeley_db, @@ -84,7 +84,7 @@ struct show_table_type_st sys_table_types[]= {"BERKELEYDB",&have_berkeley_db, "Alias for BDB", DB_TYPE_BERKELEY_DB}, {"NDBCLUSTER", &have_ndbcluster, - "Clustered, fault tolerant memory based tables", DB_TYPE_NDBCLUSTER}, + "Clustered, fault-tolerant, memory-based tables", DB_TYPE_NDBCLUSTER}, {"NDB", &have_ndbcluster, "Alias for NDBCLUSTER", DB_TYPE_NDBCLUSTER}, {"EXAMPLE",&have_example_db, diff --git a/sql/item.cc b/sql/item.cc index 9cfa12b6365..afe76818726 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -100,9 +100,9 @@ void Item::print_item_w_name(String *str) print(str); if (name) { - str->append(" AS `", 5); - str->append(name); - str->append('`'); + THD *thd= current_thd; + str->append(" AS ", 4); + append_identifier(thd, str, name, strlen(name)); } } @@ -420,6 +420,36 @@ const char *Item_ident::full_name() const return tmp; } +void Item_ident::print(String *str) +{ + THD *thd= current_thd; + if (!table_name || !field_name) + { + const char *nm= field_name ? field_name : name ? name : "tmp_field"; + append_identifier(thd, str, nm, strlen(nm)); + return; + } + if (db_name && db_name[0]) + { + append_identifier(thd, str, db_name, strlen(db_name)); + str->append('.'); + append_identifier(thd, str, table_name, strlen(table_name)); + str->append('.'); + append_identifier(thd, str, field_name, strlen(field_name)); + } + else + { + if (table_name[0]) + { + append_identifier(thd, str, table_name, strlen(table_name)); + str->append('.'); + append_identifier(thd, str, field_name, strlen(field_name)); + } + else + append_identifier(thd, str, field_name, strlen(field_name)); + } +} + /* ARGSUSED */ String *Item_field::val_str(String *str) { @@ -1115,8 +1145,27 @@ bool Item_param::convert_str_value(THD *thd) return rc; } -/* End of Item_param related */ +void Item_param::print(String *str) +{ + if (state == NO_VALUE) + { + str->append('?'); + } + else + { + char buffer[80]; + String tmp(buffer, sizeof(buffer), &my_charset_bin); + const String *res; + res= query_val_str(&tmp); + str->append(*res); + } +} + + +/**************************************************************************** + Item_copy_string +****************************************************************************/ void Item_copy_string::copy() { @@ -1274,11 +1323,21 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) table_list, ref, 0, 1)) != not_found_field) { - if (tmp && tmp != view_ref_found) - { - prev_subselect_item->used_tables_cache|= tmp->table->map; - prev_subselect_item->const_item_cache= 0; - } + if (tmp) + { + if (tmp != view_ref_found) + { + prev_subselect_item->used_tables_cache|= tmp->table->map; + prev_subselect_item->const_item_cache= 0; + } + else + { + prev_subselect_item->used_tables_cache|= + (*ref)->used_tables(); + prev_subselect_item->const_item_cache&= + (*ref)->const_item(); + } + } break; } if (sl->resolve_mode == SELECT_LEX::SELECT_MODE && @@ -1728,6 +1787,21 @@ int Item_real::save_in_field(Field *field, bool no_conversions) return field->store(nr); } + +void Item_real::print(String *str) +{ + if (presentation) + { + str->append(presentation); + return; + } + char buffer[20]; + String num(buffer, sizeof(buffer), &my_charset_bin); + num.set(value, decimals, &my_charset_bin); + str->append(num); +} + + /**************************************************************************** ** varbinary item ** In string context this is a binary string @@ -1986,11 +2060,21 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) table_list, reference, 0, 1)) != not_found_field) { - if (tmp && tmp != view_ref_found) - { - prev_subselect_item->used_tables_cache|= tmp->table->map; - prev_subselect_item->const_item_cache= 0; - } + if (tmp) + { + if (tmp != view_ref_found) + { + prev_subselect_item->used_tables_cache|= tmp->table->map; + prev_subselect_item->const_item_cache= 0; + } + else + { + prev_subselect_item->used_tables_cache|= + (*reference)->used_tables(); + prev_subselect_item->const_item_cache&= + (*reference)->const_item(); + } + } break; } @@ -2035,8 +2119,8 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) if (!((*reference)= fld= new Item_field(tmp))) return 1; mark_as_dependent(thd, last, thd->lex->current_select, fld); - return 0; register_item_tree_changing(reference); + return 0; } /* We can leave expression substituted from view for next PS/SP @@ -2113,6 +2197,7 @@ void Item_ref::cleanup() { DBUG_ENTER("Item_ref::cleanup"); Item_ident::cleanup(); + result_field= 0; if (hook_ptr) *hook_ptr= orig_item; DBUG_VOID_RETURN; @@ -2128,6 +2213,51 @@ void Item_ref::print(String *str) } +bool Item_ref::send(Protocol *prot, String *tmp) +{ + if (result_field) + return prot->store(result_field); + return (*ref)->send(prot, tmp); +} + + +double Item_ref::val_result() +{ + if (result_field) + { + if ((null_value= result_field->is_null())) + return 0.0; + return result_field->val_real(); + } + return val(); +} + + +longlong Item_ref::val_int_result() +{ + if (result_field) + { + if ((null_value= result_field->is_null())) + return 0; + return result_field->val_int(); + } + return val_int(); +} + + +String *Item_ref::str_result(String* str) +{ + if (result_field) + { + if ((null_value= result_field->is_null())) + return 0; + str->set_charset(str_value.charset()); + return result_field->val_str(str, &str_value); + } + return val_str(str); +} + + void Item_ref_null_helper::print(String *str) { str->append("<ref_null_helper>(", 18); diff --git a/sql/item.h b/sql/item.h index 354152611ae..391e6f367b8 100644 --- a/sql/item.h +++ b/sql/item.h @@ -369,7 +369,7 @@ public: return this_item()->save_in_field(field, no_conversions); } - inline void print(String *str) + void print(String *str) { str->reserve(m_name.length+8); str->append(m_name.str, m_name.length); @@ -430,6 +430,7 @@ public: void register_item_tree_changing(Item **ref) { changed_during_fix_field= ref; } bool remove_dependence_processor(byte * arg); + void print(String *str); friend bool insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, const char *table_name, List_iterator<Item> *it, @@ -635,7 +636,7 @@ public: */ virtual table_map used_tables() const { return state != NO_VALUE ? (table_map)0 : PARAM_TABLE_BIT; } - void print(String *str) { str->append('?'); } + void print(String *str); /* parameter never equal to other parameter of other item */ bool eq(const Item *item, bool binary_cmp) const { return 0; } }; @@ -669,6 +670,17 @@ public: }; +class Item_static_int_func :public Item_int +{ + const char *func_name; +public: + Item_static_int_func(const char *str_arg, longlong i, uint length) + :Item_int(NullS, i, length), func_name(str_arg) + {} + void print(String *str) { str->append(func_name); } +}; + + class Item_uint :public Item_int { public: @@ -687,12 +699,13 @@ public: class Item_real :public Item_num { + char *presentation; public: double value; // Item_real() :value(0) {} Item_real(const char *str_arg, uint length) :value(my_atof(str_arg)) { - name=(char*) str_arg; + presentation= name=(char*) str_arg; decimals=(uint8) nr_of_decimals(str_arg); max_length=length; fixed= 1; @@ -700,12 +713,12 @@ public: Item_real(const char *str,double val_arg,uint decimal_par,uint length) :value(val_arg) { - name=(char*) str; + presentation= name=(char*) str; decimals=(uint8) decimal_par; max_length=length; fixed= 1; } - Item_real(double value_par) :value(value_par) { fixed= 1; } + Item_real(double value_par) :presentation(0), value(value_par) { fixed= 1; } int save_in_field(Field *field, bool no_conversions); enum Type type() const { return REAL_ITEM; } enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } @@ -721,6 +734,19 @@ public: void cleanup() {} Item *new_item() { return new Item_real(name,value,decimals,max_length); } Item_num *neg() { value= -value; return this; } + void print(String *str); +}; + + +class Item_static_real_func :public Item_real +{ + const char *func_name; +public: + Item_static_real_func(const char *str, double val_arg, uint decimal_par, + uint length) + :Item_real(NullS, val_arg, decimal_par, length), func_name(str) + {} + void print(String *str) { str->append(func_name); } }; @@ -803,6 +829,20 @@ public: void cleanup() {} }; + +class Item_static_string_func :public Item_string +{ + const char *func_name; +public: + Item_static_string_func(const char *name_par, const char *str, uint length, + CHARSET_INFO *cs, + Derivation dv= DERIVATION_COERCIBLE) + :Item_string(NullS, str, length, cs, dv), func_name(name_par) + {} + void print(String *str) { str->append(func_name); } +}; + + /* for show tables */ class Item_datetime :public Item_string @@ -887,15 +927,15 @@ public: Item *orig_item; /* things in 'cleanup()' */ Item_ref(Item **hook, Item *original,const char *db_par, const char *table_name_par, const char *field_name_par) - :Item_ident(db_par,table_name_par,field_name_par),ref(0), hook_ptr(hook), - orig_item(original) {} - Item_ref(Item **item, Item **hook, + :Item_ident(db_par, table_name_par, field_name_par), result_field(0), + ref(0), hook_ptr(hook), orig_item(original) {} + Item_ref(Item **item, Item **hook, const char *table_name_par, const char *field_name_par) - :Item_ident(NullS,table_name_par,field_name_par), + :Item_ident(NullS, table_name_par, field_name_par), result_field(0), ref(item), hook_ptr(hook), orig_item(hook ? *hook:0) {} // Constructor need to process subselect with temporary tables (see Item) Item_ref(THD *thd, Item_ref *item, Item **hook) - :Item_ident(thd, item), ref(item->ref), + :Item_ident(thd, item), result_field(item->result_field), ref(item->ref), hook_ptr(hook), orig_item(hook ? *hook : 0) {} enum Type type() const { return REF_ITEM; } bool eq(const Item *item, bool binary_cmp) const @@ -927,7 +967,10 @@ public: { return (null_value=(*ref)->get_date_result(ltime,fuzzydate)); } - bool send(Protocol *prot, String *tmp){ return (*ref)->send(prot, tmp); } + double val_result(); + longlong val_int_result(); + String *str_result(String* tmp); + bool send(Protocol *prot, String *tmp); void make_field(Send_field *field) { (*ref)->make_field(field); } bool fix_fields(THD *, struct st_table_list *, Item **); int save_in_field(Field *field, bool no_conversions) @@ -940,6 +983,7 @@ public: return depended_from ? OUTER_REF_TABLE_BIT : (*ref)->used_tables(); } void set_result_field(Field *field) { result_field= field; } + Field *get_tmp_table_field() { return result_field; } bool is_result_field() { return 1; } void save_in_result_field(bool no_conversions) { diff --git a/sql/item_create.cc b/sql/item_create.cc index e2c85af49bc..02cface827b 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -73,12 +73,13 @@ Item *create_func_connection_id(void) { THD *thd=current_thd; thd->lex->safe_to_cache_query= 0; - return new Item_int(NullS,(longlong) - ((thd->slave_thread) ? - thd->variables.pseudo_thread_id : - thd->thread_id), - 10); -} + return new Item_static_int_func("connection_id()", + (longlong) + ((thd->slave_thread) ? + thd->variables.pseudo_thread_id : + thd->thread_id), + 10); +} Item *create_func_conv(Item* a, Item *b, Item *c) { @@ -293,7 +294,7 @@ Item *create_func_period_diff(Item* a, Item *b) Item *create_func_pi(void) { - return new Item_real("pi()",M_PI,6,8); + return new Item_static_real_func("pi()", M_PI, 6, 8); } Item *create_func_pow(Item* a, Item *b) @@ -309,8 +310,9 @@ Item *create_func_current_user() length= (uint) (strxmov(buff, thd->priv_user, "@", thd->priv_host, NullS) - buff); - return new Item_string(NullS, thd->memdup(buff, length), length, - system_charset_info); + return new Item_static_string_func("current_user()", + thd->memdup(buff, length), length, + system_charset_info); } Item *create_func_radians(Item *a) @@ -434,7 +436,7 @@ Item *create_func_uuid(void) Item *create_func_version(void) { - return new Item_string(NullS,server_version, + return new Item_static_string_func("version()", server_version, (uint) strlen(server_version), system_charset_info, DERIVATION_IMPLICIT); } diff --git a/sql/item_func.cc b/sql/item_func.cc index a7bfb727ff7..a8c3feef19b 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3280,9 +3280,25 @@ Item_func_sp::Item_func_sp(sp_name *name, List<Item> &list) const char * Item_func_sp::func_name() const { - return m_name->m_name.str; + THD *thd= current_thd; + /* Calculate length to avoud reallocation of string for sure */ + uint len= ((m_name->m_db.length + + m_name->m_name.length)*2 + //characters*quoting + 2 + // ` and ` + 1 + // . + 1 + // end of string + ALIGN_SIZE(1)); // to avoid String reallocation + String qname((char *)alloc_root(&thd->mem_root, len), len, + system_charset_info); + + qname.length(0); + append_identifier(thd, &qname, m_name->m_db.str, m_name->m_db.length); + qname.append('.'); + append_identifier(thd, &qname, m_name->m_name.str, m_name->m_name.length); + return qname.ptr(); } + int Item_func_sp::execute(Item **itp) { diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 66a223f0ebf..9d58cc37c2a 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -446,7 +446,13 @@ String *Item_func_spatial_collection::val_str(String *str) } } if (str->length() > current_thd->variables.max_allowed_packet) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), + func_name(), current_thd->variables.max_allowed_packet); goto err; + } null_value = 0; return str; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index a8805be7854..5f2c37dd8a7 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -266,7 +266,13 @@ String *Item_func_concat::val_str(String *str) continue; if (res->length()+res2->length() > current_thd->variables.max_allowed_packet) - goto null; // Error check + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), + current_thd->variables.max_allowed_packet); + goto null; + } if (res->alloced_length() >= res->length()+res2->length()) { // Use old buffer res->append(*res2); @@ -544,7 +550,13 @@ String *Item_func_concat_ws::val_str(String *str) if (res->length() + sep_str->length() + res2->length() > current_thd->variables.max_allowed_packet) - goto null; // Error check + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), + current_thd->variables.max_allowed_packet); + goto null; + } if (res->alloced_length() >= res->length() + sep_str->length() + res2->length()) { // Use old buffer @@ -801,7 +813,15 @@ redo: offset= (int) (ptr-res->ptr()); if (res->length()-from_length + to_length > current_thd->variables.max_allowed_packet) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), + func_name(), + current_thd->variables.max_allowed_packet); + goto null; + } if (!alloced) { alloced=1; @@ -822,7 +842,13 @@ skip: { if (res->length()-from_length + to_length > current_thd->variables.max_allowed_packet) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), + current_thd->variables.max_allowed_packet); goto null; + } if (!alloced) { alloced=1; @@ -882,7 +908,13 @@ String *Item_func_insert::val_str(String *str) length=res->length()-start; if (res->length() - length + res2->length() > current_thd->variables.max_allowed_packet) - goto null; // OOM check + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), + func_name(), current_thd->variables.max_allowed_packet); + goto null; + } res=copy_if_not_alloced(str,res,res->length()); res->replace(start,length,*res2); return res; @@ -1934,7 +1966,13 @@ String *Item_func_repeat::val_str(String *str) length=res->length(); // Safe length check if (length > current_thd->variables.max_allowed_packet/count) - goto err; // Probably an error + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), + func_name(), current_thd->variables.max_allowed_packet); + goto err; + } tot_length= length*(uint) count; if (!(res= alloc_buffer(res,str,&tmp_value,tot_length))) goto err; @@ -1999,8 +2037,15 @@ String *Item_func_rpad::val_str(String *str) return (res); } pad_char_length= rpad->numchars(); - if ((ulong) byte_count > current_thd->variables.max_allowed_packet || - args[2]->null_value || !pad_char_length) + if ((ulong) byte_count > current_thd->variables.max_allowed_packet) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), + func_name(), current_thd->variables.max_allowed_packet); + goto err; + } + if(args[2]->null_value || !pad_char_length) goto err; res_byte_length= res->length(); /* Must be done before alloc_buffer */ if (!(res= alloc_buffer(res,str,&tmp_value,byte_count))) @@ -2079,8 +2124,16 @@ String *Item_func_lpad::val_str(String *str) pad_char_length= pad->numchars(); byte_count= count * collation.collation->mbmaxlen; - if (byte_count > current_thd->variables.max_allowed_packet || - args[2]->null_value || !pad_char_length || str->alloc(byte_count)) + if (byte_count > current_thd->variables.max_allowed_packet) + { + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), + func_name(), current_thd->variables.max_allowed_packet); + goto err; + } + + if (args[2]->null_value || !pad_char_length || str->alloc(byte_count)) goto err; str->length(0); @@ -2221,6 +2274,18 @@ bool Item_func_set_collation::eq(const Item *item, bool binary_cmp) const return 1; } + +void Item_func_set_collation::print(String *str) +{ + str->append('('); + args[0]->print(str); + str->append(" collate ", 9); + DBUG_ASSERT(args[1]->basic_const_item() && + args[1]->type() == Item::STRING_ITEM); + args[1]->str_value.print(str); + str->append(')'); +} + String *Item_func_charset::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -2368,7 +2433,10 @@ String *Item_load_file::val_str(String *str) } if (stat_info.st_size > (long) current_thd->variables.max_allowed_packet) { - /* my_error(ER_TOO_LONG_STRING, MYF(0), file_name->c_ptr()); */ + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), + func_name(), current_thd->variables.max_allowed_packet); goto err; } if (tmp_value.alloc(stat_info.st_size)) diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 22134733393..df8861b2ee0 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -359,9 +359,10 @@ class Item_func_database :public Item_str_func public: Item_func_database() { collation.set(system_charset_info,DERIVATION_IMPLICIT); } String *val_str(String *); - void fix_length_and_dec() - { + void fix_length_and_dec() + { max_length= MAX_FIELD_NAME * system_charset_info->mbmaxlen; + maybe_null=1; } const char *func_name() const { return "database"; } }; @@ -634,7 +635,7 @@ public: void fix_length_and_dec(); bool eq(const Item *item, bool binary_cmp) const; const char *func_name() const { return "collate"; } - void print(String *str) { print_op(str); } + void print(String *str); }; class Item_func_charset :public Item_str_func diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 33e661ce46e..c615bcf7e0f 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* +/* subselect Item SUBSELECT TODO: @@ -41,7 +41,7 @@ Item_subselect::Item_subselect(): { reset(); /* - item value is NULL if select_subselect not changed this value + item value is NULL if select_subselect not changed this value (i.e. some rows will be found returned) */ null_value= 1; @@ -114,7 +114,7 @@ Item_subselect::~Item_subselect() } Item_subselect::trans_res -Item_subselect::select_transformer(JOIN *join) +Item_subselect::select_transformer(JOIN *join) { DBUG_ENTER("Item_subselect::select_transformer"); DBUG_RETURN(RES_OK); @@ -128,7 +128,12 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref) arena= thd->current_arena; char const *save_where= thd->where; - int res= engine->prepare(); + int res; + + if (check_stack_overrun(thd, (gptr)&res)) + return 1; + + res= engine->prepare(); // all transformetion is done (used by prepared statements) changed= 1; @@ -148,11 +153,11 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref) if (have_to_be_excluded) engine->exclude(); substitution= 0; - thd->where= "checking transformed subquery"; + thd->where= "checking transformed subquery"; if (!(*ref)->fixed) ret= (*ref)->fix_fields(thd, tables, ref); - // We can't substitute aggregate functions (like (SELECT (max(i))) - if ((*ref)->with_sum_func) + // We can't substitute aggregate functions like "SELECT (max(i))" + if (substype() == SINGLEROW_SUBS && (*ref)->with_sum_func) { my_error(ER_INVALID_GROUP_FUNC_USE, MYF(0)); return 1; @@ -161,7 +166,7 @@ bool Item_subselect::fix_fields(THD *thd_param, TABLE_LIST *tables, Item **ref) } // Is it one field subselect? if (engine->cols() > max_columns) - { + { my_error(ER_OPERAND_COLUMNS, MYF(0), 1); return 1; } @@ -199,7 +204,7 @@ bool Item_subselect::exec() return (res); } -Item::Type Item_subselect::type() const +Item::Type Item_subselect::type() const { return SUBSELECT_ITEM; } @@ -277,7 +282,7 @@ Item_maxmin_subselect::Item_maxmin_subselect(Item_subselect *parent, */ used_tables_cache= parent->get_used_tables_cache(); const_item_cache= parent->get_const_item_cache(); - + DBUG_VOID_RETURN; } @@ -299,7 +304,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join) { if (changed) return RES_OK; - + SELECT_LEX *select_lex= join->select_lex; Statement backup; @@ -314,10 +319,10 @@ Item_singlerow_subselect::select_transformer(JOIN *join) TODO: solve above problem */ !(select_lex->item_list.head()->type() == FIELD_ITEM || - select_lex->item_list.head()->type() == REF_ITEM) + select_lex->item_list.head()->type() == REF_ITEM) ) { - + have_to_be_excluded= 1; if (join->thd->lex->describe) { @@ -355,7 +360,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join) return RES_REDUCE; } return RES_OK; - + err: if (arena) thd->restore_backup_item_arena(arena, &backup); @@ -418,7 +423,7 @@ void Item_singlerow_subselect::bring_value() exec(); } -double Item_singlerow_subselect::val() +double Item_singlerow_subselect::val() { DBUG_ASSERT(fixed == 1); if (!exec() && !value->null_value) @@ -433,7 +438,7 @@ double Item_singlerow_subselect::val() } } -longlong Item_singlerow_subselect::val_int() +longlong Item_singlerow_subselect::val_int() { DBUG_ASSERT(fixed == 1); if (!exec() && !value->null_value) @@ -448,7 +453,7 @@ longlong Item_singlerow_subselect::val_int() } } -String *Item_singlerow_subselect::val_str (String *str) +String *Item_singlerow_subselect::val_str (String *str) { if (!exec() && !value->null_value) { @@ -553,7 +558,7 @@ double Item_exists_subselect::val() return (double) value; } -longlong Item_exists_subselect::val_int() +longlong Item_exists_subselect::val_int() { DBUG_ASSERT(fixed == 1); if (exec()) @@ -590,7 +595,7 @@ double Item_in_subselect::val() return (double) value; } -longlong Item_in_subselect::val_int() +longlong Item_in_subselect::val_int() { DBUG_ASSERT(fixed == 1); if (exec()) @@ -825,7 +830,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, */ select_lex->having= join->having= - func->create(expr, + func->create(expr, new Item_null_helper(this, item, (char *)"<no matter>", (char *)"<result>")); @@ -842,7 +847,7 @@ Item_in_subselect::single_value_transformer(JOIN *join, { // it is single select without tables => possible optimization item= func->create(left_expr, item); - // fix_field of item will be done in time of substituting + // fix_field of item will be done in time of substituting substitution= item; have_to_be_excluded= 1; if (thd->lex->describe) @@ -885,7 +890,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) thd->where= "row IN/ALL/ANY subquery"; if (arena) - thd->set_n_backup_item_arena(arena, &backup); + thd->set_n_backup_item_arena(arena, &backup); SELECT_LEX *select_lex= join->select_lex; @@ -926,7 +931,7 @@ Item_in_subselect::row_value_transformer(JOIN *join) List_iterator_fast<Item> li(select_lex->item_list); for (uint i= 0; i < n; i++) { - Item *func= new Item_ref_null_helper(this, + Item *func= new Item_ref_null_helper(this, select_lex->ref_pointer_array+i, (char *) "<no matter>", (char *) "<list ref>"); @@ -990,23 +995,6 @@ Item_in_subselect::select_transformer(JOIN *join) } -Item_subselect::trans_res -Item_in_subselect::no_select_transform() -{ - DBUG_ENTER("Item_in_subselect::no_select_transform"); - // We have execute fix_fields() for left expression - SELECT_LEX *current= thd->lex->current_select, *up; - thd->lex->current_select= up= current->return_after_parsing(); - if (left_expr->fix_fields(thd, up->get_table_list(), &left_expr)) - { - thd->lex->current_select= current; - DBUG_RETURN(RES_ERROR); - } - thd->lex->current_select= current; - DBUG_RETURN(RES_OK); -} - - void Item_in_subselect::print(String *str) { if (transformed) @@ -1119,7 +1107,7 @@ int subselect_single_select_engine::prepare() (ORDER*) select_lex->order_list.first, (ORDER*) select_lex->group_list.first, select_lex->having, - (ORDER*) 0, select_lex, + (ORDER*) 0, select_lex, select_lex->master_unit())) return 1; thd->lex->current_select= save_select; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index d50688e0b58..e5defe24756 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -47,7 +47,7 @@ protected: /* old engine if engine was changed */ subselect_engine *old_engine; /* cache of used external tables */ - table_map used_tables_cache; + table_map used_tables_cache; /* allowed number of columns (1 for single value subqueries) */ uint max_columns; /* work with 'substitution' */ @@ -69,22 +69,21 @@ public: virtual subs_type substype() { return UNKNOWN_SUBS; } - /* + /* We need this method, because some compilers do not allow 'this' pointer in constructor initialization list, but we need pass pointer to subselect Item class to select_subselect classes constructor. */ - virtual void init (st_select_lex *select_lex, + virtual void init (st_select_lex *select_lex, select_subselect *result); ~Item_subselect(); void cleanup(); - virtual void reset() + virtual void reset() { null_value= 1; } virtual trans_res select_transformer(JOIN *join); - virtual trans_res no_select_transform() { return RES_OK; } bool assigned() { return value_assigned; } void assigned(bool a) { value_assigned= a; } enum Type type() const; @@ -220,7 +219,6 @@ public: was_null= 0; } trans_res select_transformer(JOIN *join); - trans_res no_select_transform(); trans_res single_value_transformer(JOIN *join, Comp_creator *func); trans_res row_value_transformer(JOIN * join); @@ -276,7 +274,7 @@ public: } virtual ~subselect_engine() {}; // to satisfy compiler virtual void cleanup()= 0; - + // set_thd should be called before prepare() void set_thd(THD *thd_arg) { thd= thd_arg; } THD * get_thd() { return thd; } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 50864132932..b92a7f2ba80 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -2088,7 +2088,7 @@ Item_func_group_concat::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) result_field= 0; null_value= 1; max_length= group_concat_max_len; - thd->allow_sum_func= 1; + thd->allow_sum_func= 1; if (!(tmp_table_param= new TMP_TABLE_PARAM)) return 1; tables_list= tables; @@ -2102,7 +2102,6 @@ bool Item_func_group_concat::setup(THD *thd) List<Item> list; SELECT_LEX *select_lex= thd->lex->current_select; uint const_fields; - byte *record; qsort_cmp2 compare_key; DBUG_ENTER("Item_func_group_concat::setup"); @@ -2111,7 +2110,7 @@ bool Item_func_group_concat::setup(THD *thd) /* push all not constant fields to list and create temp table - */ + */ const_fields= 0; always_null= 0; for (uint i= 0; i < arg_count_field; i++) @@ -2129,15 +2128,15 @@ bool Item_func_group_concat::setup(THD *thd) } if (always_null) DBUG_RETURN(0); - + List<Item> all_fields(list); - if (arg_count_order) + if (arg_count_order) { bool hidden_group_fields; setup_group(thd, args, tables_list, list, all_fields, *order, &hidden_group_fields); } - + count_field_types(tmp_table_param,all_fields,0); if (table) { @@ -2163,7 +2162,6 @@ bool Item_func_group_concat::setup(THD *thd) table->no_rows= 1; key_length= table->reclength; - record= table->record[0]; /* Offset to first result field in table */ field_list_offset= table->fields - (list.elements - const_fields); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index e0979528b54..0e7594a1d98 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2011,7 +2011,7 @@ void Item_char_typecast::print(String *str) if (cast_cs) { str->append(" charset ", 9); - str->append(cast_cs->name); + str->append(cast_cs->csname); } str->append(')'); } @@ -2519,7 +2519,6 @@ longlong Item_func_timestamp_diff::val_int() uint year; uint year_beg, year_end, month_beg, month_end; uint diff_days= (uint) (seconds/86400L); - uint diff_months= 0; uint diff_years= 0; if (neg == -1) { diff --git a/sql/lex.h b/sql/lex.h index fc72435bb52..b148fc7d6bc 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -210,6 +210,7 @@ static SYMBOL symbols[] = { { "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION)}, { "GET_FORMAT", SYM(GET_FORMAT)}, { "GLOBAL", SYM(GLOBAL_SYM)}, + { "GOTO", SYM(GOTO_SYM)}, { "GRANT", SYM(GRANT)}, { "GRANTS", SYM(GRANTS)}, { "GROUP", SYM(GROUP)}, @@ -257,6 +258,7 @@ static SYMBOL symbols[] = { { "KEY", SYM(KEY_SYM)}, { "KEYS", SYM(KEYS)}, { "KILL", SYM(KILL_SYM)}, + { "LABEL", SYM(LABEL_SYM)}, { "LANGUAGE", SYM(LANGUAGE_SYM)}, { "LAST", SYM(LAST_SYM)}, { "LEADING", SYM(LEADING)}, diff --git a/sql/log.cc b/sql/log.cc index 5e15b2b170c..44c8ce59aaf 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -838,13 +838,13 @@ int MYSQL_LOG::purge_logs(const char *to_log, while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) && !log_in_use(log_info.log_file_name)) { - ulong tmp; - LINT_INIT(tmp); + ulong file_size; + LINT_INIT(file_size); if (decrease_log_space) //stat the file we want to delete { MY_STAT s; if (my_stat(log_info.log_file_name,&s,MYF(0))) - tmp= s.st_size; + file_size= s.st_size; else { /* @@ -852,7 +852,7 @@ int MYSQL_LOG::purge_logs(const char *to_log, of space that deletion will free. In most cases, deletion won't work either, so it's not a problem. */ - tmp= 0; + file_size= 0; } } /* @@ -861,7 +861,7 @@ int MYSQL_LOG::purge_logs(const char *to_log, */ DBUG_PRINT("info",("purging %s",log_info.log_file_name)); if (!my_delete(log_info.log_file_name, MYF(0)) && decrease_log_space) - *decrease_log_space-= tmp; + *decrease_log_space-= file_size; if (find_next_log(&log_info, 0) || exit_loop) break; } diff --git a/sql/log_event.cc b/sql/log_event.cc index 6207bf1b4dc..2f3471fee17 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1034,7 +1034,7 @@ int Query_log_event::write_data(IO_CACHE* file) int8store(start, sql_mode); start+= 8; } - if (catalog) + if (catalog_len >= 0) // i.e. "catalog inited" (false for 4.0 events) { *(start++)= Q_CATALOG_CODE; *(start++)= (uchar) catalog_len; @@ -1119,7 +1119,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, Query_log_event::Query_log_event(const char* buf, uint event_len, const Format_description_log_event *description_event) :Log_event(buf, description_event), data_buf(0), query(NULL), - catalog(NULL), db(NULL), catalog_len(0), status_vars_len(0), + db(NULL), catalog_len(-1), status_vars_len(0), flags2_inited(0), sql_mode_inited(0) { ulong data_len; @@ -1214,9 +1214,10 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, not need (and want) to change start_dup (because this would cut the previously marked status vars). */ + pos++; if (start_dup==end) - start_dup= ++pos; - pos+= catalog_len+1; + start_dup= pos; + pos+= catalog_len+1; // counting the end '\0' break; default: /* That's why you must write status vars in growing order of code */ @@ -1236,11 +1237,15 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, const char* tmp_buf= data_buf; /* Now set event's pointers to point to bits of the new string */ - if (catalog_len) + if (catalog_len >= 0) // we have seen a catalog (zero-length or not) { catalog= tmp_buf; tmp_buf+= (uint) (end-start_dup); /* "seek" to db */ } +#ifndef DBUG_OFF + else + catalog= 0; // for DBUG_PRINT +#endif db= tmp_buf; query= tmp_buf + db_len + 1; q_len = data_buf + data_len - query; @@ -1531,7 +1536,7 @@ end: TEMPORARY TABLE don't suffer from these assignments to 0 as DROP TEMPORARY TABLE uses the db.table syntax). */ - thd->db= 0; // prevent db from being freed + thd->db= thd->catalog= 0; // prevent db from being freed thd->query= 0; // just to be sure thd->query_length= 0; VOID(pthread_mutex_unlock(&LOCK_thread_count)); @@ -2428,6 +2433,11 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, thd->query_length= 0; // Should not be needed thd->query_error= 0; clear_all_errors(thd, rli); + /* + Usually mysql_init_query() is called by mysql_parse(), but we need it here + as the present method does not call mysql_parse(). + */ + mysql_init_query(thd, 0, 0); if (!use_rli_only_for_errors) { /* Saved for InnoDB, see comment in Query_log_event::exec_event() */ @@ -2457,6 +2467,13 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id = query_id++; VOID(pthread_mutex_unlock(&LOCK_thread_count)); + /* + Initing thd->row_count is not necessary in theory as this variable has no + influence in the case of the slave SQL thread (it is used to generate a + "data truncated" warning but which is absorbed and never gets to the + error log); still we init it to avoid a Valgrind message. + */ + mysql_reset_errors(thd); TABLE_LIST tables; bzero((char*) &tables,sizeof(tables)); diff --git a/sql/log_event.h b/sql/log_event.h index ec23aad1717..c9cce1d40ea 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */ @@ -34,8 +34,8 @@ #define LOG_READ_TOO_LARGE -7 #define LOG_EVENT_OFFSET 4 - -/* + +/* 3 is MySQL 4.x; 4 is MySQL 5.0.0. Compared to version 3, version 4 has: - a different Start_log_event, which includes info about the binary log @@ -58,12 +58,12 @@ timestamp_when_the_master_started (4 bytes), a counter (a sequence number which increments every time we write an event to the binlog) (3 bytes). Q: how do we handle when the counter is overflowed and restarts from 0 ? - + - Query and Load (Create or Execute) events may have a more precise timestamp - (with microseconds), number of matched/affected/warnings rows + (with microseconds), number of matched/affected/warnings rows and fields of session variables: SQL_MODE, FOREIGN_KEY_CHECKS, UNIQUE_CHECKS, SQL_AUTO_IS_NULL, the collations and - charsets, the PASSWORD() version (old/new/...). + charsets, the PASSWORD() version (old/new/...). */ #define BINLOG_VERSION 4 @@ -128,11 +128,11 @@ struct sql_ex_info char* escaped; int cached_new_format; uint8 field_term_len,enclosed_len,line_term_len,line_start_len, escaped_len; - char opt_flags; + char opt_flags; char empty_flags; - + // store in new format even if old is possible - void force_new_format() { cached_new_format = 1;} + void force_new_format() { cached_new_format = 1;} int data_size() { return (new_format() ? @@ -172,20 +172,20 @@ struct sql_ex_info #define LOG_EVENT_HEADER_LEN 19 /* the fixed header length */ #define OLD_HEADER_LEN 13 /* the fixed header length in 3.23 */ -/* +/* Fixed header length, where 4.x and 5.0 agree. That is, 5.0 may have a longer header (it will for sure when we have the unique event's ID), but at least the first 19 bytes are the same in 4.x and 5.0. So when we have the unique event's ID, LOG_EVENT_HEADER_LEN will be something like 26, but LOG_EVENT_MINIMAL_HEADER_LEN will remain 19. */ -#define LOG_EVENT_MINIMAL_HEADER_LEN 19 - +#define LOG_EVENT_MINIMAL_HEADER_LEN 19 + /* event-specific post-header sizes */ // where 3.23, 4.x and 5.0 agree #define QUERY_HEADER_MINIMAL_LEN (4 + 4 + 1 + 2) // where 5.0 differs: 2 for len of N-bytes vars. -#define QUERY_HEADER_LEN (QUERY_HEADER_MINIMAL_LEN + 2) +#define QUERY_HEADER_LEN (QUERY_HEADER_MINIMAL_LEN + 2) #define LOAD_HEADER_LEN (4 + 4 + 4 + 1 +1 + 4) #define START_V3_HEADER_LEN (2 + ST_SERVER_VER_LEN + 4) #define ROTATE_HEADER_LEN 8 // this is FROZEN (the Rotate post-header is frozen) @@ -195,8 +195,8 @@ struct sql_ex_info #define DELETE_FILE_HEADER_LEN 4 #define FORMAT_DESCRIPTION_HEADER_LEN (START_V3_HEADER_LEN+1+LOG_EVENT_TYPES) -/* - Event header offsets; +/* + Event header offsets; these point to places inside the fixed header. */ @@ -294,22 +294,22 @@ struct sql_ex_info So they are now removed and their place may later be reused for other flags. Then one must remember that Rotate events in 4.x have LOG_EVENT_FORCED_ROTATE_F set, so one should not rely on the value of the - replacing flag when reading a Rotate event. + replacing flag when reading a Rotate event. I keep the defines here just to remember what they were. */ #ifdef TO_BE_REMOVED #define LOG_EVENT_TIME_F 0x1 -#define LOG_EVENT_FORCED_ROTATE_F 0x2 +#define LOG_EVENT_FORCED_ROTATE_F 0x2 #endif -/* +/* If the query depends on the thread (for example: TEMPORARY TABLE). Currently this is used by mysqlbinlog to know it must print SET @@PSEUDO_THREAD_ID=xx; before the query (it would not hurt to print it for every query but this would be slow). */ -#define LOG_EVENT_THREAD_SPECIFIC_F 0x4 +#define LOG_EVENT_THREAD_SPECIFIC_F 0x4 -/* +/* OPTIONS_WRITTEN_TO_BIN_LOG are the bits of thd->options which must be written to the binlog. OPTIONS_WRITTEN_TO_BINLOG could be written into the Format_description_log_event, so that if later we don't want to replicate a @@ -317,7 +317,7 @@ struct sql_ex_info too hard to decide once for all of what we replicate and what we don't, among the fixed 32 bits of thd->options. I (Guilhem) have read through every option's usage, and it looks like - OPTION_AUTO_IS_NULL and OPTION_NO_FOREIGN_KEYS are the only ones which alter + OPTION_AUTO_IS_NULL and OPTION_NO_FOREIGN_KEYS are the only ones which alter how the query modifies the table. It's good to replicate OPTION_RELAXED_UNIQUE_CHECKS too because otherwise, the slave may insert data slower than the master, in InnoDB. @@ -325,7 +325,7 @@ struct sql_ex_info max_join_size=HA_POS_ERROR) and OPTION_BIG_TABLES is not needed either, as the manual says (because a too big in-memory temp table is automatically written to disk). -*/ +*/ #define OPTIONS_WRITTEN_TO_BIN_LOG (OPTION_AUTO_IS_NULL | \ OPTION_NO_FOREIGN_KEY_CHECKS | OPTION_RELAXED_UNIQUE_CHECKS) @@ -344,13 +344,13 @@ enum Log_event_type allowing multibyte TERMINATED BY etc; both types share the same class (Load_log_event) */ - NEW_LOAD_EVENT, + NEW_LOAD_EVENT, RAND_EVENT, USER_VAR_EVENT, - FORMAT_DESCRIPTION_EVENT, + FORMAT_DESCRIPTION_EVENT, ENUM_END_EVENT /* end marker */ }; -/* +/* The number of types we handle in Format_description_log_event (UNKNOWN_EVENT is not to be handled, it does not exist in binlogs, it does not have a format). @@ -388,7 +388,7 @@ typedef struct st_last_event_info bool sql_mode_inited; ulong sql_mode; /* must be same as THD.variables.sql_mode */ st_last_event_info() - : flags2_inited(0), flags2(0), sql_mode_inited(0), sql_mode(0) + : flags2_inited(0), flags2(0), sql_mode_inited(0), sql_mode(0) { db[0]= 0; /* initially, the db is unknown */ } @@ -406,7 +406,7 @@ typedef struct st_last_event_info class Log_event { public: - /* + /* The offset in the log where this event originally appeared (it is preserved in relay logs, making SHOW SLAVE STATUS able to print coordinates of the event in the master's binlog). Note: when a transaction is written by the @@ -416,13 +416,13 @@ public: may occur), except the COMMIT query which has its real offset. */ my_off_t log_pos; - /* + /* A temp buffer for read_log_event; it is later analysed according to the event's type, and its content is distributed in the event-specific fields. */ - char *temp_buf; + char *temp_buf; /* - Timestamp on the master(for debugging and replication of NOW()/TIMESTAMP). + Timestamp on the master(for debugging and replication of NOW()/TIMESTAMP). It is important for queries and LOAD DATA INFILE. This is set at the event's creation time, except for Query and Load (et al.) events where this is set at the query's execution time, which guarantees good replication (otherwise, @@ -431,9 +431,9 @@ public: time_t when; /* The number of seconds the query took to run on the master. */ ulong exec_time; - /* + /* The master's server id (is preserved in the relay log; used to prevent from - infinite loops in circular replication). + infinite loops in circular replication). */ uint32 server_id; uint cached_event_len; @@ -501,7 +501,7 @@ public: virtual void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0) = 0; void print_timestamp(FILE* file, time_t *ts = 0); void print_header(FILE* file); -#endif +#endif static void *operator new(size_t size) { @@ -511,7 +511,7 @@ public: { my_free((gptr) ptr, MYF(MY_WME|MY_ALLOW_ZERO_PTR)); } - + int write(IO_CACHE* file); int write_header(IO_CACHE* file); virtual int write_data(IO_CACHE* file) @@ -521,7 +521,7 @@ public: virtual int write_data_body(IO_CACHE* file __attribute__((unused))) { return 0; } virtual Log_event_type get_type_code() = 0; - virtual const bool is_valid() = 0; + virtual bool is_valid() const = 0; inline bool get_cache_stmt() { return cache_stmt; } Log_event(const char* buf, const Format_description_log_event* description_event); virtual ~Log_event() { free_temp_buf();} @@ -549,12 +549,12 @@ public: static Log_event* read_log_event(const char* buf, uint event_len, const char **error, const Format_description_log_event - *description_event); + *description_event); /* returns the human readable name of the event's type */ const char* get_type_str(); }; -/* +/* One class for each type of event. Two constructors for each class: - one to create the event for logging (when the server acts as a master), @@ -591,21 +591,21 @@ public: uint32 db_len; uint16 error_code; ulong thread_id; - /* + /* For events created by Query_log_event::exec_event (and Load_log_event::exec_event()) we need the *original* thread id, to be able to log the event with the original (=master's) thread id (fix for BUG#1686). */ ulong slave_proxy_id; - - /* + + /* Binlog format 3 and 4 start to differ (as far as class members are - concerned) from here. + concerned) from here. */ - - uint catalog_len; /* <= 255 char */ - + + int catalog_len; // <= 255 char; -1 means uninited + /* We want to be able to store a variable number of N-bit status vars: (generally N=32; but N=64 for SQL_MODE) a user may want to log the number of @@ -627,7 +627,7 @@ public: status variables in Query_log_event. */ uint16 status_vars_len; - + /* 'flags2' is a second set of flags (on top of those in Log_event), for session variables. These are thd->options which is & against a mask @@ -637,13 +637,13 @@ public: flags2==0 (5.0 master, we know this has a meaning of flags all down which must influence the query). */ - bool flags2_inited; + bool flags2_inited; bool sql_mode_inited; - - uint32 flags2; + + uint32 flags2; /* In connections sql_mode is 32 bits now but will be 64 bits soon */ ulong sql_mode; - + #ifndef MYSQL_CLIENT Query_log_event(THD* thd_arg, const char* query_arg, ulong query_length, @@ -669,11 +669,11 @@ public: Log_event_type get_type_code() { return QUERY_EVENT; } int write(IO_CACHE* file); int write_data(IO_CACHE* file); // returns 0 on success, -1 on error - const bool is_valid() { return query != 0; } + bool is_valid() const { return query != 0; } int get_data_size() { /* Note that the "1" below is the db's length. */ - return (q_len + db_len + 1 + status_vars_len + QUERY_HEADER_LEN); + return (q_len + db_len + 1 + status_vars_len + QUERY_HEADER_LEN); } }; @@ -700,18 +700,18 @@ public: int master_log_len; uint16 master_port; -#ifndef MYSQL_CLIENT +#ifndef MYSQL_CLIENT Slave_log_event(THD* thd_arg, struct st_relay_log_info* rli); void pack_info(Protocol* protocol); int exec_event(struct st_relay_log_info* rli); #else void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); -#endif +#endif Slave_log_event(const char* buf, uint event_len); ~Slave_log_event(); int get_data_size(); - const bool is_valid() { return master_host != 0; } + bool is_valid() const { return master_host != 0; } Log_event_type get_type_code() { return SLAVE_EVENT; } int write_data(IO_CACHE* file ); }; @@ -727,7 +727,7 @@ public: class Load_log_event: public Log_event { protected: - int copy_log_event(const char *buf, ulong event_len, + int copy_log_event(const char *buf, ulong event_len, int body_offset, const Format_description_log_event* description_event); public: @@ -769,7 +769,7 @@ public: #ifndef MYSQL_CLIENT String field_lens_buf; String fields_buf; - + Load_log_event(THD* thd, sql_exchange* ex, const char* db_arg, const char* table_name_arg, List<Item>& fields_arg, enum enum_duplicates handle_dup, @@ -782,7 +782,7 @@ public: { return exec_event(thd->slave_net,rli,0); } - int exec_event(NET* net, struct st_relay_log_info* rli, + int exec_event(NET* net, struct st_relay_log_info* rli, bool use_rli_only_for_errors); #endif /* HAVE_REPLICATION */ #else @@ -797,16 +797,16 @@ public: for the common_header_len (post_header_len will not be changed). */ Load_log_event(const char* buf, uint event_len, - const Format_description_log_event* description_event); + const Format_description_log_event* description_event); ~Load_log_event() {} Log_event_type get_type_code() { return sql_ex.new_format() ? NEW_LOAD_EVENT: LOAD_EVENT; } - int write_data_header(IO_CACHE* file); - int write_data_body(IO_CACHE* file); - const bool is_valid() { return table_name != 0; } + int write_data_header(IO_CACHE* file); + int write_data_body(IO_CACHE* file); + bool is_valid() const { return table_name != 0; } int get_data_size() { return (table_name_len + db_len + 2 + fname_len @@ -828,22 +828,22 @@ extern char server_version[SERVER_VERSION_LENGTH]; describes the other events' header/postheader lengths. This event is sent by MySQL 5.0 whenever it starts sending a new binlog if the requested position is >4 (otherwise if ==4 the event will be sent naturally). - + ****************************************************************************/ class Start_log_event_v3: public Log_event { public: - /* + /* If this event is at the start of the first binary log since server startup 'created' should be the timestamp when the event (and the binary log) was - created. + created. In the other case (i.e. this event is at the start of a binary log created by FLUSH LOGS or automatic rotation), 'created' should be 0. This "trick" is used by MySQL >=4.0.14 slaves to know if they must drop the stale temporary tables or not. Note that when 'created'!=0, it is always equal to the event's timestamp; indeed Start_log_event is written only in log.cc where the first - constructor below is called, in which 'created' is set to 'when'. + constructor below is called, in which 'created' is set to 'when'. So in fact 'created' is a useless variable. When it is 0 we can read the actual value from timestamp ('when') and when it is non-zero we can read the same value from timestamp ('when'). Conclusion: @@ -866,21 +866,21 @@ public: #else Start_log_event_v3() {} void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); -#endif +#endif Start_log_event_v3(const char* buf, const Format_description_log_event* description_event); ~Start_log_event_v3() {} Log_event_type get_type_code() { return START_EVENT_V3;} int write_data(IO_CACHE* file); - const bool is_valid() { return 1; } + bool is_valid() const { return 1; } int get_data_size() { return START_V3_HEADER_LEN; //no variable-sized part } }; -/* +/* For binlog version 4. This event is saved by threads which read it, as they need it for future use (to decode the ordinary events). @@ -889,7 +889,7 @@ public: class Format_description_log_event: public Start_log_event_v3 { public: - /* + /* The size of the fixed header which _all_ events have (for binlogs written by this version, this is equal to LOG_EVENT_HEADER_LEN), except FORMAT_DESCRIPTION_EVENT and ROTATE_EVENT @@ -906,18 +906,18 @@ public: #ifdef HAVE_REPLICATION int exec_event(struct st_relay_log_info* rli); #endif /* HAVE_REPLICATION */ -#endif - +#endif + Format_description_log_event(const char* buf, uint event_len, const Format_description_log_event* description_event); ~Format_description_log_event() { my_free((gptr)post_header_len, MYF(0)); } Log_event_type get_type_code() { return FORMAT_DESCRIPTION_EVENT;} int write_data(IO_CACHE* file); - const bool is_valid() - { + bool is_valid() const + { return ((common_header_len >= ((binlog_version==1) ? OLD_HEADER_LEN : - LOG_EVENT_MINIMAL_HEADER_LEN)) && - (post_header_len != NULL)); + LOG_EVENT_MINIMAL_HEADER_LEN)) && + (post_header_len != NULL)); } int get_event_len() { @@ -950,7 +950,7 @@ public: ulonglong val; uchar type; -#ifndef MYSQL_CLIENT +#ifndef MYSQL_CLIENT Intvar_log_event(THD* thd_arg,uchar type_arg, ulonglong val_arg) :Log_event(thd_arg,0,0),val(val_arg),type(type_arg) {} @@ -960,7 +960,7 @@ public: #endif /* HAVE_REPLICATION */ #else void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); -#endif +#endif Intvar_log_event(const char* buf, const Format_description_log_event* description_event); ~Intvar_log_event() {} @@ -968,14 +968,14 @@ public: const char* get_var_type_name(); int get_data_size() { return 9; /* sizeof(type) + sizeof(val) */;} int write_data(IO_CACHE* file); - const bool is_valid() { return 1; } + bool is_valid() const { return 1; } }; /***************************************************************************** Rand Log Event class - Logs random seed used by the next RAND(), and by PASSWORD() in 4.1.0. + Logs random seed used by the next RAND(), and by PASSWORD() in 4.1.0. 4.1.1 does not need it (it's repeatable again) so this event needn't be written in 4.1.1 for PASSWORD() (but the fact that it is written is just a waste, it does not cause bugs). @@ -1004,7 +1004,7 @@ class Rand_log_event: public Log_event Log_event_type get_type_code() { return RAND_EVENT;} int get_data_size() { return 16; /* sizeof(ulonglong) * 2*/ } int write_data(IO_CACHE* file); - const bool is_valid() { return 1; } + bool is_valid() const { return 1; } }; /***************************************************************************** @@ -1051,7 +1051,7 @@ public: UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE + val_len); } int write_data(IO_CACHE* file); - const bool is_valid() { return 1; } + bool is_valid() const { return 1; } }; /***************************************************************************** @@ -1070,14 +1070,14 @@ public: int exec_event(struct st_relay_log_info* rli); #else void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); -#endif +#endif Stop_log_event(const char* buf, const Format_description_log_event* description_event): Log_event(buf, description_event) {} ~Stop_log_event() {} Log_event_type get_type_code() { return STOP_EVENT;} - const bool is_valid() { return 1; } + bool is_valid() const { return 1; } }; #endif /* HAVE_REPLICATION */ @@ -1097,7 +1097,7 @@ public: ulonglong pos; uint ident_len; bool alloced; -#ifndef MYSQL_CLIENT +#ifndef MYSQL_CLIENT Rotate_log_event(THD* thd_arg, const char* new_log_ident_arg, uint ident_len_arg = 0, ulonglong pos_arg = LOG_EVENT_OFFSET) @@ -1126,7 +1126,7 @@ public: return (LOG_EVENT_MINIMAL_HEADER_LEN + get_data_size()); } int get_data_size() { return ident_len + ROTATE_HEADER_LEN;} - const bool is_valid() { return new_log_ident != 0; } + bool is_valid() const { return new_log_ident != 0; } int write_data(IO_CACHE* file); }; @@ -1145,7 +1145,7 @@ protected: our Load part - used on the slave when writing event out to SQL_LOAD-*.info file */ - bool fake_base; + bool fake_base; public: char* block; const char *event_buf; @@ -1167,8 +1167,8 @@ public: #else void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); void print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, bool enable_local); -#endif - +#endif + Create_file_log_event(const char* buf, uint event_len, const Format_description_log_event* description_event); ~Create_file_log_event() @@ -1186,7 +1186,7 @@ public: Load_log_event::get_data_size() + 4 + 1 + block_len); } - const bool is_valid() { return inited_from_old || block != 0; } + bool is_valid() const { return inited_from_old || block != 0; } int write_data_header(IO_CACHE* file); int write_data_body(IO_CACHE* file); /* @@ -1230,13 +1230,13 @@ public: #else void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); #endif - - Append_block_log_event(const char* buf, uint event_len, + + Append_block_log_event(const char* buf, uint event_len, const Format_description_log_event* description_event); ~Append_block_log_event() {} Log_event_type get_type_code() { return APPEND_BLOCK_EVENT;} int get_data_size() { return block_len + APPEND_BLOCK_HEADER_LEN ;} - const bool is_valid() { return block != 0; } + bool is_valid() const { return block != 0; } int write_data(IO_CACHE* file); const char* get_db() { return db; } }; @@ -1251,7 +1251,7 @@ class Delete_file_log_event: public Log_event public: uint file_id; const char* db; /* see comment in Append_block_log_event */ - + #ifndef MYSQL_CLIENT Delete_file_log_event(THD* thd, const char* db_arg, bool using_trans); #ifdef HAVE_REPLICATION @@ -1261,14 +1261,14 @@ public: #else void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); void print(FILE* file, bool short_form, LAST_EVENT_INFO* last_event_info, bool enable_local); -#endif - +#endif + Delete_file_log_event(const char* buf, uint event_len, const Format_description_log_event* description_event); ~Delete_file_log_event() {} Log_event_type get_type_code() { return DELETE_FILE_EVENT;} int get_data_size() { return DELETE_FILE_HEADER_LEN ;} - const bool is_valid() { return file_id != 0; } + bool is_valid() const { return file_id != 0; } int write_data(IO_CACHE* file); const char* get_db() { return db; } }; @@ -1282,7 +1282,7 @@ class Execute_load_log_event: public Log_event { public: uint file_id; - const char* db; /* see comment in Append_block_log_event */ + const char* db; /* see comment in Append_block_log_event */ #ifndef MYSQL_CLIENT Execute_load_log_event(THD* thd, const char* db_arg, bool using_trans); @@ -1292,14 +1292,14 @@ public: #endif /* HAVE_REPLICATION */ #else void print(FILE* file, bool short_form = 0, LAST_EVENT_INFO* last_event_info= 0); -#endif - - Execute_load_log_event(const char* buf, uint event_len, +#endif + + Execute_load_log_event(const char* buf, uint event_len, const Format_description_log_event* description_event); ~Execute_load_log_event() {} Log_event_type get_type_code() { return EXEC_LOAD_EVENT;} int get_data_size() { return EXEC_LOAD_HEADER_LEN ;} - const bool is_valid() { return file_id != 0; } + bool is_valid() const { return file_id != 0; } int write_data(IO_CACHE* file); const char* get_db() { return db; } }; @@ -1319,8 +1319,8 @@ public: ~Unknown_log_event() {} void print(FILE* file, bool short_form= 0, LAST_EVENT_INFO* last_event_info= 0); Log_event_type get_type_code() { return UNKNOWN_EVENT;} - const bool is_valid() { return 1; } + bool is_valid() const { return 1; } }; -#endif +#endif #endif /* _log_event_h */ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 48db2f37796..84370d94f47 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -398,8 +398,9 @@ void free_items(Item *item); void cleanup_items(Item *item); class THD; void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0); -int check_one_table_access(THD *thd, ulong privilege, +bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables); +bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table); bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list); int multi_update_precheck(THD *thd, TABLE_LIST *tables); @@ -479,7 +480,7 @@ bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); bool is_update_query(enum enum_sql_command command); bool alloc_query(THD *thd, char *packet, ulong packet_length); void mysql_init_select(LEX *lex); -void mysql_init_query(THD *thd, bool lexonly=0); +void mysql_init_query(THD *thd, uchar *buf, uint length, 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); @@ -634,15 +635,17 @@ extern const Field *view_ref_found; Field *find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, Item **ref, bool report_error, bool check_privileges); -Field *find_field_in_table(THD *thd, TABLE_LIST *tables, const char *name, - uint length, Item **ref, - bool check_grant_table, bool check_grant_view, - bool allow_rowid, - uint *cached_field_index_ptr); -Field *find_field_in_real_table(THD *thd, TABLE *table, - const char *name, uint length, - bool check_grants, bool allow_rowid, - uint *cached_field_index_ptr); +Field * +find_field_in_table(THD *thd, TABLE_LIST *table_list, + const char *name, const char *item_name, + uint length, Item **ref, + bool check_grants_table, bool check_grants_view, + bool allow_rowid, + uint *cached_field_index_ptr); +Field * +find_field_in_real_table(THD *thd, TABLE *table, const char *name, + uint length, bool check_grants, bool allow_rowid, + uint *cached_field_index_ptr); #ifdef HAVE_OPENSSL #include <openssl/des.h> struct st_des_keyblock @@ -700,6 +703,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, LEX_STRING *name=NULL); void mysql_stmt_execute(THD *thd, char *packet, uint packet_length); void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name); +void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length); void mysql_stmt_free(THD *thd, char *packet); void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); @@ -770,12 +774,10 @@ void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); bool close_thread_table(THD *thd, TABLE **table_ptr); void close_temporary_tables(THD *thd); -TABLE_LIST * find_real_table_in_list(TABLE_LIST *table, - const char *db_name, - const char *table_name); -TABLE_LIST * find_real_table_in_local_list(TABLE_LIST *table, - const char *db_name, - const char *table_name); +TABLE_LIST *find_table_in_list(TABLE_LIST *table, + uint offset_to_list, + const char *db_name, + const char *table_name); TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name); bool close_temporary_table(THD *thd, const char *db, const char *table_name); void close_temporary(TABLE *table, bool delete_table=1); @@ -791,6 +793,23 @@ int fill_record(List<Item> &fields,List<Item> &values, bool ignore_errors); int fill_record(Field **field,List<Item> &values, bool ignore_errors); OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild); +inline TABLE_LIST *find_table_in_global_list(TABLE_LIST *table, + const char *db_name, + const char *table_name) +{ + return find_table_in_list(table, offsetof(TABLE_LIST, next_global), + db_name, table_name); +} + +inline TABLE_LIST *find_table_in_local_list(TABLE_LIST *table, + const char *db_name, + const char *table_name) +{ + return find_table_in_list(table, offsetof(TABLE_LIST, next_local), + db_name, table_name); +} + + /* sql_calc.cc */ bool eval_const_cond(COND *cond); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 682b368e6a9..ac275f0d765 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -5124,7 +5124,7 @@ The minimum value for this variable is 4096.", (gptr*) &opt_date_time_formats[MYSQL_TIMESTAMP_TIME], 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"sql_updatable_view_key", OPT_SQL_UPDATABLE_VIEW_KEY, - "0 = NO = Don't check presence of key in updateable VIEW. 1 = YES = Prohibit update of VIEW which do not contain key of underlaying table. 2 = LIMIT1 = Same as YES but prohibited only operation with LIMIT 1 (usually get from GUI tools).", + "0 = NO = Don't check presence of key in updatable VIEW. 1 = YES = Prohibit update of VIEW which does not contain key of underlying table. 2 = LIMIT1 = Same as YES but prohibited only operation with LIMIT 1 (usually get from GUI tools).", (gptr*) &global_system_variables.sql_updatable_view_key, (gptr*) &max_system_variables.sql_updatable_view_key, 0, GET_ULONG, REQUIRED_ARG, 1, 0, 2, 0, 1, 0}, diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 7f98462cf84..d2190c35bc6 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -25,13 +25,13 @@ /* Classes in this file are used in the following way: - 1. For a selection condition a tree of SEL_IMERGE/SEL_TREE/SEL_ARG objects - is created. #of rows in table and index statistics are ignored at this + 1. For a selection condition a tree of SEL_IMERGE/SEL_TREE/SEL_ARG objects + is created. #of rows in table and index statistics are ignored at this step. - 2. Created SEL_TREE and index stats data are used to construct a - TABLE_READ_PLAN-derived object (TRP_*). Several 'candidate' table read - plans may be created. - 3. The least expensive table read plan is used to create a tree of + 2. Created SEL_TREE and index stats data are used to construct a + TABLE_READ_PLAN-derived object (TRP_*). Several 'candidate' table read + plans may be created. + 3. The least expensive table read plan is used to create a tree of QUICK_SELECT_I-derived objects which are later used for row retrieval. QUICK_RANGEs are also created in this step. */ @@ -292,20 +292,20 @@ 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) + SEL_TREE() :type(KEY) { - keys_map.clear_all(); + 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 */ - /* - Possible ways to read rows using index_merge. The list is non-empty only + /* + Possible ways to read rows using index_merge. The list is non-empty only if type==KEY. Currently can be non empty only if keys_map.is_clear_all(). */ List<SEL_IMERGE> merges; - + /* The members below are filled/used only after get_mm_tree is done */ key_map ror_scans_map; /* bitmask of ROR scan-able elements in keys */ uint n_ror_scans; /* number of set bits in ror_scans_map */ @@ -324,27 +324,27 @@ typedef struct st_qsel_param { MEM_ROOT *mem_root; table_map prev_tables,read_tables,current_table; uint baseflag, max_key_part, range_count; - + uint keys; /* number of keys used in the query */ /* used_key_no -> table_key_no translation table */ - uint real_keynr[MAX_KEY]; + uint real_keynr[MAX_KEY]; char min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH], max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; bool quick; // Don't calulate possible keys COND *cond; - uint fields_bitmap_size; + uint fields_bitmap_size; MY_BITMAP needed_fields; /* bitmask of fields needed by the query */ key_map *needed_reg; /* ptr to SQL_SELECT::needed_reg */ uint *imerge_cost_buff; /* buffer for index_merge cost estimates */ uint imerge_cost_buff_size; /* size of the buffer */ - - /* TRUE if last checked tree->key can be used for ROR-scan */ - bool is_ror_scan; + + /* TRUE if last checked tree->key can be used for ROR-scan */ + bool is_ror_scan; } PARAM; class TABLE_READ_PLAN; @@ -370,33 +370,33 @@ static ha_rows check_quick_keys(PARAM *param,uint index,SEL_ARG *key_tree, char *max_key, uint max_key_flag); QUICK_RANGE_SELECT *get_quick_select(PARAM *param,uint index, - SEL_ARG *key_tree, + SEL_ARG *key_tree, MEM_ROOT *alloc = NULL); static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, - bool index_read_must_be_used, + bool index_read_must_be_used, double read_time); static TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, double read_time, bool *are_all_covering); static -TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, - SEL_TREE *tree, +TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, + SEL_TREE *tree, double read_time); static TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, double read_time); static int get_index_merge_params(PARAM *param, key_map& needed_reg, - SEL_IMERGE *imerge, double *read_time, + SEL_IMERGE *imerge, double *read_time, ha_rows* imerge_rows); -inline double get_index_only_read_time(const PARAM* param, ha_rows records, +inline double get_index_only_read_time(const PARAM* param, ha_rows records, int keynr); #ifndef DBUG_OFF static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map, const char *msg); -static void print_ror_scans_arr(TABLE *table, const char *msg, - struct st_ror_scan_info **start, +static void print_ror_scans_arr(TABLE *table, const char *msg, + struct st_ror_scan_info **start, struct st_ror_scan_info **end); static void print_rowid(byte* val, int len); static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg); @@ -414,17 +414,17 @@ bool get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key, 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, +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 + 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) + (t_1||t_2||...||t_N) && (next) - where all t_i are SEL_TREEs, next is another SEL_IMERGE and no pair + 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. @@ -436,7 +436,7 @@ class SEL_IMERGE : public Sql_alloc { enum { PREALLOCED_TREES= 10}; public: - SEL_TREE *trees_prealloced[PREALLOCED_TREES]; + 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 */ @@ -454,11 +454,11 @@ public: }; -/* +/* Add SEL_TREE to this index_merge without any checks, - NOTES - This function implements the following: + NOTES + This function implements the following: (x_1||...||x_N) || t = (x_1||...||x_N||t), where x_i, t are SEL_TREEs RETURN @@ -491,28 +491,28 @@ int SEL_IMERGE::or_sel_tree(PARAM *param, SEL_TREE *tree) 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 + NOTES This does the following: - (t_1||...||t_k)||new_tree = - either + (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 + 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 + RETURN 0 OK - 1 One of the trees was combined with new_tree to SEL_TREE::ALWAYS, + 1 One of the trees was combined with new_tree to SEL_TREE::ALWAYS, and (*this) should be discarded. -1 An error occurred. */ @@ -546,7 +546,7 @@ int SEL_IMERGE::or_sel_tree_with_checks(PARAM *param, SEL_TREE *new_tree) RETURN 0 - OK - 1 - One of conditions in result is always TRUE and this SEL_IMERGE + 1 - One of conditions in result is always TRUE and this SEL_IMERGE should be discarded. -1 - An error occurred */ @@ -564,7 +564,7 @@ int SEL_IMERGE::or_sel_imerge_with_checks(PARAM *param, SEL_IMERGE* imerge) } -/* +/* Perform AND operation on two index_merge lists and store result in *im1. */ @@ -577,16 +577,16 @@ inline void imerge_list_and_list(List<SEL_IMERGE> *im1, List<SEL_IMERGE> *im2) /* Perform OR operation on 2 index_merge lists, storing result in first list. - NOTES + 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. + + 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 + 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 @@ -597,14 +597,14 @@ inline void imerge_list_and_list(List<SEL_IMERGE> *im1, List<SEL_IMERGE> *im2) other Error, both passed lists are unusable */ -int imerge_list_or_list(PARAM *param, +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()); } @@ -617,7 +617,7 @@ int imerge_list_or_list(PARAM *param, other Error */ -int imerge_list_or_tree(PARAM *param, +int imerge_list_or_tree(PARAM *param, List<SEL_IMERGE> *im1, SEL_TREE *tree) { @@ -688,7 +688,7 @@ void SQL_SELECT::cleanup() free_cond=0; delete cond; cond= 0; - } + } close_cached_file(&file); } @@ -705,7 +705,7 @@ QUICK_SELECT_I::QUICK_SELECT_I() used_key_parts(0) {} -QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, +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),free_file(0),cur_range(NULL),range(0) { @@ -745,27 +745,31 @@ void QUICK_RANGE_SELECT::range_end() QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() -{ +{ DBUG_ENTER("QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT"); if (!dont_free) { - range_end(); - file->extra(HA_EXTRA_NO_KEYREAD); - delete_dynamic(&ranges); /* ranges are allocated in alloc */ - if (free_file) + /* file is NULL for CPK scan on covering ROR-intersection */ + if (file) { - DBUG_PRINT("info", ("Freeing separate handler %p (free=%d)", file, - free_file)); - file->reset(); - file->close(); - } - free_root(&alloc,MYF(0)); - } - DBUG_VOID_RETURN; + range_end(); + file->extra(HA_EXTRA_NO_KEYREAD); + if (free_file) + { + DBUG_PRINT("info", ("Freeing separate handler %p (free=%d)", file, + free_file)); + file->reset(); + file->close(); + } + } + delete_dynamic(&ranges); /* ranges are allocated in alloc */ + free_root(&alloc,MYF(0)); + } + DBUG_VOID_RETURN; } -QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param, +QUICK_INDEX_MERGE_SELECT::QUICK_INDEX_MERGE_SELECT(THD *thd_param, TABLE *table) :cur_quick_it(quick_selects),pk_quick_select(NULL),unique(NULL), thd(thd_param) @@ -793,14 +797,14 @@ int QUICK_INDEX_MERGE_SELECT::reset() DBUG_RETURN(result); } -bool +bool QUICK_INDEX_MERGE_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick_sel_range) { - /* - Save quick_select that does scan on clustered primary key as it will be + /* + Save quick_select that does scan on clustered primary key as it will be processed separately. */ - if (head->file->primary_key_is_clustered() && + if (head->file->primary_key_is_clustered() && quick_sel_range->index == head->primary_key) pk_quick_select= quick_sel_range; else @@ -826,22 +830,22 @@ QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param, : cpk_quick(NULL), thd(thd_param), need_to_fetch_row(retrieve_full_rows) { index= MAX_KEY; - head= table; + head= table; record= head->record[0]; if (!parent_alloc) init_sql_alloc(&alloc,1024,0); else bzero(&alloc, sizeof(MEM_ROOT)); - last_rowid= (byte*)alloc_root(parent_alloc? parent_alloc : &alloc, + last_rowid= (byte*)alloc_root(parent_alloc? parent_alloc : &alloc, head->file->ref_length); } -/* +/* Do post-constructor initialization. SYNOPSIS QUICK_ROR_INTERSECT_SELECT::init() - + RETURN 0 OK other Error code @@ -864,14 +868,14 @@ int QUICK_ROR_INTERSECT_SELECT::init() NOTES This function creates and prepares for subsequent use a separate handler - object if it can't reuse head->file. The reason for this is that during + object if it can't reuse head->file. The reason for this is that during ROR-merge several key scans are performed simultaneously, and a single handler is only capable of preserving context of a single key scan. - In ROR-merge the quick select doing merge does full records retrieval, + In ROR-merge the quick select doing merge does full records retrieval, merged quick selects read only keys. - - RETURN + + RETURN 0 ROR child scan initialized, ok to use. 1 error */ @@ -880,7 +884,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) { handler *save_file= file; DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_merged_scan"); - + if (reuse_handler) { DBUG_PRINT("info", ("Reusing handler %p", file)); @@ -899,17 +903,17 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) /* already have own 'handler' object. */ DBUG_RETURN(0); } - + if (!(file= get_new_handler(head, head->db_type))) goto failure; DBUG_PRINT("info", ("Allocated new handler %p", file)); if (file->ha_open(head->path, head->db_stat, HA_OPEN_IGNORE_IF_LOCKED)) { - /* Caller will free the memory */ + /* Caller will free the memory */ goto failure; } - - if (file->extra(HA_EXTRA_KEYREAD) || + + if (file->extra(HA_EXTRA_KEYREAD) || file->extra(HA_EXTRA_RETRIEVE_ALL_COLS) || init() || reset()) { @@ -932,7 +936,7 @@ failure: QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan() reuse_handler If TRUE, use head->file, otherwise create separate handler object. - RETURN + RETURN 0 OK other error code */ @@ -947,7 +951,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler) if (!need_to_fetch_row && reuse_handler) { quick= quick_it++; - /* + /* There is no use of this->file. Use it for the first of merged range selects. */ @@ -973,7 +977,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler) } -/* +/* Initialize quick select for row retrieval. SYNOPSIS reset() @@ -991,29 +995,29 @@ int QUICK_ROR_INTERSECT_SELECT::reset() /* Add a merged quick select to this ROR-intersection quick select. - + SYNOPSIS QUICK_ROR_INTERSECT_SELECT::push_quick_back() quick Quick select to be added. The quick select must return rows in rowid order. NOTES This call can only be made before init() is called. - + RETURN - FALSE OK + FALSE OK TRUE Out of memory. */ -bool +bool QUICK_ROR_INTERSECT_SELECT::push_quick_back(QUICK_RANGE_SELECT *quick) { return quick_selects.push_back(quick); } QUICK_ROR_INTERSECT_SELECT::~QUICK_ROR_INTERSECT_SELECT() -{ +{ DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::~QUICK_ROR_INTERSECT_SELECT"); - quick_selects.delete_elements(); + quick_selects.delete_elements(); delete cpk_quick; free_root(&alloc,MYF(0)); if (need_to_fetch_row && head->file->inited != handler::NONE) @@ -1039,7 +1043,7 @@ QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param, Do post-constructor initialization. SYNOPSIS QUICK_ROR_UNION_SELECT::init() - + RETURN 0 OK other Error code @@ -1054,7 +1058,7 @@ int QUICK_ROR_UNION_SELECT::init() bzero(&queue, sizeof(QUEUE)); return 1; } - + if (!(cur_rowid= (byte*)alloc_root(&alloc, 2*head->file->ref_length))) return 1; prev_rowid= cur_rowid + head->file->ref_length; @@ -1063,7 +1067,7 @@ int QUICK_ROR_UNION_SELECT::init() /* - Comparison function to be used QUICK_ROR_UNION_SELECT::queue priority + Comparison function to be used QUICK_ROR_UNION_SELECT::queue priority queue. SYNPOSIS @@ -1080,11 +1084,11 @@ int QUICK_ROR_UNION_SELECT::queue_cmp(void *arg, byte *val1, byte *val2) } -/* +/* Initialize quick select for row retrieval. SYNOPSIS reset() - + RETURN 0 OK other Error code @@ -1096,15 +1100,15 @@ int QUICK_ROR_UNION_SELECT::reset() int error; DBUG_ENTER("QUICK_ROR_UNION_SELECT::reset"); have_prev_rowid= FALSE; - /* - Initialize scans for merged quick selects and put all merged quick + /* + Initialize scans for merged quick selects and put all merged quick selects into the queue. */ List_iterator_fast<QUICK_SELECT_I> it(quick_selects); while ((quick= it++)) { if (quick->init_ror_merged_scan(FALSE)) - DBUG_RETURN(1); + DBUG_RETURN(1); if ((error= quick->get_next())) { if (error == HA_ERR_END_OF_FILE) @@ -1125,7 +1129,7 @@ int QUICK_ROR_UNION_SELECT::reset() } -bool +bool QUICK_ROR_UNION_SELECT::push_quick_back(QUICK_SELECT_I *quick_sel_range) { return quick_selects.push_back(quick_sel_range); @@ -1135,7 +1139,9 @@ QUICK_ROR_UNION_SELECT::~QUICK_ROR_UNION_SELECT() { DBUG_ENTER("QUICK_ROR_UNION_SELECT::~QUICK_ROR_UNION_SELECT"); delete_queue(&queue); - quick_selects.delete_elements(); + quick_selects.delete_elements(); + if (head->file->inited != handler::NONE) + head->file->ha_rnd_end(); free_root(&alloc,MYF(0)); DBUG_VOID_RETURN; } @@ -1306,25 +1312,25 @@ SEL_ARG *SEL_ARG::clone_tree() /* - Table rows retrieval plan. Range optimizer creates QUICK_SELECT_I-derived + Table rows retrieval plan. Range optimizer creates QUICK_SELECT_I-derived objects from table read plans. */ class TABLE_READ_PLAN { public: - /* - Plan read cost, with or without cost of full row retrieval, depending + /* + Plan read cost, with or without cost of full row retrieval, depending on plan creation parameters. */ - double read_cost; + double read_cost; ha_rows records; /* estimate of #rows to be examined */ - /* - If TRUE, the scan returns rows in rowid order. This is used only for + /* + If TRUE, the scan returns rows in rowid order. This is used only for scans that can be both ROR and non-ROR. */ bool is_ror; - + /* Create quick select for this plan. SYNOPSIS @@ -1333,11 +1339,11 @@ public: retrieve_full_rows If TRUE, created quick select will do full record retrieval. parent_alloc Memory pool to use, if any. - + NOTES retrieve_full_rows is ignored by some implementations. - - RETURN + + RETURN created quick select NULL on any error. */ @@ -1357,7 +1363,7 @@ class TRP_INDEX_MERGE; /* - Plan for a QUICK_RANGE_SELECT scan. + Plan for a QUICK_RANGE_SELECT scan. TRP_RANGE::make_quick ignores retrieve_full_rows parameter because QUICK_RANGE_SELECT doesn't distinguish between 'index only' scans and full record retrieval scans. @@ -1369,10 +1375,10 @@ public: SEL_ARG *key; /* set of intervals to be used in "range" method retrieval */ uint key_idx; /* key number in PARAM::key */ - TRP_RANGE(SEL_ARG *key_arg, uint idx_arg) + TRP_RANGE(SEL_ARG *key_arg, uint idx_arg) : key(key_arg), key_idx(idx_arg) {} - + QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc) { @@ -1393,9 +1399,9 @@ public: class TRP_ROR_INTERSECT : public TABLE_READ_PLAN { public: - QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, + QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc); - + /* Array of pointers to ROR range scans used in this intersection */ struct st_ror_scan_info **first_scan; struct st_ror_scan_info **last_scan; /* End of the above array */ @@ -1405,16 +1411,16 @@ public: }; -/* +/* Plan for QUICK_ROR_UNION_SELECT scan. QUICK_ROR_UNION_SELECT always retrieves full rows, so retrieve_full_rows - is ignored by make_quick. + is ignored by make_quick. */ class TRP_ROR_UNION : public TABLE_READ_PLAN { public: - QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, + QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc); TABLE_READ_PLAN **first_ror; /* array of ptrs to plans for merged scans */ TABLE_READ_PLAN **last_ror; /* end of the above array */ @@ -1424,31 +1430,31 @@ public: /* Plan for QUICK_INDEX_MERGE_SELECT scan. QUICK_ROR_INTERSECT_SELECT always retrieves full rows, so retrieve_full_rows - is ignored by make_quick. + is ignored by make_quick. */ class TRP_INDEX_MERGE : public TABLE_READ_PLAN { public: - QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, + QUICK_SELECT_I *make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc); TRP_RANGE **range_scans; /* array of ptrs to plans of merged scans */ TRP_RANGE **range_scans_end; /* end of the array */ }; -/* +/* Fill param->needed_fields with bitmap of fields used in the query. - SYNOPSIS + SYNOPSIS fill_used_fields_bitmap() param Parameter from test_quick_select function. - + NOTES Clustered PK members are not put into the bitmap as they are implicitly present in all keys (and it is impossible to avoid reading them). - RETURN - 0 Ok - 1 Out of memory. + RETURN + 0 Ok + 1 Out of memory. */ static int fill_used_fields_bitmap(PARAM *param) @@ -1458,10 +1464,10 @@ static int fill_used_fields_bitmap(PARAM *param) uchar *tmp; uint pk; if (!(tmp= (uchar*)alloc_root(param->mem_root,param->fields_bitmap_size)) || - bitmap_init(¶m->needed_fields, tmp, param->fields_bitmap_size*8, + bitmap_init(¶m->needed_fields, tmp, param->fields_bitmap_size*8, FALSE)) return 1; - + bitmap_clear_all(¶m->needed_fields); for (uint i= 0; i < table->fields; i++) { @@ -1474,7 +1480,7 @@ static int fill_used_fields_bitmap(PARAM *param) { /* The table uses clustered PK and it is not internally generated */ KEY_PART_INFO *key_part= param->table->key_info[pk].key_part; - KEY_PART_INFO *key_part_end= key_part + + KEY_PART_INFO *key_part_end= key_part + param->table->key_info[pk].key_parts; for(;key_part != key_part_end; ++key_part) { @@ -1486,7 +1492,7 @@ static int fill_used_fields_bitmap(PARAM *param) /* - Test if a key can be used in different ranges + Test if a key can be used in different ranges SYNOPSIS SQL_SELECT::test_quick_select() @@ -1494,15 +1500,15 @@ static int fill_used_fields_bitmap(PARAM *param) keys_to_use Keys to use for range retrieval prev_tables Tables assumed to be already read when the scan is performed (but not read at the moment of this call) - limit Query limit - force_quick_range Prefer to use range (instead of full table scan) even - if it is more expensive. + limit Query limit + force_quick_range Prefer to use range (instead of full table scan) even + if it is more expensive. NOTES Updates the following in the select parameter: needed_reg - Bits for keys with may be used if all prev regs are read quick - Parameter to use when reading records. - + In the table struct the following information is updated: quick_keys - Which keys can be used quick_rows - How many rows the key matches @@ -1511,10 +1517,10 @@ static int fill_used_fields_bitmap(PARAM *param) Check if this function really needs to modify keys_to_use, and change the code to pass it by reference if it doesn't. - In addition to force_quick_range other means can be (an usually are) used + In addition to force_quick_range other means can be (an usually are) used to make this function prefer range over full table scan. Figure out if force_quick_range is really needed. - + RETURN -1 if impossible select (i.e. certainly no rows will be selected) 0 if can't use quick_select @@ -1591,10 +1597,10 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, key_parts= param.key_parts; old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); my_pthread_setspecific_ptr(THR_MALLOC,&alloc); - - /* - Make an array with description of all key parts of all table keys. - This is used in get_mm_parts function. + + /* + Make an array with description of all key parts of all table keys. + This is used in get_mm_parts function. */ key_info= head->key_info; for (idx=0 ; idx < head->keys ; idx++, key_info++) @@ -1634,7 +1640,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, if (key_read_time < read_time) read_time= key_read_time; } - + if ((tree=get_mm_tree(¶m,cond))) { if (tree->type == SEL_TREE::IMPOSSIBLE) @@ -1646,7 +1652,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, tree->type == SEL_TREE::KEY_SMALLER) { TABLE_READ_PLAN *best_trp; - /* + /* It is possible to use a quick select (but maybe it would be slower than 'all' table scan). */ @@ -1655,21 +1661,22 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, double best_read_time= read_time; TRP_ROR_INTERSECT *new_trp; bool can_build_covering= FALSE; - + /* Get best 'range' plan and prepare data for making other plans */ - if ((best_trp= get_key_scans_params(¶m, tree, FALSE, + if ((best_trp= get_key_scans_params(¶m, tree, FALSE, best_read_time))) best_read_time= best_trp->read_cost; - - /* + + /* Simultaneous key scans and row deletes on several handler - objects are not allowed so don't use ROR-intersection for + objects are not allowed so don't use ROR-intersection for table deletes. */ - if (thd->lex->sql_command != SQLCOM_DELETE) + if ((thd->lex->sql_command != SQLCOM_DELETE) )//&& +// (thd->lex->sql_command != SQLCOM_UPDATE)) { - /* - Get best non-covering ROR-intersection plan and prepare data for + /* + Get best non-covering ROR-intersection plan and prepare data for building covering ROR-intersection. */ if ((new_trp= get_best_ror_intersect(¶m, tree, best_read_time, @@ -1678,13 +1685,13 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, best_trp= new_trp; best_read_time= best_trp->read_cost; } - - /* - Try constructing covering ROR-intersect only if it looks possible + + /* + Try constructing covering ROR-intersect only if it looks possible and worth doing. */ if (new_trp && !new_trp->is_covering && can_build_covering && - (new_trp= get_best_covering_ror_intersect(¶m, tree, + (new_trp= get_best_covering_ror_intersect(¶m, tree, best_read_time))) best_trp= new_trp; } @@ -1696,14 +1703,14 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, TABLE_READ_PLAN *best_conj_trp= NULL, *new_conj_trp; LINT_INIT(new_conj_trp); /* no empty index_merge lists possible */ - + DBUG_PRINT("info",("No range reads possible," " trying to construct index_merge")); List_iterator_fast<SEL_IMERGE> it(tree->merges); while ((imerge= it++)) { new_conj_trp= get_best_disjunct_quick(¶m, imerge, read_time); - if (!best_conj_trp || (new_conj_trp && new_conj_trp->read_cost < + if (!best_conj_trp || (new_conj_trp && new_conj_trp->read_cost < best_conj_trp->read_cost)) best_conj_trp= new_conj_trp; } @@ -1720,7 +1727,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, delete quick; quick= NULL; } - } + } } } my_pthread_setspecific_ptr(THR_MALLOC, old_root); @@ -1738,14 +1745,14 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, } -/* +/* Get cost of 'sweep' full records retrieval. SYNOPSIS get_sweep_read_cost() param Parameter from test_quick_select - records # of records to be retrieved + records # of records to be retrieved RETURN - cost of sweep + cost of sweep */ double get_sweep_read_cost(const PARAM *param, ha_rows records) @@ -1757,17 +1764,17 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records) records, records); } else - { + { double n_blocks= - ceil((double)((longlong)param->table->file->data_file_length / IO_SIZE)); + ceil((double)param->table->file->data_file_length / IO_SIZE); double busy_blocks= n_blocks * (1.0 - pow(1.0 - 1.0/n_blocks, rows2double(records))); if (busy_blocks < 1.0) busy_blocks= 1.0; - DBUG_PRINT("info",("sweep: nblocks=%g, busy_blocks=%g", n_blocks, + DBUG_PRINT("info",("sweep: nblocks=%g, busy_blocks=%g", n_blocks, busy_blocks)); /* - Disabled: Bail out if # of blocks to read is bigger than # of blocks in + Disabled: Bail out if # of blocks to read is bigger than # of blocks in table data file. if (max_cost != DBL_MAX && (busy_blocks+index_reads_cost) >= n_blocks) return 1; @@ -1776,12 +1783,12 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records) if (!join || join->tables == 1) { /* No join, assume reading is done in one 'sweep' */ - result= busy_blocks*(DISK_SEEK_BASE_COST + + result= busy_blocks*(DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST*n_blocks/busy_blocks); } else { - /* + /* Possibly this is a join with source table being non-last table, so assume that disk seeks are random here. */ @@ -1800,60 +1807,60 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records) param Parameter from check_quick_select function imerge Expression to use read_time Don't create scans with cost > read_time - + NOTES index_merge cost is calculated as follows: - index_merge_cost = + index_merge_cost = cost(index_reads) + (see #1) cost(rowid_to_row_scan) + (see #2) cost(unique_use) (see #3) 1. cost(index_reads) =SUM_i(cost(index_read_i)) - For non-CPK scans, - cost(index_read_i) = {cost of ordinary 'index only' scan} + For non-CPK scans, + cost(index_read_i) = {cost of ordinary 'index only' scan} For CPK scan, cost(index_read_i) = {cost of non-'index only' scan} 2. cost(rowid_to_row_scan) If table PK is clustered then - cost(rowid_to_row_scan) = + cost(rowid_to_row_scan) = {cost of ordinary clustered PK scan with n_ranges=n_rows} - - Otherwise, we use the following model to calculate costs: + + Otherwise, we use the following model to calculate costs: We need to retrieve n_rows rows from file that occupies n_blocks blocks. - We assume that offsets of rows we need are independent variates with + We assume that offsets of rows we need are independent variates with uniform distribution in [0..max_file_offset] range. - + We'll denote block as "busy" if it contains row(s) we need to retrieve and "empty" if doesn't contain rows we need. - + Probability that a block is empty is (1 - 1/n_blocks)^n_rows (this - applies to any block in file). Let x_i be a variate taking value 1 if + applies to any block in file). Let x_i be a variate taking value 1 if block #i is empty and 0 otherwise. - + Then E(x_i) = (1 - 1/n_blocks)^n_rows; - E(n_empty_blocks) = E(sum(x_i)) = sum(E(x_i)) = - = n_blocks * ((1 - 1/n_blocks)^n_rows) = + E(n_empty_blocks) = E(sum(x_i)) = sum(E(x_i)) = + = n_blocks * ((1 - 1/n_blocks)^n_rows) = ~= n_blocks * exp(-n_rows/n_blocks). E(n_busy_blocks) = n_blocks*(1 - (1 - 1/n_blocks)^n_rows) = ~= n_blocks * (1 - exp(-n_rows/n_blocks)). - + Average size of "hole" between neighbor non-empty blocks is E(hole_size) = n_blocks/E(n_busy_blocks). - + The total cost of reading all needed blocks in one "sweep" is: E(n_busy_blocks)* (DISK_SEEK_BASE_COST + DISK_SEEK_PROP_COST*n_blocks/E(n_busy_blocks)). 3. Cost of Unique use is calculated in Unique::get_use_cost function. - - ROR-union cost is calculated in the same way index_merge, but instead of - Unique a priority queue is used. - - RETURN + + ROR-union cost is calculated in the same way index_merge, but instead of + Unique a priority queue is used. + + RETURN Created read plan NULL - Out of memory or no read scan could be built. */ @@ -1885,7 +1892,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, DBUG_ENTER("get_best_disjunct_quick"); DBUG_PRINT("info", ("Full table scan cost =%g", read_time)); - if (!(range_scans= (TRP_RANGE**)alloc_root(param->mem_root, + if (!(range_scans= (TRP_RANGE**)alloc_root(param->mem_root, sizeof(TRP_RANGE*)* n_child_scans))) DBUG_RETURN(NULL); @@ -1912,11 +1919,11 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, } if (imerge_too_expensive) continue; - + imerge_cost += (*cur_child)->read_cost; all_scans_ror_able &= ((*ptree)->n_ror_scans > 0); all_scans_rors &= (*cur_child)->is_ror; - if (pk_is_clustered && + if (pk_is_clustered && param->real_keynr[(*cur_child)->key_idx] == param->table->primary_key) { cpk_scan= cur_child; @@ -1925,45 +1932,45 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, else non_cpk_scan_records += (*cur_child)->records; } - + DBUG_PRINT("info", ("index_merge scans cost=%g", imerge_cost)); - if (imerge_too_expensive || (imerge_cost > read_time) || + if (imerge_too_expensive || (imerge_cost > read_time) || (non_cpk_scan_records+cpk_scan_records >= param->table->file->records) && read_time != DBL_MAX) { - /* - Bail out if it is obvious that both index_merge and ROR-union will be + /* + Bail out if it is obvious that both index_merge and ROR-union will be more expensive */ DBUG_PRINT("info", ("Sum of index_merge scans is more expensive than " "full table scan, bailing out")); - DBUG_RETURN(NULL); + DBUG_RETURN(NULL); } if (all_scans_rors) { roru_read_plans= (TABLE_READ_PLAN**)range_scans; goto skip_to_ror_scan; } - blocks_in_index_read= imerge_cost; - if (cpk_scan) - { + blocks_in_index_read= imerge_cost; + if (cpk_scan) + { /* Add one ROWID comparison for each row retrieved on non-CPK scan. (it - is done in QUICK_RANGE_SELECT::row_in_ranges) - */ - imerge_cost += non_cpk_scan_records / TIME_FOR_COMPARE_ROWID; + is done in QUICK_RANGE_SELECT::row_in_ranges) + */ + imerge_cost += non_cpk_scan_records / TIME_FOR_COMPARE_ROWID; } /* Calculate cost(rowid_to_row_scan) */ imerge_cost += get_sweep_read_cost(param, non_cpk_scan_records); - DBUG_PRINT("info",("index_merge cost with rowid-to-row scan: %g", + DBUG_PRINT("info",("index_merge cost with rowid-to-row scan: %g", imerge_cost)); if (imerge_cost > read_time) goto build_ror_index_merge; /* Add Unique operations cost */ - unique_calc_buff_size= - Unique::get_cost_calc_buff_size(non_cpk_scan_records, + unique_calc_buff_size= + Unique::get_cost_calc_buff_size(non_cpk_scan_records, param->table->file->ref_length, param->thd->variables.sortbuff_size); if (param->imerge_cost_buff_size < unique_calc_buff_size) @@ -1974,11 +1981,11 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, param->imerge_cost_buff_size= unique_calc_buff_size; } - imerge_cost += + imerge_cost += Unique::get_use_cost(param->imerge_cost_buff, non_cpk_scan_records, param->table->file->ref_length, param->thd->variables.sortbuff_size); - DBUG_PRINT("info",("index_merge total cost: %g (wanted: less then %g)", + DBUG_PRINT("info",("index_merge total cost: %g (wanted: less then %g)", imerge_cost, read_time)); if (imerge_cost < read_time) { @@ -1986,7 +1993,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, { imerge_trp->read_cost= imerge_cost; imerge_trp->records= non_cpk_scan_records + cpk_scan_records; - imerge_trp->records= min(imerge_trp->records, + imerge_trp->records= min(imerge_trp->records, param->table->file->records); imerge_trp->range_scans= range_scans; imerge_trp->range_scans_end= range_scans + n_child_scans; @@ -1994,13 +2001,13 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, } } -build_ror_index_merge: +build_ror_index_merge: if (!all_scans_ror_able || param->thd->lex->sql_command == SQLCOM_DELETE) DBUG_RETURN(imerge_trp); - + /* Ok, it is possible to build a ROR-union, try it. */ bool dummy; - if (!(roru_read_plans= + if (!(roru_read_plans= (TABLE_READ_PLAN**)alloc_root(param->mem_root, sizeof(TABLE_READ_PLAN*)* n_child_scans))) @@ -2017,8 +2024,8 @@ skip_to_ror_scan: { /* Assume the best ROR scan is the one that has cheapest full-row-retrieval - scan cost. - Also accumulate index_only scan costs as we'll need them to calculate + scan cost. + Also accumulate index_only scan costs as we'll need them to calculate overall index_intersection cost. */ double cost; @@ -2026,7 +2033,7 @@ skip_to_ror_scan: { /* Ok, we have index_only cost, now get full rows scan cost */ cost= param->table->file-> - read_time(param->real_keynr[(*cur_child)->key_idx], 1, + read_time(param->real_keynr[(*cur_child)->key_idx], 1, (*cur_child)->records) + rows2double((*cur_child)->records) / TIME_FOR_COMPARE; } @@ -2034,7 +2041,7 @@ skip_to_ror_scan: cost= read_time; TABLE_READ_PLAN *prev_plan= *cur_child; - if (!(*cur_roru_plan= get_best_ror_intersect(param, *ptree, cost, + if (!(*cur_roru_plan= get_best_ror_intersect(param, *ptree, cost, &dummy))) { if (prev_plan->is_ror) @@ -2044,37 +2051,37 @@ skip_to_ror_scan: roru_index_costs += (*cur_roru_plan)->read_cost; } else - roru_index_costs += - ((TRP_ROR_INTERSECT*)(*cur_roru_plan))->index_scan_costs; + roru_index_costs += + ((TRP_ROR_INTERSECT*)(*cur_roru_plan))->index_scan_costs; roru_total_records += (*cur_roru_plan)->records; - roru_intersect_part *= (*cur_roru_plan)->records / + roru_intersect_part *= (*cur_roru_plan)->records / param->table->file->records; } - /* - rows to retrieve= + /* + rows to retrieve= SUM(rows_in_scan_i) - table_rows * PROD(rows_in_scan_i / table_rows). This is valid because index_merge construction guarantees that conditions in disjunction do not share key parts. */ roru_total_records -= (ha_rows)(roru_intersect_part* - param->table->file->records); - /* ok, got a ROR read plan for each of the disjuncts - Calculate cost: + param->table->file->records); + /* ok, got a ROR read plan for each of the disjuncts + Calculate cost: cost(index_union_scan(scan_1, ... scan_n)) = SUM_i(cost_of_index_only_scan(scan_i)) + queue_use_cost(rowid_len, n) + cost_of_row_retrieval See get_merge_buffers_cost function for queue_use_cost formula derivation. */ - + double roru_total_cost; - roru_total_cost= roru_index_costs + - rows2double(roru_total_records)*log(n_child_scans) / - (TIME_FOR_COMPARE_ROWID * M_LN2) + + roru_total_cost= roru_index_costs + + rows2double(roru_total_records)*log((double)n_child_scans) / + (TIME_FOR_COMPARE_ROWID * M_LN2) + get_sweep_read_cost(param, roru_total_records); - DBUG_PRINT("info", ("ROR-union: cost %g, %d members", roru_total_cost, + DBUG_PRINT("info", ("ROR-union: cost %g, %d members", roru_total_cost, n_child_scans)); TRP_ROR_UNION* roru; if (roru_total_cost < read_time) @@ -2102,11 +2109,11 @@ skip_to_ror_scan: keynr key to read NOTES - It is assumed that we will read trough the whole key range and that all + It is assumed that we will read trough the whole key range and that all key blocks are half full (normally things are much better). */ -inline double get_index_only_read_time(const PARAM* param, ha_rows records, +inline double get_index_only_read_time(const PARAM* param, ha_rows records, int keynr) { double read_time; @@ -2115,7 +2122,7 @@ inline double get_index_only_read_time(const PARAM* param, ha_rows records, param->table->file->ref_length) + 1); read_time=((double) (records+keys_per_block-1)/ (double) keys_per_block); - return read_time; + return read_time; } typedef struct st_ror_scan_info @@ -2125,34 +2132,34 @@ typedef struct st_ror_scan_info ha_rows records; /* estimate of # records this scan will return */ /* Set of intervals over key fields that will be used for row retrieval. */ - SEL_ARG *sel_arg; + SEL_ARG *sel_arg; /* Fields used in the query and covered by this ROR scan. */ - MY_BITMAP covered_fields; - uint used_fields_covered; /* # of set bits in covered_fields */ + MY_BITMAP covered_fields; + uint used_fields_covered; /* # of set bits in covered_fields */ int key_rec_length; /* length of key record (including rowid) */ /* Cost of reading all index records with values in sel_arg intervals set (assuming there is no need to access full table records) - */ - double index_read_cost; + */ + double index_read_cost; uint first_uncovered_field; /* first unused bit in covered_fields */ uint key_components; /* # of parts in the key */ } ROR_SCAN_INFO; /* - Create ROR_SCAN_INFO* structure with a single ROR scan on index idx using + Create ROR_SCAN_INFO* structure with a single ROR scan on index idx using sel_arg set of intervals. - + SYNOPSIS make_ror_scan() param Parameter from test_quick_select function idx Index of key in param->keys sel_arg Set of intervals for a given key RETURN - NULL - out of memory + NULL - out of memory ROR scan structure containing a scan for {idx, sel_arg} */ @@ -2169,22 +2176,22 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg) ror_scan->idx= idx; ror_scan->keynr= keynr= param->real_keynr[idx]; - ror_scan->key_rec_length= param->table->key_info[keynr].key_length + + ror_scan->key_rec_length= param->table->key_info[keynr].key_length + param->table->file->ref_length; ror_scan->sel_arg= sel_arg; ror_scan->records= param->table->quick_rows[keynr]; - - if (!(bitmap_buf= (uchar*)alloc_root(param->mem_root, + + if (!(bitmap_buf= (uchar*)alloc_root(param->mem_root, param->fields_bitmap_size))) DBUG_RETURN(NULL); - + if (bitmap_init(&ror_scan->covered_fields, bitmap_buf, param->fields_bitmap_size*8, FALSE)) DBUG_RETURN(NULL); bitmap_clear_all(&ror_scan->covered_fields); - + KEY_PART_INFO *key_part= param->table->key_info[keynr].key_part; - KEY_PART_INFO *key_part_end= key_part + + KEY_PART_INFO *key_part_end= key_part + param->table->key_info[keynr].key_parts; uint n_used_covered= 0; for (;key_part != key_part_end; ++key_part) @@ -2195,14 +2202,14 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg) bitmap_set_bit(&ror_scan->covered_fields, key_part->fieldnr); } } - ror_scan->index_read_cost= + ror_scan->index_read_cost= get_index_only_read_time(param, param->table->quick_rows[ror_scan->keynr], ror_scan->keynr); DBUG_RETURN(ror_scan); } -/* +/* Compare two ROR_SCAN_INFO** by E(#records_matched) * key_record_length. SYNOPSIS cmp_ror_scan_info() @@ -2210,7 +2217,7 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg) b ptr to second compared value RETURN - -1 a < b + -1 a < b 0 a = b 1 a > b */ @@ -2222,9 +2229,9 @@ static int cmp_ror_scan_info(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b) } /* - Compare two ROR_SCAN_INFO** by - (#covered fields in F desc, - #components asc, + Compare two ROR_SCAN_INFO** by + (#covered fields in F desc, + #components asc, number of first not covered component asc) SYNOPSIS @@ -2233,7 +2240,7 @@ static int cmp_ror_scan_info(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b) b ptr to second compared value RETURN - -1 a < b + -1 a < b 0 a = b 1 a > b */ @@ -2255,19 +2262,19 @@ static int cmp_ror_scan_info_covering(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b) } /* Auxiliary structure for incremental ROR-intersection creation */ -typedef struct +typedef struct { const PARAM *param; MY_BITMAP covered_fields; /* union of fields covered by all scans */ - + /* TRUE if covered_fields is a superset of needed_fields */ - bool is_covering; - - double index_scan_costs; /* SUM(cost of 'index-only' scans) */ + bool is_covering; + + double index_scan_costs; /* SUM(cost of 'index-only' scans) */ double total_cost; - /* + /* Fraction of table records that satisfies conditions of all scans. - This is the number of full records that will be retrieved if a + This is the number of full records that will be retrieved if a non-index_only index intersection will be employed. */ double records_fract; @@ -2293,10 +2300,10 @@ static void ror_intersect_reinit(ROR_INTERSECT_INFO *info) Allocate a ROR_INTERSECT_INFO and initialize it to contain zero scans. SYNOPSIS - ror_intersect_init() - param Parameter from test_quick_select + ror_intersect_init() + param Parameter from test_quick_select is_index_only If TRUE, set ROR_INTERSECT_INFO to be covering - + RETURN allocated structure NULL on error @@ -2307,7 +2314,7 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only) { ROR_INTERSECT_INFO *info; uchar* buf; - if (!(info= (ROR_INTERSECT_INFO*)alloc_root(param->mem_root, + if (!(info= (ROR_INTERSECT_INFO*)alloc_root(param->mem_root, sizeof(ROR_INTERSECT_INFO)))) return NULL; info->param= param; @@ -2322,55 +2329,55 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only) /* - Check if adding a ROR scan to a ROR-intersection reduces its cost of + Check if adding a ROR scan to a ROR-intersection reduces its cost of ROR-intersection and if yes, update parameters of ROR-intersection, including its cost. SYNOPSIS ror_intersect_add() - param Parameter from test_quick_select + param Parameter from test_quick_select info ROR-intersection structure to add the scan to. ror_scan ROR scan info to add. is_cpk_scan If TRUE, add the scan as CPK scan (this can be inferred from other parameters and is passed separately only to avoid duplicating the inference code) - + NOTES Adding a ROR scan to ROR-intersect "makes sense" iff the cost of ROR- intersection decreases. The cost of ROR-intersection is caclulated as follows: cost= SUM_i(key_scan_cost_i) + cost_of_full_rows_retrieval - + if (union of indexes used covers all needed fields) cost_of_full_rows_retrieval= 0; else { - cost_of_full_rows_retrieval= + cost_of_full_rows_retrieval= cost_of_sweep_read(E(rows_to_retrieve), rows_in_table); } - E(rows_to_retrieve) is caclulated as follows: + E(rows_to_retrieve) is caclulated as follows: Suppose we have a condition on several keys - cond=k_11=c_11 AND k_12=c_12 AND ... // parts of first key - k_21=c_21 AND k_22=c_22 AND ... // parts of second key + cond=k_11=c_11 AND k_12=c_12 AND ... // parts of first key + k_21=c_21 AND k_22=c_22 AND ... // parts of second key ... k_n1=c_n1 AND k_n3=c_n3 AND ... (1) - + where k_ij may be the same as any k_pq (i.e. keys may have common parts). A full row is retrieved iff entire cond holds. The recursive procedure for finding P(cond) is as follows: - + First step: - Pick 1st part of 1st key and break conjunction (1) into two parts: + Pick 1st part of 1st key and break conjunction (1) into two parts: cond= (k_11=c_11 AND R) - Here R may still contain condition(s) equivalent to k_11=c_11. + Here R may still contain condition(s) equivalent to k_11=c_11. Nevertheless, the following holds: - P(k_11=c_11 AND R) = P(k_11=c_11) * P(R|k_11=c_11). + P(k_11=c_11 AND R) = P(k_11=c_11) * P(R|k_11=c_11). Mark k_11 as fixed field (and satisfied condition) F, save P(F), save R to be cond and proceed to recursion step. @@ -2390,44 +2397,44 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param, bool is_index_only) a) F contains condition on field used in t (i.e. t AND F = F). Then P(t|F) = 1 - b) F doesn't contain condition on field used in t. Then F and t are - considered independent. + b) F doesn't contain condition on field used in t. Then F and t are + considered independent. - P(t|F) = P(t|(fields_before_t_in_key AND other_fields)) = + P(t|F) = P(t|(fields_before_t_in_key AND other_fields)) = = P(t|fields_before_t_in_key). P(t|fields_before_t_in_key) = #records(fields_before_t_in_key) / #records(fields_before_t_in_key, t) - - The second multiplier is calculated by applying this step recursively. + + The second multiplier is calculated by applying this step recursively. IMPLEMENTATION This function calculates the result of application of the "recursion step" described above for all fixed key members of a single key, accumulating set of covered fields, selectivity, etc. - The calculation is conducted as follows: + The calculation is conducted as follows: Lets denote #records(keypart1, ... keypartK) as n_k. We need to calculate - + n_{k1} n_{k_2} --------- * --------- * .... (3) - n_{k1-1} n_{k2_1} + n_{k1-1} n_{k2_1} - where k1,k2,... are key parts which fields were not yet marked as fixed - ( this is result of application of option b) of the recursion step for - parts of a single key). - Since it is reasonable to expect that most of the fields are not marked - as fixed, we calcualate (3) as + where k1,k2,... are key parts which fields were not yet marked as fixed + ( this is result of application of option b) of the recursion step for + parts of a single key). + Since it is reasonable to expect that most of the fields are not marked + as fixed, we calcualate (3) as n_{i1} n_{i_2} (3) = n_{max_key_part} / ( --------- * --------- * .... ) - n_{i1-1} n_{i2_1} - - where i1,i2, .. are key parts that were already marked as fixed. - + n_{i1-1} n_{i2_1} + + where i1,i2, .. are key parts that were already marked as fixed. + In order to minimize number of expensive records_in_range calls we group and reduce adjacent fractions. - + RETURN TRUE ROR scan added to ROR-intersection, cost updated. FALSE It doesn't make sense to add this ROR scan to this ROR-intersection. @@ -2438,14 +2445,14 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, { int i; SEL_ARG *sel_arg; - KEY_PART_INFO *key_part= + KEY_PART_INFO *key_part= info->param->table->key_info[ror_scan->keynr].key_part; double selectivity_mult= 1.0; byte key_val[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; /* key values tuple */ - DBUG_ENTER("ror_intersect_add"); - DBUG_PRINT("info", ("Current selectivity= %g", info->records_fract)); - DBUG_PRINT("info", ("Adding scan on %s", + DBUG_ENTER("ror_intersect_add"); + DBUG_PRINT("info", ("Current selectivity= %g", info->records_fract)); + DBUG_PRINT("info", ("Adding scan on %s", info->param->table->key_info[ror_scan->keynr].name)); SEL_ARG *tuple_arg= NULL; char *key_ptr= (char*) key_val; @@ -2460,7 +2467,7 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, max_range.key= (byte*) key_val; max_range.flag= HA_READ_AFTER_KEY; - for(i= 0, sel_arg= ror_scan->sel_arg; sel_arg; + for(i= 0, sel_arg= ror_scan->sel_arg; sel_arg; i++, sel_arg= sel_arg->next_key_part) { cur_covered= bitmap_is_set(&info->covered_fields, (key_part + i)->fieldnr); @@ -2471,14 +2478,14 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, if (!tuple_arg) { tuple_arg= ror_scan->sel_arg; - tuple_arg->store_min(key_part->length, &key_ptr, 0); + tuple_arg->store_min(key_part->length, &key_ptr, 0); } while (tuple_arg->next_key_part != sel_arg) { - tuple_arg= tuple_arg->next_key_part; - tuple_arg->store_min(key_part->length, &key_ptr, 0); + tuple_arg= tuple_arg->next_key_part; + tuple_arg->store_min(key_part->length, &key_ptr, 0); } - } + } ha_rows records; min_range.length= max_range.length= ((char*) key_ptr - (char*) key_val); records= param->table->file-> @@ -2496,17 +2503,17 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, else { /* covered -> uncovered */ - prev_records= records; + prev_records= records; } } prev_covered= cur_covered; } if (!prev_covered) { - double tmp= rows2double(param->table->quick_rows[ror_scan->keynr]) / + double tmp= rows2double(param->table->quick_rows[ror_scan->keynr]) / rows2double(prev_records); DBUG_PRINT("info", ("Selectivity multiplier: %g", tmp)); - selectivity_mult *= tmp; + selectivity_mult *= tmp; } if (selectivity_mult == 1.0) @@ -2517,9 +2524,9 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, } info->records_fract *= selectivity_mult; - ha_rows cur_scan_records= info->param->table->quick_rows[ror_scan->keynr]; + ha_rows cur_scan_records= info->param->table->quick_rows[ror_scan->keynr]; if (is_cpk_scan) - { + { info->index_scan_costs += rows2double(cur_scan_records)* TIME_FOR_COMPARE_ROWID; } @@ -2529,31 +2536,31 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, info->index_scan_costs += ror_scan->index_read_cost; bitmap_union(&info->covered_fields, &ror_scan->covered_fields); } - + if (!info->is_covering && bitmap_is_subset(&info->param->needed_fields, &info->covered_fields)) { DBUG_PRINT("info", ("ROR-intersect is covering now")); info->is_covering= TRUE; } - + info->total_cost= info->index_scan_costs; if (!info->is_covering) { ha_rows table_recs= info->param->table->file->records; - info->total_cost += - get_sweep_read_cost(info->param, + info->total_cost += + get_sweep_read_cost(info->param, (ha_rows)(info->records_fract*table_recs)); } - DBUG_PRINT("info", ("New selectivity= %g", info->records_fract)); - DBUG_PRINT("info", ("New cost= %g, %scovering", info->total_cost, + DBUG_PRINT("info", ("New selectivity= %g", info->records_fract)); + DBUG_PRINT("info", ("New cost= %g, %scovering", info->total_cost, info->is_covering?"" : "non-")); DBUG_RETURN(TRUE); } -/* - Get best ROR-intersection plan using non-covering ROR-intersection search +/* + Get best ROR-intersection plan using non-covering ROR-intersection search algorithm. The returned plan may be covering. SYNOPSIS @@ -2562,21 +2569,21 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, tree Transformed restriction condition to be used to look for ROR scans. read_time Do not return read plans with cost > read_time. - are_all_covering [out] set to TRUE if union of all scans covers all + are_all_covering [out] set to TRUE if union of all scans covers all fields needed by the query (and it is possible to build a covering ROR-intersection) NOTES - get_key_scans_params must be called before for the same SEL_TREE before + get_key_scans_params must be called before for the same SEL_TREE before this function can be called. - + The approximate best non-covering plan search algorithm is as follows: find_min_ror_intersection_scan() { R= select all ROR scans; order R by (E(#records_matched) * key_record_length). - + S= first(R); -- set of scans that will be used for ROR-intersection R= R-first(S); min_cost= cost(S); @@ -2600,15 +2607,15 @@ bool ror_intersect_add(const PARAM *param, ROR_INTERSECT_INFO *info, See ror_intersect_add function for ROR intersection costs. Special handling for Clustered PK scans - Clustered PK contains all table fields, so using it as a regular scan in - index intersection doesn't make sense: a range scan on CPK will be less + Clustered PK contains all table fields, so using it as a regular scan in + index intersection doesn't make sense: a range scan on CPK will be less expensive in this case. Clustered PK scan has special handling in ROR-intersection: it is not used - to retrieve rows, instead its condition is used to filter row references + to retrieve rows, instead its condition is used to filter row references we get from scans on other keys. RETURN - ROR-intersection table read plan + ROR-intersection table read plan NULL if out of memory or no suitable plan found. */ @@ -2637,7 +2644,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, return NULL; uint cpk_no= (param->table->file->primary_key_is_clustered())? param->table->primary_key : MAX_KEY; - + for (idx= 0, cur_ror_scan= tree->ror_scans; idx < param->keys; idx++) { ROR_SCAN_INFO *scan; @@ -2655,20 +2662,20 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, } tree->ror_scans_end= cur_ror_scan; - DBUG_EXECUTE("info",print_ror_scans_arr(param->table, "original", - tree->ror_scans, + DBUG_EXECUTE("info",print_ror_scans_arr(param->table, "original", + tree->ror_scans, tree->ror_scans_end);); /* - Ok, [ror_scans, ror_scans_end) is array of ptrs to initialized + Ok, [ror_scans, ror_scans_end) is array of ptrs to initialized ROR_SCAN_INFOs. Now, get a minimal key scan using an approximate algorithm. */ qsort(tree->ror_scans, tree->n_ror_scans, sizeof(ROR_SCAN_INFO*), (qsort_cmp)cmp_ror_scan_info); - DBUG_EXECUTE("info",print_ror_scans_arr(param->table, "ordered", - tree->ror_scans, + DBUG_EXECUTE("info",print_ror_scans_arr(param->table, "ordered", + tree->ror_scans, tree->ror_scans_end);); - + ROR_SCAN_INFO **intersect_scans; /* ROR scans used in index intersection */ ROR_SCAN_INFO **intersect_scans_end; if (!(intersect_scans= (ROR_SCAN_INFO**)alloc_root(param->mem_root, @@ -2681,9 +2688,9 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, ROR_INTERSECT_INFO *intersect; if (!(intersect= ror_intersect_init(param, FALSE))) return NULL; - + /* [intersect_scans, intersect_scans_best) will hold the best combination */ - ROR_SCAN_INFO **intersect_scans_best; + ROR_SCAN_INFO **intersect_scans_best; ha_rows best_rows; bool is_best_covering; double best_index_scan_costs; @@ -2698,45 +2705,45 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, { /* S= S + first(R); */ if (ror_intersect_add(param, intersect, *cur_ror_scan)) - *(intersect_scans_end++)= *cur_ror_scan; + *(intersect_scans_end++)= *cur_ror_scan; /* R= R - first(R); */ cur_ror_scan++; - + if (intersect->total_cost < min_cost) { /* Local minimum found, save it */ min_cost= intersect->total_cost; - best_rows= (ha_rows)(intersect->records_fract* + best_rows= (ha_rows)(intersect->records_fract* rows2double(param->table->file->records)); is_best_covering= intersect->is_covering; intersect_scans_best= intersect_scans_end; best_index_scan_costs= intersect->index_scan_costs; } } - + DBUG_EXECUTE("info",print_ror_scans_arr(param->table, "best ROR-intersection", intersect_scans, intersect_scans_best);); - + *are_all_covering= intersect->is_covering; - uint best_num= intersect_scans_best - intersect_scans; + uint best_num= intersect_scans_best - intersect_scans; /* Ok, found the best ROR-intersection of non-CPK key scans. Check if we should add a CPK scan. - - If the obtained ROR-intersection is covering, it doesn't make sense + + If the obtained ROR-intersection is covering, it doesn't make sense to add CPK scan - Clustered PK contains all fields and if we're doing CPK scan doing other CPK scans will only add more overhead. */ if (cpk_scan && !intersect->is_covering) { /* - Handle the special case: ROR-intersect(PRIMARY, key1) is + Handle the special case: ROR-intersect(PRIMARY, key1) is the best, but cost(range(key1)) > cost(best_non_ror_range_scan) */ if (best_num == 0) - { + { cur_ror_scan= tree->ror_scans; intersect_scans_end= intersect_scans; ror_intersect_reinit(intersect); @@ -2750,7 +2757,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, { cpk_scan_used= TRUE; min_cost= intersect->total_cost; - best_rows= (ha_rows)(intersect->records_fract* + best_rows= (ha_rows)(intersect->records_fract* rows2double(param->table->file->records)); is_best_covering= intersect->is_covering; best_index_scan_costs= intersect->index_scan_costs; @@ -2763,8 +2770,8 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, { if (!(trp= new (param->mem_root) TRP_ROR_INTERSECT)) DBUG_RETURN(trp); - if (!(trp->first_scan= - (ROR_SCAN_INFO**)alloc_root(param->mem_root, + if (!(trp->first_scan= + (ROR_SCAN_INFO**)alloc_root(param->mem_root, sizeof(ROR_SCAN_INFO*)*best_num))) DBUG_RETURN(NULL); memcpy(trp->first_scan, intersect_scans, best_num*sizeof(ROR_SCAN_INFO*)); @@ -2774,7 +2781,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, trp->records= best_rows? best_rows : 1; trp->index_scan_costs= best_index_scan_costs; trp->cpk_scan= cpk_scan; - } + } DBUG_RETURN(trp); } @@ -2787,15 +2794,15 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, tree SEL_TREE with sets of intervals for different keys. read_time Don't return table read plans with cost > read_time. - RETURN - Best covering ROR-intersection plan + RETURN + Best covering ROR-intersection plan NULL if no plan found. NOTES get_best_ror_intersect must be called for a tree before calling this - function for it. + function for it. This function invalidates tree->ror_scans member values. - + The following approximate algorithm is used: I=set of all covering indexes F=set of all fields to cover @@ -2812,27 +2819,27 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, */ static -TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, - SEL_TREE *tree, +TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, + SEL_TREE *tree, double read_time) { ROR_SCAN_INFO **ror_scan_mark; - ROR_SCAN_INFO **ror_scans_end= tree->ror_scans_end; + ROR_SCAN_INFO **ror_scans_end= tree->ror_scans_end; DBUG_ENTER("get_best_covering_ror_intersect"); uint nbits= param->fields_bitmap_size*8; for (ROR_SCAN_INFO **scan= tree->ror_scans; scan != ror_scans_end; ++scan) - (*scan)->key_components= + (*scan)->key_components= param->table->key_info[(*scan)->keynr].key_parts; - + /* Run covering-ROR-search algorithm. - Assume set I is [ror_scan .. ror_scans_end) + Assume set I is [ror_scan .. ror_scans_end) */ - + /*I=set of all covering indexes */ ror_scan_mark= tree->ror_scans; - + uchar buf[MAX_KEY/8+1]; MY_BITMAP covered_fields; if (bitmap_init(&covered_fields, buf, nbits, FALSE)) @@ -2841,25 +2848,25 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, double total_cost= 0.0f; ha_rows records=0; - bool all_covered; - + bool all_covered; + DBUG_PRINT("info", ("Building covering ROR-intersection")); DBUG_EXECUTE("info", print_ror_scans_arr(param->table, "building covering ROR-I", ror_scan_mark, ror_scans_end);); do { /* - Update changed sorting info: + Update changed sorting info: #covered fields, - number of first not covered component + number of first not covered component Calculate and save these values for each of remaining scans. */ for (ROR_SCAN_INFO **scan= ror_scan_mark; scan != ror_scans_end; ++scan) { bitmap_subtract(&(*scan)->covered_fields, &covered_fields); - (*scan)->used_fields_covered= + (*scan)->used_fields_covered= bitmap_bits_set(&(*scan)->covered_fields); - (*scan)->first_uncovered_field= + (*scan)->first_uncovered_field= bitmap_get_first(&(*scan)->covered_fields); } @@ -2869,11 +2876,11 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, DBUG_EXECUTE("info", print_ror_scans_arr(param->table, "remaining scans", ror_scan_mark, ror_scans_end);); - + /* I=I-first(I) */ total_cost += (*ror_scan_mark)->index_read_cost; records += (*ror_scan_mark)->records; - DBUG_PRINT("info", ("Adding scan on %s", + DBUG_PRINT("info", ("Adding scan on %s", param->table->key_info[(*ror_scan_mark)->keynr].name)); if (total_cost > read_time) DBUG_RETURN(NULL); @@ -2881,7 +2888,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, bitmap_union(&covered_fields, &(*ror_scan_mark)->covered_fields); all_covered= bitmap_is_subset(¶m->needed_fields, &covered_fields); } while (!all_covered && (++ror_scan_mark < ror_scans_end)); - + if (!all_covered) DBUG_RETURN(NULL); /* should not happen actually */ @@ -2893,9 +2900,10 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, DBUG_EXECUTE("info", print_ror_scans_arr(param->table, "creating covering ROR-intersect", tree->ror_scans, ror_scan_mark);); - + /* Add priority queue use cost. */ - total_cost += rows2double(records)*log(ror_scan_mark - tree->ror_scans) / + total_cost += rows2double(records)* + log((double)(ror_scan_mark - tree->ror_scans)) / (TIME_FOR_COMPARE_ROWID * M_LN2); DBUG_PRINT("info", ("Covering ROR-intersect full cost: %g", total_cost)); @@ -2921,22 +2929,22 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, /* - Get best "range" table read plan for given SEL_TREE. + Get best "range" table read plan for given SEL_TREE. Also update PARAM members and store ROR scans info in the SEL_TREE. SYNOPSIS get_key_scans_params param parameters from test_quick_select - tree make range select for this SEL_TREE + tree make range select for this SEL_TREE index_read_must_be_used if TRUE, assume 'index only' option will be set (except for clustered PK indexes) read_time don't create read plans with cost > read_time. RETURN - Best range read plan + Best range read plan NULL if no plan found or error occurred */ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, - bool index_read_must_be_used, + bool index_read_must_be_used, double read_time) { int idx; @@ -2947,11 +2955,11 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, DBUG_ENTER("get_key_scans_params"); LINT_INIT(best_records); /* protected by key_to_read */ /* - Note that there may be trees that have type SEL_TREE::KEY but contain no - key reads at all, e.g. tree for expression "key1 is not null" where key1 + Note that there may be trees that have type SEL_TREE::KEY but contain no + key reads at all, e.g. tree for expression "key1 is not null" where key1 is defined as "not null". - */ - DBUG_EXECUTE("info", print_sel_tree(param, tree, &tree->keys_map, + */ + DBUG_EXECUTE("info", print_sel_tree(param, tree, &tree->keys_map, "tree scans");); tree->ror_scans_map.clear_all(); tree->n_ror_scans= 0; @@ -2967,7 +2975,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, if ((*key)->type == SEL_ARG::MAYBE_KEY || (*key)->maybe_flag) param->needed_reg->set_bit(keynr); - + bool read_index_only= index_read_must_be_used ? TRUE : (bool) param->table->used_keys.is_set(keynr); @@ -2988,7 +2996,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, } else { - /* + /* cost(read_through_index) = cost(disk_io) + cost(row_in_range_checks) The row_in_range check is in QUICK_RANGE_SELECT::cmp_next function. */ @@ -3001,9 +3009,9 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, read_time, found_read_time)); if (read_time > found_read_time && found_records != HA_POS_ERROR /*|| read_time == DBL_MAX*/ ) - { + { read_time= found_read_time; - best_records= found_records; + best_records= found_records; key_to_read= key; } @@ -3020,7 +3028,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, read_plan->records= best_records; read_plan->is_ror= tree->ror_scans_map.is_set(idx); read_plan->read_cost= read_time; - DBUG_PRINT("info",("Returning range plan for key %s, cost %g", + DBUG_PRINT("info",("Returning range plan for key %s, cost %g", param->table->key_info[param->real_keynr[idx]].name, read_plan->read_cost)); } @@ -3032,7 +3040,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree, } -QUICK_SELECT_I *TRP_INDEX_MERGE::make_quick(PARAM *param, +QUICK_SELECT_I *TRP_INDEX_MERGE::make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc) { @@ -3059,7 +3067,7 @@ QUICK_SELECT_I *TRP_INDEX_MERGE::make_quick(PARAM *param, return quick_imerge; } -QUICK_SELECT_I *TRP_ROR_INTERSECT::make_quick(PARAM *param, +QUICK_SELECT_I *TRP_ROR_INTERSECT::make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc) { @@ -3067,13 +3075,13 @@ QUICK_SELECT_I *TRP_ROR_INTERSECT::make_quick(PARAM *param, QUICK_RANGE_SELECT *quick; DBUG_ENTER("TRP_ROR_INTERSECT::make_quick"); MEM_ROOT *alloc; - - if ((quick_intrsect= + + if ((quick_intrsect= new QUICK_ROR_INTERSECT_SELECT(param->thd, param->table, retrieve_full_rows? (!is_covering):FALSE, parent_alloc))) { - DBUG_EXECUTE("info", print_ror_scans_arr(param->table, + DBUG_EXECUTE("info", print_ror_scans_arr(param->table, "creating ROR-intersect", first_scan, last_scan);); alloc= parent_alloc? parent_alloc: &quick_intrsect->alloc; @@ -3095,16 +3103,17 @@ QUICK_SELECT_I *TRP_ROR_INTERSECT::make_quick(PARAM *param, delete quick_intrsect; DBUG_RETURN(NULL); } + quick->file= NULL; quick_intrsect->cpk_quick= quick; } - quick_intrsect->records= records; + quick_intrsect->records= records; quick_intrsect->read_time= read_cost; } DBUG_RETURN(quick_intrsect); } -QUICK_SELECT_I *TRP_ROR_UNION::make_quick(PARAM *param, +QUICK_SELECT_I *TRP_ROR_UNION::make_quick(PARAM *param, bool retrieve_full_rows, MEM_ROOT *parent_alloc) { @@ -3112,15 +3121,15 @@ QUICK_SELECT_I *TRP_ROR_UNION::make_quick(PARAM *param, TABLE_READ_PLAN **scan; QUICK_SELECT_I *quick; DBUG_ENTER("TRP_ROR_UNION::make_quick"); - /* - It is impossible to construct a ROR-union that will not retrieve full + /* + It is impossible to construct a ROR-union that will not retrieve full rows, ignore retrieve_full_rows parameter. */ if ((quick_roru= new QUICK_ROR_UNION_SELECT(param->thd, param->table))) { for(scan= first_ror; scan != last_ror; scan++) { - if (!(quick= (*scan)->make_quick(param, FALSE, &quick_roru->alloc)) || + if (!(quick= (*scan)->make_quick(param, FALSE, &quick_roru->alloc)) || quick_roru->push_quick_back(quick)) DBUG_RETURN(NULL); } @@ -3275,7 +3284,7 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) static SEL_TREE * get_mm_parts(PARAM *param, COND *cond_func, Field *field, - Item_func::Functype type, + Item_func::Functype type, Item *value, Item_result cmp_type) { bool ne_func= FALSE; @@ -3317,7 +3326,7 @@ get_mm_parts(PARAM *param, COND *cond_func, Field *field, else { // This key may be used later - if (!(sel_arg= new SEL_ARG(SEL_ARG::MAYBE_KEY))) + if (!(sel_arg= new SEL_ARG(SEL_ARG::MAYBE_KEY))) DBUG_RETURN(0); // OOM } sel_arg->part=(uchar) key_part->part; @@ -3366,8 +3375,8 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, } /* - We can't use an index when comparing strings of - different collations + We can't use an index when comparing strings of + different collations */ if (field->result_type() == STRING_RESULT && value->result_type() == STRING_RESULT && @@ -3462,7 +3471,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part, value->result_type() != STRING_RESULT && field->cmp_type() != value->result_type()) DBUG_RETURN(0); - + if (value->save_in_field(field, 1) < 0) { /* This happens when we try to insert a NULL field in a not null column */ @@ -3683,8 +3692,8 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) /* - Check if two SEL_TREES can be combined into one (i.e. a single key range - read can be constructed for "cond_of_tree1 OR cond_of_tree2" ) without + Check if two SEL_TREES can be combined into one (i.e. a single key range + read can be constructed for "cond_of_tree1 OR cond_of_tree2" ) without using index_merge. */ @@ -3696,8 +3705,8 @@ bool sel_trees_can_be_ored(SEL_TREE *tree1, SEL_TREE *tree2, PARAM* param) if (common_keys.is_clear_all()) DBUG_RETURN(FALSE); - - /* trees have a common key, check if they refer to same key part */ + + /* 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++) { @@ -4317,7 +4326,7 @@ SEL_ARG::find_range(SEL_ARG *key) SYNOPSIS tree_delete() key Key that is to be deleted from tree (this) - + NOTE This also frees all sub trees that is used by the element @@ -4662,14 +4671,14 @@ void SEL_ARG::test_use_count(SEL_ARG *root) tree Transformed selection condition, tree->key[idx] holds intervals tree to be used for scanning. NOTES - param->is_ror_scan is set to reflect if the key scan is a ROR (see + param->is_ror_scan is set to reflect if the key scan is a ROR (see is_key_scan_ror function for more info) - param->table->quick_*, param->range_count (and maybe others) are + param->table->quick_*, param->range_count (and maybe others) are updated with data of given key scan, see check_quick_keys for details. - - RETURN + + RETURN Estimate # of records to be retrieved. - HA_POS_ERROR if estimate calculation failed due to table handler problems. + HA_POS_ERROR if estimate calculation failed due to table handler problems. */ @@ -4680,9 +4689,9 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree) bool cpk_scan; uint key; DBUG_ENTER("check_quick_select"); - + param->is_ror_scan= FALSE; - + if (!tree) DBUG_RETURN(HA_POS_ERROR); // Can't use it param->max_key_part=0; @@ -4708,16 +4717,16 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree) */ cpk_scan= (param->table->primary_key == param->real_keynr[idx]) && param->table->file->primary_key_is_clustered(); - param->is_ror_scan= !cpk_scan; + param->is_ror_scan= !cpk_scan; } records=check_quick_keys(param,idx,tree,param->min_key,0,param->max_key,0); if (records != HA_POS_ERROR) - { + { param->table->quick_keys.set_bit(key); param->table->quick_rows[key]=records; param->table->quick_key_parts[key]=param->max_key_part+1; - + if (cpk_scan) param->is_ror_scan= TRUE; } @@ -4727,22 +4736,22 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree) /* - Recursively calculate estimate of # rows that will be retrieved by - key scan on key idx. + Recursively calculate estimate of # rows that will be retrieved by + key scan on key idx. SYNOPSIS check_quick_keys() param Parameter from test_quick select function. - idx Number of key to use in PARAM::keys in list of used keys + idx Number of key to use in PARAM::keys in list of used keys (param->real_keynr[idx] holds the key number in table) key_tree SEL_ARG tree being examined. min_key Buffer with partial min key value tuple - min_key_flag + min_key_flag max_key Buffer with partial max key value tuple max_key_flag NOTES - The function does the recursive descent on the tree via SEL_ARG::left, - SEL_ARG::right, and SEL_ARG::next_key_part edges. The #rows estimates + The function does the recursive descent on the tree via SEL_ARG::left, + SEL_ARG::right, and SEL_ARG::next_key_part edges. The #rows estimates are calculated using records_in_range calls at the leaf nodes and then summed. @@ -4750,13 +4759,13 @@ check_quick_select(PARAM *param,uint idx,SEL_ARG *tree) tuples. The side effects are: - + param->max_key_part is updated to hold the maximum number of key parts used in scan minus 1. - - param->range_count is incremented if the function finds a range that + + param->range_count is incremented if the function finds a range that wasn't counted by the caller. - + param->is_ror_scan is cleared if the function detects that the key scan is not a Rowid-Ordered Retrieval scan ( see comments for is_key_scan_ror function for description of which key scans are ROR scans) @@ -4787,7 +4796,7 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, uint tmp_min_flag,tmp_max_flag,keynr; char *tmp_min_key=min_key,*tmp_max_key=max_key; - + key_tree->store(param->key[idx][key_tree->part].store_length, &tmp_min_key,min_key_flag,&tmp_max_key,max_key_flag); uint min_key_length= (uint) (tmp_min_key- param->min_key); @@ -4795,13 +4804,13 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, if (param->is_ror_scan) { - /* + /* If the index doesn't cover entire key, mark the scan as non-ROR scan. Actually we're cutting off some ROR scans here. */ uint16 fieldnr= param->table->key_info[param->real_keynr[idx]]. key_part[key_tree->part].fieldnr - 1; - if (param->table->field[fieldnr]->key_length() != + if (param->table->field[fieldnr]->key_length() != param->key[idx][key_tree->part].length) param->is_ror_scan= FALSE; } @@ -4866,7 +4875,7 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, */ if (!(min_key_length == max_key_length && !memcmp(min_key,max_key, (uint) (tmp_max_key - max_key)) && - !key_tree->min_flag && !key_tree->max_flag && + !key_tree->min_flag && !key_tree->max_flag && is_key_scan_ror(param, keynr, key_tree->part + 1))) param->is_ror_scan= FALSE; } @@ -4925,40 +4934,40 @@ check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree, /* - Check if key scan on given index with equality conditions on first n key + Check if key scan on given index with equality conditions on first n key parts is a ROR scan. SYNOPSIS is_key_scan_ror() - param Parameter from test_quick_select + param Parameter from test_quick_select keynr Number of key in the table. The key must not be a clustered primary key. nparts Number of first key parts for which equality conditions are present. - + NOTES ROR (Rowid Ordered Retrieval) key scan is a key scan that produces ordered sequence of rowids (ha_xxx::cmp_ref is the comparison function) - + An index scan is a ROR scan if it is done using a condition in form "key1_1=c_1 AND ... AND key1_n=c_n" (1) - + where the index is defined on (key1_1, ..., key1_N [,a_1, ..., a_n]) - and the table has a clustered Primary Key + and the table has a clustered Primary Key - PRIMARY KEY(a_1, ..., a_n, b1, ..., b_k) with first key parts being + PRIMARY KEY(a_1, ..., a_n, b1, ..., b_k) with first key parts being identical to uncovered parts ot the key being scanned (2) - - Scans on HASH indexes are not ROR scans, + + Scans on HASH indexes are not ROR scans, any range scan on clustered primary key is ROR scan (3) Check (1) is made in check_quick_keys() Check (3) is made check_quick_select() Check (2) is made by this function. - RETURN + RETURN TRUE If the scan is ROR-scan FALSE otherwise */ @@ -4967,9 +4976,9 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts) { KEY *table_key= param->table->key_info + keynr; KEY_PART_INFO *key_part= table_key->key_part + nparts; - KEY_PART_INFO *key_part_end= table_key->key_part + + KEY_PART_INFO *key_part_end= table_key->key_part + table_key->key_parts; - + if (key_part == key_part_end) return TRUE; uint pk_number= param->table->primary_key; @@ -4977,14 +4986,14 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts) return FALSE; KEY_PART_INFO *pk_part= param->table->key_info[pk_number].key_part; - KEY_PART_INFO *pk_part_end= pk_part + + KEY_PART_INFO *pk_part_end= pk_part + param->table->key_info[pk_number].key_parts; - for(;(key_part!=key_part_end) && (pk_part != pk_part_end); + for(;(key_part!=key_part_end) && (pk_part != pk_part_end); ++key_part, ++pk_part) { - if ((key_part->field != pk_part->field) || + if ((key_part->field != pk_part->field) || (key_part->length != pk_part->length)) - return FALSE; + return FALSE; } return (key_part == key_part_end); } @@ -4992,23 +5001,23 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts) /* Create a QUICK_RANGE_SELECT from given key and SEL_ARG tree for that key. - + SYNOPSIS get_quick_select() - param + param idx Index of used key in param->key. - key_tree SEL_ARG tree for the used key - parent_alloc If not NULL, use it to allocate memory for + key_tree SEL_ARG tree for the used key + parent_alloc If not NULL, use it to allocate memory for quick select data. Otherwise use quick->alloc. - - RETURN + + RETURN NULL on error otherwise created quick select NOTES The caller must call QUICK_SELCT::init for returned quick select - CAUTION! This function may change THR_MALLOC to a MEM_ROOT which will be + CAUTION! This function may change THR_MALLOC to a MEM_ROOT which will be deallocated when the returned quick select is deleted. */ @@ -5030,7 +5039,7 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree, quick=new QUICK_RANGE_SELECT(param->thd, param->table, param->real_keynr[idx], test(parent_alloc), parent_alloc); - + if (quick) { if (quick->error || @@ -5048,7 +5057,7 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree, sizeof(KEY_PART)* param->table->key_info[param->real_keynr[idx]].key_parts); } - } + } DBUG_RETURN(quick); } @@ -5189,7 +5198,7 @@ bool QUICK_RANGE_SELECT::unique_key_range() static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length) { - for (const char *end=key+length ; + for (const char *end=key+length ; key < end; key+= key_part++->store_length) { @@ -5202,7 +5211,7 @@ static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length) bool QUICK_SELECT_I::check_if_keys_used(List<Item> *fields) { - return check_if_key_used(head, index, *fields); + return check_if_key_used(head, index, *fields); } bool QUICK_INDEX_MERGE_SELECT::check_if_keys_used(List<Item> *fields) @@ -5245,10 +5254,10 @@ bool QUICK_ROR_UNION_SELECT::check_if_keys_used(List<Item> *fields) Create a QUICK RANGE based on a key ****************************************************************************/ -QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, +QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, TABLE_REF *ref) { - QUICK_RANGE_SELECT *quick=new QUICK_RANGE_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; @@ -5292,7 +5301,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, if (insert_dynamic(&quick->ranges,(gptr)&range)) goto err; - /* + /* Add a NULL range if REF_OR_NULL optimization is used. For example: if we have "WHERE A=2 OR A IS NULL" we created the (A=2) range above @@ -5323,12 +5332,12 @@ err: /* Fetch all row ids into unique. - If table has a clustered primary key that covers all rows (TRUE for bdb + If table has a clustered primary key that covers all rows (TRUE for bdb and innodb currently) and one of the index_merge scans is a scan on PK, - then - primary key scan rowids are not put into Unique and also + then + primary key scan rowids are not put into Unique and also rows that will be retrieved by PK scan are not put into Unique - + RETURN 0 OK other error @@ -5338,12 +5347,12 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() { int result; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::prepare_unique"); - + /* We're going to just read rowids. */ head->file->extra(HA_EXTRA_KEYREAD); - /* - Make innodb retrieve all PK member fields, so + /* + Make innodb retrieve all PK member fields, so * ha_innobase::position (which uses them) call works. * We can filter out rows that will be retrieved by clustered PK. (This also creates a deficiency - it is possible that we will retrieve @@ -5375,21 +5384,21 @@ int QUICK_INDEX_MERGE_SELECT::prepare_unique() } if (result) - { + { if (result != HA_ERR_END_OF_FILE) DBUG_RETURN(result); break; } - + if (thd->killed) DBUG_RETURN(1); - + /* skip row if it will be retrieved by clustered PK scan */ if (pk_quick_select && pk_quick_select->row_in_ranges()) continue; cur_quick_select->file->position(cur_quick_select->record); - result= unique->unique_add((char*)cur_quick_select->file->ref); + result= unique->unique_add((char*)cur_quick_select->file->ref); if (result) DBUG_RETURN(1); @@ -5420,7 +5429,7 @@ int QUICK_INDEX_MERGE_SELECT::get_next() { int result; DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::get_next"); - + if (doing_pk_scan) DBUG_RETURN(pk_quick_select->get_next()); @@ -5445,10 +5454,10 @@ int QUICK_INDEX_MERGE_SELECT::get_next() /* - Retrieve next record. + Retrieve next record. SYNOPSIS - QUICK_ROR_INTERSECT_SELECT::get_next() - + QUICK_ROR_INTERSECT_SELECT::get_next() + NOTES Invariant on enter/exit: all intersected selects have retrieved all index records with rowid <= some_rowid_val and no intersected select has @@ -5456,7 +5465,7 @@ int QUICK_INDEX_MERGE_SELECT::get_next() We start fresh and loop until we have retrieved the same rowid in each of the key scans or we got an error. - If a Clustered PK scan is present, it is used only to check if row + If a Clustered PK scan is present, it is used only to check if row satisfies its condition (and never used for row retrieval). RETURN @@ -5471,7 +5480,7 @@ int QUICK_ROR_INTERSECT_SELECT::get_next() int error, cmp; uint last_rowid_count=0; DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::get_next"); - + /* Get a rowid for first quick and save it as a 'candidate' */ quick= quick_it++; if (cpk_quick) @@ -5482,14 +5491,14 @@ int QUICK_ROR_INTERSECT_SELECT::get_next() } else error= quick->get_next(); - + if (error) DBUG_RETURN(error); quick->file->position(quick->record); memcpy(last_rowid, quick->file->ref, head->file->ref_length); last_rowid_count= 1; - + while (last_rowid_count < quick_selects.elements) { if (!(quick= quick_it++)) @@ -5497,12 +5506,12 @@ int QUICK_ROR_INTERSECT_SELECT::get_next() quick_it.rewind(); quick= quick_it++; } - + do { if ((error= quick->get_next())) DBUG_RETURN(error); quick->file->position(quick->record); - cmp= head->file->cmp_ref(quick->file->ref, last_rowid); + cmp= head->file->cmp_ref(quick->file->ref, last_rowid); } while (cmp < 0); /* Ok, current select 'caught up' and returned ref >= cur_ref */ @@ -5518,7 +5527,7 @@ int QUICK_ROR_INTERSECT_SELECT::get_next() } } memcpy(last_rowid, quick->file->ref, head->file->ref_length); - last_rowid_count= 1; + last_rowid_count= 1; } else { @@ -5534,17 +5543,17 @@ int QUICK_ROR_INTERSECT_SELECT::get_next() } -/* - Retrieve next record. +/* + Retrieve next record. SYNOPSIS QUICK_ROR_UNION_SELECT::get_next() - + NOTES - Enter/exit invariant: - For each quick select in the queue a {key,rowid} tuple has been + Enter/exit invariant: + For each quick select in the queue a {key,rowid} tuple has been retrieved but the corresponding row hasn't been passed to output. - RETURN + RETURN 0 - Ok other - Error code if any error occurred. */ @@ -5555,7 +5564,7 @@ int QUICK_ROR_UNION_SELECT::get_next() QUICK_SELECT_I *quick; byte *tmp; DBUG_ENTER("QUICK_ROR_UNION_SELECT::get_next"); - + do { if (!queue.elements) @@ -5577,7 +5586,7 @@ int QUICK_ROR_UNION_SELECT::get_next() quick->save_last_pos(); queue_replaced(&queue); } - + if (!have_prev_rowid) { /* No rows have been returned yet */ @@ -5587,7 +5596,7 @@ int QUICK_ROR_UNION_SELECT::get_next() else dup_row= !head->file->cmp_ref(cur_rowid, prev_rowid); }while (dup_row); - + tmp= cur_rowid; cur_rowid= prev_rowid; prev_rowid= tmp; @@ -5616,7 +5625,7 @@ int QUICK_RANGE_SELECT::get_next() if (!cur_range) range= *(cur_range= (QUICK_RANGE**) ranges.buffer); - else + else range= (cur_range == ((QUICK_RANGE**) ranges.buffer + ranges.elements - 1)) ? (QUICK_RANGE*) 0 : *(++cur_range); @@ -5695,13 +5704,13 @@ int QUICK_RANGE_SELECT_GEOM::get_next() Check if current row will be retrieved by this QUICK_RANGE_SELECT NOTES - It is assumed that currently a scan is being done on another index - which reads all necessary parts of the index that is scanned by this + It is assumed that currently a scan is being done on another index + which reads all necessary parts of the index that is scanned by this quick select. - The implementation does a binary search on sorted array of disjoint + The implementation does a binary search on sorted array of disjoint ranges, without taking size of range into account. - This function is used to filter out clustered PK scan rows in + This function is used to filter out clustered PK scan rows in index_merge quick select. RETURN @@ -5717,7 +5726,7 @@ bool QUICK_RANGE_SELECT::row_in_ranges() uint mid= (max + min)/2; while (min != max) - { + { if (cmp_next(*(QUICK_RANGE**)dynamic_array_ptr(&ranges, mid))) { /* current row value > mid->max */ @@ -5741,12 +5750,12 @@ bool QUICK_RANGE_SELECT::row_in_ranges() for now, this seems to work right at least. */ -QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, +QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts) : QUICK_RANGE_SELECT(*q), rev_it(rev_ranges) { QUICK_RANGE *r; - + QUICK_RANGE **pr= (QUICK_RANGE**)ranges.buffer; QUICK_RANGE **last_range= pr + ranges.elements; for (; pr!=last_range; pr++) @@ -5993,7 +6002,7 @@ void QUICK_INDEX_MERGE_SELECT::add_info_string(String *str) void QUICK_ROR_INTERSECT_SELECT::add_info_string(String *str) { - bool first= TRUE; + bool first= TRUE; QUICK_RANGE_SELECT *quick; List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects); str->append("intersect("); @@ -6002,7 +6011,7 @@ void QUICK_ROR_INTERSECT_SELECT::add_info_string(String *str) KEY *key_info= head->key_info + quick->index; if (!first) str->append(','); - else + else first= FALSE; str->append(key_info->name); } @@ -6017,7 +6026,7 @@ void QUICK_ROR_INTERSECT_SELECT::add_info_string(String *str) void QUICK_ROR_UNION_SELECT::add_info_string(String *str) { - bool first= TRUE; + bool first= TRUE; QUICK_SELECT_I *quick; List_iterator_fast<QUICK_SELECT_I> it(quick_selects); str->append("union("); @@ -6033,7 +6042,7 @@ void QUICK_ROR_UNION_SELECT::add_info_string(String *str) } -void QUICK_RANGE_SELECT::add_keys_and_lengths(String *key_names, +void QUICK_RANGE_SELECT::add_keys_and_lengths(String *key_names, String *used_lengths) { char buf[64]; @@ -6051,7 +6060,7 @@ void QUICK_INDEX_MERGE_SELECT::add_keys_and_lengths(String *key_names, uint length; bool first= TRUE; QUICK_RANGE_SELECT *quick; - + List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects); while ((quick= it++)) { @@ -6062,7 +6071,7 @@ void QUICK_INDEX_MERGE_SELECT::add_keys_and_lengths(String *key_names, key_names->append(','); used_lengths->append(','); } - + KEY *key_info= head->key_info + quick->index; key_names->append(key_info->name); length= longlong2str(quick->max_used_key_length, buf, 10) - buf; @@ -6084,7 +6093,7 @@ void QUICK_ROR_INTERSECT_SELECT::add_keys_and_lengths(String *key_names, { char buf[64]; uint length; - bool first= TRUE; + bool first= TRUE; QUICK_RANGE_SELECT *quick; List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects); while ((quick= it++)) @@ -6101,7 +6110,7 @@ void QUICK_ROR_INTERSECT_SELECT::add_keys_and_lengths(String *key_names, length= longlong2str(quick->max_used_key_length, buf, 10) - buf; used_lengths->append(buf, length); } - + if (cpk_quick) { KEY *key_info= head->key_info + cpk_quick->index; @@ -6116,7 +6125,7 @@ void QUICK_ROR_INTERSECT_SELECT::add_keys_and_lengths(String *key_names, void QUICK_ROR_UNION_SELECT::add_keys_and_lengths(String *key_names, String *used_lengths) { - bool first= TRUE; + bool first= TRUE; QUICK_SELECT_I *quick; List_iterator_fast<QUICK_SELECT_I> it(quick_selects); while ((quick= it++)) @@ -6124,7 +6133,7 @@ void QUICK_ROR_UNION_SELECT::add_keys_and_lengths(String *key_names, if (first) first= FALSE; else - { + { used_lengths->append(','); key_names->append(','); } @@ -6134,7 +6143,7 @@ void QUICK_ROR_UNION_SELECT::add_keys_and_lengths(String *key_names, #ifndef DBUG_OFF -static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map, +static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map, const char *msg) { SEL_ARG **key,**end; @@ -6167,7 +6176,7 @@ static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map, } static void print_ror_scans_arr(TABLE *table, const char *msg, - struct st_ror_scan_info **start, + struct st_ror_scan_info **start, struct st_ror_scan_info **end) { DBUG_ENTER("print_ror_scans"); @@ -6235,7 +6244,7 @@ static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg) if (! _db_on_ || !quick) DBUG_VOID_RETURN; DBUG_LOCK_FILE; - + quick->dbug_dump(0, TRUE); fprintf(DBUG_FILE,"other_keys: 0x%s:\n", needed_reg->print(buf)); @@ -6263,12 +6272,12 @@ void QUICK_RANGE_SELECT::dbug_dump(int indent, bool verbose) { fprintf(DBUG_FILE, "%*squick range select, key %s, length: %d\n", indent, "", head->key_info[index].name, max_used_key_length); - + if (verbose) { QUICK_RANGE *range; QUICK_RANGE **pr= (QUICK_RANGE**)ranges.buffer; - QUICK_RANGE **last_range= pr + ranges.elements; + QUICK_RANGE **last_range= pr + ranges.elements; for (; pr!=last_range; ++pr) { fprintf(DBUG_FILE, "%*s", indent + 2, ""); @@ -6306,7 +6315,7 @@ void QUICK_INDEX_MERGE_SELECT::dbug_dump(int indent, bool verbose) quick->dbug_dump(indent+2, verbose); if (pk_quick_select) { - fprintf(DBUG_FILE, "%*sclustered PK quick:\n", indent, ""); + fprintf(DBUG_FILE, "%*sclustered PK quick:\n", indent, ""); pk_quick_select->dbug_dump(indent+2, verbose); } fprintf(DBUG_FILE, "%*s}\n", indent, ""); @@ -6316,14 +6325,14 @@ void QUICK_ROR_INTERSECT_SELECT::dbug_dump(int indent, bool verbose) { List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects); QUICK_RANGE_SELECT *quick; - fprintf(DBUG_FILE, "%*squick ROR-intersect select, %scovering\n", + fprintf(DBUG_FILE, "%*squick ROR-intersect select, %scovering\n", indent, "", need_to_fetch_row? "":"non-"); fprintf(DBUG_FILE, "%*smerged scans {\n", indent, ""); while ((quick= it++)) - quick->dbug_dump(indent+2, verbose); + quick->dbug_dump(indent+2, verbose); if (cpk_quick) { - fprintf(DBUG_FILE, "%*sclustered PK quick:\n", indent, ""); + fprintf(DBUG_FILE, "%*sclustered PK quick:\n", indent, ""); cpk_quick->dbug_dump(indent+2, verbose); } fprintf(DBUG_FILE, "%*s}\n", indent, ""); diff --git a/sql/opt_range.h b/sql/opt_range.h index 1dc3a1354e7..974ed409a87 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -67,7 +67,7 @@ class QUICK_RANGE :public Sql_alloc { /* - Quick select interface. + Quick select interface. This class is a parent for all QUICK_*_SELECT and FT_SELECT classes. */ @@ -79,7 +79,7 @@ public: double read_time; /* time to perform this retrieval */ TABLE *head; /* - Index this quick select uses, or MAX_KEY for quick selects + Index this quick select uses, or MAX_KEY for quick selects that use several indexes */ uint index; @@ -99,35 +99,35 @@ public: QUICK_SELECT_I(); virtual ~QUICK_SELECT_I(){}; - + /* Do post-constructor initialization. SYNOPSIS init() - - init() performs initializations that should have been in constructor if - it was possible to return errors from constructors. The join optimizer may + + init() performs initializations that should have been in constructor if + it was possible to return errors from constructors. The join optimizer may create and then delete quick selects without retrieving any rows so init() must not contain any IO or CPU intensive code. - If init() call fails the only valid action is to delete this quick select, + If init() call fails the only valid action is to delete this quick select, reset() and get_next() must not be called. - + RETURN 0 OK other Error code */ virtual int init() = 0; - - /* + + /* Initialize quick select for row retrieval. SYNOPSIS reset() - - reset() should be called when it is certain that row retrieval will be + + reset() should be called when it is certain that row retrieval will be necessary. This call may do heavyweight initialization like buffering first N records etc. If reset() call fails get_next() must not be called. - + RETURN 0 OK other Error code @@ -140,13 +140,13 @@ public: virtual bool reverse_sorted() = 0; virtual bool unique_key_range() { return false; } - enum { + enum { QS_TYPE_RANGE = 0, QS_TYPE_INDEX_MERGE = 1, QS_TYPE_RANGE_DESC = 2, QS_TYPE_FULLTEXT = 3, QS_TYPE_ROR_INTERSECT = 4, - QS_TYPE_ROR_UNION = 5, + QS_TYPE_ROR_UNION = 5 }; /* Get type of this quick select - one of the QS_TYPE_* values */ @@ -154,7 +154,7 @@ public: /* Initialize this quick select as a merged scan inside a ROR-union or a ROR- - intersection scan. The caller must not additionally call init() if this + intersection scan. The caller must not additionally call init() if this function is called. SYNOPSIS init_ror_merged_scan() @@ -162,55 +162,55 @@ public: it must create and use a separate handler object. RETURN 0 Ok - other Error + other Error */ virtual int init_ror_merged_scan(bool reuse_handler) { DBUG_ASSERT(0); return 1; } - + /* Save ROWID of last retrieved row in file->ref. This used in ROR-merging. */ virtual void save_last_pos(){}; - /* + /* Append comma-separated list of keys this quick select uses to key_names; append comma-separated list of corresponding used lengths to used_lengths. This is used by select_describe. */ - virtual void add_keys_and_lengths(String *key_names, + virtual void add_keys_and_lengths(String *key_names, String *used_lengths)=0; - - /* - Append text representation of quick select structure (what and how is + + /* + Append text representation of quick select structure (what and how is merged) to str. The result is added to "Extra" field in EXPLAIN output. This function is implemented only by quick selects that merge other quick selects output and/or can produce output suitable for merging. */ virtual void add_info_string(String *str) {}; /* - Return 1 if any index used by this quick select - a) uses field that is listed in passed field list or + Return 1 if any index used by this quick select + a) uses field that is listed in passed field list or b) is automatically updated (like a timestamp) */ virtual bool check_if_keys_used(List<Item> *fields); /* - rowid of last row retrieved by this quick select. This is used only when - doing ROR-index_merge selects + rowid of last row retrieved by this quick select. This is used only when + doing ROR-index_merge selects */ byte *last_rowid; /* - Table record buffer used by this quick select. + Table record buffer used by this quick select. */ byte *record; #ifndef DBUG_OFF /* - Print quick select information to DBUG_FILE. Caller is responsible + Print quick select information to DBUG_FILE. Caller is responsible for locking DBUG_FILE before this call and unlocking it afterwards. */ virtual void dbug_dump(int indent, bool verbose)= 0; -#endif +#endif }; @@ -218,7 +218,7 @@ struct st_qsel_param; class SEL_ARG; /* - Quick select that does a range scan on a single key. The records are + Quick select that does a range scan on a single key. The records are returned in key order. */ class QUICK_RANGE_SELECT : public QUICK_SELECT_I @@ -236,8 +236,9 @@ protected: bool free_file; protected: + friend class TRP_ROR_INTERSECT; friend - QUICK_RANGE_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); friend bool get_quick_keys(struct st_qsel_param *param, QUICK_RANGE_SELECT *quick,KEY_PART *key, @@ -255,7 +256,7 @@ protected: QUICK_RANGE **cur_range; /* current element in ranges */ QUICK_RANGE *range; - KEY_PART *key_parts; + KEY_PART *key_parts; KEY_PART_INFO *key_part_info; int cmp_next(QUICK_RANGE *range); int cmp_prev(QUICK_RANGE *range); @@ -266,11 +267,11 @@ public: QUICK_RANGE_SELECT(THD *thd, TABLE *table,uint index_arg,bool no_alloc=0, MEM_ROOT *parent_alloc=NULL); ~QUICK_RANGE_SELECT(); - + int reset(void) { - next=0; - range= NULL; + next=0; + range= NULL; cur_range= NULL; return 0; } @@ -308,29 +309,29 @@ public: /* QUICK_INDEX_MERGE_SELECT - index_merge access method quick select. - QUICK_INDEX_MERGE_SELECT uses + QUICK_INDEX_MERGE_SELECT uses * QUICK_RANGE_SELECTs to get rows * Unique class to remove duplicate rows INDEX MERGE OPTIMIZER - Current implementation doesn't detect all cases where index_merge could + Current implementation doesn't detect all cases where index_merge could be used, in particular: - * index_merge will never be used if range scan is possible (even if + * index_merge will never be used if range scan is possible (even if range scan is more expensive) - * index_merge+'using index' is not supported (this the consequence of + * index_merge+'using index' is not supported (this the consequence of the above restriction) - + * If WHERE part contains complex nested AND and OR conditions, some ways - to retrieve rows using index_merge will not be considered. The choice - of read plan may depend on the order of conjuncts/disjuncts in WHERE + to retrieve rows using index_merge will not be considered. The choice + of read plan may depend on the order of conjuncts/disjuncts in WHERE part of the query, see comments near imerge_list_or_list and SEL_IMERGE::or_sel_tree_with_checks functions for details. * There is no "index_merge_ref" method (but index_merge on non-first table in join is possible with 'range checked for each record'). - See comments around SEL_IMERGE class and test_quick_select for more + See comments around SEL_IMERGE class and test_quick_select for more details. ROW RETRIEVAL ALGORITHM @@ -352,7 +353,7 @@ public: } deactivate 'index only'; } - + Phase 2 (implemented as sequence of QUICK_INDEX_MERGE_SELECT::get_next calls): @@ -364,7 +365,7 @@ public: } */ -class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I +class QUICK_INDEX_MERGE_SELECT : public QUICK_SELECT_I { public: QUICK_INDEX_MERGE_SELECT(THD *thd, TABLE *table); @@ -387,23 +388,23 @@ public: /* 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; - + /* quick select that uses clustered primary key (NULL if none) */ QUICK_RANGE_SELECT* pk_quick_select; - + /* true if this select is currently doing a clustered PK scan */ bool doing_pk_scan; - + Unique *unique; MEM_ROOT alloc; THD *thd; int prepare_unique(); - + /* used to get rows collected in Unique */ READ_RECORD read_record; }; @@ -411,26 +412,26 @@ public: /* Rowid-Ordered Retrieval (ROR) index intersection quick select. - This quick select produces intersection of row sequences returned + This quick select produces intersection of row sequences returned by several QUICK_RANGE_SELECTs it "merges". - - All merged QUICK_RANGE_SELECTs must return rowids in rowid order. + + All merged QUICK_RANGE_SELECTs must return rowids in rowid order. QUICK_ROR_INTERSECT_SELECT will return rows in rowid order, too. - All merged quick selects retrieve {rowid, covered_fields} tuples (not full + All merged quick selects retrieve {rowid, covered_fields} tuples (not full table records). - QUICK_ROR_INTERSECT_SELECT retrieves full records if it is not being used - by QUICK_ROR_INTERSECT_SELECT and all merged quick selects together don't + QUICK_ROR_INTERSECT_SELECT retrieves full records if it is not being used + by QUICK_ROR_INTERSECT_SELECT and all merged quick selects together don't cover needed all fields. - + If one of the merged quick selects is a Clustered PK range scan, it is used only to filter rowid sequence produced by other merged quick selects. */ -class QUICK_ROR_INTERSECT_SELECT : public QUICK_SELECT_I +class QUICK_ROR_INTERSECT_SELECT : public QUICK_SELECT_I { public: - QUICK_ROR_INTERSECT_SELECT(THD *thd, TABLE *table, + QUICK_ROR_INTERSECT_SELECT(THD *thd, TABLE *table, bool retrieve_full_rows, MEM_ROOT *parent_alloc); ~QUICK_ROR_INTERSECT_SELECT(); @@ -450,14 +451,14 @@ public: int init_ror_merged_scan(bool reuse_handler); bool push_quick_back(QUICK_RANGE_SELECT *quick_sel_range); - /* + /* Range quick selects this intersection consists of, not including cpk_quick. */ List<QUICK_RANGE_SELECT> quick_selects; - - /* - Merged quick select that uses Clustered PK, if there is one. This quick + + /* + Merged quick select that uses Clustered PK, if there is one. This quick select is not used for row retrieval, it is used for row retrieval. */ QUICK_RANGE_SELECT *cpk_quick; @@ -473,15 +474,15 @@ public: This quick select produces union of row sequences returned by several quick select it "merges". - All merged quick selects must return rowids in rowid order. + All merged quick selects must return rowids in rowid order. QUICK_ROR_UNION_SELECT will return rows in rowid order, too. All merged quick selects are set not to retrieve full table records. ROR-union quick select always retrieves full records. - + */ -class QUICK_ROR_UNION_SELECT : public QUICK_SELECT_I +class QUICK_ROR_UNION_SELECT : public QUICK_SELECT_I { public: QUICK_ROR_UNION_SELECT(THD *thd, TABLE *table); @@ -503,7 +504,7 @@ public: bool push_quick_back(QUICK_SELECT_I *quick_sel_range); List<QUICK_SELECT_I> quick_selects; /* Merged quick selects */ - + QUEUE queue; /* Priority queue for merge operation */ MEM_ROOT alloc; /* Memory pool for this and merged quick selects data. */ @@ -511,7 +512,7 @@ public: byte *cur_rowid; /* buffer used in get_next() */ byte *prev_rowid; /* rowid of last row returned by get_next() */ bool have_prev_rowid; /* true if prev_rowid has valid data */ - uint rowid_length; /* table rowid length */ + uint rowid_length; /* table rowid length */ private: static int queue_cmp(void *arg, byte *val1, byte *val2); }; @@ -569,4 +570,6 @@ public: int get_type() { return QS_TYPE_FULLTEXT; } }; +QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, + struct st_table_ref *ref); #endif diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 4bd526d7136..d10d395d6e5 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -268,8 +268,8 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, } } - my_snprintf(path_to, FN_REFLEN, "%s/%s-%04lld", - path_arc, file_name->str, old_version); + my_snprintf(path_to, FN_REFLEN, "%s/%s-%04lu", + path_arc, file_name->str, (ulong) old_version); if (my_rename(path, path_to, MYF(MY_WME))) { DBUG_RETURN(TRUE); @@ -278,9 +278,9 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, // remove very old version if (old_version > max_versions) { - my_snprintf(path_to, FN_REFLEN, "%s/%s-%04lld", + my_snprintf(path_to, FN_REFLEN, "%s/%s-%04lu", path_arc, file_name->str, - old_version - max_versions); + (ulong)(old_version - max_versions)); if (!access(path_arc, F_OK) && my_delete(path_to, MYF(MY_WME))) { DBUG_RETURN(TRUE); diff --git a/sql/protocol.cc b/sql/protocol.cc index 065fcd3d4af..e14eafc86a2 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -487,6 +487,7 @@ void Protocol::init(THD *thd_arg) flag Bit mask with the following functions: 1 send number of rows 2 send default values + 4 don't write eof packet DESCRIPTION Sum fields has table name empty and field_name. @@ -497,7 +498,7 @@ void Protocol::init(THD *thd_arg) */ #ifndef EMBEDDED_LIBRARY -bool Protocol::send_fields(List<Item> *list, uint flag) +bool Protocol::send_fields(List<Item> *list, int flags) { List_iterator_fast<Item> it(*list); Item *item; @@ -508,7 +509,7 @@ bool Protocol::send_fields(List<Item> *list, uint flag) CHARSET_INFO *thd_charset= thd->variables.character_set_results; DBUG_ENTER("send_fields"); - if (flag & 1) + if (flags & SEND_NUM_ROWS) { // Packet with number of elements char *pos=net_store_length(buff, (uint) list->elements); (void) my_net_write(&thd->net, buff,(uint) (pos-buff)); @@ -594,7 +595,7 @@ bool Protocol::send_fields(List<Item> *list, uint flag) } } local_packet->length((uint) (pos - local_packet->ptr())); - if (flag & 2) + if (flags & SEND_DEFAULTS) item->send(&prot, &tmp); // Send default value if (prot.write()) break; /* purecov: inspected */ @@ -603,7 +604,8 @@ bool Protocol::send_fields(List<Item> *list, uint flag) #endif } - my_net_write(&thd->net, eof_buff, 1); + if (flags & SEND_EOF) + my_net_write(&thd->net, eof_buff, 1); DBUG_RETURN(prepare_for_send(list)); err: @@ -962,12 +964,6 @@ void Protocol_prep::prepare_for_resend() bool Protocol_prep::store(const char *from, uint length, CHARSET_INFO *fromcs) { CHARSET_INFO *tocs= thd->variables.character_set_results; -#ifndef DEBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_DECIMAL || - (field_types[field_pos] >= MYSQL_TYPE_ENUM && - field_types[field_pos] <= MYSQL_TYPE_GEOMETRY)); -#endif field_pos++; return store_string_aux(from, length, fromcs, tocs); } @@ -975,12 +971,6 @@ bool Protocol_prep::store(const char *from, uint length, CHARSET_INFO *fromcs) bool Protocol_prep::store(const char *from,uint length, CHARSET_INFO *fromcs, CHARSET_INFO *tocs) { -#ifndef DEBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_DECIMAL || - (field_types[field_pos] >= MYSQL_TYPE_ENUM && - field_types[field_pos] <= MYSQL_TYPE_GEOMETRY)); -#endif field_pos++; return store_string_aux(from, length, fromcs, tocs); } @@ -998,10 +988,6 @@ bool Protocol_prep::store_null() bool Protocol_prep::store_tiny(longlong from) { -#ifndef DEBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_TINY); -#endif char buff[1]; field_pos++; buff[0]= (uchar) from; @@ -1011,11 +997,6 @@ bool Protocol_prep::store_tiny(longlong from) bool Protocol_prep::store_short(longlong from) { -#ifndef DEBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_SHORT || - field_types[field_pos] == MYSQL_TYPE_YEAR); -#endif field_pos++; char *to= packet->prep_append(2, PACKET_BUFFER_EXTRA_ALLOC); if (!to) @@ -1027,11 +1008,6 @@ bool Protocol_prep::store_short(longlong from) bool Protocol_prep::store_long(longlong from) { -#ifndef DEBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_INT24 || - field_types[field_pos] == MYSQL_TYPE_LONG); -#endif field_pos++; char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC); if (!to) @@ -1043,10 +1019,6 @@ bool Protocol_prep::store_long(longlong from) bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag) { -#ifndef DEBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_LONGLONG); -#endif field_pos++; char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC); if (!to) @@ -1058,10 +1030,6 @@ bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag) bool Protocol_prep::store(float from, uint32 decimals, String *buffer) { -#ifndef DEBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_FLOAT); -#endif field_pos++; char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC); if (!to) @@ -1073,10 +1041,6 @@ bool Protocol_prep::store(float from, uint32 decimals, String *buffer) bool Protocol_prep::store(double from, uint32 decimals, String *buffer) { -#ifndef DEBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_DOUBLE); -#endif field_pos++; char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC); if (!to) @@ -1100,12 +1064,6 @@ bool Protocol_prep::store(Field *field) bool Protocol_prep::store(TIME *tm) { -#ifndef DEBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_DATETIME || - field_types[field_pos] == MYSQL_TYPE_DATE || - field_types[field_pos] == MYSQL_TYPE_TIMESTAMP); -#endif char buff[12],*pos; uint length; field_pos++; @@ -1140,10 +1098,6 @@ bool Protocol_prep::store_date(TIME *tm) bool Protocol_prep::store_time(TIME *tm) { -#ifndef DEBUG_OFF - DBUG_ASSERT(field_types == 0 || - field_types[field_pos] == MYSQL_TYPE_TIME); -#endif char buff[13], *pos; uint length; field_pos++; diff --git a/sql/protocol.h b/sql/protocol.h index 079c06ae155..1a5896a3ae5 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -50,7 +50,10 @@ public: Protocol(THD *thd_arg) { init(thd_arg); } virtual ~Protocol() {} void init(THD* thd_arg); - virtual bool send_fields(List<Item> *list, uint flag); + + enum { SEND_NUM_ROWS= 1, SEND_DEFAULTS= 2, SEND_EOF= 4 }; + virtual bool send_fields(List<Item> *list, int flags); + 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); @@ -163,7 +166,7 @@ public: prev_record= &data; return Protocol_simple::prepare_for_send(item_list); } - bool send_fields(List<Item> *list, uint flag); + bool send_fields(List<Item> *list, int flags); bool write(); uint get_field_count() { return field_count; } }; diff --git a/sql/protocol_cursor.cc b/sql/protocol_cursor.cc index 749b66785d4..31eaa894045 100644 --- a/sql/protocol_cursor.cc +++ b/sql/protocol_cursor.cc @@ -26,7 +26,7 @@ #include "mysql_priv.h" #include <mysql.h> -bool Protocol_cursor::send_fields(List<Item> *list, uint flag) +bool Protocol_cursor::send_fields(List<Item> *list, int flags) { List_iterator_fast<Item> it(*list); Item *item; @@ -67,7 +67,7 @@ bool Protocol_cursor::send_fields(List<Item> *list, uint flag) if (INTERNAL_NUM_FIELD(client_field)) client_field->flags|= NUM_FLAG; - if (flag & 2) + if (flags & (int) Protocol::SEND_DEFAULTS) { char buff[80]; String tmp(buff, sizeof(buff), default_charset_info), *res; diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc index 4feb24f06b2..bb70b793d3b 100644 --- a/sql/repl_failsafe.cc +++ b/sql/repl_failsafe.cc @@ -461,7 +461,8 @@ int show_new_master(THD* thd) field_list.push_back(new Item_empty_string("Log_name", 20)); field_list.push_back(new Item_return_int("Log_pos", 10, MYSQL_TYPE_LONGLONG)); - if (protocol->send_fields(&field_list, 1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(-1); protocol->prepare_for_resend(); protocol->store(lex_mi->log_file_name, &my_charset_bin); @@ -651,7 +652,8 @@ int show_slave_hosts(THD* thd) field_list.push_back(new Item_return_int("Master_id", 10, MYSQL_TYPE_LONG)); - if (protocol->send_fields(&field_list, 1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(-1); pthread_mutex_lock(&LOCK_slave_list); diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 7b5c2cb327a..aa23a7a005a 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -313,6 +313,7 @@ character-set=latin2 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -360,12 +361,15 @@ character-set=latin2 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index 7fd0ef6270d..ac66eff0b5f 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -1,4 +1,4 @@ -/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB +:/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB This file is public domain and comes with NO WARRANTY of any kind */ /* Knud Riishøjgård knudriis@post.tele.dk 99 && @@ -307,6 +307,7 @@ character-set=latin1 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -354,12 +355,15 @@ character-set=latin1 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 159bace6667..1f85a9ed42f 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -315,6 +315,7 @@ character-set=latin1 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -362,12 +363,15 @@ character-set=latin1 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 2306df21321..ebf58088c9f 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -304,6 +304,7 @@ character-set=latin1 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -351,12 +352,15 @@ character-set=latin1 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index 03877b4656b..b446f133241 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -309,6 +309,7 @@ character-set=latin7 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -356,12 +357,15 @@ character-set=latin7 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index f1d68fca6d7..db5647abcb6 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -304,6 +304,7 @@ character-set=latin1 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -351,12 +352,15 @@ character-set=latin1 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 800409bb07f..ae9110ff301 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -316,6 +316,7 @@ character-set=latin1 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -363,12 +364,15 @@ character-set=latin1 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index b94364d256c..c674d038b16 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -304,6 +304,7 @@ character-set=greek "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -351,12 +352,15 @@ character-set=greek "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index b8560317b48..47ff00223af 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -306,6 +306,7 @@ character-set=latin2 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -353,12 +354,15 @@ character-set=latin2 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index 7c72fb03476..eae07ddefcc 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -304,6 +304,7 @@ character-set=latin1 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -351,12 +352,15 @@ character-set=latin1 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index 4c1bf8e5cc0..5c36473eda8 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -306,6 +306,7 @@ character-set=ujis "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -353,12 +354,15 @@ character-set=ujis "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index 45479950378..1e1ffbeec20 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -304,6 +304,7 @@ character-set=euckr "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -351,12 +352,15 @@ character-set=euckr "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 02096bc46b9..5e6879dd415 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -306,6 +306,7 @@ character-set=latin1 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -353,12 +354,15 @@ character-set=latin1 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 615014cef94..0f97b65a46f 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -306,6 +306,7 @@ character-set=latin1 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -353,12 +354,15 @@ character-set=latin1 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 9368dd927d6..aed5d1c5827 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -308,6 +308,7 @@ character-set=latin2 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -355,12 +356,15 @@ character-set=latin2 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 12c4c43bfe0..b2acb008932 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -305,6 +305,7 @@ character-set=latin1 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -352,12 +353,15 @@ character-set=latin1 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 4b04ef3729e..f065504da5b 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -308,6 +308,7 @@ character-set=latin2 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -355,12 +356,15 @@ character-set=latin2 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index b73b7d8d7b9..56b0b9c8953 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -306,6 +306,7 @@ character-set=koi8r "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -353,12 +354,15 @@ character-set=koi8r "'%-.64s.%-.64s' - ÎÅ %s" "óÔÏÌÂÅà '%-.64s' ÎÅ ÏÂÎÏ×ÌÑÅÍÙÊ" "View SELECT ÓÏÄÅÒÖÉÔ ÐÏÄÚÁÐÒÏÓ × ËÏÎÓÔÒÕËÃÉÉ FROM" -"View SELECT ÓÏÄÅÒÖÉÔ ËÏÎÓÔÒÕËÃÉÀ PROCEDURE" +"View SELECT ÓÏÄÅÒÖÉÔ ËÏÎÓÔÒÕËÃÉÀ '%s'" "View SELECT ÓÏÄÅÒÖÉÔ ÐÅÒÅÍÅÎÎÕÀ ÉÌÉ ÐÁÒÁÍÅÔÒ" "View SELECT ÓÏÄÅÒÖÉÔ ÓÓÙÌËÕ ÎÁ ×ÒÅÍÅÎÎÕÀ ÔÁÂÌÉÃÕ '%-.64s'" "View SELECT É ÓÐÉÓÏË ÐÏÌÅÊ view ÉÍÅÀÔ ÒÁÚÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÓÔÏÌÂÃÏ×" "áÌÇÏÒÉÔÍ ÓÌÉÑÎÉÑ view ÎÅ ÍÏÖÅÔ ÂÙÔØ ÉÓÐÏÌØÚÏ×ÁÎ ÓÅÊÞÁÓ (ÁÌÇÏÒÉÔÍ ÂÕÄÅÔ ÎÅÏÐÅÒÅÄÅÌÅÎÎÙÍ)" "ïÂÎÏ×ÌÑÅÍÙÊ view ÎÅ ÓÏÄÅÒÖÉÔ ËÌÀÞÁ ÉÓÐÏÌØÚÏ×ÁÎÎÏÊ × ÎÅÍ ÔÁÂÌÉÃ(Ù)" +"View '%-.64s.%-.64s' ÓÓÙÌÁÅÔÓÑ ÎÁ ÎÅÓÕÝÅÓÔ×ÕÀÝÉÅ ÔÁÂÌÉÃÙ ÉÌÉ ÓÔÏÌÂÃÙ" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index d0f4e0e64eb..a3ecba11e2e 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -310,6 +310,7 @@ character-set=cp1250 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -357,12 +358,15 @@ character-set=cp1250 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index 66f75e5b44d..88be98bb6f4 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -312,6 +312,7 @@ character-set=latin2 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -359,12 +360,15 @@ character-set=latin2 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index 080fba045a4..c35a2b9a917 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -306,6 +306,7 @@ character-set=latin1 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -353,12 +354,15 @@ character-set=latin1 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 0d4e4d489f4..00c3e4b0016 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -304,6 +304,7 @@ character-set=latin1 "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -351,12 +352,15 @@ character-set=latin1 "'%-.64s.%-.64s' is not %s" "Column '%-.64s' is not updatable" "View's SELECT contains a subquery in the FROM clause" -"View's SELECT contains a PROCEDURE clause" +"View's SELECT contains a '%s' clause" "View's SELECT contains a variable or parameter" "View's SELECT contains a temporary table '%-.64s'" "View's SELECT and view's field list have different column counts" "View merge algorithm can't be used here for now (assumed undefined algorithm)" -"View being update does not have complete key of underlying table in it" +"View being updated does not have complete key of underlying table in it" +"View '%-.64s.%-.64s' references invalid table(s) or column(s)" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index a1369730790..c5329fccaea 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -309,6 +309,7 @@ character-set=koi8u "Unknown or incorrect time zone: '%-.64s'", "Invalid TIMESTAMP value in column '%s' at row %ld", "Invalid %s character string: '%.64s'", +"Result of %s() was larger than max_allowed_packet (%d) - truncated" "Can't create a %s from within another stored routine" "%s %s already exists" "%s %s does not exist" @@ -356,12 +357,15 @@ character-set=koi8u "'%-.64s.%-.64s' ÎÅ ¤ %s" "óÔÏ×ÂÅÃØ '%-.64s' ÎÅ ÍÏÖÅ ÂÕÔÉ ÚÍÉÎÅÎÉÊ" "View SELECT ÍÁ¤ ЦÄÚÁÐÉÔ Õ ËÏÎÓÔÒÕËæ§ FROM" -"View SELECT ÍÁ¤ ËÏÎÓÔÒÕËæÀ PROCEDURE" +"View SELECT ÍÁ¤ ËÏÎÓÔÒÕËæÀ '%s'" "View SELECT ÍÁ¤ ÚÍÉÎÎÕ ÁÂÏ ÐÁÒÁÍÅÔÅÒ" "View SELECT ×ÉËÏÒÉÓÔÏ×Õ¤ ÔÉÍÞÁÓÏ×Õ ÔÁÂÌÉÃÀ '%-.64s'" "View SELECT ¦ ÐÅÒÅÌ¦Ë ÓÔÏ×ÂÃ¦× view ÍÁÀÔØ Ò¦ÚÎÕ Ë¦ÌØ˦ÓÔØ ÓËÏ×Âæ×" "áÌÇÏÒÉÔÍ ÚÌÉ×ÁÎÎÑ view ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÒÉÓÔÁÎÉÊ ÚÁÒÁÚ (ÁÌÇÏÒÉÔÍ ÂÕÄÅ ÎÅ×ÉÚÎÁÞÅÎÉÊ)" "View, ÝÏ ÏÎÏ×ÌÀÅÔØÓÑ, ΊͦÓÔÉÔØ ÐÏ×ÎÏÇÏ ËÌÀÞÁ ÔÁÂÌÉæ(Ø), ÝÏ ×ÉËÏÒ¦ÓÔÁÎÁ × ÎØÀÏÍÕ" +"View '%-.64s.%-.64s' ÐÏÓÉÌÁ¤ÔÓÑ ÎÁ ÎŦÓÎÕÀÞ¦ ÔÁÂÌÉæ ÁÂÏ ÓÔÏ×Âæ" +"Can't drop a %s from within another stored routine" +"GOTO is not allowed in a stored procedure handler" "Trigger already exists" "Trigger does not exist" "Trigger's '%-.64s' is view or temporary table" diff --git a/sql/slave.cc b/sql/slave.cc index 8899635e2c6..0defbe35163 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -372,7 +372,7 @@ int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, (starting from position 4): Format_desc (of slave) Rotate (of master) - Format_desc (of slave) + Format_desc (of master) So the Format_desc which really describes the rest of the relay log is the 3rd event (it can't be further than that, because we rotate the relay log when we queue a Rotate event from the master). @@ -2335,7 +2335,8 @@ int show_master_info(THD* thd, MASTER_INFO* mi) field_list.push_back(new Item_return_int("Seconds_Behind_Master", 10, MYSQL_TYPE_LONGLONG)); - if (protocol->send_fields(&field_list, 1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(-1); if (mi->host[0]) @@ -3873,7 +3874,10 @@ static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev) mi->rli.relay_log.description_event_for_queue= new Format_description_log_event(3); } - + /* + Rotate the relay log makes binlog format detection easier (at next slave + start or mysqlbinlog) + */ rotate_relay_log(mi); /* will take the right mutexes */ DBUG_RETURN(0); } diff --git a/sql/sp.cc b/sql/sp.cc index 408d676aebe..cd52cff90a3 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -93,10 +93,15 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, 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 (thd->lex->proc_table) + table= thd->lex->proc_table->table; + else + { + 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 @@ -265,8 +270,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) */ List<Item> vals= thd->lex->value_list; - mysql_init_query(thd, TRUE); - lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length()); + mysql_init_query(thd, (uchar*)defstr.c_ptr(), defstr.length(), TRUE); thd->lex->value_list= vals; } @@ -293,6 +297,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) *sphp= thd->lex->sphead; (*sphp)->set_info((char *)definer, (uint)strlen(definer), created, modified, &chistics, sql_mode); + (*sphp)->optimize(); } thd->lex->sql_command= oldcmd; thd->variables.sql_mode= old_sql_mode; @@ -425,6 +430,7 @@ db_update_routine(THD *thd, int type, sp_name *name, if (ret == SP_OK) { store_record(table,record[1]); + table->timestamp_on_update_now = 0; // Don't update create time now. ((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); @@ -564,7 +570,8 @@ db_show_routine_status(THD *thd, int type, const char *wild) } } /* Print header */ - if (thd->protocol->send_fields(&field_list,1)) + if (thd->protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | + Protocol::SEND_EOF)) { res= SP_INTERNAL_ERROR; goto err_case; @@ -721,11 +728,14 @@ int sp_drop_procedure(THD *thd, sp_name *name) { int ret; + bool found; DBUG_ENTER("sp_drop_procedure"); DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - sp_cache_remove(&thd->sp_proc_cache, name); + found= sp_cache_remove(&thd->sp_proc_cache, name); ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name); + if (!found && !ret) + sp_cache_invalidate(); DBUG_RETURN(ret); } @@ -736,12 +746,15 @@ sp_update_procedure(THD *thd, sp_name *name, st_sp_chistics *chistics) { int ret; + bool found; DBUG_ENTER("sp_update_procedure"); DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - sp_cache_remove(&thd->sp_proc_cache, name); + found= sp_cache_remove(&thd->sp_proc_cache, name); ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name, newname, newnamelen, chistics); + if (!found && !ret) + sp_cache_invalidate(); DBUG_RETURN(ret); } @@ -813,11 +826,14 @@ int sp_drop_function(THD *thd, sp_name *name) { int ret; + bool found; DBUG_ENTER("sp_drop_function"); DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - sp_cache_remove(&thd->sp_func_cache, name); + found= sp_cache_remove(&thd->sp_func_cache, name); ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name); + if (!found && !ret) + sp_cache_invalidate(); DBUG_RETURN(ret); } @@ -828,12 +844,15 @@ sp_update_function(THD *thd, sp_name *name, st_sp_chistics *chistics) { int ret; + bool found; DBUG_ENTER("sp_update_procedure"); DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); - sp_cache_remove(&thd->sp_func_cache, name); + found= sp_cache_remove(&thd->sp_func_cache, name); ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name, newname, newnamelen, chistics); + if (!found && !ret) + sp_cache_invalidate(); DBUG_RETURN(ret); } @@ -941,6 +960,7 @@ sp_cache_functions(THD *thd, LEX *lex) LEX *newlex= new st_lex; thd->lex= newlex; + newlex->proc_table= oldlex->proc_table; // hint if mysql.oper is opened name.m_name.str= strchr(name.m_qname.str, '.'); name.m_db.length= name.m_name.str - name.m_qname.str; name.m_db.str= strmake_root(&thd->mem_root, diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 44357170ff5..3ff5f06103f 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -45,6 +45,55 @@ sp_map_result_type(enum enum_field_types type) } } +/* + * Returns TRUE if the 'cmd' is a command that might result in + * multiple result sets being sent back. + * Note: This does not include SQLCOM_SELECT which is treated + * separately in sql_yacc.yy. + */ +bool +sp_multi_results_command(enum enum_sql_command cmd) +{ + switch (cmd) { + case SQLCOM_ANALYZE: + case SQLCOM_CHECKSUM: + case SQLCOM_HA_READ: + case SQLCOM_SHOW_BINLOGS: + case SQLCOM_SHOW_BINLOG_EVENTS: + case SQLCOM_SHOW_CHARSETS: + case SQLCOM_SHOW_COLLATIONS: + case SQLCOM_SHOW_COLUMN_TYPES: + case SQLCOM_SHOW_CREATE: + case SQLCOM_SHOW_CREATE_DB: + case SQLCOM_SHOW_CREATE_FUNC: + case SQLCOM_SHOW_CREATE_PROC: + case SQLCOM_SHOW_DATABASES: + case SQLCOM_SHOW_ERRORS: + case SQLCOM_SHOW_FIELDS: + case SQLCOM_SHOW_GRANTS: + case SQLCOM_SHOW_INNODB_STATUS: + case SQLCOM_SHOW_KEYS: + case SQLCOM_SHOW_LOGS: + case SQLCOM_SHOW_MASTER_STAT: + case SQLCOM_SHOW_NEW_MASTER: + case SQLCOM_SHOW_OPEN_TABLES: + case SQLCOM_SHOW_PRIVILEGES: + case SQLCOM_SHOW_PROCESSLIST: + case SQLCOM_SHOW_SLAVE_HOSTS: + case SQLCOM_SHOW_SLAVE_STAT: + case SQLCOM_SHOW_STATUS: + case SQLCOM_SHOW_STATUS_FUNC: + case SQLCOM_SHOW_STATUS_PROC: + case SQLCOM_SHOW_STORAGE_ENGINES: + case SQLCOM_SHOW_TABLES: + case SQLCOM_SHOW_VARIABLES: + case SQLCOM_SHOW_WARNS: + return TRUE; + default: + return FALSE; + } +} + /* Evaluate a (presumed) func item. Always returns an item, the parameter ** if nothing else. */ @@ -58,7 +107,7 @@ sp_eval_func_item(THD *thd, Item *it, enum enum_field_types type) if (!it->fixed && it->fix_fields(thd, 0, &it)) { DBUG_PRINT("info", ("fix_fields() failed")); - DBUG_RETURN(it); // Shouldn't happen? + DBUG_RETURN(NULL); } /* QQ How do we do this? Is there some better way? */ @@ -209,7 +258,7 @@ sp_head::operator delete(void *ptr, size_t size) sp_head::sp_head() :Item_arena((bool)FALSE), m_returns_cs(NULL), m_has_return(FALSE), - m_simple_case(FALSE), m_multi_results(FALSE) + m_simple_case(FALSE), m_multi_results(FALSE), m_in_handler(FALSE) { DBUG_ENTER("sp_head::sp_head"); @@ -224,7 +273,7 @@ sp_head::init(LEX *lex) { DBUG_ENTER("sp_head::init"); - lex->spcont= m_pcont= new sp_pcontext(); + lex->spcont= m_pcont= new sp_pcontext(NULL); 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_qname.str= m_db.str= m_name.str= m_params.str= m_retstr.str= @@ -239,6 +288,7 @@ void sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) { DBUG_ENTER("sp_head::init_strings"); + uint n; /* Counter for nul trimming */ /* During parsing, we must use thd->mem_root */ MEM_ROOT *root= &thd->mem_root; @@ -314,9 +364,17 @@ sp_head::init_strings(THD *thd, LEX *lex, sp_name *name) (char *)m_returns_begin, m_retstr.length); } } - m_body.length= lex->end_of_query - m_body_begin; + m_body.length= lex->ptr - m_body_begin; + /* Trim nuls at the end */ + n= 0; + while (m_body.length && m_body_begin[m_body.length-1] == '\0') + { + m_body.length-= 1; + n+= 1; + } m_body.str= strmake_root(root, (char *)m_body_begin, m_body.length); - m_defstr.length= lex->end_of_query - lex->buf; + m_defstr.length= lex->ptr - lex->buf; + m_defstr.length-= n; m_defstr.str= strmake_root(root, (char *)lex->buf, m_defstr.length); DBUG_VOID_RETURN; } @@ -329,22 +387,26 @@ sp_head::create(THD *thd) DBUG_PRINT("info", ("type: %d name: %s params: %s body: %s", m_type, m_name.str, m_params.str, m_body.str)); + #ifndef DBUG_OFF - String s; - sp_instr *i; - uint ip= 0; - while ((i = get_instr(ip))) + optimize(); { - char buf[8]; + String s; + sp_instr *i; + uint ip= 0; + while ((i = get_instr(ip))) + { + char buf[8]; - sprintf(buf, "%4u: ", ip); - s.append(buf); - i->print(&s); - s.append('\n'); - ip+= 1; + sprintf(buf, "%4u: ", ip); + s.append(buf); + i->print(&s); + s.append('\n'); + ip+= 1; + } + s.append('\0'); + DBUG_PRINT("info", ("Code %s\n%s", m_qname.str, s.ptr())); } - s.append('\0'); - DBUG_PRINT("info", ("Code %s\n%s", m_qname.str, s.ptr())); #endif if (m_type == TYPE_ENUM_FUNCTION) @@ -389,9 +451,11 @@ sp_head::execute(THD *thd) DBUG_ENTER("sp_head::execute"); char olddb[128]; bool dbchanged; - sp_rcontext *ctx= thd->spcont; + sp_rcontext *ctx; int ret= 0; uint ip= 0; + Item_arena *old_arena; + #ifndef EMBEDDED_LIBRARY if (check_stack_overrun(thd, olddb)) @@ -404,10 +468,12 @@ sp_head::execute(THD *thd) if ((ret= sp_use_new_db(thd, m_db.str, olddb, sizeof(olddb), 0, &dbchanged))) goto done; - if (ctx) + if ((ctx= thd->spcont)) ctx->clear_handler(); thd->query_error= 0; + old_arena= thd->current_arena; thd->current_arena= this; + do { sp_instr *i; @@ -442,18 +508,17 @@ sp_head::execute(THD *thd) continue; } } - } while (ret == 0 && !thd->killed && !thd->query_error && - !thd->net.report_error); + } while (ret == 0 && !thd->killed && !thd->query_error); + + if (thd->current_arena) + cleanup_items(thd->current_arena->free_list); + thd->current_arena= old_arena; done: DBUG_PRINT("info", ("ret=%d killed=%d query_error=%d", ret, thd->killed, thd->query_error)); - if (thd->current_arena) - cleanup_items(thd->current_arena->free_list); - thd->current_arena= 0; - - if (thd->killed || thd->query_error || thd->net.report_error) + if (thd->killed || thd->query_error) ret= -1; /* If the DB has changed, the pointer has changed too, but the original thd->db will then have been freed */ @@ -471,10 +536,10 @@ 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(); + uint csize = m_pcont->max_pvars(); + uint params = m_pcont->current_pvars(); + uint hmax = m_pcont->max_handlers(); + uint cmax = m_pcont->max_cursors(); sp_rcontext *octx = thd->spcont; sp_rcontext *nctx = NULL; uint i; @@ -494,8 +559,14 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) for (i= 0 ; i < params && i < argcount ; i++) { sp_pvar_t *pvar = m_pcont->find_pvar(i); + Item *it= sp_eval_func_item(thd, *argp++, pvar->type); - nctx->push_item(sp_eval_func_item(thd, *argp++, pvar->type)); + if (it) + nctx->push_item(it); + else + { + DBUG_RETURN(-1); + } } #ifdef NOT_WORKING /* @@ -546,11 +617,11 @@ 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; - uint csize = m_pcont->max_framesize(); - uint params = m_pcont->params(); - uint hmax = m_pcont->handlers(); - uint cmax = m_pcont->cursors(); + int ret= 0; + uint csize = m_pcont->max_pvars(); + uint params = m_pcont->current_pvars(); + uint hmax = m_pcont->max_handlers(); + uint cmax = m_pcont->max_cursors(); sp_rcontext *octx = thd->spcont; sp_rcontext *nctx = NULL; my_bool tmp_octx = FALSE; // True if we have allocated a temporary octx @@ -591,7 +662,17 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) nctx->push_item(nit); // OUT } else - nctx->push_item(sp_eval_func_item(thd, it,pvar->type)); // IN or INOUT + { + Item *it2= sp_eval_func_item(thd, it,pvar->type); + + if (it2) + nctx->push_item(it2); // IN or INOUT + else + { + ret= -1; // Eval failed + break; + } + } // 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? @@ -616,7 +697,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) thd->spcont= nctx; } - ret= execute(thd); + if (! ret) + ret= execute(thd); // Don't copy back OUT values if we got an error if (ret) @@ -687,20 +769,21 @@ sp_head::reset_lex(THD *thd) (void)m_lex.push_front(oldlex); thd->lex= sublex= new st_lex; + /* Reset most stuff. The length arguments doesn't matter here. */ - lex_start(thd, oldlex->buf, oldlex->end_of_query - oldlex->ptr); - sublex->yylineno= oldlex->yylineno; + mysql_init_query(thd,oldlex->buf, oldlex->end_of_query - oldlex->ptr, TRUE); + /* 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; + sublex->yylineno= oldlex->yylineno; /* And keep the SP stuff too */ sublex->sphead= oldlex->sphead; sublex->spcont= oldlex->spcont; /* And trigger related stuff too */ sublex->trg_chistics= oldlex->trg_chistics; sublex->trg_table= oldlex->trg_table; - mysql_init_query(thd, true); // Only init lex sublex->sp_lex_in_use= FALSE; DBUG_VOID_RETURN; } @@ -790,12 +873,42 @@ sp_head::backpatch(sp_label_t *lab) List_iterator_fast<bp_t> li(m_backpatch); while ((bp= li++)) - if (bp->lab == lab) + { + if (bp->lab == lab || + (bp->lab->type == SP_LAB_REF && + my_strcasecmp(system_charset_info, bp->lab->name, lab->name) == 0)) { - sp_instr_jump *i= static_cast<sp_instr_jump *>(bp->instr); + if (bp->lab->type != SP_LAB_REF) + bp->instr->backpatch(dest, lab->ctx); + else + { + sp_label_t *dstlab= bp->lab->ctx->find_label(lab->name); + + if (dstlab) + { + bp->lab= lab; + bp->instr->backpatch(dest, dstlab->ctx); + } + } + } + } +} - i->set_destination(dest); +int +sp_head::check_backpatch(THD *thd) +{ + bp_t *bp; + List_iterator_fast<bp_t> li(m_backpatch); + + while ((bp= li++)) + { + if (bp->lab->type == SP_LAB_REF) + { + net_printf(thd, ER_SP_LILABEL_MISMATCH, "GOTO", bp->lab->name); + return -1; } + } + return 0; } void @@ -875,7 +988,9 @@ sp_head::show_create_procedure(THD *thd) DBUG_ENTER("sp_head::show_create_procedure"); DBUG_PRINT("info", ("procedure %s", m_name.str)); - + LINT_INIT(sql_mode_str); + LINT_INIT(sql_mode_len); + old_sql_mode= thd->variables.sql_mode; thd->variables.sql_mode= m_sql_mode; sql_mode_var= find_sys_var("SQL_MODE", 8); @@ -891,7 +1006,8 @@ sp_head::show_create_procedure(THD *thd) // 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)) + if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | + Protocol::SEND_EOF)) { res= 1; goto done; @@ -940,6 +1056,8 @@ sp_head::show_create_function(THD *thd) ulong sql_mode_len; DBUG_ENTER("sp_head::show_create_function"); DBUG_PRINT("info", ("procedure %s", m_name.str)); + LINT_INIT(sql_mode_str); + LINT_INIT(sql_mode_len); old_sql_mode= thd->variables.sql_mode; thd->variables.sql_mode= m_sql_mode; @@ -955,7 +1073,8 @@ sp_head::show_create_function(THD *thd) field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len)); field_list.push_back(new Item_empty_string("Create Function", max(buffer.length(),1024))); - if (protocol->send_fields(&field_list, 1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) { res= 1; goto done; @@ -972,6 +1091,58 @@ sp_head::show_create_function(THD *thd) thd->variables.sql_mode= old_sql_mode; DBUG_RETURN(res); } + +void +sp_head::optimize() +{ + List<sp_instr> bp; + sp_instr *i; + uint src, dst; + + opt_mark(0); + + bp.empty(); + src= dst= 0; + while ((i= get_instr(src))) + { + if (! i->marked) + { + delete i; + src+= 1; + } + else + { + if (src != dst) + { + sp_instr *ibp; + List_iterator_fast<sp_instr> li(bp); + + set_dynamic(&m_instr, (gptr)&i, dst); + while ((ibp= li++)) + { + sp_instr_jump *ji= static_cast<sp_instr_jump *>(ibp); + if (ji->m_dest == src) + ji->m_dest= dst; + } + } + i->opt_move(dst, &bp); + src+= 1; + dst+= 1; + } + } + m_instr.elements= dst; + bp.empty(); +} + +void +sp_head::opt_mark(uint ip) +{ + sp_instr *i; + + while ((i= get_instr(ip)) && !i->marked) + ip= i->opt_mark(this); +} + // ------------------------------------------------------------------ // @@ -1042,9 +1213,26 @@ 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)); + Item *it; + int res; + + if (tables && + ((res= check_table_access(thd, SELECT_ACL, tables, 0)) || + (res= open_and_lock_tables(thd, tables)))) + DBUG_RETURN(res); + + it= sp_eval_func_item(thd, m_value, m_type); + if (! it) + res= -1; + else + { + res= 0; + thd->spcont->set_item(m_offset, it); + } *nextp = m_ip+1; - DBUG_RETURN(0); + if (thd->lock || thd->open_tables || thd->derived_tables) + close_thread_tables(thd); + DBUG_RETURN(res); } void @@ -1132,6 +1320,46 @@ sp_instr_jump::print(String *str) str->qs_append(m_dest); } +uint +sp_instr_jump::opt_mark(sp_head *sp) +{ + m_dest= opt_shortcut_jump(sp, this); + if (m_dest != m_ip+1) /* Jumping to following instruction? */ + marked= 1; + m_optdest= sp->get_instr(m_dest); + return m_dest; +} + +uint +sp_instr_jump::opt_shortcut_jump(sp_head *sp, sp_instr *start) +{ + uint dest= m_dest; + sp_instr *i; + + while ((i= sp->get_instr(dest))) + { + uint ndest; + + if (start == i) + break; + ndest= i->opt_shortcut_jump(sp, start); + if (ndest == dest) + break; + dest= ndest; + } + return dest; +} + +void +sp_instr_jump::opt_move(uint dst, List<sp_instr> *bp) +{ + if (m_dest > m_ip) + bp->push_back(this); // Forward + else if (m_optdest) + m_dest= m_optdest->m_ip; // Backward + m_ip= dst; +} + // // sp_instr_jump_if // @@ -1140,13 +1368,28 @@ 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); + Item *it; + int res; - if (it->val_int()) - *nextp = m_dest; + if (tables && + ((res= check_table_access(thd, SELECT_ACL, tables, 0)) || + (res= open_and_lock_tables(thd, tables)))) + DBUG_RETURN(res); + + it= sp_eval_func_item(thd, m_expr, MYSQL_TYPE_TINY); + if (!it) + res= -1; else - *nextp = m_ip+1; - DBUG_RETURN(0); + { + res= 0; + if (it->val_int()) + *nextp = m_dest; + else + *nextp = m_ip+1; + } + if (thd->lock || thd->open_tables || thd->derived_tables) + close_thread_tables(thd); + DBUG_RETURN(res); } void @@ -1159,6 +1402,21 @@ sp_instr_jump_if::print(String *str) m_expr->print(str); } +uint +sp_instr_jump_if::opt_mark(sp_head *sp) +{ + sp_instr *i; + + marked= 1; + if ((i= sp->get_instr(m_dest))) + { + m_dest= i->opt_shortcut_jump(sp, this); + m_optdest= sp->get_instr(m_dest); + } + sp->opt_mark(m_dest); + return m_ip+1; +} + // // sp_instr_jump_if_not // @@ -1167,13 +1425,28 @@ 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); + Item *it; + int res; + + if (tables && + ((res= check_table_access(thd, SELECT_ACL, tables, 0)) || + (res= open_and_lock_tables(thd, tables)))) + DBUG_RETURN(res); - if (! it->val_int()) - *nextp = m_dest; + it= sp_eval_func_item(thd, m_expr, MYSQL_TYPE_TINY); + if (! it) + res= -1; else - *nextp = m_ip+1; - DBUG_RETURN(0); + { + res= 0; + if (! it->val_int()) + *nextp = m_dest; + else + *nextp = m_ip+1; + } + if (thd->lock || thd->open_tables || thd->derived_tables) + close_thread_tables(thd); + DBUG_RETURN(res); } void @@ -1186,6 +1459,21 @@ sp_instr_jump_if_not::print(String *str) m_expr->print(str); } +uint +sp_instr_jump_if_not::opt_mark(sp_head *sp) +{ + sp_instr *i; + + marked= 1; + if ((i= sp->get_instr(m_dest))) + { + m_dest= i->opt_shortcut_jump(sp, this); + m_optdest= sp->get_instr(m_dest); + } + sp->opt_mark(m_dest); + return m_ip+1; +} + // // sp_instr_freturn // @@ -1193,9 +1481,24 @@ 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)); + Item *it; + int res; + + if (tables && + ((res= check_table_access(thd, SELECT_ACL, tables, 0)) || + (res= open_and_lock_tables(thd, tables)))) + DBUG_RETURN(res); + + it= sp_eval_func_item(thd, m_value, m_type); + if (! it) + res= -1; + else + { + res= 0; + thd->spcont->set_result(it); + } *nextp= UINT_MAX; - DBUG_RETURN(0); + DBUG_RETURN(res); } void @@ -1239,6 +1542,21 @@ sp_instr_hpush_jump::print(String *str) str->qs_append(m_handler); } +uint +sp_instr_hpush_jump::opt_mark(sp_head *sp) +{ + sp_instr *i; + + marked= 1; + if ((i= sp->get_instr(m_dest))) + { + m_dest= i->opt_shortcut_jump(sp, this); + m_optdest= sp->get_instr(m_dest); + } + sp->opt_mark(m_dest); + return m_ip+1; +} + // // sp_instr_hpop // @@ -1259,6 +1577,13 @@ sp_instr_hpop::print(String *str) str->qs_append(m_count); } +void +sp_instr_hpop::backpatch(uint dest, sp_pcontext *dst_ctx) +{ + m_count= m_ctx->diff_handlers(dst_ctx); +} + + // // sp_instr_hreturn // @@ -1323,6 +1648,12 @@ sp_instr_cpop::print(String *str) str->qs_append(m_count); } +void +sp_instr_cpop::backpatch(uint dest, sp_pcontext *dst_ctx) +{ + m_count= m_ctx->diff_cursors(dst_ctx); +} + // // sp_instr_copen // diff --git a/sql/sp_head.h b/sql/sp_head.h index 1622d197d81..a253d9edcbe 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -33,6 +33,9 @@ Item_result sp_map_result_type(enum enum_field_types type); +bool +sp_multi_results_command(enum enum_sql_command cmd); + struct sp_label; class sp_instr; struct sp_cond_type; @@ -84,6 +87,7 @@ public: 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) + my_bool m_in_handler; // TRUE if parser in a handler body uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value st_sp_chistics *m_chistics; ulong m_sql_mode; // For SHOW CREATE @@ -180,6 +184,13 @@ public: void backpatch(struct sp_label *); + // Check that no unresolved references exist. + // If none found, 0 is returned, otherwise errors have been issued + // and -1 is returned. + // This is called by the parser at the end of a create procedure/function. + int + check_backpatch(THD *thd); + char *name(uint *lenp = 0) const { if (lenp) @@ -202,6 +213,20 @@ public: void restore_thd_mem_root(THD *thd); + void optimize(); + void opt_mark(uint ip); + + 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; + } private: @@ -219,18 +244,6 @@ private: } 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); @@ -248,11 +261,14 @@ class sp_instr : public Sql_alloc public: + uint marked; Item *free_list; // My Items + uint m_ip; // My index + sp_pcontext *m_ctx; // My parse context // Should give each a name or type code for debugging purposes? - sp_instr(uint ip) - :Sql_alloc(), free_list(0), m_ip(ip) + sp_instr(uint ip, sp_pcontext *ctx) + :Sql_alloc(), marked(0), free_list(0), m_ip(ip), m_ctx(ctx) {} virtual ~sp_instr() @@ -266,9 +282,24 @@ public: virtual void print(String *str) = 0; -protected: + virtual void backpatch(uint dest, sp_pcontext *dst_ctx) + {} - uint m_ip; // My index + virtual uint opt_mark(sp_head *sp) + { + marked= 1; + return m_ip+1; + } + + virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start) + { + return m_ip; + } + + virtual void opt_move(uint dst, List<sp_instr> *ibp) + { + m_ip= dst; + } }; // class sp_instr : public Sql_alloc @@ -283,8 +314,8 @@ class sp_instr_stmt : public sp_instr public: - sp_instr_stmt(uint ip) - : sp_instr(ip), m_lex(NULL) + sp_instr_stmt(uint ip, sp_pcontext *ctx) + : sp_instr(ip, ctx), m_lex(NULL) {} virtual ~sp_instr_stmt(); @@ -323,8 +354,12 @@ class sp_instr_set : public sp_instr 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) + TABLE_LIST *tables; + + sp_instr_set(uint ip, sp_pcontext *ctx, + uint offset, Item *val, enum enum_field_types type) + : sp_instr(ip, ctx), + tables(NULL), m_offset(offset), m_value(val), m_type(type) {} virtual ~sp_instr_set() @@ -415,12 +450,14 @@ class sp_instr_jump : public sp_instr public: - sp_instr_jump(uint ip) - : sp_instr(ip), m_dest(0) + uint m_dest; // Where we will go + + sp_instr_jump(uint ip, sp_pcontext *ctx) + : sp_instr(ip, ctx), m_dest(0), m_optdest(0) {} - sp_instr_jump(uint ip, uint dest) - : sp_instr(ip), m_dest(dest) + sp_instr_jump(uint ip, sp_pcontext *ctx, uint dest) + : sp_instr(ip, ctx), m_dest(dest), m_optdest(0) {} virtual ~sp_instr_jump() @@ -430,8 +467,13 @@ public: virtual void print(String *str); - virtual void - set_destination(uint dest) + virtual uint opt_mark(sp_head *sp); + + virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start); + + virtual void opt_move(uint dst, List<sp_instr> *ibp); + + virtual void backpatch(uint dest, sp_pcontext *dst_ctx) { if (m_dest == 0) // Don't reset m_dest= dest; @@ -439,7 +481,7 @@ public: protected: - int m_dest; // Where we will go + sp_instr *m_optdest; // Used during optimization }; // class sp_instr_jump : public sp_instr @@ -451,12 +493,14 @@ class sp_instr_jump_if : public sp_instr_jump public: - sp_instr_jump_if(uint ip, Item *i) - : sp_instr_jump(ip), m_expr(i) + TABLE_LIST *tables; + + sp_instr_jump_if(uint ip, sp_pcontext *ctx, Item *i) + : sp_instr_jump(ip, ctx), tables(NULL), m_expr(i) {} - sp_instr_jump_if(uint ip, Item *i, uint dest) - : sp_instr_jump(ip, dest), m_expr(i) + sp_instr_jump_if(uint ip, sp_pcontext *ctx, Item *i, uint dest) + : sp_instr_jump(ip, ctx, dest), tables(NULL), m_expr(i) {} virtual ~sp_instr_jump_if() @@ -466,6 +510,13 @@ public: virtual void print(String *str); + virtual uint opt_mark(sp_head *sp); + + virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start) + { + return m_ip; + } + private: Item *m_expr; // The condition @@ -480,12 +531,14 @@ class sp_instr_jump_if_not : public sp_instr_jump public: - sp_instr_jump_if_not(uint ip, Item *i) - : sp_instr_jump(ip), m_expr(i) + TABLE_LIST *tables; + + sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i) + : sp_instr_jump(ip, ctx), tables(NULL), m_expr(i) {} - sp_instr_jump_if_not(uint ip, Item *i, uint dest) - : sp_instr_jump(ip, dest), m_expr(i) + sp_instr_jump_if_not(uint ip, sp_pcontext *ctx, Item *i, uint dest) + : sp_instr_jump(ip, ctx, dest), tables(NULL), m_expr(i) {} virtual ~sp_instr_jump_if_not() @@ -495,6 +548,13 @@ public: virtual void print(String *str); + virtual uint opt_mark(sp_head *sp); + + virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start) + { + return m_ip; + } + private: Item *m_expr; // The condition @@ -509,8 +569,11 @@ class sp_instr_freturn : public sp_instr public: - sp_instr_freturn(uint ip, Item *val, enum enum_field_types type) - : sp_instr(ip), m_value(val), m_type(type) + TABLE_LIST *tables; + + sp_instr_freturn(uint ip, sp_pcontext *ctx, + Item *val, enum enum_field_types type) + : sp_instr(ip, ctx), tables(NULL), m_value(val), m_type(type) {} virtual ~sp_instr_freturn() @@ -520,6 +583,12 @@ public: virtual void print(String *str); + virtual uint opt_mark(sp_head *sp) + { + marked= 1; + return UINT_MAX; + } + protected: Item *m_value; @@ -535,8 +604,8 @@ class sp_instr_hpush_jump : public sp_instr_jump public: - sp_instr_hpush_jump(uint ip, int htype, uint fp) - : sp_instr_jump(ip), m_type(htype), m_frame(fp) + sp_instr_hpush_jump(uint ip, sp_pcontext *ctx, int htype, uint fp) + : sp_instr_jump(ip, ctx), m_type(htype), m_frame(fp) { m_handler= ip+1; m_cond.empty(); @@ -551,6 +620,13 @@ public: virtual void print(String *str); + virtual uint opt_mark(sp_head *sp); + + virtual uint opt_shortcut_jump(sp_head *sp, sp_instr *start) + { + return m_ip; + } + inline void add_condition(struct sp_cond_type *cond) { m_cond.push_front(cond); @@ -573,8 +649,8 @@ class sp_instr_hpop : public sp_instr public: - sp_instr_hpop(uint ip, uint count) - : sp_instr(ip), m_count(count) + sp_instr_hpop(uint ip, sp_pcontext *ctx, uint count) + : sp_instr(ip, ctx), m_count(count) {} virtual ~sp_instr_hpop() @@ -584,6 +660,15 @@ public: virtual void print(String *str); + virtual void backpatch(uint dest, sp_pcontext *dst_ctx); + + virtual uint opt_mark(sp_head *sp) + { + if (m_count) + marked= 1; + return m_ip+1; + } + private: uint m_count; @@ -598,8 +683,8 @@ class sp_instr_hreturn : public sp_instr public: - sp_instr_hreturn(uint ip, uint fp) - : sp_instr(ip), m_frame(fp) + sp_instr_hreturn(uint ip, sp_pcontext *ctx, uint fp) + : sp_instr(ip, ctx), m_frame(fp) {} virtual ~sp_instr_hreturn() @@ -609,6 +694,12 @@ public: virtual void print(String *str); + virtual uint opt_mark(sp_head *sp) + { + marked= 1; + return UINT_MAX; + } + private: uint m_frame; @@ -623,8 +714,8 @@ class sp_instr_cpush : public sp_instr public: - sp_instr_cpush(uint ip, LEX *lex) - : sp_instr(ip), m_lex(lex) + sp_instr_cpush(uint ip, sp_pcontext *ctx, LEX *lex) + : sp_instr(ip, ctx), m_lex(lex) {} virtual ~sp_instr_cpush(); @@ -647,8 +738,8 @@ class sp_instr_cpop : public sp_instr public: - sp_instr_cpop(uint ip, uint count) - : sp_instr(ip), m_count(count) + sp_instr_cpop(uint ip, sp_pcontext *ctx, uint count) + : sp_instr(ip, ctx), m_count(count) {} virtual ~sp_instr_cpop() @@ -658,6 +749,15 @@ public: virtual void print(String *str); + virtual void backpatch(uint dest, sp_pcontext *dst_ctx); + + virtual uint opt_mark(sp_head *sp) + { + if (m_count) + marked= 1; + return m_ip+1; + } + private: uint m_count; @@ -672,8 +772,8 @@ class sp_instr_copen : public sp_instr_stmt public: - sp_instr_copen(uint ip, uint c) - : sp_instr_stmt(ip), m_cursor(c) + sp_instr_copen(uint ip, sp_pcontext *ctx, uint c) + : sp_instr_stmt(ip, ctx), m_cursor(c) {} virtual ~sp_instr_copen() @@ -697,8 +797,8 @@ class sp_instr_cclose : public sp_instr public: - sp_instr_cclose(uint ip, uint c) - : sp_instr(ip), m_cursor(c) + sp_instr_cclose(uint ip, sp_pcontext *ctx, uint c) + : sp_instr(ip, ctx), m_cursor(c) {} virtual ~sp_instr_cclose() @@ -722,8 +822,8 @@ class sp_instr_cfetch : public sp_instr public: - sp_instr_cfetch(uint ip, uint c) - : sp_instr(ip), m_cursor(c) + sp_instr_cfetch(uint ip, sp_pcontext *ctx, uint c) + : sp_instr(ip, ctx), m_cursor(c) { m_varlist.empty(); } @@ -755,8 +855,8 @@ class sp_instr_error : public sp_instr public: - sp_instr_error(uint ip, int errcode) - : sp_instr(ip), m_errcode(errcode) + sp_instr_error(uint ip, sp_pcontext *ctx, int errcode) + : sp_instr(ip, ctx), m_errcode(errcode) {} virtual ~sp_instr_error() @@ -766,6 +866,12 @@ public: virtual void print(String *str); + virtual uint opt_mark(sp_head *sp) + { + marked= 1; + return UINT_MAX; + } + private: int m_errcode; diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index 03333a2da72..3b60f0936f9 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -26,43 +26,94 @@ #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) +sp_pcontext::sp_pcontext(sp_pcontext *prev) + : Sql_alloc(), m_psubsize(0), m_csubsize(0), m_hsubsize(0), + m_handlers(0), m_parent(prev) { 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(); + m_children.empty(); + if (!prev) + m_poffset= m_coffset= 0; + else + { + m_poffset= prev->current_pvars(); + m_coffset= prev->current_cursors(); + } } void sp_pcontext::destroy() { + List_iterator_fast<sp_pcontext> li(m_children); + sp_pcontext *child; + + while ((child= li++)) + child->destroy(); + + m_children.empty(); + m_label.empty(); 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_pcontext * +sp_pcontext::push_context() { - sp_scope_t s; + sp_pcontext *child= new sp_pcontext(this); - s.vars= m_pvar.elements; - s.conds= m_cond.elements; - s.curs= m_cursor.elements; - insert_dynamic(&m_scopes, (gptr)&s); + if (child) + m_children.push_back(child); + return child; } -void -sp_pcontext::pop_scope() +sp_pcontext * +sp_pcontext::pop_context() { - (void)pop_dynamic(&m_scopes); + uint submax= max_pvars(); + + if (submax > m_parent->m_psubsize) + m_parent->m_psubsize= submax; + submax= max_handlers(); + if (submax > m_parent->m_hsubsize) + m_parent->m_hsubsize= submax; + submax= max_cursors(); + if (submax > m_parent->m_csubsize) + m_parent->m_csubsize= submax; + return m_parent; } +uint +sp_pcontext::diff_handlers(sp_pcontext *ctx) +{ + uint n= 0; + sp_pcontext *pctx= this; + + while (pctx && pctx != ctx) + { + n+= pctx->m_handlers; + pctx= pctx->parent_context(); + } + if (pctx) + return n; + return 0; // Didn't find ctx +} + +uint +sp_pcontext::diff_cursors(sp_pcontext *ctx) +{ + uint n= 0; + sp_pcontext *pctx= this; + + while (pctx && pctx != ctx) + pctx= pctx->parent_context(); + if (pctx) + return ctx->current_cursors() - pctx->current_cursors(); + return 0; // Didn't find ctx +} /* This does a linear search (from newer to older variables, in case ** we have shadowed names). @@ -74,20 +125,9 @@ sp_pcontext::pop_scope() sp_pvar_t * sp_pcontext::find_pvar(LEX_STRING *name, my_bool scoped) { - uint i = m_pvar.elements; - uint limit; + uint i= m_pvar.elements; - 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) + while (i--) { sp_pvar_t *p; @@ -99,6 +139,8 @@ sp_pcontext::find_pvar(LEX_STRING *name, my_bool scoped) return p; } } + if (!scoped && m_parent) + return m_parent->find_pvar(name, scoped); return NULL; } @@ -110,13 +152,13 @@ sp_pcontext::push_pvar(LEX_STRING *name, enum enum_field_types type, if (p) { - if (m_pvar.elements == m_framesize) - m_framesize += 1; + if (m_pvar.elements == m_psubsize) + m_psubsize+= 1; p->name.str= name->str; p->name.length= name->length; p->type= type; p->mode= mode; - p->offset= m_pvar.elements; + p->offset= current_pvars(); p->isset= (mode == sp_param_out ? FALSE : TRUE); p->dflt= NULL; insert_dynamic(&m_pvar, (gptr)&p); @@ -132,7 +174,8 @@ sp_pcontext::push_label(char *name, uint ip) { lab->name= name; lab->ip= ip; - lab->isbegin= FALSE; + lab->type= SP_LAB_GOTO; + lab->ctx= this; m_label.push_front(lab); } return lab; @@ -148,6 +191,8 @@ sp_pcontext::find_label(char *name) if (my_strcasecmp(system_charset_info, name, lab->name) == 0) return lab; + if (m_parent) + return m_parent->find_label(name); return NULL; } @@ -171,20 +216,9 @@ sp_pcontext::push_cond(LEX_STRING *name, sp_cond_type_t *val) 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; + uint i= m_cond.elements; - get_dynamic(&m_scopes, (gptr)&s, m_scopes.elements-1); - limit= s.conds; - } - - while (i-- > limit) + while (i--) { sp_cond_t *p; @@ -196,6 +230,8 @@ sp_pcontext::find_cond(LEX_STRING *name, my_bool scoped) return p->val; } } + if (!scoped && m_parent) + return m_parent->find_cond(name, scoped); return NULL; } @@ -204,11 +240,11 @@ sp_pcontext::push_cursor(LEX_STRING *name) { LEX_STRING n; + if (m_cursor.elements == m_csubsize) + m_csubsize+= 1; 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; } /* @@ -217,20 +253,9 @@ sp_pcontext::push_cursor(LEX_STRING *name) my_bool sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped) { - uint i = m_cursor.elements; - uint limit; + uint i= m_cursor.elements; - 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) + while (i--) { LEX_STRING n; @@ -243,5 +268,7 @@ sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped) return TRUE; } } + if (!scoped && m_parent) + return m_parent->find_cursor(name, poff, scoped); return FALSE; } diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index a82cefa2e42..36e4ed06aa7 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -39,11 +39,18 @@ typedef struct sp_pvar Item *dflt; } sp_pvar_t; + +#define SP_LAB_REF 0 // Unresolved reference (for goto) +#define SP_LAB_GOTO 1 // Free goto label +#define SP_LAB_BEGIN 2 // Label at BEGIN +#define SP_LAB_ITER 3 // Label at iteration control + typedef struct sp_label { char *name; uint ip; // Instruction index - my_bool isbegin; // For ITERATE error checking + int type; // begin/iter or ref/free + struct sp_pcontext *ctx; // The label's context } sp_label_t; typedef struct sp_cond_type @@ -59,11 +66,6 @@ typedef struct sp_cond 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 */ @@ -71,46 +73,58 @@ class sp_pcontext : public Sql_alloc public: - sp_pcontext(); + sp_pcontext(sp_pcontext *prev); // Free memory void destroy(); - // For error checking of duplicate things - void - push_scope(); + sp_pcontext * + push_context(); + + // Returns the previous context, not the one we pop + sp_pcontext * + pop_context(); + + sp_pcontext * + parent_context() + { + return m_parent; + } + + uint + diff_handlers(sp_pcontext *ctx); + + uint + diff_cursors(sp_pcontext *ctx); - void - pop_scope(); // // Parameters and variables // inline uint - max_framesize() + max_pvars() { - return m_framesize; + return m_psubsize + m_pvar.elements; } inline uint - current_framesize() + current_pvars() { - return m_pvar.elements; + return m_poffset + m_pvar.elements; } inline uint - params() + context_pvars() { - return m_params; + return m_pvar.elements; } - // Set the number of parameters to the current esize - inline void - set_params() + inline uint + pvar_context2index(uint i) { - m_params= m_pvar.elements; + return m_poffset + i; } inline void @@ -181,7 +195,11 @@ class sp_pcontext : public Sql_alloc inline sp_label_t * last_label() { - return m_label.head(); + sp_label_t *lab= m_label.head(); + + if (!lab && m_parent) + lab= m_parent->last_label(); + return lab; } inline sp_label_t * @@ -218,9 +236,15 @@ class sp_pcontext : public Sql_alloc } inline uint - handlers() + max_handlers() + { + return m_hsubsize + m_handlers; + } + + inline void + push_handlers(uint n) { - return m_handlers; + m_handlers+= n; } // @@ -233,33 +257,41 @@ class sp_pcontext : public Sql_alloc my_bool find_cursor(LEX_STRING *name, uint *poff, my_bool scoped=0); - inline void - pop_cursor(uint num) + inline uint + max_cursors() { - while (num--) - pop_dynamic(&m_cursor); + return m_csubsize + m_cursor.elements; } inline uint - cursors() + current_cursors() { - return m_cursmax; + return m_coffset + m_cursor.elements; } +protected: + + // The maximum sub context's framesizes + uint m_psubsize; + uint m_csubsize; + uint m_hsubsize; + uint m_handlers; // No. of handlers in this context + 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 + sp_pcontext *m_parent; // Parent context + + uint m_poffset; // Variable offset for this context + uint m_coffset; // Cursor offset for this context 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 + List<sp_pcontext> m_children; // Children contexts, used for destruction + }; // class sp_pcontext : public Sql_alloc diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 0b2b20fe3b3..7fa44f217f6 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -40,12 +40,19 @@ sp_rcontext::sp_rcontext(uint fsize, uint hmax, uint cmax) m_saved.empty(); } -void +int 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); + Item *it= sp_eval_func_item(current_thd, i, type); - set_item(idx, sp_eval_func_item(current_thd, i, type)); + if (! it) + return -1; + else + { + set_item(idx, it); + return 0; + } } int diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 1b83d3518b6..15a2fe62138 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -69,7 +69,8 @@ class sp_rcontext : public Sql_alloc m_frame[idx] = i; } - void + /* Returns 0 on success, -1 on (eval) failure */ + int set_item_eval(uint idx, Item *i, enum_field_types type); inline Item * diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 10a12ef6d04..0a970640e32 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -36,8 +36,6 @@ #ifndef NO_EMBEDDED_ACCESS_CHECKS -#define FIRST_NON_YN_FIELD 26 - class acl_entry :public hash_filo_element { public: @@ -66,7 +64,7 @@ static DYNAMIC_ARRAY acl_wild_hosts; static hash_filo *acl_cache; static uint grant_version=0; static uint priv_version=0; /* Version of priv tables. incremented by acl_init */ -static ulong get_access(TABLE *form,uint fieldnr); +static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0); static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b); static ulong get_sort(uint count,...); static void init_check_host(void); @@ -301,18 +299,15 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) } else // password is correct { - user.access= get_access(table,3) & GLOBAL_ACLS; + uint next_field; + user.access= get_access(table,3,&next_field) & GLOBAL_ACLS; user.sort= get_sort(2,user.host.hostname,user.user); user.hostname_length= (user.host.hostname ? (uint) strlen(user.host.hostname) : 0); if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */ { - uint base_field= 24; - if (table->fields > 31) /* Starting from 5.1 we have more privileges */ - base_field= 26; - - char *ssl_type=get_field(&mem, table->field[base_field]); + char *ssl_type=get_field(&mem, table->field[next_field++]); if (!ssl_type) user.ssl_type=SSL_TYPE_NONE; else if (!strcmp(ssl_type, "ANY")) @@ -322,16 +317,16 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) else /* !strcmp(ssl_type, "SPECIFIED") */ user.ssl_type=SSL_TYPE_SPECIFIED; - user.ssl_cipher= get_field(&mem, table->field[base_field+1]); - user.x509_issuer= get_field(&mem, table->field[base_field+2]); - user.x509_subject= get_field(&mem, table->field[base_field+3]); + user.ssl_cipher= get_field(&mem, table->field[next_field++]); + user.x509_issuer= get_field(&mem, table->field[next_field++]); + user.x509_subject= get_field(&mem, table->field[next_field++]); - char *ptr = get_field(&mem, table->field[base_field+4]); - user.user_resource.questions=atoi(ptr); - ptr = get_field(&mem, table->field[base_field+5]); - user.user_resource.updates=atoi(ptr); - ptr = get_field(&mem, table->field[base_field+6]); - user.user_resource.connections=atoi(ptr); + char *ptr = get_field(&mem, table->field[next_field++]); + user.user_resource.questions=ptr ? atoi(ptr) : 0; + ptr = get_field(&mem, table->field[next_field++]); + user.user_resource.updates=ptr ? atoi(ptr) : 0; + ptr = get_field(&mem, table->field[next_field++]); + user.user_resource.connections=ptr ? atoi(ptr) : 0; if (user.user_resource.questions || user.user_resource.updates || user.user_resource.connections) mqh_used=1; @@ -496,11 +491,24 @@ void acl_reload(THD *thd) /* Get all access bits from table after fieldnr + + IMPLEMENTATION We know that the access privileges ends when there is no more fields or the field is not an enum with two elements. + + SYNOPSIS + get_access() + form an open table to read privileges from. + The record should be already read in table->record[0] + fieldnr number of the first privilege (that is ENUM('N','Y') field + next_field on return - number of the field next to the last ENUM + (unless next_field == 0) + + RETURN VALUE + privilege mask */ -static ulong get_access(TABLE *form, uint fieldnr) +static ulong get_access(TABLE *form, uint fieldnr, uint *next_field) { ulong access_bits=0,bit; char buff[2]; @@ -510,12 +518,14 @@ static ulong get_access(TABLE *form, uint fieldnr) for (pos=form->field+fieldnr, bit=1; *pos && (*pos)->real_type() == FIELD_TYPE_ENUM && ((Field_enum*) (*pos))->typelib->count == 2 ; - pos++ , bit<<=1) + pos++, fieldnr++, bit<<=1) { (*pos)->val_str(&res); if (my_toupper(&my_charset_latin1, res[0]) == 'Y') access_bits|= bit; } + if (next_field) + *next_field=fieldnr; return access_bits; } @@ -2300,6 +2310,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, { uint unused_field_idx= NO_CACHED_FIELD_INDEX; if (!find_field_in_table(thd, table_list, column->column.ptr(), + column->column.ptr(), column->column.length(), 0, 0, 0, 0, &unused_field_idx)) { @@ -2750,7 +2761,22 @@ void grant_reload(THD *thd) /**************************************************************************** Check table level grants - All errors are written directly to the client if no_errors is given ! + + SYNPOSIS + bool check_grant() + thd Thread handler + want_access Bits of privileges user needs to have + tables List of tables to check. The user should have 'want_access' + to all tables in list. + show_table <> 0 if we are in show table. In this case it's enough to have + any privilege for the table + number Check at most this number of tables. + no_errors If 0 then we write an error. The error is sent directly to + the client + + RETURN + 0 ok + 1 Error: User did not have the requested privielges ****************************************************************************/ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, @@ -2758,14 +2784,17 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, { TABLE_LIST *table; char *user = thd->priv_user; + DBUG_ENTER("check_grant"); + DBUG_ASSERT(number > 0); - want_access &= ~thd->master_access; + want_access&= ~thd->master_access; if (!want_access) - return 0; // ok + DBUG_RETURN(0); // ok rw_rdlock(&LOCK_grant); for (table= tables; table && number--; table= table->next_global) { + GRANT_TABLE *grant_table; if (!(~table->grant.privilege & want_access) || table->derived) { /* @@ -2775,10 +2804,8 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, table->grant.want_privilege= 0; continue; // Already checked } - GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip, - table->db,user, - table->real_name,0); - if (!grant_table) + if (!(grant_table= table_hash_search(thd->host,thd->ip, + table->db,user, table->real_name,0))) { want_access &= ~table->grant.privilege; goto err; // No grants @@ -2802,7 +2829,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, } } rw_unlock(&LOCK_grant); - return 0; + DBUG_RETURN(0); err: rw_unlock(&LOCK_grant); @@ -2837,7 +2864,7 @@ err: thd->host_or_ip, table ? table->real_name : "unknown"); } - return 1; + DBUG_RETURN(1); } @@ -2931,7 +2958,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant, if (!(grant_table= grant->grant_table)) goto err; /* purecov: inspected */ - for (; fields->end(); fields->next()) + for (; !fields->end_of_fields(); fields->next()) { const char *field_name= fields->name(); grant_column= column_hash_search(grant_table, field_name, @@ -3153,7 +3180,8 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) strxmov(buff,"Grants for ",lex_user->user.str,"@", lex_user->host.str,NullS); field_list.push_back(field); - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(-1); rw_wrlock(&LOCK_grant); @@ -3838,22 +3866,27 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, { /* global privileges */ grant->privilege= thd->master_access; + + /* if privileges ignored (--skip-grant-tables) above is enough */ + if (!grant_option) + return; + /* db privileges */ grant->privilege|= acl_get(thd->host, thd->ip, thd->priv_user, db, 0); /* table privileges */ - rw_rdlock(&LOCK_grant); if (grant->version != grant_version) { + rw_rdlock(&LOCK_grant); grant->grant_table= table_hash_search(thd->host, thd->ip, db, thd->priv_user, table, 0); /* purecov: inspected */ grant->version= grant_version; /* purecov: inspected */ + rw_unlock(&LOCK_grant); } if (grant->grant_table != 0) { grant->privilege|= grant->grant_table->privs; } - rw_unlock(&LOCK_grant); } #endif diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1060c9cd25d..44ce10981ad 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -40,6 +40,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, static void free_cache_entry(TABLE *entry); static void mysql_rm_tmp_tables(void); static my_bool open_new_frm(const char *path, const char *alias, + const char *db, const char *table_name, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, MEM_ROOT *mem_root); @@ -547,81 +548,37 @@ void close_temporary_tables(THD *thd) thd->temporary_tables=0; } -#ifdef UNUSED -/* - Find first suitable table by alias in given list. - - SYNOPSIS - find_table_in_list() - table - pointer to table list - db_name - data base name or 0 for any - table_name - table name or 0 for any - - RETURN VALUES - NULL Table not found - # Pointer to found table. -*/ - -TABLE_LIST * find_table_in_list(TABLE_LIST *table, - const char *db_name, const char *table_name) -{ - for (; table; table= table->next) - if ((!db_name || !strcmp(table->db, db_name)) && - (!table_name || !my_strcasecmp(table_alias_charset, - table->alias, table_name))) - break; - return table; -} -#endif /*UNUSED*/ /* - Find real table in given global list. + Find table in list. SYNOPSIS - find_real_table_in_list() - table - pointer to table list - db_name - data base name - table_name - table name - - RETURN VALUES - NULL Table not found - # Pointer to found table. -*/ - -TABLE_LIST * find_real_table_in_list(TABLE_LIST *table, - const char *db_name, - const char *table_name) -{ - for (; table; table= table->next_global) - if (!strcmp(table->db, db_name) && - !strcmp(table->real_name, table_name)) - break; - return table; -} - - -/* - Find real table in given local list. + find_table_in_list() + table Pointer to table list + offset Offset to which list in table structure to use + db_name Data base name + table_name Table name - SYNOPSIS - find_real_table_in_local_list() - table - pointer to table list - db_name - data base name - table_name - table name + NOTES: + This is called by find_table_in_local_list() and + find_table_in_global_list(). RETURN VALUES NULL Table not found # Pointer to found table. */ -TABLE_LIST * find_real_table_in_local_list(TABLE_LIST *table, - const char *db_name, - const char *table_name) +TABLE_LIST *find_table_in_list(TABLE_LIST *table, + uint offset, + const char *db_name, + const char *table_name) { - for (; table; table= table->next_local) + for (; table; table= *(TABLE_LIST **) ((char*) table + offset)) + { if (!strcmp(table->db, db_name) && !strcmp(table->real_name, table_name)) break; + } return table; } @@ -1365,7 +1322,7 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name) db Database name name Table name alias Alias name - table_desc TABLE_LIST descriptor + table_desc TABLE_LIST descriptor (used with views) mem_root temporary mem_root for parsing NOTES @@ -1381,8 +1338,6 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, { char path[FN_REFLEN]; int error; - // we support new format only is have all parameters for it - uint new_frm_flag= (table_desc && mem_root) ? NO_ERR_ON_NEW_FRM : 0; uint discover_retry_count= 0; DBUG_ENTER("open_unireg_entry"); @@ -1390,12 +1345,12 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, while ((error= openfrm(path, alias, (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX | HA_TRY_READ_ONLY | - new_frm_flag), + NO_ERR_ON_NEW_FRM), READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, thd->open_options, entry)) && (error != 5 || fn_format(path, path, 0, reg_ext, MY_UNPACK_FILENAME), - open_new_frm(path, alias, + open_new_frm(path, alias, db, name, (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX | HA_TRY_READ_ONLY), READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, @@ -1405,10 +1360,10 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, if (!entry->crashed) { /* - Frm file could not be found on disk - Since it does not exist, no one can be using it - LOCK_open has been locked to protect from someone else - trying to discover the table at the same time. + Frm file could not be found on disk + Since it does not exist, no one can be using it + LOCK_open has been locked to protect from someone else + trying to discover the table at the same time. */ if (discover_retry_count++ != 0) goto err; @@ -1511,6 +1466,14 @@ memory to write 'DELETE FROM `%s`.`%s`' to the binary log",db,name); } DBUG_RETURN(0); err: + /* Hide "Table doesn't exist" errors if table belong to view */ + if (thd->net.last_errno == ER_NO_SUCH_TABLE && + table_desc && table_desc->belong_to_view) + { + TABLE_LIST * view= table_desc->belong_to_view; + thd->clear_error(); + my_error(ER_VIEW_INVALID, MYF(0), view->view_db.str, view->view_name.str); + } DBUG_RETURN(1); } @@ -1609,7 +1572,7 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter) tables->table->grant= tables->grant; } thd->proc_info=0; - free_root(&new_frm_mem, MYF(0)); + free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block DBUG_RETURN(result); } @@ -1677,7 +1640,10 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) thd->proc_info="Opening table"; thd->current_tablenr= 0; - while (!(table= open_table(thd, table_list, 0, &refresh)) && refresh) ; + /* open_ltable can be used only for BASIC TABLEs */ + table_list->required_type= FRMTYPE_TABLE; + while (!(table= open_table(thd, table_list, 0, &refresh)) && refresh) + ; if (table) { @@ -1748,6 +1714,7 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables) RETURN 0 - ok -1 - error + 1 - error reported to user NOTE The lock will automaticly be freed by close_thread_tables() @@ -1757,9 +1724,9 @@ int open_and_lock_tables(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("open_and_lock_tables"); uint counter; - if (open_tables(thd, tables, &counter) || lock_tables(thd, tables, counter)) - DBUG_RETURN(-1); /* purecov: inspected */ - DBUG_RETURN(mysql_handle_derived(thd->lex)); + if (open_tables(thd, tables, &counter) || lock_tables(thd, tables, counter) + || mysql_handle_derived(thd->lex)) + DBUG_RETURN(thd->net.report_error ? -1 : 1); /* purecov: inspected */ } @@ -1906,7 +1873,7 @@ bool rm_temporary_table(enum db_type base, char *path) ** return unique field ******************************************************************************/ -// Special Field pointers for find_field_in_tables returning +/* Special Field pointers for find_field_in_tables returning */ const Field *not_found_field= (Field*) 0x1; const Field *view_ref_found= (Field*) 0x2; @@ -1921,6 +1888,7 @@ const Field *view_ref_found= (Field*) 0x2; thd thread handler table_list table where to find name name of field + item_name name of item if it will be created (VIEW) length length of name ref expression substituted in VIEW should be passed using this reference (return @@ -1937,11 +1905,13 @@ const Field *view_ref_found= (Field*) 0x2; # pointer to field */ -Field *find_field_in_table(THD *thd, TABLE_LIST *table_list, - const char *name, uint length, Item **ref, - bool check_grants_table, bool check_grants_view, - bool allow_rowid, - uint *cached_field_index_ptr) +Field * +find_field_in_table(THD *thd, TABLE_LIST *table_list, + const char *name, const char *item_name, + uint length, Item **ref, + bool check_grants_table, bool check_grants_view, + bool allow_rowid, + uint *cached_field_index_ptr) { Field *fld; if (table_list->field_translation) @@ -1953,7 +1923,6 @@ Field *find_field_in_table(THD *thd, TABLE_LIST *table_list, { if (strcmp(trans[i]->name, name) == 0) { - *ref= trans[i]; #ifndef NO_EMBEDDED_ACCESS_CHECKS if (check_grants_view && check_grant_column(thd, &table_list->grant, @@ -1962,6 +1931,21 @@ Field *find_field_in_table(THD *thd, TABLE_LIST *table_list, name, length)) return WRONG_GRANT; #endif + if (thd->lex->current_select->no_wrap_view_item) + *ref= trans[i]; + else + { + Item_arena *arena= thd->current_arena, backup; + if (arena) + thd->set_n_backup_item_arena(arena, &backup); + *ref= new Item_ref(trans + i, 0, table_list->view_name.str, + item_name); + if (arena) + thd->restore_backup_item_arena(arena, &backup); + /* as far as Item_ref have defined refernce it do not need tables */ + if (*ref) + (*ref)->fix_fields(thd, 0, ref); + } return (Field*) view_ref_found; } } @@ -1985,6 +1969,7 @@ Field *find_field_in_table(THD *thd, TABLE_LIST *table_list, return fld; } + /* Find field in table @@ -2146,7 +2131,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, (!db || !tables->db || !tables->db[0] || !strcmp(db,tables->db))) { found_table=1; - Field *find= find_field_in_table(thd, tables, name, length, ref, + Field *find= find_field_in_table(thd, tables, name, item->name, + length, ref, (test(tables->table->grant. want_privilege) && check_privileges), @@ -2209,7 +2195,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, return (Field*) not_found_field; } - Field *field= find_field_in_table(thd, tables, name, length, ref, + Field *field= find_field_in_table(thd, tables, name, item->name, + length, ref, (test(tables->table->grant. want_privilege) && check_privileges), @@ -2439,6 +2426,7 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, { reg2 Item *item; List_iterator<Item> it(fields); + SELECT_LEX *select_lex= thd->lex->current_select; DBUG_ENTER("setup_fields"); thd->set_query_id=set_query_id; @@ -2450,7 +2438,10 @@ int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, { if (!item->fixed && item->fix_fields(thd, tables, it.ref()) || (item= *(it.ref()))->check_cols(1)) + { + select_lex->no_wrap_view_item= 0; DBUG_RETURN(-1); /* purecov: inspected */ + } if (ref) *(ref++)= item; if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM && @@ -2517,8 +2508,8 @@ bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds) table->keys_in_use_for_query.subtract(map); } table->used_keys.intersect(table->keys_in_use_for_query); - if (table_list->ancestor) - table_list->setup_ancestor(thd, conds); + if (table_list->ancestor && table_list->setup_ancestor(thd, conds)) + DBUG_RETURN(1); } if (tablenr > MAX_TABLES) { @@ -2553,8 +2544,9 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, map->clear_all(); while ((name=it++)) { - if ((pos= find_type(&table->keynames, name->ptr(), name->length(), 1)) <= - 0) + if (table->keynames.type_names == 0 || + (pos= find_type(&table->keynames, name->ptr(), name->length(), 1)) <= + 0) { my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), name->c_ptr(), table->real_name); @@ -2567,23 +2559,47 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, } -/**************************************************************************** - This just drops in all fields instead of current '*' field - Returns pointer to last inserted field if ok -****************************************************************************/ +/* + Drops in all fields instead of current '*' field + + SYNOPSIS + insert_fields() + thd Thread handler + tables List of tables + db_name Database name in case of 'database_name.table_name.*' + table_name Table name in case of 'table_name.*' + it Pointer to '*' + any_privileges 0 If we should ensure that we have SELECT privileges + for all columns + 1 If any privilege is ok + RETURN + 0 ok + 'it' is updated to point at last inserted + 1 error. The error message is sent to client +*/ bool insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, const char *table_name, List_iterator<Item> *it, bool any_privileges) { + /* allocate variables on stack to avoid pool alloaction */ + Field_iterator_table table_iter; + Field_iterator_view view_iter; uint found; DBUG_ENTER("insert_fields"); - found=0; + found= 0; for (; tables; tables= tables->next_local) { - TABLE *table=tables->table; + Field_iterator *iterator; + TABLE_LIST *natural_join_table; + Field *field; + TABLE_LIST *embedded; + TABLE_LIST *last; + TABLE_LIST *embedding; + TABLE *table= tables->table; + if (!table_name || (!my_strcasecmp(table_alias_charset, table_name, tables->alias) && (!db_name || !strcmp(tables->db,db_name)))) @@ -2594,36 +2610,26 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, { if (tables->view) { - Field_iterator_view fields; - fields.set(tables); + view_iter.set(tables); if (check_grant_all_columns(thd, SELECT_ACL, &tables->grant, tables->view_db.str, tables->view_name.str, - &fields)) - DBUG_RETURN(1); + &view_iter)) + goto err; } else { - Field_iterator_table fields; - fields.set(tables); + table_iter.set(tables); if (check_grant_all_columns(thd, SELECT_ACL, &table->grant, table->table_cache_key, table->real_name, - &fields)) - DBUG_RETURN(1); + &table_iter)) + goto err; } } #endif - /* allocate 2 variables on stack to avoid pool alloaction */ - Field_iterator_table table_iter; - Field_iterator_view view_iter; - Field_iterator *iterator; - TABLE_LIST *natural_join_table= 0; - Field *field; - - thd->used_tables|=table->map; - TABLE_LIST *embedded= tables; - TABLE_LIST *last= embedded; - TABLE_LIST *embedding; + natural_join_table= 0; + thd->used_tables|= table->map; + last= embedded= tables; while ((embedding= embedded->embedding) && embedding->join_list->elements != 1) @@ -2654,14 +2660,15 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, iterator= &table_iter; iterator->set(tables); - for (; iterator->end(); iterator->next()) + for (; !iterator->end_of_fields(); iterator->next()) { Item *not_used_item; uint not_used_field_index= NO_CACHED_FIELD_INDEX; const char *field_name= iterator->name(); /* Skip duplicate field names if NATURAL JOIN is used */ if (!natural_join_table || - !find_field_in_table(thd, natural_join_table, field_name, + !find_field_in_table(thd, natural_join_table, field_name, + field_name, strlen(field_name), ¬_used_item, 0, 0, 0, ¬_used_field_index)) { @@ -2705,9 +2712,7 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, thd->host_or_ip, fld->field_name, tab); - /* TODO: should be removed to have only one send_error */ - send_error(thd); - DBUG_RETURN(1); + goto err; } } #endif @@ -2741,15 +2746,17 @@ insert_fields(THD *thd, TABLE_LIST *tables, const char *db_name, table->used_fields=table->fields; } } - if (!found) - { - if (!table_name) - my_error(ER_NO_TABLES_USED,MYF(0)); - else - my_error(ER_BAD_TABLE_ERROR,MYF(0),table_name); - send_error(thd); - } - DBUG_RETURN(!found); + if (found) + DBUG_RETURN(0); + + if (!table_name) + my_error(ER_NO_TABLES_USED, MYF(0)); + else + my_error(ER_BAD_TABLE_ERROR, MYF(0), table_name); + +err: + send_error(thd); + DBUG_RETURN(1); } @@ -2838,13 +2845,16 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) if (arena) thd->set_n_backup_item_arena(arena, &backup); + TABLE *t1=tab1->table; TABLE *t2=tab2->table; - /* allocate 2 variables on stack to avoid pool alloaction */ Field_iterator_table table_iter; Field_iterator_view view_iter; Field_iterator *iterator; + Field *t1_field, *t2_field; + Item *item_t2; Item_cond_and *cond_and=new Item_cond_and(); + if (!cond_and) // If not out of memory DBUG_RETURN(1); cond_and->top_level_item(); @@ -2860,14 +2870,13 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) table_iter.set(tab1); } - Field *t1_field, *t2_field; - Item *item_t2; - for (; iterator->end(); iterator->next()) + for (; !iterator->end_of_fields(); iterator->next()) { const char *t1_field_name= iterator->name(); uint not_used_field_index= NO_CACHED_FIELD_INDEX; if ((t2_field= find_field_in_table(thd, tab2, t1_field_name, + t1_field_name, strlen(t1_field_name), &item_t2, 0, 0, 0, ¬_used_field_index))) @@ -3188,6 +3197,8 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) open_new_frm() path path to .frm alias alias for table + db database + table_name name of table db_stat open flags (for example HA_OPEN_KEYFILE|HA_OPEN_RNDFILE..) can be 0 (example in ha_example_table) prgflag READ_ALL etc.. @@ -3196,37 +3207,46 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) table_desc TABLE_LIST descriptor mem_root temporary MEM_ROOT for parsing */ + static my_bool -open_new_frm(const char *path, const char *alias, uint db_stat, uint prgflag, +open_new_frm(const char *path, const char *alias, + const char *db, const char *table_name, + uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, MEM_ROOT *mem_root) { - DBUG_ENTER("open_new_frm"); LEX_STRING pathstr; - pathstr.str= (char *)path; + File_parser *parser; + DBUG_ENTER("open_new_frm"); + + pathstr.str= (char*) path; pathstr.length= strlen(path); - File_parser *parser= sql_parse_prepare(&pathstr, mem_root, 1); - if (parser) + + if (!mem_root) + mem_root= ¤t_thd->mem_root; + + if ((parser= sql_parse_prepare(&pathstr, mem_root, 1))) { if (!strncmp("VIEW", parser->type()->str, parser->type()->length)) { - if (mysql_make_view(parser, table_desc)) + if (table_desc == 0 || table_desc->required_type == FRMTYPE_TABLE) { - bzero(outparam, sizeof(*outparam)); // do not run repair - DBUG_RETURN(1); + my_error(ER_WRONG_OBJECT, MYF(0), db, table_name, "BASE TABLE"); + goto err; } + if (mysql_make_view(parser, table_desc)) + goto err; } else { /* only VIEWs are supported now */ my_error(ER_FRM_UNKNOWN_TYPE, MYF(0), path, parser->type()->str); - bzero(outparam, sizeof(outparam)); // do not run repair - DBUG_RETURN(1); + goto err; } + DBUG_RETURN(0); } - else - { - DBUG_RETURN(1); - } - DBUG_RETURN(0); + +err: + bzero(outparam, sizeof(TABLE)); // do not run repair + DBUG_RETURN(1); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b86bfb07386..da64479abf2 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -675,7 +675,8 @@ int THD::send_explain_fields(select_result *result) item->maybe_null=1; field_list.push_back(new Item_return_int("rows",10, MYSQL_TYPE_LONGLONG)); field_list.push_back(new Item_empty_string("Extra",255)); - return (result->send_fields(field_list,1)); + return (result->send_fields(field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)); } #ifdef SIGNAL_WITH_VIO_CLOSE @@ -722,9 +723,9 @@ sql_exchange::sql_exchange(char *name,bool flag) escaped= &default_escaped; } -bool select_send::send_fields(List<Item> &list,uint flag) +bool select_send::send_fields(List<Item> &list, uint flags) { - return thd->protocol->send_fields(&list,flag); + return thd->protocol->send_fields(&list, flags); } /* Send data to client. Returns 0 if ok */ @@ -1354,7 +1355,8 @@ Statement::Statement(THD *thd) allow_sum_func(0), lex(&main_lex), query(0), - query_length(0) + query_length(0), + cursor(0) { name.str= NULL; } @@ -1372,7 +1374,8 @@ Statement::Statement() allow_sum_func(0), /* initialized later */ lex(&main_lex), query(0), /* these two are set */ - query_length(0) /* in alloc_query() */ + query_length(0), /* in alloc_query() */ + cursor(0) { } @@ -1391,6 +1394,7 @@ void Statement::set_statement(Statement *stmt) lex= stmt->lex; query= stmt->query; query_length= stmt->query_length; + cursor= stmt->cursor; } @@ -1515,7 +1519,8 @@ bool select_dumpvar::send_data(List<Item> &items) { if ((yy=var_li++)) { - thd->spcont->set_item_eval(yy->get_offset(), item, zz->type); + if (thd->spcont->set_item_eval(yy->get_offset(), item, zz->type)) + DBUG_RETURN(1); } } else diff --git a/sql/sql_class.h b/sql/sql_class.h index 00d32543da0..eccaf072008 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -487,6 +487,9 @@ public: void set_item_arena(Item_arena *set); }; + +class Cursor; + /* State of a single command executed against this connection. One connection can contain a lot of simultaneously running statements, @@ -543,6 +546,7 @@ public: */ char *query; uint32 query_length; // current query length + Cursor *cursor; public: /* We build without RTTI, so dynamic_cast can't be used. */ @@ -1054,6 +1058,7 @@ public: { DBUG_ASSERT(current_arena!=0); cleanup_items(current_arena->free_list); + /* no need to reset free_list as it won't be used anymore */ free_items(free_list); close_thread_tables(this); // to close derived tables free_root(&mem_root, MYF(0)); @@ -1108,7 +1113,7 @@ public: unit= u; return 0; } - virtual bool send_fields(List<Item> &list,uint flag)=0; + virtual bool send_fields(List<Item> &list, uint flags)=0; virtual bool send_data(List<Item> &items)=0; virtual bool initialize_tables (JOIN *join=0) { return 0; } virtual void send_error(uint errcode,const char *err); @@ -1120,7 +1125,7 @@ public: class select_send :public select_result { public: select_send() {} - bool send_fields(List<Item> &list,uint flag); + bool send_fields(List<Item> &list, uint flags); bool send_data(List<Item> &items); bool send_eof(); }; @@ -1138,7 +1143,7 @@ public: select_to_file(sql_exchange *ex) :exchange(ex), file(-1),row_count(0L) { path[0]=0; } ~select_to_file(); - bool send_fields(List<Item> &list, uint flag) { return 0; } + bool send_fields(List<Item> &list, uint flags) { return 0; } void send_error(uint errcode,const char *err); }; @@ -1185,8 +1190,7 @@ class select_insert :public select_result { } ~select_insert(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); - bool send_fields(List<Item> &list, uint flag) - { return 0; } + bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_data(List<Item> &items); void send_error(uint errcode,const char *err); bool send_eof(); @@ -1273,8 +1277,7 @@ class select_union :public select_result { select_union(TABLE *table_par); ~select_union(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); - bool send_fields(List<Item> &list, uint flag) - { return 0; } + bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_data(List<Item> &items); bool send_eof(); bool flush(); @@ -1288,7 +1291,7 @@ protected: Item_subselect *item; public: select_subselect(Item_subselect *item); - bool send_fields(List<Item> &list, uint flag) { return 0; }; + bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_data(List<Item> &items)=0; bool send_eof() { return 0; }; @@ -1457,8 +1460,7 @@ public: multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables); ~multi_delete(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); - bool send_fields(List<Item> &list, - uint flag) { return 0; } + bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_data(List<Item> &items); bool initialize_tables (JOIN *join); void send_error(uint errcode,const char *err); @@ -1486,7 +1488,7 @@ public: List<Item> *values, enum_duplicates handle_duplicates); ~multi_update(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); - bool send_fields(List<Item> &list, uint flag) { return 0; } + bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_data(List<Item> &items); bool initialize_tables (JOIN *join); void send_error(uint errcode,const char *err); @@ -1515,7 +1517,7 @@ public: 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;} + bool send_fields(List<Item> &list, uint flags) { return 0; } bool send_data(List<Item> &items); bool send_eof(); }; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index a1ccfe8d22c..dad39da271b 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -39,7 +39,8 @@ static TYPELIB known_extentions= static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, const char *path, uint level); - +static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path); +static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error); /* Database options hash */ static HASH dboptions; static my_bool dboptions_init= 0; @@ -225,7 +226,8 @@ void del_dbopt(const char *path) { my_dbopt_t *opt; rw_wrlock(&LOCK_dboptions); - if ((opt= (my_dbopt_t *)hash_search(&dboptions, (byte*) path, strlen(path)))) + if ((opt= (my_dbopt_t *)hash_search(&dboptions, (const byte*) path, + strlen(path)))) hash_delete(&dboptions, (byte*) opt); rw_unlock(&LOCK_dboptions); } @@ -327,6 +329,7 @@ bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create) { sql_print_error("Error while loading database options: '%s':",path); sql_print_error(ER(ER_UNKNOWN_CHARACTER_SET),pos+1); + create->default_table_charset= default_charset_info; } } else if (!strncmp(buf,"default-collation", (pos-buf))) @@ -336,6 +339,7 @@ bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create) { sql_print_error("Error while loading database options: '%s':",path); sql_print_error(ER(ER_UNKNOWN_COLLATION),pos+1); + create->default_table_charset= default_charset_info; } } } @@ -541,7 +545,6 @@ exit2: -1 Error generated */ - int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) { long deleted=0; @@ -695,9 +698,9 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, continue; /* Check if file is a raid directory */ - if ((my_isdigit(&my_charset_latin1, file->name[0]) || + if ((my_isdigit(system_charset_info, file->name[0]) || (file->name[0] >= 'a' && file->name[0] <= 'f')) && - (my_isdigit(&my_charset_latin1, file->name[1]) || + (my_isdigit(system_charset_info, file->name[1]) || (file->name[1] >= 'a' && file->name[1] <= 'f')) && !file->name[2] && !level) { @@ -723,6 +726,25 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, found_other_files++; continue; } + else if (file->name[0] == 'a' && file->name[1] == 'r' && + file->name[2] == 'c' && file->name[3] == '\0') + { + /* .frm archive */ + char newpath[FN_REFLEN], *copy_of_path; + MY_DIR *new_dirp; + uint length; + strxmov(newpath, org_path, "/", "arc", NullS); + length= unpack_filename(newpath, newpath); + if ((new_dirp = my_dir(newpath, MYF(MY_DONT_SORT)))) + { + DBUG_PRINT("my",("Archive subdir found: %s", newpath)); + if ((mysql_rm_arc_files(thd, new_dirp, newpath)) < 0) + goto err; + continue; + } + found_other_files++; + continue; + } extension= fn_ext(file->name); if (find_type(extension, &deletable_extentions,1+2) <= 0) { @@ -782,44 +804,140 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, } else { - char tmp_path[FN_REFLEN], *pos; - char *path= tmp_path; - unpack_filename(tmp_path,org_path); + /* Don't give errors if we can't delete 'RAID' directory */ + if (rm_dir_w_symlink(org_path, level == 0)) + DBUG_RETURN(-1); + } + + DBUG_RETURN(deleted); + +err: + my_dirend(dirp); + DBUG_RETURN(-1); +} + + +/* + Remove directory with symlink + + SYNOPSIS + rm_dir_w_symlink() + org_path path of derictory + send_error send errors + RETURN + 0 OK + 1 ERROR +*/ + +static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error) +{ + char tmp_path[FN_REFLEN], tmp2_path[FN_REFLEN], *pos; + char *path= tmp_path; + DBUG_ENTER("rm_dir_w_symlink"); + unpack_filename(tmp_path, org_path); #ifdef HAVE_READLINK - int error; - - /* Remove end FN_LIBCHAR as this causes problem on Linux in readlink */ - pos=strend(path); - if (pos > path && pos[-1] == FN_LIBCHAR) - *--pos=0; + int error; - if ((error=my_readlink(filePath, path, MYF(MY_WME))) < 0) - DBUG_RETURN(-1); - if (!error) + /* Remove end FN_LIBCHAR as this causes problem on Linux in readlink */ + pos= strend(path); + if (pos > path && pos[-1] == FN_LIBCHAR) + *--pos=0; + + if ((error= my_readlink(tmp2_path, path, MYF(MY_WME))) < 0) + DBUG_RETURN(1); + if (!error) + { + if (my_delete(path, MYF(send_error ? MY_WME : 0))) { - if (my_delete(path,MYF(!level ? MY_WME : 0))) - { - /* Don't give errors if we can't delete 'RAID' directory */ - if (level) - DBUG_RETURN(deleted); - DBUG_RETURN(-1); - } - /* Delete directory symbolic link pointed at */ - path= filePath; + DBUG_RETURN(send_error); } + /* Delete directory symbolic link pointed at */ + path= tmp2_path; + } #endif - /* Remove last FN_LIBCHAR to not cause a problem on OS/2 */ - pos=strend(path); + /* Remove last FN_LIBCHAR to not cause a problem on OS/2 */ + pos= strend(path); - if (pos > path && pos[-1] == FN_LIBCHAR) - *--pos=0; - /* Don't give errors if we can't delete 'RAID' directory */ - if (rmdir(path) < 0 && !level) + if (pos > path && pos[-1] == FN_LIBCHAR) + *--pos=0; + if (rmdir(path) < 0 && send_error) + { + my_error(ER_DB_DROP_RMDIR, MYF(0), path, errno); + DBUG_RETURN(-1); + } + DBUG_RETURN(0); +} + + +/* + Remove .frm archives from directory + + SYNOPSIS + thd thread handler + dirp list of files in archive directory + db data base name + org_path path of archive directory + + RETURN + > 0 number of removed files + -1 error +*/ +static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, + const char *org_path) +{ + long deleted= 0; + ulong found_other_files= 0; + char filePath[FN_REFLEN]; + DBUG_ENTER("mysql_rm_arc_files"); + DBUG_PRINT("enter", ("path: %s", org_path)); + + for (uint idx=0 ; + idx < (uint) dirp->number_off_files && !thd->killed ; + idx++) + { + FILEINFO *file=dirp->dir_entry+idx; + char *extension, *revision; + DBUG_PRINT("info",("Examining: %s", file->name)); + + /* skiping . and .. */ + if (file->name[0] == '.' && (!file->name[1] || + (file->name[1] == '.' && !file->name[2]))) + continue; + + extension= fn_ext(file->name); + if (extension[0] != '.' || + extension[1] != 'f' || extension[2] != 'r' || + extension[3] != 'm' || extension[4] != '-') { - my_error(ER_DB_DROP_RMDIR, MYF(0), path, errno); - DBUG_RETURN(-1); + found_other_files++; + continue; + } + revision= extension+5; + while (*revision && my_isdigit(system_charset_info, *revision)) + revision++; + if (*revision) + { + found_other_files++; + continue; + } + strxmov(filePath, org_path, "/", file->name, NullS); + if (my_delete_with_symlink(filePath,MYF(MY_WME))) + { + goto err; } } + if (thd->killed) + goto err; + + my_dirend(dirp); + + /* + If the directory is a symbolic link, remove the link first, then + remove the directory the symbolic link pointed at + */ + if (!found_other_files && + rm_dir_w_symlink(org_path, 0)) + DBUG_RETURN(-1); DBUG_RETURN(deleted); err: diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 0453990fbac..587cb28bcb1 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -41,8 +41,8 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, ha_rows deleted; DBUG_ENTER("mysql_delete"); - if ((open_and_lock_tables(thd, table_list))) - DBUG_RETURN(-1); + if ((error= open_and_lock_tables(thd, table_list))) + DBUG_RETURN(error); table= table_list->table; table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); thd->proc_info="init"; @@ -293,7 +293,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); DBUG_RETURN(-1); } - if (find_real_table_in_list(table_list->next_global, + if (find_table_in_global_list(table_list->next_global, table_list->db, table_list->real_name)) { my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 3137890f2ba..5c827741e6d 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -220,10 +220,12 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, table->next= thd->derived_tables; thd->derived_tables= table; } + } + else + free_tmp_table(thd, table); exit: - delete derived_result; - lex->current_select= save_current_select; - } + delete derived_result; + lex->current_select= save_current_select; DBUG_RETURN(res); } diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 8aa7bdf9a7f..d68d62a8820 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -185,7 +185,8 @@ my_bool mysqld_show_warnings(THD *thd, ulong levels_to_show) field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG)); field_list.push_back(new Item_empty_string("Message",MYSQL_ERRMSG_SIZE)); - if (thd->protocol->send_fields(&field_list,1)) + if (thd->protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); MYSQL_ERROR *err; diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 8d06d3f0017..0df3d617d7f 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -252,7 +252,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables, insert_fields(thd, tables, tables->db, tables->alias, &it, 0); select_limit+=offset_limit; - protocol->send_fields(&list,1); + protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); HANDLER_TABLES_HACK(thd); MYSQL_LOCK *lock=mysql_lock_tables(thd,&tables->table,1); diff --git a/sql/sql_help.cc b/sql/sql_help.cc index 08a8fc626cc..8fc0671c808 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -426,7 +426,8 @@ int send_answer_1(Protocol *protocol, String *s1, String *s2, String *s3) field_list.push_back(new Item_empty_string("description",1000)); field_list.push_back(new Item_empty_string("example",1000)); - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); protocol->prepare_for_resend(); @@ -468,7 +469,8 @@ int send_header_2(Protocol *protocol, bool for_category) field_list.push_back(new Item_empty_string("source_category_name",64)); field_list.push_back(new Item_empty_string("name",64)); field_list.push_back(new Item_empty_string("is_it_category",1)); - DBUG_RETURN(protocol->send_fields(&field_list,1)); + DBUG_RETURN(protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | + Protocol::SEND_EOF)); } /* @@ -638,11 +640,8 @@ int mysqld_help(THD *thd, const char *mask) uint mlen= strlen(mask); MEM_ROOT *mem_root= &thd->mem_root; - if (open_and_lock_tables(thd, tables)) - { - res= -1; + if (res= open_and_lock_tables(thd, tables)) goto end; - } /* Init tables and fields to be usable from items diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 72ca7cf5dea..a5c1999da8c 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -53,6 +53,7 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, List<Item> &values, ulong counter, bool check_unique) { TABLE *table= table_list->table; + if (fields.elements == 0 && values.elements != 0) { if (values.elements != table->fields) @@ -63,11 +64,11 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, return -1; } #ifndef NO_EMBEDDED_ACCESS_CHECKS + if (grant_option) { Field_iterator_table fields; fields.set_table(table); - if (grant_option && - check_grant_all_columns(thd, INSERT_ACL, &table->grant, + if (check_grant_all_columns(thd, INSERT_ACL, &table->grant, table->table_cache_key, table->real_name, &fields)) return -1; @@ -77,7 +78,8 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, } else { // Part field list - TABLE_LIST *save_next= table_list->next_local; + TABLE_LIST *save_next; + int res; if (fields.elements != values.elements) { my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW, @@ -86,14 +88,15 @@ check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, return -1; } - table_list->next_local= 0; thd->dupp_field=0; - if (setup_fields(thd, 0, table_list, fields, 1, 0, 0)) - { - table_list->next_local= save_next; - return -1; - } + thd->lex->select_lex.no_wrap_view_item= 1; + save_next= table_list->next_local; // fields only from first table + table_list->next_local= 0; + res= setup_fields(thd, 0, table_list, fields, 1, 0, 0); table_list->next_local= save_next; + thd->lex->select_lex.no_wrap_view_item= 0; + if (res) + return -1; if (check_unique && thd->dupp_field) { @@ -418,7 +421,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, !thd->cuted_fields)) { thd->row_count_func= info.copied+info.deleted+info.updated; - send_ok(thd, (ulong) (ulong) thd->row_count_func, id); + send_ok(thd, (ulong) thd->row_count_func, id); } else { @@ -431,7 +434,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records, (ulong) (info.deleted+info.updated), (ulong) thd->cuted_fields); thd->row_count_func= info.copied+info.deleted+info.updated; - ::send_ok(thd, (ulong) thd->row_count_func, (ulonglong)id,buff); + ::send_ok(thd, (ulong) thd->row_count_func, id, buff); } free_underlaid_joins(thd, &thd->lex->select_lex); table->insert_values=0; @@ -455,46 +458,58 @@ abort: check_view_insertability() view - reference on VIEW + IMPLEMENTATION + A view is insertable if the folloings are true: + - All columns in the view are columns from a table + - All not used columns in table have a default values + - All field in view are unique (not referring to the same column) + RETURN FALSE - OK + view->contain_auto_increment is 1 if and only if the view contains an + auto_increment field + TRUE - can't be used for insert */ static bool check_view_insertability(TABLE_LIST *view, ulong query_id) { - DBUG_ENTER("check_key_in_view"); - - uint i; + uint num= view->view->select_lex.item_list.elements; TABLE *table= view->table; - Item **trans= view->field_translation; + Item **trans_start= view->field_translation, **trans_end=trans_start+num; + Item **trans; Field **field_ptr= table->field; - uint num= view->view->select_lex.item_list.elements; ulong other_query_id= query_id - 1; + DBUG_ENTER("check_key_in_view"); + DBUG_ASSERT(view->table != 0 && view->field_translation != 0); view->contain_auto_increment= 0; /* check simplicity and prepare unique test of view */ - for (i= 0; i < num; i++) + for (trans= trans_start; trans != trans_end; trans++) { + Item_field *field; /* simple SELECT list entry (field without expression) */ - if (trans[i]->type() != Item::FIELD_ITEM) + if ((*trans)->type() != Item::FIELD_ITEM) DBUG_RETURN(TRUE); - if (((Item_field *)trans[i])->field->type() == Field::NEXT_NUMBER) + field= (Item_field *)(*trans); + if (field->field->unireg_check == Field::NEXT_NUMBER) view->contain_auto_increment= 1; /* prepare unique test */ - ((Item_field *)trans[i])->field->query_id= other_query_id; + field->field->query_id= other_query_id; } /* unique test */ - for (i= 0; i < num; i++) + for (trans= trans_start; trans != trans_end; trans++) { - Item_field *field= (Item_field *)trans[i]; + /* Thanks to test above, we know that all columns are of type Item_field */ + Item_field *field= (Item_field *)(*trans); if (field->field->query_id == query_id) DBUG_RETURN(TRUE); field->field->query_id= query_id; } /* VIEW contain all fields without default value */ - for (; *field_ptr; ++field_ptr) + for (; *field_ptr; field_ptr++) { Field *field= *field_ptr; /* field have not default value */ @@ -502,14 +517,13 @@ static bool check_view_insertability(TABLE_LIST *view, ulong query_id) (table->timestamp_field != field || field->unireg_check == Field::TIMESTAMP_UN_FIELD)) { - uint i= 0; - for (; i < num; i++) + for (trans= trans_start; ; trans++) { - if (((Item_field *)trans[i])->field == *field_ptr) - break; + if (trans == trans_end) + DBUG_RETURN(TRUE); // Field was not part of view + if (((Item_field *)(*trans))->field == *field_ptr) + break; // ok } - if (i >= num) - DBUG_RETURN(TRUE); } } DBUG_RETURN(FALSE); @@ -517,29 +531,28 @@ static bool check_view_insertability(TABLE_LIST *view, ulong query_id) /* - Prepare items in INSERT statement + Check if table can be updated SYNOPSIS - mysql_prepare_insert() - thd - thread handler - table_list - global/local table list - - RETURN VALUE - 0 - OK - -1 - error (message is not sent to user) + mysql_prepare_insert_check_table() + thd Thread handle + table_list Table list (only one table) + fields List of fields to be updated + where Pointer to where clause + + RETURN + 0 ok + 1 ERROR */ -int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, - List<Item> &fields, List_item *values, - List<Item> &update_fields, List<Item> &update_values, - enum_duplicates duplic) + +static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, + List<Item> &fields, COND **where) { bool insert_into_view= (table_list->view != 0); - DBUG_ENTER("mysql_prepare_insert"); + DBUG_ENTER("mysql_prepare_insert_check_table"); - /* TODO: use this condition for 'WHITH CHECK OPTION' */ - Item *unused_conds= 0; - if (setup_tables(thd, table_list, &unused_conds)) - DBUG_RETURN(-1); + if (setup_tables(thd, table_list, where)) + DBUG_RETURN(1); if (insert_into_view && !fields.elements) { @@ -553,18 +566,51 @@ int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, check_view_insertability(table_list, thd->query_id))) { my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT"); - DBUG_RETURN(-1); + DBUG_RETURN(1); } + DBUG_RETURN(0); +} + + +/* + Prepare items in INSERT statement + + SYNOPSIS + mysql_prepare_insert() + thd Thread handler + table_list Global/local table list + + RETURN VALUE + 0 OK + -1 error (message is not sent to user) +*/ + +int mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, + List<Item> &fields, List_item *values, + List<Item> &update_fields, List<Item> &update_values, + enum_duplicates duplic) +{ + bool insert_into_view= (table_list->view != 0); + /* TODO: use this condition for 'WITH CHECK OPTION' */ + Item *unused_conds= 0; + int res; + DBUG_ENTER("mysql_prepare_insert"); + + if (mysql_prepare_insert_check_table(thd, table_list, fields, &unused_conds)) + DBUG_RETURN(-1); if (check_insert_fields(thd, table_list, fields, *values, 1, !insert_into_view) || setup_fields(thd, 0, table_list, *values, 0, 0, 0) || (duplic == DUP_UPDATE && - (setup_fields(thd, 0, table_list, update_fields, 0, 0, 0) || + ((thd->lex->select_lex.no_wrap_view_item= 1, + (res= setup_fields(thd, 0, table_list, update_fields, 0, 0, 0)), + thd->lex->select_lex.no_wrap_view_item= 0, + res) || setup_fields(thd, 0, table_list, update_values, 0, 0, 0)))) DBUG_RETURN(-1); - if (find_real_table_in_list(table_list->next_global, + if (find_table_in_global_list(table_list->next_global, table_list->db, table_list->real_name)) { my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); @@ -1566,27 +1612,11 @@ bool delayed_insert::handle_inserts(void) int mysql_insert_select_prepare(THD *thd) { LEX *lex= thd->lex; - TABLE_LIST *table_list= lex->query_tables; - bool insert_into_view= (table_list->view != 0); DBUG_ENTER("mysql_insert_select_prepare"); - - if (setup_tables(thd, table_list, &lex->select_lex.where)) + if (mysql_prepare_insert_check_table(thd, lex->query_tables, + lex->field_list, + &lex->select_lex.where)) DBUG_RETURN(-1); - - if (insert_into_view && !lex->field_list.elements) - { - lex->empty_field_list_on_rset= 1; - insert_view_fields(&lex->field_list, table_list); - } - - if (!table_list->updatable || - check_key_in_view(thd, table_list) || - (insert_into_view && - check_view_insertability(table_list, thd->query_id))) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "INSERT"); - DBUG_RETURN(-1); - } DBUG_RETURN(0); } @@ -1844,9 +1874,13 @@ void select_create::abort() table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); enum db_type table_type=table->db_type; if (!table->tmp_table) + { hash_delete(&open_cache,(byte*) table); - if (!create_info->table_existed) - quick_rm_table(table_type, create_table->db, create_table->real_name); + if (!create_info->table_existed) + quick_rm_table(table_type, create_table->db, create_table->real_name); + } + else if (!create_info->table_existed) + close_temporary_table(thd, create_table->db, create_table->real_name); table=0; } VOID(pthread_mutex_unlock(&LOCK_open)); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 8a1b59d9585..a809a0dc1b4 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -112,15 +112,14 @@ void lex_free(void) (We already do too much here) */ -LEX *lex_start(THD *thd, uchar *buf,uint length) +void lex_start(THD *thd, uchar *buf,uint length) { 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->buf= lex->ptr= buf; + lex->end_of_query=buf+length; lex->yylineno = 1; - lex->select_lex.parsing_place= SELECT_LEX_NODE::NO_MATTER; lex->in_comment=0; lex->length=0; lex->select_lex.in_sum_expr=0; @@ -143,7 +142,6 @@ LEX *lex_start(THD *thd, uchar *buf,uint length) hash_init(&lex->spfuns, system_charset_info, 0, 0, 0, sp_lex_spfuns_key, 0, 0); - return lex; } void lex_end(LEX *lex) @@ -1013,6 +1011,8 @@ void st_select_lex::init_query() subquery_in_having= explicit_limit= 0; first_execution= 1; first_cond_optimization= 1; + parsing_place= SELECT_LEX_NODE::NO_MATTER; + no_wrap_view_item= 0; } void st_select_lex::init_select() @@ -1026,9 +1026,9 @@ void st_select_lex::init_select() in_sum_expr= with_wild= 0; options= 0; braces= 0; - when_list.empty(); + when_list.empty(); expr_list.empty(); - interval_list.empty(); + interval_list.empty(); use_index.empty(); ftfunc_list_alloc.empty(); ftfunc_list= &ftfunc_list_alloc; @@ -1039,7 +1039,6 @@ void st_select_lex::init_select() select_limit= HA_POS_ERROR; offset_limit= 0; with_sum_func= 0; - parsing_place= SELECT_LEX_NODE::NO_MATTER; } /* @@ -1059,7 +1058,7 @@ void st_select_lex_node::include_down(st_select_lex_node *upper) /* include on level down (but do not link) - + SYNOPSYS st_select_lex_node::include_standalone() upper - reference on node underr which this node should be included @@ -1429,7 +1428,7 @@ bool st_select_lex_unit::check_updateable(char *db, char *table) */ bool st_select_lex::check_updateable(char *db, char *table) { - if (find_real_table_in_local_list(get_table_list(), db, table)) + if (find_table_in_local_list(get_table_list(), db, table)) return 1; for (SELECT_LEX_UNIT *un= first_inner_unit(); @@ -1478,7 +1477,14 @@ void st_select_lex::print_order(String *str, ORDER *order) { for (; order; order= order->next) { - (*order->item)->print(str); + if (order->counter_used) + { + char buffer[20]; + my_snprintf(buffer, 20, "%u", order->counter); + str->append(buffer); + } + else + (*order->item)->print(str); if (!order->asc) str->append(" desc", 5); if (order->next) @@ -1489,8 +1495,9 @@ void st_select_lex::print_order(String *str, ORDER *order) void st_select_lex::print_limit(THD *thd, String *str) { - Item_subselect *item= master_unit()->item; - if (item && + SELECT_LEX_UNIT *unit= master_unit(); + Item_subselect *item= unit->item; + if (item && unit->global_parameters == this && (item->substype() == Item_subselect::EXISTS_SUBS || item->substype() == Item_subselect::IN_SUBS || item->substype() == Item_subselect::ALL_SUBS)) @@ -1547,13 +1554,14 @@ bool st_lex::can_be_merged() select_lex.order_list.elements == 0 && select_lex.group_list.elements == 0 && select_lex.having == 0 && + select_lex.with_sum_func == 0 && select_lex.table_list.elements == 1 && !(select_lex.options & SELECT_DISTINCT) && select_lex.select_limit == HA_POS_ERROR); } /* - check if command can use VIEW with MERGE algorithm + check if command can use VIEW with MERGE algorithm (for top VIEWs) SYNOPSIS st_lex::can_use_merged() @@ -1584,6 +1592,29 @@ bool st_lex::can_use_merged() } /* + check if command can't use merged views in any part of command + + SYNOPSIS + st_lex::can_not_use_merged() + + RETURN + FALSE - command can't use merged VIEWs + TRUE - VIEWs with MERGE algorithms can be used +*/ + +bool st_lex::can_not_use_merged() +{ + switch (sql_command) + { + case SQLCOM_CREATE_VIEW: + case SQLCOM_SHOW_CREATE: + return TRUE; + default: + return FALSE; + } +} + +/* Detect that we need only table structure of derived table/view SYNOPSIS @@ -1638,14 +1669,16 @@ void st_select_lex_unit::set_limit(SELECT_LEX *values, SYNOPSIS unlink_first_table() - link_to_local do we need link this table to local + link_to_local Set to 1 if caller should link this table to local NOTES We rely on fact that first table in both list are same or local list is empty RETURN + 0 If 'query_tables' == 0 unlinked table + In this case link_to_local is set. */ TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local) @@ -1699,11 +1732,11 @@ void st_lex::first_lists_tables_same() TABLE_LIST *first_table= (TABLE_LIST*) select_lex.table_list.first; if (query_tables != first_table && first_table != 0) { + TABLE_LIST *next; if (query_tables_last == &first_table->next_global) query_tables_last= first_table->prev_global; - TABLE_LIST *next= *first_table->prev_global= first_table->next_global; - first_table->next_global= 0; - if (next) + + if ((next= *first_table->prev_global= first_table->next_global)) next->prev_global= first_table->prev_global; /* include in new place */ first_table->next_global= query_tables; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 83154b277f5..803a8ef2a23 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -407,7 +407,7 @@ public: int change_result(select_subselect *result, select_subselect *old_result); void set_limit(st_select_lex *values, st_select_lex *sl); - friend void mysql_init_query(THD *thd, bool lexonly); + friend void mysql_init_query(THD *thd, uchar *buf, uint length, bool lexonly); friend int subselect_union_engine::exec(); }; typedef class st_select_lex_unit SELECT_LEX_UNIT; @@ -479,6 +479,8 @@ public: bool subquery_in_having; bool first_execution; /* first execution in SP or PS */ bool first_cond_optimization; + /* do not wrap view fields with Item_ref */ + bool no_wrap_view_item; /* SELECT for SELECT command st_select_lex. Used to privent scaning @@ -561,7 +563,7 @@ public: bool test_limit(); - friend void mysql_init_query(THD *thd, bool lexonly); + friend void mysql_init_query(THD *thd, uchar *buf, uint length, bool lexonly); st_select_lex() {} void make_empty_select() { @@ -649,6 +651,7 @@ typedef struct st_lex TABLE_LIST *query_tables; /* global list of all tables in this query */ /* last element next_global of previous list */ TABLE_LIST **query_tables_last; + TABLE_LIST *proc_table; /* refer to mysql.proc if it was opened by VIEW */ List<key_part_spec> col_list; List<key_part_spec> ref_list; @@ -765,6 +768,7 @@ typedef struct st_lex bool can_be_merged(); bool can_use_merged(); + bool can_not_use_merged(); bool only_view_structure(); } LEX; @@ -783,7 +787,7 @@ struct st_lex_local: public st_lex void lex_init(void); void lex_free(void); -LEX *lex_start(THD *thd, uchar *buf,uint length); +void lex_start(THD *thd, uchar *buf,uint length); void lex_end(LEX *lex); extern pthread_key(LEX*,THR_LEX); diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 47295d4c6a0..b629493a692 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -99,8 +99,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, loaded is located */ char *tdb= thd->db ? thd->db : db; // Result is never null - bool transactional_table, log_delayed; ulong skip_lines= ex->skip_lines; + int res; + bool transactional_table, log_delayed; DBUG_ENTER("mysql_load"); #ifdef EMBEDDED_LIBRARY @@ -114,8 +115,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, DBUG_RETURN(-1); } table_list->lock_type= lock_type; - if (open_and_lock_tables(thd, table_list)) - DBUG_RETURN(-1); + if ((res= open_and_lock_tables(thd, table_list))) + DBUG_RETURN(res); /* TODO: add key check when we will support VIEWs in LOAD */ if (!table_list->updatable) { @@ -320,6 +321,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, { if (transactional_table) ha_autocommit_or_rollback(thd,error); + + if (read_file_from_client) + while (!read_info.next_line()) + ; + #ifndef EMBEDDED_LIBRARY if (mysql_bin_log.is_open()) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 212339151a4..f4fb80daa88 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -78,6 +78,7 @@ const char *command_name[]={ "Connect","Kill","Debug","Ping","Time","Delayed_insert","Change user", "Binlog Dump","Table Dump", "Connect Out", "Register Slave", "Prepare", "Prepare Execute", "Long Data", "Close stmt", + "Reset stmt", "Set option", "Fetch", "Error" // Last command number }; @@ -1452,6 +1453,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, mysql_stmt_execute(thd, packet, packet_length); break; } + case COM_FETCH: + { + mysql_stmt_fetch(thd, packet, packet_length); + break; + } case COM_LONG_DATA: { mysql_stmt_get_longdata(thd, packet, packet_length); @@ -1545,7 +1551,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, send_error(thd,ER_NO_DB_ERROR); break; } - thd->free_list=0; pend= strend(packet); thd->convert_string(&conv_name, system_charset_info, packet, (uint) (pend-packet), thd->charset()); @@ -1567,6 +1572,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; mysqld_list_fields(thd,&table_list,fields); free_items(thd->free_list); + thd->free_list=0; /* free_list should never point to garbage */ break; } #endif @@ -1905,7 +1911,7 @@ mysql_execute_command(THD *thd) DBUG_ASSERT(first_table == all_tables && first_table != 0); */ lex->first_lists_tables_same(); - /* should be assigned after making firts tables same */ + /* should be assigned after making first tables same */ all_tables= lex->query_tables; if (lex->sql_command != SQLCOM_CREATE_PROCEDURE && @@ -2131,7 +2137,7 @@ mysql_execute_command(THD *thd) case SQLCOM_DO: if (all_tables && ((res= check_table_access(thd, SELECT_ACL, all_tables, 0)) || - (res= open_and_lock_tables(thd, all_tables)))) + (res= open_and_lock_tables(thd, all_tables)))) break; res= mysql_do(thd, *lex->insert_list); @@ -2381,8 +2387,8 @@ mysql_execute_command(THD *thd) of query */ if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && - find_real_table_in_list(select_tables, create_table->db, - create_table->real_name)) + find_table_in_global_list(select_tables, create_table->db, + create_table->real_name)) { net_printf(thd, ER_UPDATE_TABLE_USED, create_table->real_name); goto create_error; @@ -2395,7 +2401,6 @@ mysql_execute_command(THD *thd) if (!(res= open_and_lock_tables(thd, select_tables))) { - res= -1; // If error if ((result= new select_create(create_table, &lex->create_info, lex->create_list, @@ -2752,7 +2757,7 @@ unsent_create_error: unit->set_limit(select_lex, select_lex); // is table which we are changing used somewhere in other parts of query - if (find_real_table_in_list(all_tables->next_global, + if (find_table_in_global_list(all_tables->next_global, first_table->db, first_table->real_name)) { /* Using same table for INSERT and SELECT */ @@ -3579,21 +3584,25 @@ purposes internal to the MySQL server", MYF(0)); switch (res) { case SP_OK: send_ok(thd); + lex->unit.cleanup(); delete lex->sphead; lex->sphead= 0; break; case SP_WRITE_ROW_FAILED: net_printf(thd, ER_SP_ALREADY_EXISTS, SP_TYPE_STRING(lex), name); + lex->unit.cleanup(); delete lex->sphead; lex->sphead= 0; goto error; case SP_NO_DB_ERROR: - net_printf(thd, ER_BAD_DB_ERROR, lex->sphead->m_db); + net_printf(thd, ER_BAD_DB_ERROR, lex->sphead->m_db.str); + lex->unit.cleanup(); delete lex->sphead; lex->sphead= 0; goto error; default: net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name); + lex->unit.cleanup(); delete lex->sphead; lex->sphead= 0; goto error; @@ -3921,7 +3930,7 @@ error: 1 - access denied, error is sent to client */ -int check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) +bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) { if (check_access(thd, privilege, all_tables->db, &all_tables->grant.privilege, 0, 0)) @@ -3965,13 +3974,13 @@ bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, bool dont_check_global_grants, bool no_errors) { - DBUG_ENTER("check_access"); - DBUG_PRINT("enter",("want_access: %lu master_access: %lu", want_access, - thd->master_access)); #ifndef NO_EMBEDDED_ACCESS_CHECKS ulong db_access; #endif ulong dummy; + DBUG_ENTER("check_access"); + DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu", + db ? db : "", want_access, thd->master_access)); if (save_priv) *save_priv=0; else @@ -3979,8 +3988,9 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if ((!db || !db[0]) && !thd->db && !dont_check_global_grants) { + DBUG_PRINT("error",("No database")); if (!no_errors) - send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */ + send_error(thd,ER_NO_DB_ERROR); /* purecov: tested */ DBUG_RETURN(TRUE); /* purecov: tested */ } @@ -4005,6 +4015,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) || ! db && dont_check_global_grants) { // We can never grant this + DBUG_PRINT("error",("No possible access")); if (!no_errors) net_printf(thd,ER_ACCESS_DENIED_ERROR, thd->priv_user, @@ -4023,13 +4034,17 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, db_access=thd->db_access; /* Remove SHOW attribute and access rights we already have */ want_access &= ~(thd->master_access | EXTRA_ACL); + DBUG_PRINT("info",("db_access: %lu want_access: %lu", + db_access, want_access)); db_access= ((*save_priv=(db_access | thd->master_access)) & want_access); /* grant_option is set if there exists a single table or column grant */ if (db_access == want_access || - ((grant_option && !dont_check_global_grants) && + (grant_option && !dont_check_global_grants && !(want_access & ~(db_access | TABLE_ACLS)))) DBUG_RETURN(FALSE); /* Ok */ + + DBUG_PRINT("error",("Access denied")); if (!no_errors) net_printf(thd,ER_DBACCESS_DENIED_ERROR, thd->priv_user, @@ -4117,6 +4132,42 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, return FALSE; } + +/* + Check if the given table has any of the asked privileges + + SYNOPSIS + check_some_access() + thd Thread handler + want_access Bitmap of possible privileges to check for + + RETURN + 0 ok + 1 error +*/ + + +bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) +{ + ulong access; + DBUG_ENTER("check_some_access"); + + /* This loop will work as long as we have less than 32 privileges */ + for (access= 1; access < want_access ; access<<= 1) + { + if (access & want_access) + { + if (!check_access(thd, access, table->db, + &table->grant.privilege, 0, 1) && + !grant_option || !check_grant(thd, access, table, 0, 1, 1)) + DBUG_RETURN(0); + } + } + DBUG_PRINT("exit",("no matching access rights")); + DBUG_RETURN(1); +} + + bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list) { @@ -4223,7 +4274,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) ****************************************************************************/ void -mysql_init_query(THD *thd, bool lexonly) +mysql_init_query(THD *thd, uchar *buf, uint length, bool lexonly) { DBUG_ENTER("mysql_init_query"); LEX *lex= thd->lex; @@ -4252,11 +4303,12 @@ mysql_init_query(THD *thd, bool lexonly) lex->lock_option= TL_READ; lex->found_colon= 0; lex->safe_to_cache_query= 1; - lex->query_tables= 0; + lex->proc_table= lex->query_tables= 0; lex->query_tables_last= &lex->query_tables; lex->variables_used= 0; lex->select_lex.parent_lex= lex; lex->empty_field_list_on_rset= 0; + lex_start(thd, buf, length); if (! lexonly) { thd->select_number= lex->select_lex.select_number= 1; @@ -4405,10 +4457,10 @@ void mysql_parse(THD *thd, char *inBuf, uint length) { DBUG_ENTER("mysql_parse"); - mysql_init_query(thd); + mysql_init_query(thd, (uchar*) inBuf, length); if (query_cache_send_result_to_client(thd, inBuf, length) <= 0) { - LEX *lex=lex_start(thd, (uchar*) inBuf, length); + LEX *lex= thd->lex; if (!yyparse((void *)thd) && ! thd->is_fatal_error) { #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -4456,6 +4508,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) } thd->proc_info="freeing items"; free_items(thd->free_list); /* Free strings used by items */ + thd->free_list= 0; /* free_list should never point to garbage */ lex_end(lex); } DBUG_VOID_RETURN; @@ -4474,16 +4527,16 @@ void mysql_parse(THD *thd, char *inBuf, uint length) bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) { - LEX *lex; + LEX *lex= thd->lex; bool error= 0; DBUG_ENTER("mysql_test_parse_for_slave"); - mysql_init_query(thd); - lex= lex_start(thd, (uchar*) inBuf, length); + mysql_init_query(thd, (uchar*) inBuf, length); if (!yyparse((void*) thd) && ! thd->is_fatal_error && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) error= 1; /* Ignore question */ free_items(thd->free_list); /* Free strings used by items */ + thd->free_list= 0; /* free_list should never point to garbage */ lex_end(lex); DBUG_RETURN(error); @@ -4939,6 +4992,7 @@ bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc) order->asc = asc; order->free_me=0; order->used=0; + order->counter_used= 0; list.link_in_list((byte*) order,(byte**) &order->next); DBUG_RETURN(0); } @@ -4974,6 +5028,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, { register TABLE_LIST *ptr; char *alias_str; + LEX *lex= thd->lex; DBUG_ENTER("add_table_to_list"); if (!table) @@ -5025,7 +5080,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX); ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES); ptr->derived= table->sel; - ptr->select_lex= thd->lex->current_select; + ptr->select_lex= lex->current_select; ptr->cacheable_table= 1; if (use_index_arg) ptr->use_index=(List<String> *) thd->memdup((gptr) use_index_arg, @@ -5049,8 +5104,9 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, } } } + /* Link table in local list (list for current select) */ table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local); - LEX *lex= thd->lex; + /* Link table in global list (all used tables) */ *(ptr->prev_global= lex->query_tables_last)= ptr; lex->query_tables_last= &ptr->next_global; DBUG_RETURN(ptr); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 5214ae95b86..92e235399da 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -75,6 +75,8 @@ Long data handling: #ifdef EMBEDDED_LIBRARY /* include MYSQL_BIND headers */ #include <mysql.h> +#else +#include <mysql_com.h> #endif /****************************************************************************** @@ -107,7 +109,7 @@ public: }; static void execute_stmt(THD *thd, Prepared_statement *stmt, - String *expanded_query, bool set_context); + String *expanded_query); /****************************************************************************** Implementation @@ -166,7 +168,8 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) return my_net_write(net, buff, sizeof(buff)) || (stmt->param_count && stmt->thd->protocol_simple.send_fields((List<Item> *) - &stmt->lex->param_list, 0)) || + &stmt->lex->param_list, + Protocol::SEND_EOF)) || net_flush(net); return 0; } @@ -894,10 +897,10 @@ static int mysql_test_insert(Prepared_statement *stmt, tables & preparation procedure */ thd->allocate_temporary_memory_pool_for_ps_preparing(); - if (open_and_lock_tables(thd, table_list)) + if ((res= open_and_lock_tables(thd, table_list))) { thd->free_temporary_memory_pool_for_ps_preparing(); - DBUG_RETURN(-1); + DBUG_RETURN(res); } if ((values= its++)) @@ -966,20 +969,26 @@ static int mysql_test_update(Prepared_statement *stmt, */ thd->allocate_temporary_memory_pool_for_ps_preparing(); - if (open_and_lock_tables(thd, table_list)) - res= -1; - else + if (!(res= open_and_lock_tables(thd, table_list))) { if (!(res= mysql_prepare_update(thd, table_list, &select->where, select->order_list.elements, (ORDER *) select->order_list.first))) { - if (setup_fields(thd, 0, table_list, - select->item_list, 1, 0, 0) || - setup_fields(thd, 0, table_list, - stmt->lex->value_list, 0, 0, 0)) - res= -1; + thd->lex->select_lex.no_wrap_view_item= 1; + if (setup_fields(thd, 0, table_list, select->item_list, 1, 0, 0)) + { + res= -1; + thd->lex->select_lex.no_wrap_view_item= 0; + } + else + { + thd->lex->select_lex.no_wrap_view_item= 0; + if (setup_fields(thd, 0, table_list, + stmt->lex->value_list, 0, 0, 0)) + res= -1; + } } stmt->lex->unit.cleanup(); } @@ -1019,9 +1028,7 @@ static int mysql_test_delete(Prepared_statement *stmt, */ thd->allocate_temporary_memory_pool_for_ps_preparing(); - if (open_and_lock_tables(thd, table_list)) - res= -1; - else + if (!(res= open_and_lock_tables(thd, table_list))) { res= mysql_prepare_delete(thd, table_list, &lex->select_lex.where); lex->unit.cleanup(); @@ -1054,6 +1061,7 @@ static int mysql_test_select(Prepared_statement *stmt, THD *thd= stmt->thd; LEX *lex= stmt->lex; SELECT_LEX_UNIT *unit= &lex->unit; + int res; DBUG_ENTER("mysql_test_select"); @@ -1073,11 +1081,11 @@ static int mysql_test_select(Prepared_statement *stmt, tables & preparation procedure */ thd->allocate_temporary_memory_pool_for_ps_preparing(); - if (open_and_lock_tables(thd, tables)) + if ((res= open_and_lock_tables(thd, tables))) { - send_error(thd); goto err; } + res= 1; thd->used_tables= 0; // Updated by setup_fields @@ -1098,7 +1106,8 @@ static int mysql_test_select(Prepared_statement *stmt, if (!text_protocol) { if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) || - thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0) + thd->protocol_simple.send_fields(&lex->select_lex.item_list, + Protocol::SEND_EOF) #ifndef EMBEDDED_LIBRARY || net_flush(&thd->net) #endif @@ -1114,7 +1123,7 @@ err_prep: unit->cleanup(); err: thd->free_temporary_memory_pool_for_ps_preparing(); - DBUG_RETURN(1); + DBUG_RETURN(res); } @@ -1476,6 +1485,12 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol) case SQLCOM_SHOW_GRANTS: case SQLCOM_DROP_TABLE: case SQLCOM_RENAME_TABLE: + case SQLCOM_ALTER_TABLE: + case SQLCOM_COMMIT: + case SQLCOM_CREATE_INDEX: + case SQLCOM_DROP_INDEX: + case SQLCOM_ROLLBACK: + case SQLCOM_TRUNCATE: break; default: @@ -1611,8 +1626,8 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, mysql_log.write(thd, COM_PREPARE, "%s", packet); thd->current_arena= stmt; - lex= lex_start(thd, (uchar *) thd->query, thd->query_length); - mysql_init_query(thd); + mysql_init_query(thd, (uchar *) thd->query, thd->query_length); + lex= thd->lex; lex->safe_to_cache_query= 0; error= yyparse((void *)thd) || thd->is_fatal_error || @@ -1665,8 +1680,8 @@ void reset_stmt_for_execute(THD *thd, LEX *lex) if (lex->empty_field_list_on_rset) { - lex->field_list.empty(); lex->empty_field_list_on_rset= 0; + lex->field_list.empty(); } for (; sl; sl= sl->next_select_in_list()) { @@ -1679,7 +1694,10 @@ void reset_stmt_for_execute(THD *thd, LEX *lex) Copy WHERE clause pointers to avoid damaging they by optimisation */ if (sl->prep_where) + { sl->where= sl->prep_where->copy_andor_structure(thd); + sl->where->cleanup(); + } DBUG_ASSERT(sl->join == 0); ORDER *order; /* Fix GROUP list */ @@ -1753,6 +1771,7 @@ static void reset_stmt_params(Prepared_statement *stmt) void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) { ulong stmt_id= uint4korr(packet); + ulong flags= (ulong) ((uchar) packet[4]); /* Query text for binary log, or empty string if the query is not put into binary log. @@ -1779,6 +1798,28 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) DBUG_VOID_RETURN; } + if (flags & (ulong) CURSOR_TYPE_READ_ONLY) + { + if (stmt->lex->result) + { + /* + If lex->result is set in the parser, this is not a SELECT + statement: we can't open a cursor for it. + */ + flags= 0; + } + else + { + if (!stmt->cursor && + !(stmt->cursor= new (&stmt->mem_root) Cursor())) + { + send_error(thd, ER_OUT_OF_RESOURCES); + DBUG_VOID_RETURN; + } + /* If lex->result is set, mysql_execute_command will use it */ + stmt->lex->result= &stmt->cursor->result; + } + } #ifndef EMBEDDED_LIBRARY if (stmt->param_count) { @@ -1797,16 +1838,55 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query)) goto set_params_data_err; #endif + thd->stmt_backup.set_statement(thd); + thd->set_statement(stmt); thd->current_arena= stmt; + reset_stmt_for_execute(thd, stmt->lex); + /* From now cursors assume that thd->mem_root is clean */ + if (expanded_query.length() && + alloc_query(thd, (char *)expanded_query.ptr(), + expanded_query.length()+1)) + { + my_error(ER_OUTOFMEMORY, 0, expanded_query.length()); + goto err; + } + thd->protocol= &thd->protocol_prep; // Switch to binary protocol - execute_stmt(thd, stmt, &expanded_query, true); + if (!(specialflag & SPECIAL_NO_PRIOR)) + my_pthread_setprio(pthread_self(),QUERY_PRIOR); + mysql_execute_command(thd); + if (!(specialflag & SPECIAL_NO_PRIOR)) + my_pthread_setprio(pthread_self(), WAIT_PRIOR); thd->protocol= &thd->protocol_simple; // Use normal protocol + + if (flags & (ulong) CURSOR_TYPE_READ_ONLY) + { + if (stmt->cursor->is_open()) + stmt->cursor->init_from_thd(thd); + thd->set_item_arena(&thd->stmt_backup); + } + else + { + thd->lex->unit.cleanup(); + cleanup_items(stmt->free_list); + reset_stmt_params(stmt); + close_thread_tables(thd); /* to close derived tables */ + /* + Free items that were created during this execution of the PS by + query optimizer. + */ + free_items(thd->free_list); + thd->free_list= 0; + } + + thd->set_statement(&thd->stmt_backup); thd->current_arena= 0; DBUG_VOID_RETURN; set_params_data_err: reset_stmt_params(stmt); my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute"); +err: send_error(thd); DBUG_VOID_RETURN; } @@ -1842,7 +1922,6 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) DBUG_VOID_RETURN; } - thd->free_list= NULL; thd->stmt_backup.set_statement(thd); thd->set_statement(stmt); if (stmt->set_params_from_vars(stmt, @@ -1853,7 +1932,7 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) send_error(thd); } thd->current_arena= stmt; - execute_stmt(thd, stmt, &expanded_query, false); + execute_stmt(thd, stmt, &expanded_query); thd->current_arena= 0; DBUG_VOID_RETURN; } @@ -1869,20 +1948,13 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) placeholders replaced with actual values. Otherwise empty string. NOTES - Caller must set parameter values and thd::protocol. - thd->free_list is assumed to be garbage. + Caller must set parameter values and thd::protocol. */ static void execute_stmt(THD *thd, Prepared_statement *stmt, - String *expanded_query, bool set_context) + String *expanded_query) { DBUG_ENTER("execute_stmt"); - if (set_context) - { - thd->free_list= NULL; - thd->stmt_backup.set_statement(thd); - thd->set_statement(stmt); - } reset_stmt_for_execute(thd, stmt->lex); if (expanded_query->length() && @@ -1896,22 +1968,77 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); mysql_execute_command(thd); - thd->lex->unit.cleanup(); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); + thd->lex->unit.cleanup(); cleanup_items(stmt->free_list); reset_stmt_params(stmt); close_thread_tables(thd); // to close derived tables thd->set_statement(&thd->stmt_backup); /* Free Items that were created during this execution of the PS. */ free_items(thd->free_list); + /* + In the rest of prepared statements code we assume that free_list + never points to garbage: keep this predicate true. + */ thd->free_list= 0; DBUG_VOID_RETURN; } /* + COM_FETCH handler: fetches requested amount of rows from cursor + SYNOPSIS +*/ + +void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) +{ + /* assume there is always place for 8-16 bytes */ + ulong stmt_id= uint4korr(packet); + ulong num_rows= uint4korr(packet+=4); + Statement *stmt; + int error; + + DBUG_ENTER("mysql_stmt_fetch"); + + if (!(stmt= thd->stmt_map.find(stmt_id)) || + !stmt->cursor || + !stmt->cursor->is_open()) + { + my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_id, "fetch"); + send_error(thd); + DBUG_VOID_RETURN; + } + + thd->stmt_backup.set_statement(thd); + thd->stmt_backup.set_item_arena(thd); + thd->set_statement(stmt); + stmt->cursor->init_thd(thd); + + if (!(specialflag & SPECIAL_NO_PRIOR)) + my_pthread_setprio(pthread_self(), QUERY_PRIOR); + + thd->protocol= &thd->protocol_prep; // Switch to binary protocol + error= stmt->cursor->fetch(num_rows); + thd->protocol= &thd->protocol_simple; // Use normal protocol + + if (!(specialflag & SPECIAL_NO_PRIOR)) + my_pthread_setprio(pthread_self(), WAIT_PRIOR); + + /* Restore THD state */ + stmt->cursor->reset_thd(thd); + thd->set_statement(&thd->stmt_backup); + thd->set_item_arena(&thd->stmt_backup); + + if (error && error != -4) + send_error(thd, ER_OUT_OF_RESOURCES); + + DBUG_VOID_RETURN; +} + + +/* Reset a prepared statement in case there was a recoverable error. SYNOPSIS mysql_stmt_reset() @@ -2081,8 +2208,11 @@ void Prepared_statement::setup_set_params() } } + Prepared_statement::~Prepared_statement() { + if (cursor) + cursor->Cursor::~Cursor(); free_items(free_list); } diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 98bf4e86aba..9e38a65d412 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1297,7 +1297,8 @@ int show_binlog_events(THD* thd) Format_description_log_event(3); /* MySQL 4.0 by default */ Log_event::init_show_field_list(&field_list); - if (protocol-> send_fields(&field_list, 1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(-1); if (mysql_bin_log.is_open()) @@ -1426,7 +1427,8 @@ int show_binlog_info(THD* thd) field_list.push_back(new Item_empty_string("Binlog_Do_DB",255)); field_list.push_back(new Item_empty_string("Binlog_Ignore_DB",255)); - if (protocol->send_fields(&field_list, 1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(-1); protocol->prepare_for_resend(); @@ -1476,7 +1478,8 @@ int show_binlogs(THD* thd) } field_list.push_back(new Item_empty_string("Log_name", 255)); - if (protocol->send_fields(&field_list, 1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); mysql_bin_log.lock_index(); index_file=mysql_bin_log.get_index_file(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index db5c933545b..a7ce5320ae4 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -225,25 +225,18 @@ int handle_select(THD *thd, LEX *lex, select_result *result) thd->net.report_error)); if (thd->net.report_error) res= 1; - if (res > 0) + if (unlikely(res)) { if (result) { - result->send_error(0, NullS); + if (res > 0) + result->send_error(0, NullS); result->abort(); } - else + else if (res > 0) send_error(thd, 0, NullS); res= 1; // Error sent to client } - if (res < 0) - { - if (result) - { - result->abort(); - } - res= 1; - } if (result != lex->result) delete result; DBUG_RETURN(res); @@ -348,9 +341,7 @@ JOIN::prepare(Item ***rref_pointer_array, if ((subselect= select_lex->master_unit()->item)) { Item_subselect::trans_res res; - if ((res= ((!thd->lex->view_prepare_mode) ? - subselect->select_transformer(this) : - subselect->no_select_transform())) != + if ((res= subselect->select_transformer(this)) != Item_subselect::RES_OK) { select_lex->fix_prepare_information(thd, &conds); @@ -552,6 +543,7 @@ JOIN::optimize() if (cond_value == Item::COND_FALSE || (!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS))) { /* Impossible cond */ + DBUG_PRINT("info", ("Impossible WHERE")); zero_result_cause= "Impossible WHERE"; error= 0; DBUG_RETURN(0); @@ -569,20 +561,24 @@ JOIN::optimize() { if (res > 1) { + DBUG_PRINT("error",("Error from opt_sum_query")); DBUG_RETURN(1); } if (res < 0) { + DBUG_PRINT("info",("No matching min/max row")); zero_result_cause= "No matching min/max row"; error=0; DBUG_RETURN(0); } + DBUG_PRINT("info",("Select tables optimized away")); zero_result_cause= "Select tables optimized away"; tables_list= 0; // All tables resolved } } if (!tables_list) { + DBUG_PRINT("info",("No tables")); error= 0; DBUG_RETURN(0); } @@ -1105,7 +1101,8 @@ JOIN::exec() (zero_result_cause?zero_result_cause:"No tables used")); else { - result->send_fields(fields_list,1); + result->send_fields(fields_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); if (!having || having->val_int()) { if (do_send_rows && (procedure ? (procedure->send_row(fields_list) || @@ -1512,12 +1509,45 @@ JOIN::exec() DBUG_VOID_RETURN; } } + /* XXX: When can we have here thd->net.report_error not zero? */ + if (thd->net.report_error) + { + error= thd->net.report_error; + DBUG_VOID_RETURN; + } curr_join->having= curr_join->tmp_having; - thd->proc_info="Sending data"; - error= thd->net.report_error || - do_select(curr_join, curr_fields_list, NULL, procedure); - thd->limit_found_rows= curr_join->send_records; - thd->examined_row_count= curr_join->examined_rows; + curr_join->fields= curr_fields_list; + curr_join->procedure= procedure; + + if (unit == &thd->lex->unit && + (unit->fake_select_lex == 0 || select_lex == unit->fake_select_lex) && + thd->cursor && tables != const_tables) + { + /* + We are here if this is JOIN::exec for the last select of the main unit + and the client requested to open a cursor. + We check that not all tables are constant because this case is not + handled by do_select() separately, and this case is not implemented + for cursors yet. + */ + DBUG_ASSERT(error == 0); + /* + curr_join is used only for reusable joins - that is, + to perform SELECT for each outer row (like in subselects). + This join is main, so we know for sure that curr_join == join. + */ + DBUG_ASSERT(curr_join == this); + /* Open cursor for the last join sweep */ + error= thd->cursor->open(this); + } + else + { + thd->proc_info="Sending data"; + error= do_select(curr_join, curr_fields_list, NULL, procedure); + thd->limit_found_rows= curr_join->send_records; + thd->examined_row_count= curr_join->examined_rows; + } + DBUG_VOID_RETURN; } @@ -1566,6 +1596,306 @@ JOIN::cleanup() } +/************************* Cursor ******************************************/ + +void +Cursor::init_from_thd(THD *thd) +{ + /* + We need to save and reset thd->mem_root, otherwise it'll be freed + later in mysql_parse. + */ + mem_root= thd->mem_root; + init_sql_alloc(&thd->mem_root, + thd->variables.query_alloc_block_size, + thd->variables.query_prealloc_size); + + /* + The same is true for open tables and lock: save tables and zero THD + pointers to prevent table close in close_thread_tables (This is a part + of the temporary solution to make cursors work with minimal changes to + the current source base). + */ + derived_tables= thd->derived_tables; + open_tables= thd->open_tables; + lock= thd->lock; + query_id= thd->query_id; + free_list= thd->free_list; + reset_thd(thd); + /* + XXX: thd->locked_tables is not changed. + What problems can we have with it if cursor is open? + */ + /* + TODO: grab thd->free_list here? + */ +} + + +void +Cursor::init_thd(THD *thd) +{ + thd->mem_root= mem_root; + + DBUG_ASSERT(thd->derived_tables == 0); + thd->derived_tables= derived_tables; + + DBUG_ASSERT(thd->open_tables == 0); + thd->open_tables= open_tables; + + DBUG_ASSERT(thd->lock== 0); + thd->lock= lock; + thd->query_id= query_id; + thd->free_list= free_list; +} + + +void +Cursor::reset_thd(THD *thd) +{ + thd->derived_tables= 0; + thd->open_tables= 0; + thd->lock= 0; + thd->free_list= 0; +} + + +int +Cursor::open(JOIN *join_arg) +{ + join= join_arg; + + THD *thd= join->thd; + + /* First non-constant table */ + JOIN_TAB *join_tab= join->join_tab + join->const_tables; + + /* + Send fields description to the client; server_status is sent + in 'EOF' packet, which ends send_fields(). + */ + thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; + join->result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS); + ::send_eof(thd); + thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; + + /* Prepare JOIN for reading rows. */ + + Next_select_func end_select= join->sort_and_group || join->procedure && + join->procedure->flags & PROC_GROUP ? + end_send_group : end_send; + + join->join_tab[join->tables-1].next_select= end_select; + join->send_records= 0; + join->fetch_limit= join->unit->offset_limit_cnt; + + /* Disable JOIN CACHE as it is not working with cursors yet */ + for (JOIN_TAB *tab= join_tab; tab != join->join_tab + join->tables - 1; ++tab) + { + if (tab->next_select == sub_select_cache) + tab->next_select= sub_select; + } + + DBUG_ASSERT(join_tab->table->reginfo.not_exists_optimize == 0); + DBUG_ASSERT(join_tab->not_used_in_distinct == 0); + /* + null_row is set only if row not found and it's outer join: should never + happen for the first table in join_tab list + */ + DBUG_ASSERT(join_tab->table->null_row == 0); + + return join_tab->read_first_record(join_tab); +} + + +/* + DESCRIPTION + Fetch next num_rows rows from the cursor and sent them to the client + PRECONDITION: + Cursor is open + RETURN VALUES: + -4 there are more rows, send_eof sent to the client + 0 no more rows, send_eof was sent to the client, cursor is closed + other fatal fetch error, cursor is closed (error is not reported) +*/ + +int +Cursor::fetch(ulong num_rows) +{ + THD *thd= join->thd; + JOIN_TAB *join_tab= join->join_tab + join->const_tables;; + COND *on_expr= join_tab->on_expr; + COND *select_cond= join_tab->select_cond; + READ_RECORD *info= &join_tab->read_record; + + int error= 0; + + join->fetch_limit+= num_rows; + + /* + Run while there are new rows in the first table; + For each row, satisfying ON and WHERE clauses (those parts of them which + can be evaluated early), call next_select. + */ + do + { + int no_more_rows; + + join->examined_rows++; + + if (thd->killed) /* Aborted by user */ + { + my_error(ER_SERVER_SHUTDOWN,MYF(0)); + return -1; + } + + if (on_expr == 0 || on_expr->val_int()) + { + if (select_cond == 0 || select_cond->val_int()) + { + /* + TODO: call table->unlock_row() to unlock row failed selection, + when this feature will be used. + */ + error= join_tab->next_select(join, join_tab + 1, 0); + DBUG_ASSERT(error <= 0); + if (error) + { + /* real error or LIMIT/FETCH LIMIT worked */ + if (error == -4) + { + /* + FETCH LIMIT, read ahead one row, and close cursor + if there is no more rows XXX: to be fixed to support + non-equi-joins! + */ + if ((no_more_rows= info->read_record(info))) + error= no_more_rows > 0 ? -1: 0; + } + break; + } + } + } + /* read next row; break loop if there was an error */ + if ((no_more_rows= info->read_record(info))) + { + if (no_more_rows > 0) + error= -1; + else + { + enum { END_OF_RECORDS= 1 }; + error= join_tab->next_select(join, join_tab+1, (int) END_OF_RECORDS); + } + break; + } + } + while (thd->net.report_error == 0); + + if (thd->net.report_error) + error= -1; + + switch (error) { + /* Fetch limit worked, possibly more rows are there */ + case -4: + if (thd->transaction.all.innobase_tid) + ha_release_temporary_latches(thd); + thd->server_status|= SERVER_STATUS_CURSOR_EXISTS; + ::send_eof(thd); + thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS; + /* save references to memory, allocated during fetch */ + mem_root= thd->mem_root; + free_list= thd->free_list; + break; + /* Limit clause worked: this is the same as 'no more rows' */ + case -3: /* LIMIT clause worked */ + error= 0; + /* fallthrough */ + case 0: /* No more rows */ + if (thd->transaction.all.innobase_tid) + ha_release_temporary_latches(thd); + close(); + thd->server_status|= SERVER_STATUS_LAST_ROW_SENT; + ::send_eof(thd); + thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT; + join= 0; + unit= 0; + free_items(thd->free_list); + thd->free_list= free_list= 0; + /* + Must be last, as some memory might be allocated for free purposes, + like in free_tmp_table() (TODO: fix this issue) + */ + mem_root= thd->mem_root; + free_root(&mem_root, MYF(0)); + break; + default: + close(); + join= 0; + unit= 0; + free_items(thd->free_list); + thd->free_list= free_list= 0; + /* + Must be last, as some memory might be allocated for free purposes, + like in free_tmp_table() (TODO: fix this issue) + */ + mem_root= thd->mem_root; + free_root(&mem_root, MYF(0)); + break; + } + return error; +} + + +void +Cursor::close() +{ + THD *thd= join->thd; + join->join_free(0); + if (unit) + { + /* In case of UNIONs JOIN is freed inside unit->cleanup() */ + unit->cleanup(); + } + else + { + join->cleanup(); + delete join; + } + /* XXX: Another hack: closing tables used in the cursor */ + { + DBUG_ASSERT(lock || open_tables || derived_tables); + + TABLE *tmp_open_tables= thd->open_tables; + TABLE *tmp_derived_tables= thd->derived_tables; + MYSQL_LOCK *tmp_lock= thd->lock; + + thd->open_tables= open_tables; + thd->derived_tables= derived_tables; + thd->lock= lock; + close_thread_tables(thd); + + thd->open_tables= tmp_derived_tables; + thd->derived_tables= tmp_derived_tables; + thd->lock= tmp_lock; + } +} + + +Cursor::~Cursor() +{ + if (is_open()) + close(); + free_items(free_list); + /* + Must be last, as some memory might be allocated for free purposes, + like in free_tmp_table() (TODO: fix this issue) + */ + free_root(&mem_root, MYF(0)); +} + +/*********************************************************************/ + + int mysql_select(THD *thd, Item ***rref_pointer_array, TABLE_LIST *tables, uint wild_num, List<Item> &fields, @@ -1637,6 +1967,16 @@ mysql_select(THD *thd, Item ***rref_pointer_array, join->exec(); + if (thd->cursor && thd->cursor->is_open()) + { + /* + A cursor was opened for the last sweep in exec(). + We are here only if this is mysql_select for top-level SELECT_LEX_UNIT + and there were no error. + */ + free_join= 0; + } + if (thd->lex->describe & DESCRIBE_EXTENDED) { select_lex->where= join->conds_history; @@ -1726,7 +2066,6 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, tables; s++, tables= tables->next_local, i++) { - table_map dep_tables; TABLE_LIST *embedding= tables->embedding; stat_vector[i]=s; s->keys.init(); @@ -1861,7 +2200,8 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, if (s->dependent & ~(found_const_table_map)) continue; if (table->file->records <= 1L && - !(table->file->table_flags() & HA_NOT_EXACT_COUNT)) + !(table->file->table_flags() & HA_NOT_EXACT_COUNT) && + !table->pos_in_table_list->embedding) { // system table int tmp= 0; s->type=JT_SYSTEM; @@ -1960,7 +2300,8 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, if (s->worst_seeks < 2.0) // Fix for small tables s->worst_seeks=2.0; - if (! s->const_keys.is_clear_all()) + if (!s->const_keys.is_clear_all() && + !s->table->pos_in_table_list->embedding) { ha_rows records; SQL_SELECT *select; @@ -3614,7 +3955,7 @@ best_extension_by_limited_search(JOIN *join, { memcpy((gptr) join->best_positions, (gptr) join->positions, sizeof(POSITION) * (idx + 1)); - join->best_read= current_read_time; + join->best_read= current_read_time - 0.001; } DBUG_EXECUTE("opt", print_plan(join, current_read_time, current_record_count, idx, "full_plan");); @@ -5311,7 +5652,8 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables, if (having && having->val_int() == 0) send_row=0; } - if (!(result->send_fields(fields,1))) + if (!(result->send_fields(fields, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))) { if (send_row) { @@ -5783,7 +6125,20 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top) if (prev_table->straight) prev_table->dep_tables|= used_tables; if (prev_table->on_expr) + { prev_table->dep_tables|= table->on_expr_dep_tables; + table_map prev_used_tables= prev_table->nested_join ? + prev_table->nested_join->used_tables : + prev_table->table->map; + /* + If on expression contains only references to inner tables + we still make the inner tables dependent on the outer tables. + It would be enough to set dependency only on one outer table + for them. Yet this is really a rare case. + */ + if (!(prev_table->on_expr->used_tables() & ~prev_used_tables)) + prev_table->dep_tables|= used_tables; + } } prev_table= table; } @@ -7036,7 +7391,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) { int error= 0; JOIN_TAB *join_tab; - int (*end_select)(JOIN *, struct st_join_table *,bool); + Next_select_func end_select; DBUG_ENTER("do_select"); join->procedure=procedure; @@ -7044,7 +7399,8 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) Tell the client how many fields there are in a row */ if (!table) - join->result->send_fields(*fields,1); + join->result->send_fields(*fields, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF); else { VOID(table->file->extra(HA_EXTRA_WRITE_CACHE)); @@ -8077,6 +8433,14 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), } DBUG_RETURN(-3); // Abort nicely } + else if (join->send_records >= join->fetch_limit) + { + /* + There is a server side cursor and all rows for + this fetch request are sent. + */ + DBUG_RETURN(-4); + } } else { @@ -8151,6 +8515,14 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), join->do_send_rows=0; join->unit->select_limit_cnt = HA_POS_ERROR; } + else if (join->send_records >= join->fetch_limit) + { + /* + There is a server side cursor and all rows + for this fetch request are sent. + */ + DBUG_RETURN(-4); + } } } else @@ -9682,7 +10054,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, Item *itemptr=*order->item; if (itemptr->type() == Item::INT_ITEM) { /* Order by position */ - uint count= (uint) ((Item_int*)itemptr)->value; + uint count= itemptr->val_int(); if (!count || count > fields.elements) { my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR), @@ -9690,8 +10062,10 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, thd->where); return 1; } - order->item= ref_pointer_array + count-1; + order->item= ref_pointer_array + count - 1; order->in_field_list= 1; + order->counter= count; + order->counter_used= 1; return 0; } uint counter; @@ -11192,7 +11566,7 @@ int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) SYNOPSIS print_join() thd thread handler - str string where table should bbe printed + str string where table should be printed tables list of tables in join */ @@ -11248,45 +11622,47 @@ void st_table_list::print(THD *thd, String *str) print_join(thd, str, &nested_join->join_list); str->append(')'); } - else if (view_name.str) + else { - str->append(view_db.str, view_db.length); - str->append('.'); - str->append(view_name.str, view_name.length); - if (my_strcasecmp(table_alias_charset, view_name.str, alias)) + const char *cmp_name; // Name to compare with alias + if (view_name.str) { - str->append(' '); - str->append(alias); + append_identifier(thd, str, view_db.str, view_db.length); + str->append('.'); + append_identifier(thd, str, view_name.str, view_name.length); + cmp_name= view_name.str; } - } - else if (derived) - { - str->append('('); - derived->print(str); - str->append(") ", 2); - str->append(alias); - } - else - { - str->append(db); - str->append('.'); - str->append(real_name); - if (my_strcasecmp(table_alias_charset, real_name, alias)) + else if (derived) + { + str->append('('); + derived->print(str); + str->append(')'); + cmp_name= ""; // Force printing of alias + } + else + { + append_identifier(thd, str, db, db_length); + str->append('.'); + append_identifier(thd, str, real_name, real_name_length); + cmp_name= real_name; + } + if (my_strcasecmp(table_alias_charset, cmp_name, alias)) { str->append(' '); - str->append(alias); + append_identifier(thd, str, alias, strlen(alias)); } } } + void st_select_lex::print(THD *thd, String *str) { if (!thd) thd= current_thd; str->append("select ", 7); - - //options + + /* First add options */ if (options & SELECT_STRAIGHT_JOIN) str->append("straight_join ", 14); if ((thd->lex->lock_option == TL_READ_HIGH_PRIORITY) && diff --git a/sql/sql_select.h b/sql/sql_select.h index 8ffe50e6db2..284e4315917 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -81,6 +81,10 @@ enum join_type { JT_UNKNOWN,JT_SYSTEM,JT_CONST,JT_EQ_REF,JT_REF,JT_MAYBE_REF, class JOIN; +typedef int (*Next_select_func)(JOIN *,struct st_join_table *,bool); +typedef int (*Read_record_func)(struct st_join_table *tab); + + typedef struct st_join_table { TABLE *table; KEYUSE *keyuse; /* pointer to first used key */ @@ -95,8 +99,8 @@ typedef struct st_join_table { st_join_table *first_upper; /* first inner table for embedding outer join */ st_join_table *first_unmatched; /* used for optimization purposes only */ const char *info; - int (*read_first_record)(struct st_join_table *tab); - int (*next_select)(JOIN *,struct st_join_table *,bool); + Read_record_func read_first_record; + Next_select_func next_select; READ_RECORD read_record; double worst_seeks; key_map const_keys; /* Keys with constant part */ @@ -149,6 +153,16 @@ class JOIN :public Sql_alloc bool do_send_rows; table_map const_table_map,found_const_table_map,outer_join; ha_rows send_records,found_records,examined_rows,row_limit, select_limit; + /* + Used to fetch no more than given amount of rows per one + fetch operation of server side cursor. + The value is checked in end_send and end_send_group in fashion, similar + to offset_limit_cnt: + - fetch_limit= HA_POS_ERROR if there is no cursor. + - when we open a cursor, we set fetch_limit to 0, + - on each fetch iteration we add num_rows to fetch to fetch_limit + */ + ha_rows fetch_limit; POSITION positions[MAX_TABLES+1],best_positions[MAX_TABLES+1]; double best_read; List<Item> *fields; @@ -239,6 +253,7 @@ class JOIN :public Sql_alloc do_send_rows= 1; send_records= 0; found_records= 0; + fetch_limit= HA_POS_ERROR; examined_rows= 0; exec_tmp_table1= 0; exec_tmp_table2= 0; @@ -319,6 +334,44 @@ class JOIN :public Sql_alloc }; +/* + Server-side cursor (now stands only for basic read-only cursor) + See class implementation in sql_select.cc +*/ + +class Cursor: public Sql_alloc, public Item_arena +{ + JOIN *join; + SELECT_LEX_UNIT *unit; + + TABLE *open_tables; + MYSQL_LOCK *lock; + TABLE *derived_tables; + /* List of items created during execution */ + ulong query_id; +public: + select_send result; + + /* Temporary implementation as now we replace THD state by value */ + /* Save THD state into cursor */ + void init_from_thd(THD *thd); + /* Restore THD from cursor to continue cursor execution */ + void init_thd(THD *thd); + /* bzero cursor state in THD */ + void reset_thd(THD *thd); + + int open(JOIN *join); + int fetch(ulong num_rows); + void reset() { join= 0; } + bool is_open() const { return join != 0; } + void close(); + + void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; } + Cursor() :join(0), unit(0) {} + ~Cursor(); +}; + + typedef struct st_select_check { uint const_ref,reg_ref; } SELECT_CHECK; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 00d083dc150..629b7a3cf3a 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -66,7 +66,8 @@ mysqld_show_dbs(THD *thd,const char *wild) strxmov(end," (",wild,")",NullS); field_list.push_back(field); - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); if (mysql_find_files(thd,&files,NullS,mysql_data_home,wild,1)) DBUG_RETURN(1); @@ -107,7 +108,8 @@ int mysqld_show_open_tables(THD *thd,const char *wild) field_list.push_back(new Item_return_int("In_use", 1, MYSQL_TYPE_TINY)); field_list.push_back(new Item_return_int("Name_locked", 4, MYSQL_TYPE_TINY)); - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); if (!(open_list=list_open_tables(thd,wild)) && thd->is_fatal_error) @@ -159,8 +161,9 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild) len= FN_LEN - len; field_list.push_back(field); if (show_type) - field_list.push_back(new Item_empty_string("Type", 5)); - if (protocol->send_fields(&field_list,1)) + field_list.push_back(new Item_empty_string("table_type", 10)); + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); if (mysql_find_files(thd,&files,db,path,wild,0)) DBUG_RETURN(-1); @@ -175,14 +178,16 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild) switch (mysql_frm_type(path)) { case FRMTYPE_ERROR: - protocol->store("error", system_charset_info); + protocol->store("ERROR", system_charset_info); break; case FRMTYPE_TABLE: - protocol->store("table", system_charset_info); + protocol->store("BASE TABLE", system_charset_info); break; case FRMTYPE_VIEW: - protocol->store("view", system_charset_info); + protocol->store("VIEW", system_charset_info); break; + default: + DBUG_ASSERT(0); // this should be impossible } } if (protocol->write()) @@ -206,7 +211,8 @@ int mysqld_show_storage_engines(THD *thd) field_list.push_back(new Item_empty_string("Support",10)); field_list.push_back(new Item_empty_string("Comment",80)); - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); const char *default_type_name= @@ -245,10 +251,11 @@ struct show_privileges_st { static struct show_privileges_st sys_privileges[]= { {"Alter", "Tables", "To alter the table"}, - {"Create temporary tables","Databases","To use CREATE TEMPORARY TABLE"}, {"Create", "Databases,Tables,Indexes", "To create new databases and tables"}, + {"Create temporary tables","Databases","To use CREATE TEMPORARY TABLE"}, + {"Create view", "Tables", "To create new views"}, {"Delete", "Tables", "To delete existing rows"}, - {"Drop", "Databases,Tables", "To drop databases and tables"}, + {"Drop", "Databases,Tables", "To drop databases, tables, and views"}, {"File", "File access on server", "To read and write files on the server"}, {"Grant option", "Databases,Tables", "To give to other users those privileges you possess"}, {"Index", "Tables", "To create or drop indexes"}, @@ -261,7 +268,8 @@ static struct show_privileges_st sys_privileges[]= {"Replication slave","Server Admin","To read binary log events from the master"}, {"Select", "Tables", "To retrieve rows from table"}, {"Show databases","Server Admin","To see all databases with SHOW DATABASES"}, - {"Shutdown","Server Admin", "To shutdown the server"}, + {"Show view","Tables","To see views with SHOW CREATE VIEW"}, + {"Shutdown","Server Admin", "To shut down the server"}, {"Super","Server Admin","To use KILL thread, SET GLOBAL, CHANGE MASTER, etc."}, {"Update", "Tables", "To update existing rows"}, {"Usage","Server Admin","No privileges - allow connect only"}, @@ -278,7 +286,8 @@ int mysqld_show_privileges(THD *thd) field_list.push_back(new Item_empty_string("Context",15)); field_list.push_back(new Item_empty_string("Comment",NAME_LEN)); - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); show_privileges_st *privilege= sys_privileges; @@ -353,7 +362,8 @@ int mysqld_show_column_types(THD *thd) field_list.push_back(new Item_empty_string("Default",NAME_LEN)); field_list.push_back(new Item_empty_string("Comment",NAME_LEN)); - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); /* TODO: Change the loop to not use 'i' */ @@ -487,6 +497,7 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) TABLE *table; Protocol *protocol= thd->protocol; TIME time; + int res; DBUG_ENTER("mysqld_extend_show_tables"); (void) sprintf(path,"%s/%s",mysql_data_home,db); @@ -526,7 +537,8 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) item->maybe_null=1; field_list.push_back(item=new Item_empty_string("Comment",80)); item->maybe_null=1; - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); if (mysql_find_files(thd,&files,db,path,wild,0)) @@ -543,13 +555,18 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) table_list.select_lex= &thd->lex->select_lex; if (lower_case_table_names) my_casedn_str(files_charset_info, file_name); - if (open_and_lock_tables(thd, &table_list)) + if ((res= open_and_lock_tables(thd, &table_list))) { for (uint i=2 ; i < field_list.elements ; i++) protocol->store_null(); - // Send error to Comment field - protocol->store(thd->net.last_error, system_charset_info); - thd->clear_error(); + // Send error to Comment field if possible + if (res < 0) + { + protocol->store(thd->net.last_error, system_charset_info); + thd->clear_error(); + } + else + DBUG_RETURN(1); } else if (table_list.view) { @@ -684,14 +701,16 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, char tmp[MAX_FIELD_WIDTH]; Item *item; Protocol *protocol= thd->protocol; + int res; DBUG_ENTER("mysqld_show_fields"); DBUG_PRINT("enter",("db: %s table: %s",table_list->db, table_list->real_name)); table_list->lock_type= TL_UNLOCK; - if (open_and_lock_tables(thd, table_list)) + if ((res= open_and_lock_tables(thd, table_list))) { - send_error(thd); + if (res < 0) + send_error(thd); DBUG_RETURN(1); } table= table_list->table; @@ -717,7 +736,7 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, } // Send first number of fields and records if (protocol->send_records_num(&field_list, (ulonglong)file->records) || - protocol->send_fields(&field_list,0)) + protocol->send_fields(&field_list, Protocol::SEND_EOF)) DBUG_RETURN(1); restore_record(table,default_values); // Get empty record @@ -825,14 +844,16 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) Protocol *protocol= thd->protocol; char buff[2048]; String buffer(buff, sizeof(buff), system_charset_info); + int res; DBUG_ENTER("mysqld_show_create"); DBUG_PRINT("enter",("db: %s table: %s",table_list->db, table_list->real_name)); /* Only one table for now, but VIEW can involve several tables */ - if (open_and_lock_tables(thd, table_list)) + if ((res= open_and_lock_tables(thd, table_list))) { - send_error(thd); + if (res < 0) + send_error(thd); DBUG_RETURN(1); } /* TODO: add environment variables show when it become possible */ @@ -855,7 +876,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) field_list.push_back(new Item_empty_string("Create Table", max(buffer.length(),1024))); - if (protocol->send_fields(&field_list, 1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); protocol->prepare_for_resend(); buffer.length(0); @@ -939,7 +961,8 @@ int mysqld_show_create_db(THD *thd, char *dbname, field_list.push_back(new Item_empty_string("Database",NAME_LEN)); field_list.push_back(new Item_empty_string("Create Database",1024)); - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); protocol->prepare_for_resend(); @@ -981,7 +1004,8 @@ mysqld_show_logs(THD *thd) field_list.push_back(new Item_empty_string("Type",10)); field_list.push_back(new Item_empty_string("Status",10)); - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); #ifdef HAVE_BERKELEY_DB @@ -1030,7 +1054,8 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list) field_list.push_back(new Item_empty_string("Comment",255)); item->maybe_null=1; - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); KEY *key_info=table->key_info; @@ -1117,7 +1142,8 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) field_list.push_back(new Item_field(field)); } restore_record(table,default_values); // Get empty record - if (thd->protocol->send_fields(&field_list,2)) + if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS | + Protocol::SEND_EOF)) DBUG_VOID_RETURN; net_flush(&thd->net); DBUG_VOID_RETURN; @@ -1549,19 +1575,19 @@ view_store_create_info(THD *thd, TABLE_LIST *table, String *buff) MODE_MAXDB | MODE_ANSI)) != 0; buff->append("CREATE ", 7); - if(!foreign_db_mode && (table->algorithm == VIEW_ALGORITHM_MERGE || - table->algorithm == VIEW_ALGORITHM_TMEPTABLE)) + if (!foreign_db_mode && (table->algorithm == VIEW_ALGORITHM_MERGE || + table->algorithm == VIEW_ALGORITHM_TMPTABLE)) { buff->append("ALGORITHM=", 10); - if (table->algorithm == VIEW_ALGORITHM_TMEPTABLE) + if (table->algorithm == VIEW_ALGORITHM_TMPTABLE) buff->append("TMPTABLE ", 9); else buff->append("MERGE ", 6); } buff->append("VIEW ", 5); - buff->append(table->view_db.str, table->view_db.length); + append_identifier(thd, buff, table->view_db.str, table->view_db.length); buff->append('.'); - buff->append(table->view_name.str, table->view_name.length); + append_identifier(thd, buff, table->view_name.str, table->view_name.length); buff->append(" AS ", 4); buff->append(table->query.str, table->query.length); return 0; @@ -1611,7 +1637,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) field->maybe_null=1; field_list.push_back(field=new Item_empty_string("Info",max_query_length)); field->maybe_null=1; - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_VOID_RETURN; VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list @@ -1747,7 +1774,8 @@ int mysqld_show_collations(THD *thd, const char *wild) field_list.push_back(new Item_empty_string("Compiled",30)); field_list.push_back(new Item_return_int("Sortlen",3, FIELD_TYPE_SHORT)); - if (protocol->send_fields(&field_list, 1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); for ( cs= all_charsets ; cs < all_charsets+255 ; cs++ ) @@ -1800,7 +1828,8 @@ int mysqld_show_charsets(THD *thd, const char *wild) field_list.push_back(new Item_empty_string("Default collation",60)); field_list.push_back(new Item_return_int("Maxlen",3, FIELD_TYPE_SHORT)); - if (protocol->send_fields(&field_list, 1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); for ( cs= all_charsets ; cs < all_charsets+255 ; cs++ ) @@ -1834,7 +1863,8 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables, field_list.push_back(new Item_empty_string("Variable_name",30)); field_list.push_back(new Item_empty_string("Value",256)); - if (protocol->send_fields(&field_list,1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); /* purecov: inspected */ null_lex_str.str= 0; // For sys_var->value_ptr() null_lex_str.length= 0; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index c79005ad83f..1530f8f7fcc 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1737,7 +1737,8 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, item->maybe_null = 1; field_list.push_back(item = new Item_empty_string("Msg_text", 255)); item->maybe_null = 1; - if (protocol->send_fields(&field_list, 1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(-1); mysql_ha_close(thd, tables, /*dont_send_ok*/ 1, /*dont_lock*/ 1); @@ -3443,7 +3444,8 @@ int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt) item->maybe_null= 1; field_list.push_back(item=new Item_int("Checksum",(longlong) 1,21)); item->maybe_null= 1; - if (protocol->send_fields(&field_list, 1)) + if (protocol->send_fields(&field_list, + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(-1); for (table= tables; table; table= table->next_local) diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 35f8a390308..d6b776571f2 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -31,7 +31,13 @@ int mysql_union(THD *thd, LEX *lex, select_result *result, int res, res_cln; if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK))) res= unit->exec(); - res_cln= unit->cleanup(); + if (res == 0 && thd->cursor && thd->cursor->is_open()) + { + thd->cursor->set_unit(unit); + res_cln= 0; + } + else + res_cln= unit->cleanup(); DBUG_RETURN(res?res:res_cln); } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index bc72589b142..1fd32516198 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -55,6 +55,7 @@ static bool compare_record(TABLE *table, ulong query_id) SYNOPSIS check_fields() + thd thread handler items Items for check RETURN @@ -62,9 +63,9 @@ static bool compare_record(TABLE *table, ulong query_id) FALSE Items are OK */ -static bool check_fields(List<Item> &items) +static bool check_fields(THD *thd, List<Item> &items) { - List_iterator_fast<Item> it(items); + List_iterator<Item> it(items); Item *item; while ((item= it++)) { @@ -74,6 +75,13 @@ static bool check_fields(List<Item> &items) my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name); return TRUE; } + /* + we make temporary copy of Item_field, to avoid influence of changing + result_field on Item_ref which refer on this field + */ + Item_field *field= new Item_field(thd, (Item_field *)item); + it.replace(field); + ((Item_field *)item)->register_item_tree_changing(it.ref()); } return FALSE; } @@ -107,8 +115,8 @@ int mysql_update(THD *thd, LINT_INIT(used_index); LINT_INIT(timestamp_query_id); - if ((open_and_lock_tables(thd, table_list))) - DBUG_RETURN(-1); + if ((error= open_and_lock_tables(thd, table_list))) + DBUG_RETURN(error); thd->proc_info="init"; table= table_list->table; table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); @@ -141,9 +149,14 @@ int mysql_update(THD *thd, #ifndef NO_EMBEDDED_ACCESS_CHECKS table_list->grant.want_privilege= table->grant.want_privilege= want_privilege; #endif - if (setup_fields(thd, 0, table_list, fields, 1, 0, 0)) - DBUG_RETURN(-1); /* purecov: inspected */ - if (check_fields(fields)) + { + thd->lex->select_lex.no_wrap_view_item= 1; + int res= setup_fields(thd, 0, table_list, fields, 1, 0, 0); + thd->lex->select_lex.no_wrap_view_item= 0; + if (res) + DBUG_RETURN(-1); /* purecov: inspected */ + } + if (table_list->view && check_fields(thd, fields)) { DBUG_RETURN(-1); } @@ -493,7 +506,7 @@ int mysql_prepare_update(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(-1); /* Check that we are not using table that we are updating in a sub select */ - if (find_real_table_in_list(table_list->next_global, + if (find_table_in_global_list(table_list->next_global, table_list->db, table_list->real_name)) { my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name); @@ -531,6 +544,8 @@ int mysql_multi_update_prepare(THD *thd) List<Item> *fields= &lex->select_lex.item_list; TABLE_LIST *tl; table_map tables_for_update= 0, readonly_tables= 0; + int res; + bool update_view= 0; DBUG_ENTER("mysql_multi_update_prepare"); /* Ensure that we have update privilege for all tables and columns in the @@ -555,9 +570,22 @@ int mysql_multi_update_prepare(THD *thd) time. */ if (setup_tables(thd, table_list, &lex->select_lex.where) || - setup_fields(thd, 0, table_list, *fields, 1, 0, 0)) + (thd->lex->select_lex.no_wrap_view_item= 1, + res= setup_fields(thd, 0, table_list, *fields, 1, 0, 0), + thd->lex->select_lex.no_wrap_view_item= 0, + res)) DBUG_RETURN(-1); - if (check_fields(*fields)) + + for (tl= table_list; tl ; tl= tl->next_local) + { + if (tl->view) + { + update_view= 1; + break; + } + } + + if (update_view && check_fields(thd, *fields)) { DBUG_RETURN(-1); } @@ -770,7 +798,7 @@ int multi_update::prepare(List<Item> ¬_used_values, { TABLE *table=table_ref->table; if (!(tables_to_update & table->map) && - find_real_table_in_list(update_tables, table_ref->db, + find_table_in_global_list(update_tables, table_ref->db, table_ref->real_name)) table->no_cache= 1; // Disable row cache } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 5a16f0ff3fc..2b1971907b3 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -54,17 +54,26 @@ int mysql_create_view(THD *thd, TABLE_LIST *view= lex->unlink_first_table(&link_to_local); TABLE_LIST *tables= lex->query_tables; TABLE_LIST *tbl; - SELECT_LEX *select_lex= &lex->select_lex; + SELECT_LEX *select_lex= &lex->select_lex, *sl; SELECT_LEX_UNIT *unit= &lex->unit; int res= 0; DBUG_ENTER("mysql_create_view"); - if (lex->derived_tables || lex->proc_list.first || + if (lex->proc_list.first || + lex->result) + { + my_error(ER_VIEW_SELECT_CLAUSE, MYF(0), (lex->result ? + "INTO" : + "PROCEDURE")); + res= -1; + goto err; + } + if (lex->derived_tables || lex->variables_used || lex->param_list.elements) { - my_error((lex->derived_tables ? ER_VIEW_SELECT_DERIVED : - (lex->proc_list.first ? ER_VIEW_SELECT_PROCEDURE : - ER_VIEW_SELECT_VARIABLE)), MYF(0)); + my_error((lex->derived_tables ? + ER_VIEW_SELECT_DERIVED : + ER_VIEW_SELECT_VARIABLE), MYF(0)); res= -1; goto err; } @@ -74,56 +83,42 @@ int mysql_create_view(THD *thd, 0, 0) || grant_option && check_grant(thd, CREATE_VIEW_ACL, view, 0, 1, 0)) DBUG_RETURN(1); - for (tbl= tables; tbl; tbl= tbl->next_local) + for (sl= select_lex; sl; sl= sl->next_select()) { - /* - Ensure that we have some privilage on this table, more strict check - will be done on column level after preparation, - - SELECT_ACL will be checked for sure for all fields because it is - listed first (if we have not rights to SELECT from whole table this - right will be written as tbl->grant.want_privilege and will be checked - later (except fields which need any privilege and can be updated). - */ - if ((check_access(thd, SELECT_ACL, tbl->db, - &tbl->grant.privilege, 0, 1) || - grant_option && check_grant(thd, SELECT_ACL, tbl, 0, 1, 1)) && - (check_access(thd, INSERT_ACL, tbl->db, - &tbl->grant.privilege, 0, 1) || - grant_option && check_grant(thd, INSERT_ACL, tbl, 0, 1, 1)) && - (check_access(thd, DELETE_ACL, tbl->db, - &tbl->grant.privilege, 0, 1) || - grant_option && check_grant(thd, DELETE_ACL, tbl, 0, 1, 1)) && - (check_access(thd, UPDATE_ACL, tbl->db, - &tbl->grant.privilege, 0, 1) || - grant_option && check_grant(thd, UPDATE_ACL, tbl, 0, 1, 1)) - ) + for (tbl= sl->get_table_list(); tbl; tbl= tbl->next_local) { - my_printf_error(ER_TABLEACCESS_DENIED_ERROR, - ER(ER_TABLEACCESS_DENIED_ERROR), - MYF(0), - "ANY", - thd->priv_user, - thd->host_or_ip, - tbl->real_name); - DBUG_RETURN(-1); - } - /* mark this table as table which will be checked after preparation */ - tbl->table_in_first_from_clause= 1; + /* + Ensure that we have some privilage on this table, more strict check + will be done on column level after preparation, + */ + if (check_some_access(thd, VIEW_ANY_ACL, tbl)) + { + my_printf_error(ER_TABLEACCESS_DENIED_ERROR, + ER(ER_TABLEACCESS_DENIED_ERROR), + MYF(0), + "ANY", + thd->priv_user, + thd->host_or_ip, + tbl->real_name); + DBUG_RETURN(-1); + } + /* mark this table as table which will be checked after preparation */ + tbl->table_in_first_from_clause= 1; - /* - We need to check only SELECT_ACL for all normal fields, fields - where we need any privilege will be pmarked later - */ - tbl->grant.want_privilege= SELECT_ACL; - /* - Make sure that all rights are loaded to table 'grant' field. + /* + We need to check only SELECT_ACL for all normal fields, fields + where we need any privilege will be marked later + */ + tbl->grant.want_privilege= SELECT_ACL; + /* + Make sure that all rights are loaded to table 'grant' field. - tbl->real_name will be correct name of table because VIEWs are - not opened yet. - */ - fill_effective_table_privileges(thd, &tbl->grant, tbl->db, - tbl->real_name); + tbl->real_name will be correct name of table because VIEWs are + not opened yet. + */ + fill_effective_table_privileges(thd, &tbl->grant, tbl->db, + tbl->real_name); + } } if (&lex->select_lex != lex->all_selects_list) @@ -145,12 +140,10 @@ int mysql_create_view(THD *thd, } /* Mark fields for special privilege check (any privilege) - - 'if' should be changed if we made updateable UNION. */ - if (lex->select_lex.next_select() == 0) + for (sl= select_lex; sl; sl= sl->next_select()) { - List_iterator_fast<Item> it(lex->select_lex.item_list); + List_iterator_fast<Item> it(sl->item_list); Item *item; while ((item= it++)) { @@ -166,7 +159,7 @@ int mysql_create_view(THD *thd, /* check that tables are not temporary */ for (tbl= tables; tbl; tbl= tbl->next_global) { - if (tbl->table->tmp_table != NO_TMP_TABLE && !test(tbl->view)) + if (tbl->table->tmp_table != NO_TMP_TABLE && !tbl->view) { my_error(ER_VIEW_SELECT_TMPTABLE, MYF(0), tbl->alias); res= -1; @@ -183,24 +176,50 @@ int mysql_create_view(THD *thd, // prepare select to resolve all fields lex->view_prepare_mode= 1; - if ((res= unit->prepare(thd, 0, 0))) + if (unit->prepare(thd, 0, 0)) + { + /* + some errors from prepare are reported to user, if is not then + it will be checked after err: label + */ + res= 1; goto err; + } /* view list (list of view fields names) */ if (lex->view_list.elements) { + List_iterator_fast<Item> it(select_lex->item_list); + List_iterator_fast<LEX_STRING> nm(lex->view_list); + Item *item; + LEX_STRING *name; + if (lex->view_list.elements != select_lex->item_list.elements) { my_message(ER_VIEW_WRONG_LIST, ER(ER_VIEW_WRONG_LIST), MYF(0)); goto err; } - List_iterator_fast<Item> it(select_lex->item_list); - List_iterator_fast<LEX_STRING> nm(lex->view_list); + while ((item= it++, name= nm++)) + item->set_name(name->str, name->length, system_charset_info); + } + + /* Test absence of duplicates names */ + { Item *item; - LEX_STRING *name; - while((item= it++, name= nm++)) + List_iterator_fast<Item> it(select_lex->item_list); + it++; + while ((item= it++)) { - item->set_name(name->str, name->length, system_charset_info); + Item *check; + List_iterator_fast<Item> itc(select_lex->item_list); + while ((check= itc++) && check != item) + { + if (strcmp(item->name, check->name) == 0) + { + my_error(ER_DUP_FIELDNAME, MYF(0), item->name); + DBUG_RETURN(-1); + } + } } } @@ -208,13 +227,14 @@ int mysql_create_view(THD *thd, /* Compare/check grants on view with grants of underlaying tables */ + for (sl= select_lex; sl; sl= sl->next_select()) { char *db= view->db ? view->db : thd->db; - List_iterator_fast<Item> it(select_lex->item_list); + List_iterator_fast<Item> it(sl->item_list); Item *item; fill_effective_table_privileges(thd, &view->grant, db, view->real_name); - while((item= it++)) + while ((item= it++)) { uint priv= (get_column_grant(thd, &view->grant, db, view->real_name, item->name) & @@ -223,10 +243,10 @@ int mysql_create_view(THD *thd, { Item_field *fld= (Item_field *)item; /* - There are no any privileges on VIWE column or there are + There are no any privileges on VIEW column or there are some other privileges then we have for underlaying table */ - if (priv == 0 || test(~fld->have_privileges & priv)) + if (priv == 0 || (~fld->have_privileges & priv)) { /* VIEW column has more privileges */ my_printf_error(ER_COLUMNACCESS_DENIED_ERROR, @@ -242,7 +262,7 @@ int mysql_create_view(THD *thd, } else { - if (!test(priv & SELECT_ACL)) + if (!(priv & SELECT_ACL)) { /* user have not privilege to SELECT expression */ my_printf_error(ER_COLUMNACCESS_DENIED_ERROR, @@ -266,14 +286,11 @@ int mysql_create_view(THD *thd, goto err; } VOID(pthread_mutex_lock(&LOCK_open)); - if ((res= mysql_register_view(thd, view, mode))) - { - VOID(pthread_mutex_unlock(&LOCK_open)); - start_waiting_global_read_lock(thd); - goto err; - } + res= mysql_register_view(thd, view, mode); VOID(pthread_mutex_unlock(&LOCK_open)); start_waiting_global_read_lock(thd); + if (res) + goto err; send_ok(thd); lex->link_first_table_back(view, link_to_local); @@ -289,38 +306,34 @@ err: } -// index of revision number in following table -static const int revision_number_position= 4; +/* index of revision number in following table */ +static const int revision_number_position= 5; +/* index of last required parameter for making view */ +static const int required_view_parameters= 7; -static char *view_field_names[]= -{ - (char*)"query", - (char*)"md5", - (char*)"updatable", - (char*)"algorithm", - (char*)"revision", - (char*)"timestamp", - (char*)"create-version", - (char*)"source" -}; +/* + table of VIEW .frm field descriptors + + Note that one should NOT change the order for this, as it's used by + parse() +*/ -// table of VIEW .frm field descriprors static File_option view_parameters[]= -{{{view_field_names[0], 5}, offsetof(TABLE_LIST, query), +{{{(char*) "query", 5}, offsetof(TABLE_LIST, query), FILE_OPTIONS_STRING}, - {{view_field_names[1], 3}, offsetof(TABLE_LIST, md5), + {{(char*) "md5", 3}, offsetof(TABLE_LIST, md5), FILE_OPTIONS_STRING}, - {{view_field_names[2], 9}, offsetof(TABLE_LIST, updatable), + {{(char*) "updatable", 9}, offsetof(TABLE_LIST, updatable_view), FILE_OPTIONS_ULONGLONG}, - {{view_field_names[3], 9}, offsetof(TABLE_LIST, algorithm), + {{(char*) "algorithm", 9}, offsetof(TABLE_LIST, algorithm), FILE_OPTIONS_ULONGLONG}, - {{view_field_names[4], 8}, offsetof(TABLE_LIST, revision), + {{(char*) "revision", 8}, offsetof(TABLE_LIST, revision), FILE_OPTIONS_REV}, - {{view_field_names[5], 9}, offsetof(TABLE_LIST, timestamp), + {{(char*) "timestamp", 9}, offsetof(TABLE_LIST, timestamp), FILE_OPTIONS_TIMESTAMP}, - {{view_field_names[6], 14}, offsetof(TABLE_LIST, file_version), + {{(char*)"create-version", 14},offsetof(TABLE_LIST, file_version), FILE_OPTIONS_ULONGLONG}, - {{view_field_names[7], 6}, offsetof(TABLE_LIST, source), + {{(char*) "source", 6}, offsetof(TABLE_LIST, source), FILE_OPTIONS_ESTRING}, {{NULL, 0}, 0, FILE_OPTIONS_STRING} @@ -356,7 +369,12 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, // print query str.length(0); - thd->lex->unit.print(&str); + { + ulong sql_mode= thd->variables.sql_mode & MODE_ANSI_QUOTES; + thd->variables.sql_mode&= ~MODE_ANSI_QUOTES; + thd->lex->unit.print(&str); + thd->variables.sql_mode|= sql_mode; + } str.append('\0'); DBUG_PRINT("VIEW", ("View: %s", str.ptr())); @@ -368,16 +386,18 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, dir.length= strlen(dir_buff); file.str= file_buff; - file.length= my_snprintf(file_buff, FN_REFLEN, "%s%s", - view->real_name, reg_ext); + file.length= (strxnmov(file_buff, FN_REFLEN, view->real_name, reg_ext, + NullS) - file_buff); /* init timestamp */ - if (!test(view->timestamp.str)) + if (!view->timestamp.str) view->timestamp.str= view->timestamp_buffer; // check old .frm { char path_buff[FN_REFLEN]; LEX_STRING path; + File_parser *parser; + path.str= path_buff; fn_format(path_buff, file.str, dir.str, 0, MY_UNPACK_FILENAME); path.length= strlen(path_buff); @@ -390,34 +410,27 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, DBUG_RETURN(1); } - File_parser *parser= sql_parse_prepare(&path, &thd->mem_root, 0); - if (parser) + if (!(parser= sql_parse_prepare(&path, &thd->mem_root, 0))) + DBUG_RETURN(1); + + if (!parser->ok() || + strncmp("VIEW", parser->type()->str, parser->type()->length)) { - if(parser->ok() && - !strncmp("VIEW", parser->type()->str, parser->type()->length)) - { - /* - read revision number - - TODO: read dependense list, too, to process cascade/restrict - TODO: special cascade/restrict procedure for alter? - */ - if (parser->parse((gptr)view, &thd->mem_root, - view_parameters + revision_number_position, 1)) - { - DBUG_RETURN(1); - } - } - else - { - my_error(ER_WRONG_OBJECT, MYF(0), (view->db?view->db:thd->db), - view->real_name, "VIEW"); - DBUG_RETURN(1); - } + my_error(ER_WRONG_OBJECT, MYF(0), (view->db ? view->db : thd->db), + view->real_name, "VIEW"); + DBUG_RETURN(1); } - else + + /* + read revision number + + TODO: read dependense list, too, to process cascade/restrict + TODO: special cascade/restrict procedure for alter? + */ + if (parser->parse((gptr)view, &thd->mem_root, + view_parameters + revision_number_position, 1)) { - DBUG_RETURN(1); + DBUG_RETURN(1); } } else @@ -447,17 +460,17 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, thd->lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; } view->algorithm= thd->lex->create_view_algorithm; - if ((view->updatable= (can_be_merged && - view->algorithm != VIEW_ALGORITHM_TMEPTABLE))) + if ((view->updatable_view= (can_be_merged && + view->algorithm != VIEW_ALGORITHM_TMPTABLE))) { // TODO: change here when we will support UNIONs for (TABLE_LIST *tbl= (TABLE_LIST *)thd->lex->select_lex.table_list.first; tbl; tbl= tbl->next_local) { - if (tbl->view != 0 && !tbl->updatable) + if (tbl->view && !tbl->updatable_view) { - view->updatable= 0; + view->updatable_view= 0; break; } } @@ -478,16 +491,23 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, mysql_make_view() parser - parser object; table - TABLE_LIST structure for filling + + RETURN + 0 ok + 1 error + */ + my_bool mysql_make_view(File_parser *parser, TABLE_LIST *table) { + bool include_proc_table= 0; DBUG_ENTER("mysql_make_view"); if (table->view) { DBUG_PRINT("info", - ("VIEW %s.%s is already processed on previos PS/SP execution", + ("VIEW %s.%s is already processed on previous PS/SP execution", table->view_db.str, table->view_name.str)); DBUG_RETURN(0); } @@ -507,13 +527,14 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) thd->set_n_backup_item_arena(arena, &backup); /* init timestamp */ - if (!test(table->timestamp.str)) + if (!table->timestamp.str) table->timestamp.str= table->timestamp_buffer; /* TODO: when VIEWs will be stored in cache, table mem_root should be used here */ - if (parser->parse((gptr)table, &thd->mem_root, view_parameters, 6)) + if (parser->parse((gptr)table, &thd->mem_root, view_parameters, + required_view_parameters)) goto err; /* @@ -534,8 +555,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) now Lex placed in statement memory */ table->view= lex= thd->lex= (LEX*) new(&thd->mem_root) st_lex_local; - lex_start(thd, (uchar*)table->query.str, table->query.length); - mysql_init_query(thd, true); + mysql_init_query(thd, (uchar*)table->query.str, table->query.length, TRUE); lex->select_lex.select_number= ++thd->select_number; old_lex->derived_tables|= DERIVED_VIEW; { @@ -566,14 +586,35 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) */ thd->options&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES); + CHARSET_INFO *save_cs= thd->variables.character_set_client; + thd->variables.character_set_client= system_charset_info; res= yyparse((void *)thd); + thd->variables.character_set_client= save_cs; thd->options= options; } if (!res && !thd->is_fatal_error) { + TABLE_LIST *top_view= (table->belong_to_view ? + table->belong_to_view : + table); - /* move SP to main LEX */ - sp_merge_funs(old_lex, lex); + if (lex->spfuns.records) + { + /* move SP to main LEX */ + sp_merge_funs(old_lex, lex); + if (old_lex->proc_table == 0 && + (old_lex->proc_table= + (TABLE_LIST*)thd->calloc(sizeof(TABLE_LIST))) != 0) + { + TABLE_LIST *table= old_lex->proc_table; + table->db= (char*)"mysql"; + table->db_length= 5; + table->real_name= table->alias= (char*)"proc"; + table->real_name_length= 4; + table->cacheable_table= 1; + include_proc_table= 1; + } + } if (lex->spfuns.array.buffer) hash_free(&lex->spfuns); @@ -581,9 +622,12 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) if ((table->next_global= lex->query_tables)) table->next_global->prev_global= &table->next_global; - /* mark to avoid temporary table using */ + /* mark to avoid temporary table using and put view reference*/ for (TABLE_LIST *tbl= table->next_global; tbl; tbl= tbl->next_global) + { tbl->skip_temporary= 1; + tbl->belong_to_view= top_view; + } /* check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show @@ -613,10 +657,11 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) - VIEW SELECT allow marging - VIEW used in subquery or command support MERGE algorithm */ - if (table->algorithm != VIEW_ALGORITHM_TMEPTABLE && + if (table->algorithm != VIEW_ALGORITHM_TMPTABLE && lex->can_be_merged() && (table->select_lex->master_unit() != &old_lex->unit || - old_lex->can_use_merged())) + old_lex->can_use_merged()) && + !old_lex->can_not_use_merged()) { /* TODO: support multi tables substitutions @@ -629,6 +674,8 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) DBUG_ASSERT(view_table != 0); table->effective_algorithm= VIEW_ALGORITHM_MERGE; + DBUG_PRINT("info", ("algorithm: MERGE")); + table->updatable= (table->updatable_view != 0); if (old_next) { @@ -656,13 +703,10 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) goto ok; } - table->effective_algorithm= VIEW_ALGORITHM_TMEPTABLE; - if (table->updatable) - { - //TOTO: warning: can't be updateable, .frm edited by hand. version - //downgrade? - table->updatable= 0; - } + table->effective_algorithm= VIEW_ALGORITHM_TMPTABLE; + DBUG_PRINT("info", ("algorithm: TEMPORARY TABLE")); + lex->select_lex.linkage= DERIVED_TABLE_TYPE; + table->updatable= 0; /* SELECT tree link */ lex->unit.include_down(table->select_lex); @@ -672,10 +716,16 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) { if ((tbl_end= table->next_global)) { - for (; (tbl_next= tbl_end->next_global); tbl_end= tbl_next); + for (; (tbl_next= tbl_end->next_global); tbl_end= tbl_next) + ; if ((tbl_end->next_global= old_next)) tbl_end->next_global->prev_global= &tbl_end->next_global; } + else + { + /* VIEW do not contain tables */ + table->next_global= old_next; + } } table->derived= &lex->unit; @@ -694,6 +744,17 @@ ok: lex->all_selects_list->link_prev= (st_select_lex_node**)&old_lex->all_selects_list; + if (include_proc_table) + { + TABLE_LIST *proc= old_lex->proc_table; + if((proc->next_global= table->next_global)) + { + table->next_global->prev_global= &proc->next_global; + } + proc->prev_global= &table->next_global; + table->next_global= proc; + } + thd->lex= old_lex; DBUG_RETURN(0); @@ -720,6 +781,7 @@ err: -1 Error 1 Error and error message given */ + int mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) { DBUG_ENTER("mysql_drop_view"); @@ -729,8 +791,8 @@ int mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) for (view= views; view; view= view->next_local) { - strxmov(path, mysql_data_home, "/", view->db, "/", view->real_name, - reg_ext, NullS); + strxnmov(path, FN_REFLEN, mysql_data_home, "/", view->db, "/", + view->real_name, reg_ext, NullS); (void) unpack_filename(path, path); VOID(pthread_mutex_lock(&LOCK_open)); if (access(path, F_OK) || (type= (mysql_frm_type(path) != FRMTYPE_VIEW))) @@ -782,21 +844,20 @@ frm_type_enum mysql_frm_type(char *path) { File file; char header[10]; //"TYPE=VIEW\n" it is 10 characters + int length; DBUG_ENTER("mysql_frm_type"); if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0) { DBUG_RETURN(FRMTYPE_ERROR); } - if (my_read(file, header, 10, MYF(MY_WME)) == MY_FILE_ERROR) - { - my_close(file, MYF(MY_WME)); - DBUG_RETURN(FRMTYPE_ERROR); - } + length= my_read(file, (byte*) header, 10, MYF(MY_WME)); my_close(file, MYF(MY_WME)); - if (strncmp(header, "TYPE=VIEW\n", 10) != 0) - DBUG_RETURN(FRMTYPE_TABLE); - DBUG_RETURN(FRMTYPE_VIEW); + if (length == (int) MY_FILE_ERROR) + DBUG_RETURN(FRMTYPE_ERROR); + if (!strncmp(header, "TYPE=VIEW\n", 10)) + DBUG_RETURN(FRMTYPE_VIEW); + DBUG_RETURN(FRMTYPE_TABLE); // Is probably a .frm table } @@ -815,72 +876,81 @@ frm_type_enum mysql_frm_type(char *path) bool check_key_in_view(THD *thd, TABLE_LIST *view) { + TABLE *table; + Item **trans; + KEY *key_info, *key_info_end; + uint i, elements_in_view; DBUG_ENTER("check_key_in_view"); + if (!view->view) DBUG_RETURN(FALSE); /* it is normal table */ + table= view->table; + trans= view->field_translation; + key_info_end= (key_info= table->key_info)+ table->keys; - TABLE *table= view->table; - Item **trans= view->field_translation; - KEY *key_info= table->key_info; - uint primary_key= table->primary_key; - uint num= view->view->select_lex.item_list.elements; + elements_in_view= view->view->select_lex.item_list.elements; DBUG_ASSERT(view->table != 0 && view->field_translation != 0); - /* try to find key */ - for (uint i=0; i < table->keys; i++, key_info++) + /* Loop over all keys to see if a unique-not-null key is used */ + for (;key_info != key_info_end ; key_info++) { - if (i == primary_key && !strcmp(key_info->name, primary_key_name) || - key_info->flags & HA_NOSAME) + if ((key_info->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME) { KEY_PART_INFO *key_part= key_info->key_part; - bool found= 1; - for (uint j=0; j < key_info->key_parts && found; j++, key_part++) + KEY_PART_INFO *key_part_end= key_part + key_info->key_parts; + + /* check that all key parts are used */ + for (;;) { - found= 0; - for (uint k= 0; k < num; k++) + uint k; + for (k= 0; k < elements_in_view; k++) { if (trans[k]->type() == Item::FIELD_ITEM && - ((Item_field *)trans[k])->field == key_part->field && - (key_part->field->flags & NOT_NULL_FLAG)) - { - found= 1; + ((Item_field *)trans[k])->field == key_part->field) break; - } } + if (k == elements_in_view) + break; // Key is not possible + if (++key_part == key_part_end) + DBUG_RETURN(FALSE); // Found usable key } - if (found) - DBUG_RETURN(FALSE); } } + DBUG_PRINT("info", ("checking if all fields of table are used")); /* check all fields presence */ { - Field **field_ptr= table->field; - for (; *field_ptr; ++field_ptr) + Field **field_ptr; + for (field_ptr= table->field; *field_ptr; field_ptr++) { - uint i= 0; - for (; i < num; i++) + for (i= 0; i < elements_in_view; i++) { if (trans[i]->type() == Item::FIELD_ITEM && ((Item_field *)trans[i])->field == *field_ptr) break; } - if (i >= num) + if (i == elements_in_view) // If field didn't exists { ulong mode= thd->variables.sql_updatable_view_key; - /* 1 == YES, 2 == LIMIT1 */ + /* + 0 == NO ; Don't give any errors + 1 == YES ; Give always an error + 2 == LIMIT1 ; Give an error if this is used with LIMIT 1 + This is used to protect against gui programs that + uses LIMIT 1 to update just the current row. This + doesn't work reliable if the view doesn't have a + unique key or if the view doesn't use all fields in + table. + */ if (mode == 1 || (mode == 2 && - thd->lex->select_lex.select_limit == 1)) + thd->lex->unit.global_parameters->select_limit == 1)) { DBUG_RETURN(TRUE); } - else - { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_WARN_VIEW_WITHOUT_KEY, ER(ER_WARN_VIEW_WITHOUT_KEY)); - DBUG_RETURN(FALSE); - } + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_WARN_VIEW_WITHOUT_KEY, ER(ER_WARN_VIEW_WITHOUT_KEY)); + DBUG_RETURN(FALSE); } } } @@ -899,18 +969,17 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) void insert_view_fields(List<Item> *list, TABLE_LIST *view) { - uint num= view->view->select_lex.item_list.elements; - Item **trans= view->field_translation; + uint elements_in_view= view->view->select_lex.item_list.elements; + Item **trans; DBUG_ENTER("insert_view_fields"); - if (!trans) + + if (!(trans= view->field_translation)) DBUG_VOID_RETURN; - for (uint i= 0; i < num; i++) + for (uint i= 0; i < elements_in_view; i++) { if (trans[i]->type() == Item::FIELD_ITEM) - { list->push_back(trans[i]); - } } DBUG_VOID_RETURN; } diff --git a/sql/sql_view.h b/sql/sql_view.h index 36eae6cdcfc..431f82a5bb8 100644 --- a/sql/sql_view.h +++ b/sql/sql_view.h @@ -27,13 +27,6 @@ bool check_key_in_view(THD *thd, TABLE_LIST * view); void insert_view_fields(List<Item> *list, TABLE_LIST *view); -enum frm_type_enum -{ - FRMTYPE_ERROR, - FRMTYPE_TABLE, - FRMTYPE_VIEW -}; - frm_type_enum mysql_frm_type(char *path); extern TYPELIB sql_updatable_view_key_typelib; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 65b364c6387..41432cee673 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -613,6 +613,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token CURSOR_SYM %token ELSEIF_SYM %token ITERATE_SYM +%token GOTO_SYM +%token LABEL_SYM %token LEAVE_SYM %token LOOP_SYM %token REPEAT_SYM @@ -1169,7 +1171,6 @@ create: 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 @@ -1182,13 +1183,16 @@ create: sp_proc_stmt { LEX *lex= Lex; + sp_head *sp= lex->sphead; - lex->sphead->init_strings(YYTHD, lex, $3); + if (sp->check_backpatch(YYTHD)) + YYABORT; + sp->init_strings(YYTHD, lex, $3); lex->sql_command= SQLCOM_CREATE_PROCEDURE; /* Restore flag if it was cleared above */ - if (lex->sphead->m_old_cmq) + if (sp->m_old_cmq) YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; - lex->sphead->restore_thd_mem_root(YYTHD); + sp->restore_thd_mem_root(YYTHD); } | CREATE or_replace algorithm VIEW_SYM table_ident { @@ -1306,7 +1310,6 @@ create_function_tail: { LEX *lex= Lex; - lex->spcont->set_params(); lex->sphead->m_param_end= lex->tok_start; } RETURNS_SYM @@ -1339,6 +1342,8 @@ create_function_tail: LEX *lex= Lex; sp_head *sp= lex->sphead; + if (sp->check_backpatch(YYTHD)) + YYABORT; lex->sql_command= SQLCOM_CREATE_SPFUNCTION; sp->init_strings(YYTHD, lex, lex->spname); /* Restore flag if it was cleared above */ @@ -1480,7 +1485,8 @@ sp_opt_inout: sp_proc_stmts: /* Empty */ {} - | sp_proc_stmts sp_proc_stmt ';' + | sp_proc_stmts { Lex->query_tables= 0; } sp_proc_stmt ';' + ; sp_decls: @@ -1515,23 +1521,28 @@ sp_decl: DECLARE_SYM sp_decl_idents type sp_opt_default { LEX *lex= Lex; - uint max= lex->spcont->current_framesize(); + sp_pcontext *ctx= lex->spcont; + uint max= ctx->context_pvars(); 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); + ctx->set_type(i, type); if (! it) - lex->spcont->set_isset(i, FALSE); + ctx->set_isset(i, FALSE); else { sp_instr_set *in= new sp_instr_set(lex->sphead->instructions(), - i, it, type); + ctx, + ctx->pvar_context2index(i), + it, type); + in->tables= lex->query_tables; + lex->query_tables= 0; lex->sphead->add_instr(in); - lex->spcont->set_isset(i, TRUE); - lex->spcont->set_default(i, it); + ctx->set_isset(i, TRUE); + ctx->set_default(i, it); } } $$.vars= $2; @@ -1557,30 +1568,33 @@ sp_decl: 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()); + new sp_instr_hpush_jump(sp->instructions(), ctx, $2, + ctx->current_pvars()); sp->add_instr(i); sp->push_backpatch(i, ctx->push_label((char *)"", 0)); ctx->add_handler(); + sp->m_in_handler= TRUE; } sp_hcond_list sp_proc_stmt { LEX *lex= Lex; sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; 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())); + sp->add_instr(new sp_instr_hreturn(sp->instructions(), ctx, + ctx->current_pvars())); else { /* EXIT or UNDO handler, just jump to the end of the block */ - sp_instr_jump *i= new sp_instr_jump(sp->instructions()); + sp_instr_jump *i= new sp_instr_jump(sp->instructions(), ctx); sp->add_instr(i); sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */ } lex->sphead->backpatch(hlab); + sp->m_in_handler= FALSE; $$.vars= $$.conds= $$.curs= 0; $$.hndlrs= $6; } @@ -1588,19 +1602,19 @@ sp_decl: { LEX *lex= Lex; sp_head *sp= lex->sphead; - sp_pcontext *spc= lex->spcont; + sp_pcontext *ctx= lex->spcont; uint offp; sp_instr_cpush *i; - if (spc->find_cursor(&$2, &offp, TRUE)) + if (ctx->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); + i= new sp_instr_cpush(sp->instructions(), ctx, $5); sp->add_instr(i); - lex->spcont->push_cursor(&$2); + ctx->push_cursor(&$2); $$.vars= $$.conds= $$.hndlrs= 0; $$.curs= 1; } @@ -1759,14 +1773,8 @@ sp_proc_stmt: { LEX *lex= Lex; - /* QQ What else? This doesn't seem to be a practical way, - but at the moment we can't think of anything better... */ if ((lex->sql_command == SQLCOM_SELECT && !lex->result) || - lex->sql_command == SQLCOM_SHOW_CREATE_PROC || - lex->sql_command == SQLCOM_SHOW_CREATE_FUNC || - lex->sql_command == SQLCOM_SHOW_STATUS_PROC || - lex->sql_command == SQLCOM_SHOW_STATUS_FUNC || - lex->sql_command == SQLCOM_ANALYZE) + sp_multi_results_command(lex->sql_command)) { /* We maybe have one or more SELECT without INTO */ lex->sphead->m_multi_results= TRUE; @@ -1797,7 +1805,8 @@ sp_proc_stmt: } else { - sp_instr_stmt *i=new sp_instr_stmt(lex->sphead->instructions()); + sp_instr_stmt *i=new sp_instr_stmt(lex->sphead->instructions(), + lex->spcont); i->set_lex(lex); lex->sphead->add_instr(i); @@ -1825,6 +1834,7 @@ sp_proc_stmt: YYABORT; } i= new sp_instr_freturn(lex->sphead->instructions(), + lex->spcont, $2, lex->sphead->m_returns); lex->sphead->add_instr(i); lex->sphead->m_has_return= TRUE; @@ -1843,14 +1853,17 @@ sp_proc_stmt: 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(); + uint offset= lex->spcont->current_pvars(); sp_instr_set *i = new sp_instr_set(lex->sphead->instructions(), + lex->spcont, 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); + i->tables= lex->query_tables; + lex->query_tables= 0; lex->sphead->add_instr(i); lex->sphead->m_simple_case= TRUE; } @@ -1875,7 +1888,8 @@ sp_proc_stmt: { LEX *lex= Lex; sp_head *sp = lex->sphead; - sp_label_t *lab= lex->spcont->find_label($2.str); + sp_pcontext *ctx= lex->spcont; + sp_label_t *lab= ctx->find_label($2.str); if (! lab) { @@ -1884,8 +1898,18 @@ sp_proc_stmt: } else { - sp_instr_jump *i= new sp_instr_jump(sp->instructions()); - + uint ip= sp->instructions(); + sp_instr_jump *i; + sp_instr_hpop *ih; + sp_instr_cpop *ic; + + ih= new sp_instr_hpop(ip++, ctx, 0); + sp->push_backpatch(ih, lab); + sp->add_instr(ih); + ic= new sp_instr_cpop(ip++, ctx, 0); + sp->push_backpatch(ic, lab); + sp->add_instr(ic); + i= new sp_instr_jump(ip, ctx); sp->push_backpatch(i, lab); /* Jumping forward */ sp->add_instr(i); } @@ -1893,19 +1917,104 @@ sp_proc_stmt: | ITERATE_SYM IDENT { LEX *lex= Lex; - sp_label_t *lab= lex->spcont->find_label($2.str); + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_label_t *lab= ctx->find_label($2.str); - if (! lab || lab->isbegin) + if (! lab || lab->type != SP_LAB_ITER) { 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 */ + sp_instr_jump *i; + uint ip= sp->instructions(); + uint n; + + n= ctx->diff_handlers(lab->ctx); + if (n) + sp->add_instr(new sp_instr_hpop(ip++, ctx, n)); + n= ctx->diff_cursors(lab->ctx); + if (n) + sp->add_instr(new sp_instr_cpop(ip++, ctx, n)); + i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */ + sp->add_instr(i); + } + } + | LABEL_SYM IDENT + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_label_t *lab= ctx->find_label($2.str); - lex->sphead->add_instr(i); + if (lab) + { + net_printf(YYTHD, ER_SP_LABEL_REDEFINE, $2.str); + YYABORT; + } + else + { + lab= ctx->push_label($2.str, sp->instructions()); + lab->type= SP_LAB_GOTO; + lab->ctx= ctx; + sp->backpatch(lab); + } + } + | GOTO_SYM IDENT + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + uint ip= lex->sphead->instructions(); + sp_label_t *lab; + sp_instr_jump *i; + sp_instr_hpop *ih; + sp_instr_cpop *ic; + + if (sp->m_in_handler) + { + send_error(lex->thd, ER_SP_GOTO_IN_HNDLR); + YYABORT; + } + lab= ctx->find_label($2.str); + if (! lab) + { + lab= (sp_label_t *)YYTHD->alloc(sizeof(sp_label_t)); + lab->name= $2.str; + lab->ip= 0; + lab->type= SP_LAB_REF; + lab->ctx= ctx; + + ih= new sp_instr_hpop(ip++, ctx, 0); + sp->push_backpatch(ih, lab); + sp->add_instr(ih); + ic= new sp_instr_cpop(ip++, ctx, 0); + sp->add_instr(ic); + sp->push_backpatch(ic, lab); + i= new sp_instr_jump(ip, ctx); + sp->push_backpatch(i, lab); /* Jumping forward */ + sp->add_instr(i); + } + else + { + uint n; + + n= ctx->diff_handlers(lab->ctx); + if (n) + { + ih= new sp_instr_hpop(ip++, ctx, n); + sp->add_instr(ih); + } + n= ctx->diff_cursors(lab->ctx); + if (n) + { + ic= new sp_instr_cpop(ip++, ctx, n); + sp->add_instr(ic); + } + i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */ + sp->add_instr(i); } } | OPEN_SYM ident @@ -1920,7 +2029,7 @@ sp_proc_stmt: net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str); YYABORT; } - i= new sp_instr_copen(sp->instructions(), offset); + i= new sp_instr_copen(sp->instructions(), lex->spcont, offset); sp->add_instr(i); } | FETCH_SYM ident INTO @@ -1935,7 +2044,7 @@ sp_proc_stmt: net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str); YYABORT; } - i= new sp_instr_cfetch(sp->instructions(), offset); + i= new sp_instr_cfetch(sp->instructions(), lex->spcont, offset); sp->add_instr(i); } sp_fetch_list @@ -1952,7 +2061,7 @@ sp_proc_stmt: net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str); YYABORT; } - i= new sp_instr_cclose(sp->instructions(), offset); + i= new sp_instr_cclose(sp->instructions(), lex->spcont, offset); sp->add_instr(i); } ; @@ -2004,11 +2113,14 @@ sp_fetch_list: sp_if: expr THEN_SYM { - sp_head *sp= Lex->sphead; - sp_pcontext *ctx= Lex->spcont; + LEX *lex= Lex; + 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_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx, $1); + i->tables= lex->query_tables; + lex->query_tables= 0; sp->push_backpatch(i, ctx->push_label((char *)"", 0)); sp->add_instr(i); } @@ -2017,7 +2129,7 @@ sp_if: sp_head *sp= Lex->sphead; sp_pcontext *ctx= Lex->spcont; uint ip= sp->instructions(); - sp_instr_jump *i = new sp_instr_jump(ip); + sp_instr_jump *i = new sp_instr_jump(ip, ctx); sp->add_instr(i); sp->backpatch(ctx->pop_label()); @@ -2040,13 +2152,14 @@ sp_elseifs: sp_case: expr THEN_SYM { - sp_head *sp= Lex->sphead; + LEX *lex= Lex; + sp_head *sp= lex->sphead; sp_pcontext *ctx= Lex->spcont; uint ip= sp->instructions(); sp_instr_jump_if_not *i; if (! sp->m_simple_case) - i= new sp_instr_jump_if_not(ip, $1); + i= new sp_instr_jump_if_not(ip, ctx, $1); else { /* Simple case: <caseval> = <whenval> */ LEX_STRING ivar; @@ -2054,12 +2167,15 @@ sp_case: ivar.str= (char *)"_tmp_"; ivar.length= 5; Item *var= (Item*) new Item_splocal(ivar, - ctx->current_framesize()-1); + ctx->current_pvars()-1); Item *expr= new Item_func_eq(var, $1); - i= new sp_instr_jump_if_not(ip, expr); + i= new sp_instr_jump_if_not(ip, ctx, expr); + lex->variables_used= 1; } sp->push_backpatch(i, ctx->push_label((char *)"", 0)); + i->tables= lex->query_tables; + lex->query_tables= 0; sp->add_instr(i); } sp_proc_stmts @@ -2067,7 +2183,7 @@ sp_case: sp_head *sp= Lex->sphead; sp_pcontext *ctx= Lex->spcont; uint ip= sp->instructions(); - sp_instr_jump *i = new sp_instr_jump(ip); + sp_instr_jump *i = new sp_instr_jump(ip, ctx); sp->add_instr(i); sp->backpatch(ctx->pop_label()); @@ -2086,7 +2202,8 @@ sp_whens: { sp_head *sp= Lex->sphead; uint ip= sp->instructions(); - sp_instr_error *i= new sp_instr_error(ip, ER_SP_CASE_NOT_FOUND); + sp_instr_error *i= new sp_instr_error(ip, Lex->spcont, + ER_SP_CASE_NOT_FOUND); sp->add_instr(i); } @@ -2098,7 +2215,8 @@ sp_labeled_control: IDENT ':' { LEX *lex= Lex; - sp_label_t *lab= lex->spcont->find_label($1.str); + sp_pcontext *ctx= lex->spcont; + sp_label_t *lab= ctx->find_label($1.str); if (lab) { @@ -2107,8 +2225,9 @@ sp_labeled_control: } else { - lex->spcont->push_label($1.str, - lex->sphead->instructions()); + lab= lex->spcont->push_label($1.str, + lex->sphead->instructions()); + lab->type= SP_LAB_ITER; } } sp_unlabeled_control sp_opt_label @@ -2145,9 +2264,8 @@ sp_unlabeled_control: LEX *lex= Lex; sp_label_t *lab= lex->spcont->last_label(); - lab->isbegin= TRUE; - /* Scope duplicate checking */ - lex->spcont->push_scope(); + lab->type= SP_LAB_BEGIN; + lex->spcont= lex->spcont->push_context(); } sp_decls sp_proc_stmts @@ -2157,15 +2275,14 @@ sp_unlabeled_control: 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); + sp->backpatch(ctx->last_label()); /* We always have a label */ if ($3.hndlrs) - sp->add_instr(new sp_instr_hpop(sp->instructions(),$3.hndlrs)); + sp->add_instr(new sp_instr_hpop(sp->instructions(), ctx, + $3.hndlrs)); if ($3.curs) - sp->add_instr(new sp_instr_cpop(sp->instructions(), $3.curs)); - ctx->pop_scope(); + sp->add_instr(new sp_instr_cpop(sp->instructions(), ctx, + $3.curs)); + lex->spcont= ctx->pop_context(); } | LOOP_SYM sp_proc_stmts END LOOP_SYM @@ -2173,7 +2290,7 @@ sp_unlabeled_control: 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); + sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip); lex->sphead->add_instr(i); } @@ -2182,10 +2299,13 @@ sp_unlabeled_control: 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); + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont, + $2); /* Jumping forward */ sp->push_backpatch(i, lex->spcont->last_label()); + i->tables= lex->query_tables; + lex->query_tables= 0; sp->add_instr(i); } sp_proc_stmts END WHILE_SYM @@ -2193,7 +2313,7 @@ sp_unlabeled_control: 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); + sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip); lex->sphead->add_instr(i); } @@ -2202,8 +2322,11 @@ sp_unlabeled_control: 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); + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont, + $4, lab->ip); + i->tables= lex->query_tables; + lex->query_tables= 0; lex->sphead->add_instr(i); } ; @@ -3676,8 +3799,12 @@ select_item: YYABORT; if ($4.str) $2->set_name($4.str,$4.length,system_charset_info); - else if (!$2->name) - $2->set_name($1,(uint) ($3 - $1), YYTHD->charset()); + else if (!$2->name) { + char *str = $1; + if (str[-1] == '`') + str--; + $2->set_name(str,(uint) ($3 - str), YYTHD->charset()); + } }; remember_name: @@ -4662,7 +4789,7 @@ join_table: '(' using_list ')' { add_join_on($3,$7); $$=$3; } - | table_ref LEFT opt_outer JOIN_SYM table_factor ON expr + | table_ref LEFT opt_outer JOIN_SYM table_ref ON expr { add_join_on($5,$7); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; } | table_ref LEFT opt_outer JOIN_SYM table_factor { @@ -4677,7 +4804,7 @@ join_table: $6->outer_join|=JOIN_TYPE_LEFT; $$=$6; } - | table_ref RIGHT opt_outer JOIN_SYM table_factor ON expr + | table_ref RIGHT opt_outer JOIN_SYM table_ref ON expr { LEX *lex= Lex; if (!($$= lex->current_select->convert_right_join())) @@ -5253,6 +5380,11 @@ drop: | DROP FUNCTION_SYM if_exists sp_name opt_restrict { LEX *lex=Lex; + if (lex->sphead) + { + net_printf(YYTHD, ER_SP_NO_DROP_SP, "FUNCTION"); + YYABORT; + } lex->sql_command = SQLCOM_DROP_FUNCTION; lex->drop_if_exists= $3; lex->spname= $4; @@ -5260,6 +5392,11 @@ drop: | DROP PROCEDURE if_exists sp_name opt_restrict { LEX *lex=Lex; + if (lex->sphead) + { + net_printf(YYTHD, ER_SP_NO_DROP_SP, "PROCEDURE"); + YYABORT; + } lex->sql_command = SQLCOM_DROP_PROCEDURE; lex->drop_if_exists= $3; lex->spname= $4; @@ -6253,6 +6390,7 @@ simple_ident: $1.str); } $$ = (Item*) new Item_splocal($1, spv->offset); + lex->variables_used= 1; } else { @@ -6590,6 +6728,7 @@ keyword: | INNOBASE_SYM {} | INSERT_METHOD {} | RELAY_THREAD {} + | LABEL_SYM {} | LANGUAGE_SYM {} | LAST_SYM {} | LEAVES {} @@ -6849,16 +6988,12 @@ option_value: } else { /* An SP local variable */ + sp_pcontext *ctx= lex->spcont; sp_pvar_t *spv; sp_instr_set *i; Item *it; - if ($3 && $3->type() == Item::SUBSELECT_ITEM) - { /* QQ For now, just disallow subselects as values */ - send_error(lex->thd, ER_SP_SUBSELECT_NYI); - YYABORT; - } - spv= lex->spcont->find_pvar(&$1.base_name); + spv= ctx->find_pvar(&$1.base_name); if ($3) it= $3; @@ -6866,8 +7001,10 @@ option_value: it= spv->dflt; else it= new Item_null(); - i= new sp_instr_set(lex->sphead->instructions(), + i= new sp_instr_set(lex->sphead->instructions(), ctx, spv->offset, it, spv->type); + i->tables= lex->query_tables; + lex->query_tables= 0; lex->sphead->add_instr(i); spv->isset= TRUE; } @@ -7640,7 +7777,7 @@ algorithm: | ALGORITHM_SYM EQ MERGE_SYM { Lex->create_view_algorithm= VIEW_ALGORITHM_MERGE; } | ALGORITHM_SYM EQ TEMPTABLE_SYM - { Lex->create_view_algorithm= VIEW_ALGORITHM_TMEPTABLE; } + { Lex->create_view_algorithm= VIEW_ALGORITHM_TMPTABLE; } ; check_option: /* empty */ {} diff --git a/sql/table.cc b/sql/table.cc index 43b39ffb37e..1764df75a7e 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -88,6 +88,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, DBUG_PRINT("enter",("name: '%s' form: %lx",name,outparam)); error=1; + disk_buff=NULL; + old_root= my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); if ((file=my_open(fn_format(index_file, name, "", reg_ext, MY_UNPACK_FILENAME), @@ -118,11 +120,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, bzero((char*) outparam,sizeof(*outparam)); outparam->blob_ptr_size=sizeof(char*); - disk_buff=NULL; record= NULL; keynames=NullS; outparam->db_stat = db_stat; - init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); - old_root= my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); my_pthread_setspecific_ptr(THR_MALLOC,&outparam->mem_root); outparam->real_name=strdup_root(&outparam->mem_root, @@ -742,11 +741,9 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, DBUG_RETURN (0); err_w_init: - //awoid problem with uninitialized data + /* Avoid problem with uninitialized data */ bzero((char*) outparam,sizeof(*outparam)); outparam->real_name= (char*)name+dirname_length(name); - old_root= my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); - disk_buff= 0; err_not_open: x_free((gptr) disk_buff); @@ -1447,13 +1444,14 @@ db_type get_table_type(const char *name) st_table_list::calc_md5() buffer buffer for md5 writing */ + void st_table_list::calc_md5(char *buffer) { my_MD5_CTX context; - unsigned char digest[16]; - my_MD5Init (&context); - my_MD5Update (&context,(unsigned char *) query.str, query.length); - my_MD5Final (digest, &context); + uchar digest[16]; + my_MD5Init(&context); + my_MD5Update(&context,(uchar *) query.str, query.length); + my_MD5Final(digest, &context); sprintf((char *) buffer, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", digest[0], digest[1], digest[2], digest[3], @@ -1469,6 +1467,7 @@ void st_table_list::calc_md5(char *buffer) SYNOPSIS st_table_list::set_ancestor() */ + void st_table_list::set_ancestor() { if (ancestor->ancestor) @@ -1496,14 +1495,18 @@ void st_table_list::set_ancestor() (without fields) for name resolving, but substituted expressions will return correct used tables mask. */ + bool st_table_list::setup_ancestor(THD *thd, Item **conds) { Item **transl; SELECT_LEX *select= &view->select_lex; + SELECT_LEX *current_select_save= thd->lex->current_select; Item *item; List_iterator_fast<Item> it(select->item_list); uint i= 0; bool save_set_query_id= thd->set_query_id; + bool save_wrapper= thd->lex->select_lex.no_wrap_view_item; + bool save_allow_sum_func= thd->allow_sum_func; DBUG_ENTER("st_table_list::setup_ancestor"); if (ancestor->ancestor && @@ -1512,16 +1515,20 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) if (field_translation) { + /* prevent look up in SELECTs tree */ + thd->lex->current_select= &thd->lex->select_lex; thd->set_query_id= 1; /* this view was prepared already on previous PS/SP execution */ Item **end= field_translation + select->item_list.elements; - for (Item **i= field_translation; i < end; i++) + for (Item **item= field_translation; item < end; item++) { - //TODO: fix for several tables in VIEW + /* TODO: fix for several tables in VIEW */ uint want_privilege= ancestor->table->grant.want_privilege; /* real rights will be checked in VIEW field */ ancestor->table->grant.want_privilege= 0; - if (!(*i)->fixed && (*i)->fix_fields(thd, ancestor, i)) + /* aggregate function are allowed */ + thd->allow_sum_func= 1; + if (!(*item)->fixed && (*item)->fix_fields(thd, ancestor, item)) goto err; ancestor->table->grant.want_privilege= want_privilege; } @@ -1536,6 +1543,11 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) { DBUG_RETURN(1); } + + /* prevent look up in SELECTs tree */ + thd->lex->current_select= &thd->lex->select_lex; + thd->lex->select_lex.no_wrap_view_item= 1; + /* Resolve all view items against ancestor table. @@ -1545,19 +1557,19 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) thd->set_query_id= 1; while ((item= it++)) { - //TODO: fix for several tables in VIEW + /* TODO: fix for several tables in VIEW */ uint want_privilege= ancestor->table->grant.want_privilege; /* real rights will be checked in VIEW field */ ancestor->table->grant.want_privilege= 0; + /* aggregate function are allowed */ + thd->allow_sum_func= 1; if (!item->fixed && item->fix_fields(thd, ancestor, &item)) - { goto err; - } ancestor->table->grant.want_privilege= want_privilege; transl[i++]= item; } field_translation= transl; - //TODO: sort this list? Use hash for big number of fields + /* TODO: sort this list? Use hash for big number of fields */ if (where) { @@ -1566,12 +1578,12 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) goto err; if (arena) - thd->set_n_backup_item_arena(arena, &backup); + thd->set_n_backup_item_arena(arena, &backup); if (outer_join) { /* Store WHERE condition to ON expression for outer join, because we - can't use WHERE to correctly execute jeft joins on VIEWs and this + can't use WHERE to correctly execute jeft joins on VIEWs and this expression will not be moved to WHERE condition (i.e. will be clean correctly for PS/SP) */ @@ -1589,12 +1601,34 @@ bool st_table_list::setup_ancestor(THD *thd, Item **conds) thd->restore_backup_item_arena(arena, &backup); } + /* full text function moving to current select */ + if (view->select_lex.ftfunc_list->elements) + { + Item_func_match *ifm; + List_iterator_fast<Item_func_match> + li(*(view->select_lex.ftfunc_list)); + while ((ifm= li++)) + current_select_save->ftfunc_list->push_front(ifm); + } + ok: + thd->lex->select_lex.no_wrap_view_item= save_wrapper; + thd->lex->current_select= current_select_save; thd->set_query_id= save_set_query_id; + thd->allow_sum_func= save_allow_sum_func; DBUG_RETURN(0); err: + /* Hide "Unknown column" error */ + if (thd->net.last_errno == ER_BAD_FIELD_ERROR) + { + thd->clear_error(); + my_error(ER_VIEW_INVALID, MYF(0), view_db.str, view_name.str); + } + thd->lex->select_lex.no_wrap_view_item= save_wrapper; + thd->lex->current_select= current_select_save; thd->set_query_id= save_set_query_id; + thd->allow_sum_func= save_allow_sum_func; DBUG_RETURN(1); } diff --git a/sql/table.h b/sql/table.h index 6bf700cc684..2d90a935b74 100644 --- a/sql/table.h +++ b/sql/table.h @@ -29,9 +29,12 @@ typedef struct st_order { Item **item; /* Point at item in select fields */ Item *item_ptr; /* Storage for initial item */ Item **item_copy; /* For SPs; the original item ptr */ + int counter; /* position in SELECT list, correct + only if counter_used is true*/ bool asc; /* true if ascending */ bool free_me; /* true if item isn't shared */ bool in_field_list; /* true if in select field list */ + bool counter_used; /* parapeter was counter of columns */ Field *field; /* If tmp-table group */ char *buff; /* If tmp-table group */ table_map used,depend_map; @@ -47,6 +50,13 @@ typedef struct st_grant_info enum tmp_table_type {NO_TMP_TABLE=0, TMP_TABLE=1, TRANSACTIONAL_TMP_TABLE=2}; +enum frm_type_enum +{ + FRMTYPE_ERROR= 0, + FRMTYPE_TABLE, + FRMTYPE_VIEW +}; + typedef struct st_filesort_info { IO_CACHE *io_cache; /* If sorted through filebyte */ @@ -175,7 +185,7 @@ struct st_table { #define JOIN_TYPE_RIGHT 2 #define VIEW_ALGORITHM_UNDEFINED 0 -#define VIEW_ALGORITHM_TMEPTABLE 1 +#define VIEW_ALGORITHM_TMPTABLE 1 #define VIEW_ALGORITHM_MERGE 2 struct st_lex; @@ -208,6 +218,8 @@ typedef struct st_table_list Item **field_translation; /* array of VIEW fields */ /* ancestor of this table (VIEW merge algorithm) */ st_table_list *ancestor; + /* most upper view this table belongs to */ + st_table_list *belong_to_view; Item *where; /* VIEW WHERE clause condition */ LEX_STRING query; /* text of (CRETE/SELECT) statement */ LEX_STRING md5; /* md5 of query tesxt */ @@ -216,8 +228,8 @@ typedef struct st_table_list LEX_STRING view_name; /* save view name */ LEX_STRING timestamp; /* GMT time stamp of last operation */ ulonglong file_version; /* version of file's field set */ + ulonglong updatable_view; /* VIEW can be updated */ ulonglong revision; /* revision control number */ - ulonglong updatable; /* Is VIEW updateable */ ulonglong algorithm; /* 0 any, 1 tmp tables , 2 merging */ uint effective_algorithm; /* which algorithm was really used */ GRANT_INFO grant; @@ -225,6 +237,7 @@ typedef struct st_table_list uint outer_join; /* Which join type */ uint shared; /* Used in multi-upd */ uint32 db_length, real_name_length; + bool updatable; /* VIEW/TABLE can be updated now */ bool straight; /* optimize with prev table */ bool updating; /* for replicate-do/ignore table */ bool force_index; /* prefer index over table scan */ @@ -241,6 +254,8 @@ typedef struct st_table_list bool setup_is_done; /* setup_tables() is done */ /* do view contain auto_increment field */ bool contain_auto_increment; + /* FRMTYPE_ERROR if any type is acceptable */ + enum frm_type_enum required_type; char timestamp_buffer[20]; /* buffer for timestamp (19+1) */ void calc_md5(char *buffer); @@ -258,7 +273,7 @@ public: virtual ~Field_iterator() {} virtual void set(TABLE_LIST *)= 0; virtual void next()= 0; - virtual bool end()= 0; + virtual bool end_of_fields()= 0; /* Return 1 at end of list */ virtual const char *name()= 0; virtual Item *item(THD *)= 0; virtual Field *field()= 0; @@ -273,7 +288,7 @@ public: void set(TABLE_LIST *table) { ptr= table->table->field; } void set_table(TABLE *table) { ptr= table->field; } void next() { ptr++; } - bool end() { return test(*ptr); } + bool end_of_fields() { return *ptr == 0; } const char *name(); Item *item(THD *thd); Field *field() { return *ptr; } @@ -287,7 +302,7 @@ public: Field_iterator_view() :ptr(0), array_end(0) {} void set(TABLE_LIST *table); void next() { ptr++; } - bool end() { return ptr < array_end; } + bool end_of_fields() { return ptr == array_end; } const char *name(); Item *item(THD *thd) { return *ptr; } Field *field() { return 0; } |