diff options
Diffstat (limited to 'sql/table.cc')
-rw-r--r-- | sql/table.cc | 1061 |
1 files changed, 732 insertions, 329 deletions
diff --git a/sql/table.cc b/sql/table.cc index 631c61f8391..016f79607d0 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -13,23 +13,40 @@ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Some general useful functions */ -#include "mysql_priv.h" +#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ +#include "sql_priv.h" +#include "unireg.h" // REQUIRED: for other includes +#include "table.h" +#include "frm_crypt.h" // get_crypt_for_frm +#include "key.h" // find_ref_key +#include "sql_table.h" // build_table_filename, + // primary_key_name #include "sql_trigger.h" +#include "sql_parse.h" // free_items +#include "strfunc.h" // unhex_type2 +#include "sql_partition.h" // mysql_unpack_partition, + // fix_partition_func, partition_info +#include "sql_acl.h" // *_ACL, acl_getroot_no_password +#include "sql_base.h" // release_table_share #include "create_options.h" #include <m_ctype.h> #include "my_md5.h" #include "my_bit.h" #include "sql_select.h" +#include "sql_derived.h" +#include "mdl.h" // MDL_wait_for_graph_visitor /* INFORMATION_SCHEMA name */ LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")}; +/* PERFORMANCE_SCHEMA name */ +LEX_STRING PERFORMANCE_SCHEMA_DB_NAME= {C_STRING_WITH_LEN("performance_schema")}; + /* MYSQL_SCHEMA name */ LEX_STRING MYSQL_SCHEMA_NAME= {C_STRING_WITH_LEN("mysql")}; @@ -226,36 +243,34 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name) DBUG_ASSERT(db != NULL); DBUG_ASSERT(name != NULL); - if (is_schema_db(db->str, db->length)) - { + if (is_infoschema_db(db->str, db->length)) return TABLE_CATEGORY_INFORMATION; - } + + if ((db->length == PERFORMANCE_SCHEMA_DB_NAME.length) && + (my_strcasecmp(system_charset_info, + PERFORMANCE_SCHEMA_DB_NAME.str, + db->str) == 0)) + return TABLE_CATEGORY_PERFORMANCE; if ((db->length == MYSQL_SCHEMA_NAME.length) && (my_strcasecmp(system_charset_info, - MYSQL_SCHEMA_NAME.str, - db->str) == 0)) + MYSQL_SCHEMA_NAME.str, + db->str) == 0)) { if (is_system_table_name(name->str, name->length)) - { return TABLE_CATEGORY_SYSTEM; - } if ((name->length == GENERAL_LOG_NAME.length) && (my_strcasecmp(system_charset_info, - GENERAL_LOG_NAME.str, - name->str) == 0)) - { - return TABLE_CATEGORY_PERFORMANCE; - } + GENERAL_LOG_NAME.str, + name->str) == 0)) + return TABLE_CATEGORY_LOG; if ((name->length == SLOW_LOG_NAME.length) && (my_strcasecmp(system_charset_info, - SLOW_LOG_NAME.str, - name->str) == 0)) - { - return TABLE_CATEGORY_PERFORMANCE; - } + SLOW_LOG_NAME.str, + name->str) == 0)) + return TABLE_CATEGORY_LOG; } return TABLE_CATEGORY_USER; @@ -320,9 +335,13 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, share->table_map_id= ~0UL; share->cached_row_logging_check= -1; + share->used_tables.empty(); + share->free_tables.empty(); + share->m_flush_tickets.empty(); + memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root)); - pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST); - pthread_cond_init(&share->cond, NULL); + mysql_mutex_init(key_TABLE_SHARE_LOCK_ha_data, + &share->LOCK_ha_data, MY_MUTEX_INIT_FAST); } DBUG_RETURN(share); } @@ -381,70 +400,106 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, */ share->table_map_id= (ulong) thd->query_id; + share->used_tables.empty(); + share->free_tables.empty(); + share->m_flush_tickets.empty(); + DBUG_VOID_RETURN; } +/** + Release resources (plugins) used by the share and free its memory. + TABLE_SHARE is self-contained -- it's stored in its own MEM_ROOT. + Free this MEM_ROOT. +*/ + +void TABLE_SHARE::destroy() +{ + uint idx; + KEY *info_it; + + /* The mutex is initialized only for shares that are part of the TDC */ + if (tmp_table == NO_TMP_TABLE) + mysql_mutex_destroy(&LOCK_ha_data); + my_hash_free(&name_hash); + + plugin_unlock(NULL, db_plugin); + db_plugin= NULL; + + /* Release fulltext parsers */ + info_it= key_info; + for (idx= keys; idx; idx--, info_it++) + { + if (info_it->flags & HA_USES_PARSER) + { + plugin_unlock(NULL, info_it->parser); + info_it->flags= 0; + } + } + if (ha_data_destroy) + { + ha_data_destroy(ha_data); + ha_data_destroy= NULL; + } +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (ha_part_data_destroy) + { + ha_part_data_destroy(ha_part_data); + ha_part_data_destroy= NULL; + } +#endif /* WITH_PARTITION_STORAGE_ENGINE */ + /* + Make a copy since the share is allocated in its own root, + and free_root() updates its argument after freeing the memory. + */ + MEM_ROOT own_root= mem_root; + free_root(&own_root, MYF(0)); +} + /* Free table share and memory used by it SYNOPSIS free_table_share() share Table share - - NOTES - share->mutex must be locked when we come here if it's not a temp table */ void free_table_share(TABLE_SHARE *share) { - MEM_ROOT mem_root; - uint idx; - KEY *key_info; DBUG_ENTER("free_table_share"); DBUG_PRINT("enter", ("table: %s.%s", share->db.str, share->table_name.str)); DBUG_ASSERT(share->ref_count == 0); - /* - If someone is waiting for this to be deleted, inform it about this. - Don't do a delete until we know that no one is refering to this anymore. - */ - if (share->tmp_table == NO_TMP_TABLE) + if (share->m_flush_tickets.is_empty()) { - /* share->mutex is locked in release_table_share() */ - while (share->waiting_on_cond) - { - pthread_cond_broadcast(&share->cond); - pthread_cond_wait(&share->cond, &share->mutex); - } - /* No thread refers to this anymore */ - pthread_mutex_unlock(&share->mutex); - pthread_mutex_destroy(&share->mutex); - pthread_cond_destroy(&share->cond); - } - hash_free(&share->name_hash); - - plugin_unlock(NULL, share->db_plugin); - share->db_plugin= NULL; - - /* Release fulltext parsers */ - key_info= share->key_info; - for (idx= share->keys; idx; idx--, key_info++) - { - if (key_info->flags & HA_USES_PARSER) - { - plugin_unlock(NULL, key_info->parser); - key_info->flags= 0; - } + /* + No threads are waiting for this share to be flushed (the + share is not old, is for a temporary table, or just nobody + happens to be waiting for it). Destroy it. + */ + share->destroy(); } - if (share->ha_data_destroy) + else { - share->ha_data_destroy(share->ha_data); - share->ha_data_destroy= NULL; + Wait_for_flush_list::Iterator it(share->m_flush_tickets); + Wait_for_flush *ticket; + /* + We're about to iterate over a list that is used + concurrently. Make sure this never happens without a lock. + */ + mysql_mutex_assert_owner(&LOCK_open); + + while ((ticket= it++)) + (void) ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED); + /* + If there are threads waiting for this share to be flushed, + the last one to receive the notification will destroy the + share. At this point the share is removed from the table + definition cache, so is OK to proceed here without waiting + for this thread to do the work. + */ } - /* We must copy mem_root from share because share is allocated through it */ - memcpy((char*) &mem_root, (char*) &share->mem_root, sizeof(mem_root)); - free_root(&mem_root, MYF(0)); // Free's share DBUG_VOID_RETURN; } @@ -552,7 +607,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) int error, table_type; bool error_given; File file; - uchar head[288]; + uchar head[64]; char path[FN_REFLEN]; MEM_ROOT **root_ptr, *old_root; DBUG_ENTER("open_table_def"); @@ -563,7 +618,8 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) error_given= 0; strxmov(path, share->normalized_path.str, reg_ext, NullS); - if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0) + if ((file= mysql_file_open(key_file_frm, + path, O_RDONLY | O_SHARE, MYF(0))) < 0) { /* We don't try to open 5.0 unencoded name, if @@ -574,7 +630,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) - non-encoded db or table name contain "#mysql50#" prefix. This kind of tables must have been opened only by the - my_open() above. + mysql_file_open() above. */ if (has_disabled_path_chars(share->table_name.str) || has_disabled_path_chars(share->db.str) || @@ -601,7 +657,8 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) so no need to check the old file name. */ if (length == share->normalized_path.length || - ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0)) + ((file= mysql_file_open(key_file_frm, + path, O_RDONLY | O_SHARE, MYF(0))) < 0)) goto err_not_open; /* Unencoded 5.0 table name found */ @@ -611,7 +668,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) } error= 4; - if (my_read(file, head, 64, MYF(MY_NABP))) + if (mysql_file_read(file, head, 64, MYF(MY_NABP))) goto err; if (head[0] == (uchar) 254 && head[1] == 1) @@ -664,7 +721,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) thd->status_var.opened_shares++; err: - my_close(file, MYF(MY_WME)); + mysql_file_close(file, MYF(MY_WME)); err_not_open: if (error && !error_given) @@ -689,14 +746,13 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, uint interval_count, interval_parts, read_length, int_length; uint db_create_options, keys, key_parts, n_length; uint key_info_length, com_length, null_bit_pos; - uint vcol_screen_length; - uint extra_rec_buf_length, options_len; + uint extra_rec_buf_length; uint i,j; bool use_hash; - char *keynames, *names, *comment_pos, *vcol_screen_pos; + char *keynames, *names, *comment_pos; + uchar forminfo[288]; uchar *record; - uchar *disk_buff, *strpos, *null_flags, *null_pos, *options; - uchar *buff= 0; + uchar *disk_buff, *strpos, *null_flags, *null_pos; ulong pos, record_offset; ulong *rec_per_key= NULL; ulong rec_buff_length; @@ -709,6 +765,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, enum legacy_db_type legacy_db_type; my_bitmap_map *bitmaps; bool null_bits_are_used; + uint vcol_screen_length, UNINIT_VAR(options_len); + char *vcol_screen_pos; + uchar *UNINIT_VAR(options); + uchar *extra_segment_buff= 0; KEY first_keyinfo; uint len; KEY_PART_INFO *first_key_part= NULL; @@ -718,9 +778,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, share->ext_key_parts= 0; DBUG_ENTER("open_binary_frm"); - LINT_INIT(options); - LINT_INIT(options_len); - new_field_pack_flag= head[27]; new_frm_ver= (head[2] - FRM_VER); field_pack_length= new_frm_ver < 2 ? 11 : 17; @@ -731,6 +788,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (!(pos= get_form_pos(file, head))) goto err; /* purecov: inspected */ + mysql_file_seek(file,pos,MY_SEEK_SET,MYF(0)); + if (mysql_file_read(file, forminfo,288,MYF(MY_NABP))) + goto err; share->frm_version= head[2]; /* Check if .frm file created by MySQL 5.0. In this case we want to @@ -768,7 +828,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, share->transactional= (ha_choice) (head[39] & 3); share->page_checksum= (ha_choice) ((head[39] >> 2) & 3); share->row_type= (row_type) head[40]; - share->table_charset= get_charset((uint) head[38],MYF(0)); + share->table_charset= get_charset((((uint) head[41]) << 8) + + (uint) head[38],MYF(0)); share->null_field_first= 1; } if (!share->table_charset) @@ -793,7 +854,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, /* Read keyinformation */ key_info_length= (uint) uint2korr(head+28); - VOID(my_seek(file,(ulong) uint2korr(head+6),MY_SEEK_SET,MYF(0))); + mysql_file_seek(file, (ulong) uint2korr(head+6), MY_SEEK_SET, MYF(0)); if (read_string(file,(uchar**) &disk_buff,key_info_length)) goto err; /* purecov: inspected */ if (disk_buff[0] & 0x80) @@ -823,7 +884,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (!(keyinfo = (KEY*) alloc_root(&share->mem_root, len))) goto err; bzero((char*) keyinfo, len); - key_part= my_reinterpret_cast(KEY_PART_INFO*) (keyinfo+keys); + key_part= reinterpret_cast<KEY_PART_INFO*> (keyinfo+keys); } strpos= disk_buff+6; @@ -872,7 +933,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, goto err; /* purecov: inspected */ bzero((char*) keyinfo,n_length); share->key_info= keyinfo; - key_part= my_reinterpret_cast(KEY_PART_INFO*) (keyinfo + keys); + key_part= reinterpret_cast<KEY_PART_INFO*> (keyinfo + keys); if (!(rec_per_key= (ulong*) alloc_root(&share->mem_root, sizeof(ulong) * ext_key_parts))) @@ -948,6 +1009,20 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, keynames=(char*) key_part; strpos+= (strmov(keynames, (char *) strpos) - keynames)+1; + //reading index comments + for (keyinfo= share->key_info, i=0; i < keys; i++, keyinfo++) + { + if (keyinfo->flags & HA_USES_COMMENT) + { + keyinfo->comment.length= uint2korr(strpos); + keyinfo->comment.str= strmake_root(&share->mem_root, (char*) strpos+2, + keyinfo->comment.length); + strpos+= 2 + keyinfo->comment.length; + } + DBUG_ASSERT(test(keyinfo->flags & HA_USES_COMMENT) == + (keyinfo->comment.length > 0)); + } + share->reclength = uint2korr((head+16)); share->stored_rec_length= share->reclength; if (*(head+26) == 1) @@ -969,23 +1044,25 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, /* Read extra data segment */ uchar *next_chunk, *buff_end; DBUG_PRINT("info", ("extra segment size is %u bytes", n_length)); - if (!(next_chunk= buff= (uchar*) my_malloc(n_length+1, MYF(MY_WME)))) + if (!(extra_segment_buff= (uchar*) my_malloc(n_length + 1, MYF(MY_WME)))) goto err; - if (my_pread(file, buff, n_length, record_offset + share->reclength, - MYF(MY_NABP))) + next_chunk= extra_segment_buff; + if (mysql_file_pread(file, extra_segment_buff, + n_length, record_offset + share->reclength, + MYF(MY_NABP))) { - goto free_and_err; + goto err; } - share->connect_string.length= uint2korr(buff); + share->connect_string.length= uint2korr(next_chunk); if (!(share->connect_string.str= strmake_root(&share->mem_root, (char*) next_chunk + 2, share->connect_string. length))) { - goto free_and_err; + goto err; } next_chunk+= share->connect_string.length + 2; - buff_end= buff + n_length; + buff_end= extra_segment_buff + n_length; if (next_chunk + 2 < buff_end) { uint str_db_type_length= uint2korr(next_chunk); @@ -1002,7 +1079,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, plugin_data(tmp_plugin, handlerton *))) { /* bad file, legacy_db_type did not match the name */ - goto free_and_err; + goto err; } /* tmp_plugin is locked with a local lock. @@ -1031,7 +1108,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, error= 8; my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-partition"); - goto free_and_err; + goto err; } plugin_unlock(NULL, share->db_plugin); share->db_plugin= ha_lock_engine(NULL, partition_hton); @@ -1044,35 +1121,35 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { /* purecov: begin inspected */ error= 8; - name.str[name.length]= 0; + name.str[name.length]=0; my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str); - goto free_and_err; + goto err; /* purecov: end */ } next_chunk+= str_db_type_length + 2; } if (next_chunk + 5 < buff_end) { - uint32 partition_info_len = uint4korr(next_chunk); + uint32 partition_info_str_len = uint4korr(next_chunk); #ifdef WITH_PARTITION_STORAGE_ENGINE if ((share->partition_info_buffer_size= - share->partition_info_len= partition_info_len)) + share->partition_info_str_len= partition_info_str_len)) { - if (!(share->partition_info= (char*) + if (!(share->partition_info_str= (char*) memdup_root(&share->mem_root, next_chunk + 4, - partition_info_len + 1))) + partition_info_str_len + 1))) { - goto free_and_err; + goto err; } } #else - if (partition_info_len) + if (partition_info_str_len) { DBUG_PRINT("info", ("WITH_PARTITION_STORAGE_ENGINE is not defined")); - goto free_and_err; + goto err; } #endif - next_chunk+= 5 + partition_info_len; + next_chunk+= 5 + partition_info_str_len; } if (share->mysql_version >= 50110 && next_chunk < buff_end) { @@ -1092,7 +1169,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { DBUG_PRINT("error", ("fulltext key uses parser that is not defined in .frm")); - goto free_and_err; + goto err; } parser_name.str= (char*) next_chunk; parser_name.length= strlen((char*) next_chunk); @@ -1102,11 +1179,31 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (! keyinfo->parser) { my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), parser_name.str); - goto free_and_err; + goto err; } } } + + if (forminfo[46] == (uchar)255) + { + //reading long table comment + if (next_chunk + 2 > buff_end) + { + DBUG_PRINT("error", + ("long table comment is not defined in .frm")); + goto err; + } + share->comment.length = uint2korr(next_chunk); + if (! (share->comment.str= strmake_root(&share->mem_root, + (char*)next_chunk + 2, share->comment.length))) + { + goto err; + } + next_chunk+= 2 + share->comment.length; + } + DBUG_ASSERT(next_chunk <= buff_end); + if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS) { /* @@ -1127,38 +1224,39 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, share->rec_buff_length= rec_buff_length; if (!(record= (uchar *) alloc_root(&share->mem_root, rec_buff_length))) - goto free_and_err; /* purecov: inspected */ + goto err; /* purecov: inspected */ share->default_values= record; - if (my_pread(file, record, (size_t) share->reclength, - record_offset, MYF(MY_NABP))) - goto free_and_err; /* purecov: inspected */ + if (mysql_file_pread(file, record, (size_t) share->reclength, + record_offset, MYF(MY_NABP))) + goto err; /* purecov: inspected */ - VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0))); - if (my_read(file, head,288,MYF(MY_NABP))) - goto free_and_err; + mysql_file_seek(file, pos+288, MY_SEEK_SET, MYF(0)); #ifdef HAVE_CRYPTED_FRM if (crypted) { - crypted->decode((char*) head+256,288-256); - if (sint2korr(head+284) != 0) // Should be 0 - goto free_and_err; // Wrong password + crypted->decode((char*) forminfo+256,288-256); + if (sint2korr(forminfo+284) != 0) // Should be 0 + goto err; // Wrong password } #endif - share->fields= uint2korr(head+258); - pos= uint2korr(head+260); /* Length of all screens */ - n_length= uint2korr(head+268); - interval_count= uint2korr(head+270); - interval_parts= uint2korr(head+272); - int_length= uint2korr(head+274); - share->null_fields= uint2korr(head+282); - com_length= uint2korr(head+284); - vcol_screen_length= uint2korr(head+286); + share->fields= uint2korr(forminfo+258); + pos= uint2korr(forminfo+260); /* Length of all screens */ + n_length= uint2korr(forminfo+268); + interval_count= uint2korr(forminfo+270); + interval_parts= uint2korr(forminfo+272); + int_length= uint2korr(forminfo+274); + share->null_fields= uint2korr(forminfo+282); + com_length= uint2korr(forminfo+284); + vcol_screen_length= uint2korr(forminfo+286); share->vfields= 0; share->stored_fields= share->fields; - share->comment.length= (int) (head[46]); - share->comment.str= strmake_root(&share->mem_root, (char*) head+47, - share->comment.length); + if (forminfo[46] != (uchar)255) + { + share->comment.length= (int) (forminfo[46]); + share->comment.str= strmake_root(&share->mem_root, (char*) forminfo+47, + share->comment.length); + } DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d vcol_screen_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length, vcol_screen_length)); @@ -1171,14 +1269,14 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, keys+3)*sizeof(char *)+ (n_length+int_length+com_length+ vcol_screen_length))))) - goto free_and_err; /* purecov: inspected */ + goto err; /* purecov: inspected */ share->field= field_ptr; read_length=(uint) (share->fields * field_pack_length + pos+ (uint) (n_length+int_length+com_length+ vcol_screen_length)); if (read_string(file,(uchar**) &disk_buff,read_length)) - goto free_and_err; /* purecov: inspected */ + goto err; /* purecov: inspected */ #ifdef HAVE_CRYPTED_FRM if (crypted) { @@ -1205,7 +1303,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, fix_type_pointers(&interval_array, &share->fieldnames, 1, &names); if (share->fieldnames.count != share->fields) - goto free_and_err; + goto err; fix_type_pointers(&interval_array, share->intervals, interval_count, &names); @@ -1219,7 +1317,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, uint count= (uint) (interval->count + 1) * sizeof(uint); if (!(interval->type_lengths= (uint *) alloc_root(&share->mem_root, count))) - goto free_and_err; + goto err; for (count= 0; count < interval->count; count++) { char *val= (char*) interval->type_names[count]; @@ -1235,7 +1333,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, /* Allocate handler */ if (!(handler_file= get_new_handler(share, thd->mem_root, share->db_type()))) - goto free_and_err; + goto err; record= share->default_values-1; /* Fieldstart = 1 */ null_bits_are_used= share->null_fields != 0; @@ -1262,10 +1360,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, use_hash= share->fields >= MAX_FIELDS_BEFORE_HASH; if (use_hash) - use_hash= !hash_init(&share->name_hash, - system_charset_info, - share->fields,0,0, - (hash_get_key) get_field_name,0,0); + use_hash= !my_hash_init(&share->name_hash, + system_charset_info, + share->fields,0,0, + (my_hash_get_key) get_field_name,0,0); for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++) { @@ -1298,18 +1396,19 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, charset= &my_charset_bin; #else error= 4; // unsupported field type - goto free_and_err; + goto err; #endif } else { - if (!strpos[14]) + uint csid= strpos[14] + (((uint) strpos[11]) << 8); + if (!csid) charset= &my_charset_bin; - else if (!(charset=get_charset((uint) strpos[14], MYF(0)))) + else if (!(charset= get_charset(csid, MYF(0)))) { error= 5; // Unknown or unavailable charset - errarg= (int) strpos[14]; - goto free_and_err; + errarg= (int) csid; + goto err; } } @@ -1349,7 +1448,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if ((uint)vcol_screen_pos[0] != 1) { error= 4; - goto free_and_err; + goto err; } field_type= (enum_field_types) (uchar) vcol_screen_pos[1]; fld_stored_in_db= (bool) (uint) vcol_screen_pos[2]; @@ -1358,7 +1457,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, (char *)memdup_root(&share->mem_root, vcol_screen_pos+(uint)FRM_VCOL_HEADER_SIZE, vcol_expr_length))) - goto free_and_err; + goto err; vcol_info->expr_str.length= vcol_expr_length; vcol_screen_pos+= vcol_info_length; share->vfields++; @@ -1421,7 +1520,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, "Please do \"ALTER TABLE '%s' FORCE\" to fix it!", share->fieldnames.type_names[i], share->table_name.str, share->table_name.str); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_CRASHED_ON_USAGE, "Found incompatible DECIMAL field '%s' in %s; " "Please do \"ALTER TABLE '%s' FORCE\" to fix it!", @@ -1448,7 +1547,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (!reg_field) // Not supported field type { error= 4; - goto free_and_err; /* purecov: inspected */ + goto err; /* purecov: inspected */ } reg_field->field_index= i; @@ -1488,7 +1587,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, sent (OOM). */ error= 8; - goto free_and_err; + goto err; } } if (!reg_field->stored_in_db) @@ -1507,8 +1606,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (key_parts) { uint add_first_key_parts= 0; - uint primary_key=(uint) (find_type((char*) primary_key_name, - &share->keynames, 3) - 1); + uint primary_key=(uint) (find_type(primary_key_name, &share->keynames, + FIND_TYPE_NO_PREFIX) - 1); longlong ha_option= handler_file->ha_table_flags(); keyinfo= share->key_info; @@ -1641,7 +1740,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (!key_part->fieldnr) { error= 4; // Wrong file - goto free_and_err; + goto err; } field= key_part->field= share->field[key_part->fieldnr-1]; key_part->type= field->key_type(); @@ -1728,7 +1827,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, "Please do \"ALTER TABLE '%s' FORCE \" to fix it!", share->table_name.str, share->table_name.str); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_CRASHED_ON_USAGE, "Found wrong key definition in %s; " "Please do \"ALTER TABLE '%s' FORCE\" to fix " @@ -1790,7 +1889,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, } else share->primary_key= MAX_KEY; - x_free((uchar*) disk_buff); + my_free(disk_buff); disk_buff=0; if (new_field_pack_flag <= 1) { @@ -1804,11 +1903,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { DBUG_ASSERT(options_len); if (engine_table_options_frm_read(options, options_len, share)) - goto free_and_err; + goto err; } if (parse_engine_table_options(thd, handler_file->partition_ht(), share)) - goto free_and_err; - my_free(buff, MYF(MY_ALLOW_ZERO_PTR)); + goto err; if (share->found_next_number_field) { @@ -1866,25 +1964,32 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, delete handler_file; #ifndef DBUG_OFF if (use_hash) - (void) hash_check(&share->name_hash); + (void) my_hash_check(&share->name_hash); #endif + my_free(extra_segment_buff); DBUG_RETURN (0); - free_and_err: - my_free(buff, MYF(MY_ALLOW_ZERO_PTR)); err: share->error= error; share->open_errno= my_errno; share->errarg= errarg; - x_free((uchar*) disk_buff); + my_free(disk_buff); + my_free(extra_segment_buff); delete crypted; delete handler_file; - hash_free(&share->name_hash); + my_hash_free(&share->name_hash); if (share->ha_data_destroy) { share->ha_data_destroy(share->ha_data); share->ha_data_destroy= NULL; } +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (share->ha_part_data_destroy) + { + share->ha_part_data_destroy(share->ha_part_data); + share->ha_data_destroy= NULL; + } +#endif /* WITH_PARTITION_STORAGE_ENGINE */ open_table_error(share, error, share->open_errno, errarg); DBUG_RETURN(error); @@ -1949,66 +2054,22 @@ bool fix_vcol_expr(THD *thd, { Virtual_column_info *vcol_info= vcol_field->vcol_info; Item* func_expr= vcol_info->expr_item; - uint dir_length, home_dir_length; bool result= TRUE; TABLE_LIST tables; - TABLE_LIST *save_table_list, *save_first_table, *save_last_table; int error; - Name_resolution_context *context; const char *save_where; - char* db_name; - char db_name_string[FN_REFLEN]; - bool save_use_only_table_context; Field **ptr, *field; enum_mark_columns save_mark_used_columns= thd->mark_used_columns; DBUG_ASSERT(func_expr); DBUG_ENTER("fix_vcol_expr"); - /* - Set-up the TABLE_LIST object to be a list with a single table - Set the object to zero to create NULL pointers and set alias - and real name to table name and get database name from file name. - */ - - bzero((void*)&tables, sizeof(TABLE_LIST)); - tables.alias= tables.table_name= (char*) table->s->table_name.str; - tables.table= table; - tables.next_local= 0; - tables.next_name_resolution_table= 0; - strmov(db_name_string, table->s->normalized_path.str); - dir_length= dirname_length(db_name_string); - db_name_string[dir_length - 1]= 0; - home_dir_length= dirname_length(db_name_string); - db_name= &db_name_string[home_dir_length]; - tables.db= db_name; - thd->mark_used_columns= MARK_COLUMNS_NONE; - context= thd->lex->current_context(); - table->map= 1; //To ensure correct calculation of const item - table->get_fields_in_item_tree= TRUE; - save_table_list= context->table_list; - save_first_table= context->first_name_resolution_table; - save_last_table= context->last_name_resolution_table; - context->table_list= &tables; - context->first_name_resolution_table= &tables; - context->last_name_resolution_table= NULL; - func_expr->walk(&Item::change_context_processor, 0, (uchar*) context); save_where= thd->where; thd->where= "virtual column function"; - /* Save the context before fixing the fields*/ - save_use_only_table_context= thd->lex->use_only_table_context; - thd->lex->use_only_table_context= TRUE; /* Fix fields referenced to by the virtual column function */ - thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VCOL_EXPR; error= func_expr->fix_fields(thd, (Item**)0); - thd->lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_VCOL_EXPR; - /* Restore the original context*/ - thd->lex->use_only_table_context= save_use_only_table_context; - context->table_list= save_table_list; - context->first_name_resolution_table= save_first_table; - context->last_name_resolution_table= save_last_table; if (unlikely(error)) { @@ -2112,6 +2173,8 @@ bool unpack_vcol_info_from_frm(THD *thd, Query_arena backup_arena; Query_arena *vcol_arena= 0; Parser_state parser_state; + LEX *old_lex= thd->lex; + LEX lex; DBUG_ENTER("unpack_vcol_info_from_frm"); DBUG_ASSERT(vcol_expr); @@ -2159,7 +2222,7 @@ bool unpack_vcol_info_from_frm(THD *thd, any new items created by fix_fields() are not reverted. */ Query_arena expr_arena(&table->mem_root, - Query_arena::CONVENTIONAL_EXECUTION); + Query_arena::STMT_CONVENTIONAL_EXECUTION); if (!(vcol_arena= (Query_arena *) alloc_root(&table->mem_root, sizeof(Query_arena)))) goto err; @@ -2169,6 +2232,9 @@ bool unpack_vcol_info_from_frm(THD *thd, thd->set_n_backup_active_arena(vcol_arena, &backup_arena); thd->stmt_arena= vcol_arena; + if (init_lex_with_single_table(thd, table, &lex)) + goto err; + thd->lex->parse_vcol_expr= TRUE; /* @@ -2193,12 +2259,12 @@ bool unpack_vcol_info_from_frm(THD *thd, err: rc= TRUE; - thd->lex->parse_vcol_expr= FALSE; thd->free_items(); end: thd->stmt_arena= backup_stmt_arena_ptr; if (vcol_arena) thd->restore_active_arena(vcol_arena, &backup_arena); + end_lex_with_single_table(thd, table, old_lex); thd->variables.character_set_client= old_character_set_client; DBUG_RETURN(rc); @@ -2247,10 +2313,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str, share->table_name.str, (long) outparam)); - /* Parsing of partitioning information from .frm needs thd->lex set up. */ - DBUG_ASSERT(thd->lex->is_lex_started); - - thd->lex->context_analysis_only= 0; // not a view + thd->lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_VIEW; // not a view error= 1; bzero((char*) outparam, sizeof(*outparam)); @@ -2362,7 +2425,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, if (!(key_info= (KEY*) alloc_root(&outparam->mem_root, n_length))) goto err; outparam->key_info= key_info; - key_part= (my_reinterpret_cast(KEY_PART_INFO*) (key_info+share->keys)); + key_part= (reinterpret_cast<KEY_PART_INFO*>(key_info+share->keys)); memcpy(key_info, share->key_info, sizeof(*key_info)*share->keys); memcpy(key_part, share->key_info[0].key_part, (sizeof(*key_part) * @@ -2429,7 +2492,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, *vfield_ptr= 0; // End marker #ifdef WITH_PARTITION_STORAGE_ENGINE - if (share->partition_info_len && outparam->file) + if (share->partition_info_str_len && outparam->file) { /* In this execution we must avoid calling thd->change_item_tree since @@ -2444,16 +2507,15 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, Query_arena *backup_stmt_arena_ptr= thd->stmt_arena; Query_arena backup_arena; - Query_arena part_func_arena(&outparam->mem_root, Query_arena::INITIALIZED); + Query_arena part_func_arena(&outparam->mem_root, + Query_arena::STMT_INITIALIZED); thd->set_n_backup_active_arena(&part_func_arena, &backup_arena); thd->stmt_arena= &part_func_arena; bool tmp; bool work_part_info_used; - tmp= mysql_unpack_partition(thd, share->partition_info, - share->partition_info_len, - share->part_state, - share->part_state_len, + tmp= mysql_unpack_partition(thd, share->partition_info_str, + share->partition_info_str_len, outparam, is_create_table, share->default_part_db_type, &work_part_info_used); @@ -2649,7 +2711,7 @@ int closefrm(register TABLE *table, bool free_share) if (free_share) { if (table->s->tmp_table == NO_TMP_TABLE) - release_table_share(table->s, RELEASE_NORMAL); + release_table_share(table->s); else free_table_share(table->s); } @@ -2666,7 +2728,15 @@ void free_blobs(register TABLE *table) for (ptr= table->s->blob_field, end=ptr + table->s->blob_fields ; ptr != end ; ptr++) - ((Field_blob*) table->field[*ptr])->free(); + { + /* + Reduced TABLE objects which are used by row-based replication for + type conversion might have some fields missing. Skip freeing BLOB + buffers for such missing fields. + */ + if (table->field[*ptr]) + ((Field_blob*) table->field[*ptr])->free(); + } } @@ -2716,21 +2786,21 @@ static ulong get_form_pos(File file, uchar *head) length= uint2korr(head+4); - my_seek(file, 64L, MY_SEEK_SET, MYF(0)); + mysql_file_seek(file, 64L, MY_SEEK_SET, MYF(0)); if (!(buf= (uchar*) my_malloc(length+names*4, MYF(MY_WME)))) DBUG_RETURN(0); - if (my_read(file, buf, length+names*4, MYF(MY_NABP))) + if (mysql_file_read(file, buf, length+names*4, MYF(MY_NABP))) { - x_free(buf); + my_free(buf); DBUG_RETURN(0); } pos= buf+length; ret_value= uint4korr(pos); - my_free(buf, MYF(0)); + my_free(buf); DBUG_RETURN(ret_value); } @@ -2747,11 +2817,11 @@ int read_string(File file, uchar**to, size_t length) { DBUG_ENTER("read_string"); - x_free(*to); + my_free(*to); if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) || - my_read(file, *to, length,MYF(MY_NABP))) + mysql_file_read(file, *to, length, MYF(MY_NABP))) { - x_free(*to); /* purecov: inspected */ + my_free(*to); /* purecov: inspected */ *to= 0; /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } @@ -2781,23 +2851,24 @@ ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames, { /* Expand file */ newpos+=IO_SIZE; int4store(fileinfo+10,newpos); - endpos=(ulong) my_seek(file,0L,MY_SEEK_END,MYF(0));/* Copy from file-end */ + /* Copy from file-end */ + endpos= (ulong) mysql_file_seek(file, 0L, MY_SEEK_END, MYF(0)); bufflength= (uint) (endpos & (IO_SIZE-1)); /* IO_SIZE is a power of 2 */ while (endpos > maxlength) { - VOID(my_seek(file,(ulong) (endpos-bufflength),MY_SEEK_SET,MYF(0))); - if (my_read(file, buff, bufflength, MYF(MY_NABP+MY_WME))) + mysql_file_seek(file, (ulong) (endpos-bufflength), MY_SEEK_SET, MYF(0)); + if (mysql_file_read(file, buff, bufflength, MYF(MY_NABP+MY_WME))) DBUG_RETURN(0L); - VOID(my_seek(file,(ulong) (endpos-bufflength+IO_SIZE),MY_SEEK_SET, - MYF(0))); - if ((my_write(file, buff,bufflength,MYF(MY_NABP+MY_WME)))) + mysql_file_seek(file, (ulong) (endpos-bufflength+IO_SIZE), MY_SEEK_SET, + MYF(0)); + if ((mysql_file_write(file, buff, bufflength, MYF(MY_NABP+MY_WME)))) DBUG_RETURN(0); endpos-=bufflength; bufflength=IO_SIZE; } bzero(buff,IO_SIZE); /* Null new block */ - VOID(my_seek(file,(ulong) maxlength,MY_SEEK_SET,MYF(0))); - if (my_write(file,buff,bufflength,MYF(MY_NABP+MY_WME))) + mysql_file_seek(file, (ulong) maxlength, MY_SEEK_SET, MYF(0)); + if (mysql_file_write(file, buff, bufflength, MYF(MY_NABP+MY_WME))) DBUG_RETURN(0L); maxlength+=IO_SIZE; /* Fix old ref */ int2store(fileinfo+6,maxlength); @@ -2812,20 +2883,21 @@ ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames, if (n_length == 1 ) { /* First name */ length++; - VOID(strxmov((char*) buff,"/",newname,"/",NullS)); + (void) strxmov((char*) buff,"/",newname,"/",NullS); } else - VOID(strxmov((char*) buff,newname,"/",NullS)); /* purecov: inspected */ - VOID(my_seek(file,63L+(ulong) n_length,MY_SEEK_SET,MYF(0))); - if (my_write(file, buff, (size_t) length+1,MYF(MY_NABP+MY_WME)) || - (names && my_write(file,(uchar*) (*formnames->type_names+n_length-1), - names*4, MYF(MY_NABP+MY_WME))) || - my_write(file, fileinfo+10, 4,MYF(MY_NABP+MY_WME))) + (void) strxmov((char*) buff,newname,"/",NullS); /* purecov: inspected */ + mysql_file_seek(file, 63L+(ulong) n_length, MY_SEEK_SET, MYF(0)); + if (mysql_file_write(file, buff, (size_t) length+1, MYF(MY_NABP+MY_WME)) || + (names && mysql_file_write(file, + (uchar*) (*formnames->type_names+n_length-1), + names*4, MYF(MY_NABP+MY_WME))) || + mysql_file_write(file, fileinfo+10, 4, MYF(MY_NABP+MY_WME))) DBUG_RETURN(0L); /* purecov: inspected */ int2store(fileinfo+8,names+1); int2store(fileinfo+4,n_length+length); - VOID(my_chsize(file, newpos, 0, MYF(MY_WME)));/* Append file with '\0' */ + (void) mysql_file_chsize(file, newpos, 0, MYF(MY_WME));/* Append file with '\0' */ DBUG_RETURN(newpos); } /* make_new_entry */ @@ -3095,12 +3167,14 @@ void append_unescaped(String *res, const char *pos, uint length) File create_frm(THD *thd, const char *name, const char *db, const char *table, uint reclength, uchar *fileinfo, - HA_CREATE_INFO *create_info, uint keys) + HA_CREATE_INFO *create_info, uint keys, KEY *key_info) { register File file; ulong length; uchar fill[IO_SIZE]; int create_flags= O_RDWR | O_TRUNC; + ulong key_comment_total_bytes= 0; + uint i; DBUG_ENTER("create_frm"); if (create_info->options & HA_LEX_CREATE_TMP_TABLE) @@ -3112,10 +3186,10 @@ File create_frm(THD *thd, const char *name, const char *db, if (create_info->min_rows > UINT_MAX32) create_info->min_rows= UINT_MAX32; - if ((file= my_create(name, CREATE_MODE, create_flags, MYF(0))) >= 0) + if ((file= mysql_file_create(key_file_frm, + name, CREATE_MODE, create_flags, MYF(0))) >= 0) { - ulong key_length, tmp_key_length; - uint tmp; + uint key_length, tmp_key_length, tmp, csid; bzero((char*) fileinfo,64); /* header */ fileinfo[0]=(uchar) 254; @@ -3138,7 +3212,17 @@ File create_frm(THD *thd, const char *name, const char *db, 1 byte for the NAMES_SEP_CHAR (after the last name) 9 extra bytes (padding for safety? alignment?) */ - key_length= keys * (8 + MAX_REF_PARTS * 9 + NAME_LEN + 1) + 16; + for (i= 0; i < keys; i++) + { + DBUG_ASSERT(test(key_info[i].flags & HA_USES_COMMENT) == + (key_info[i].comment.length > 0)); + if (key_info[i].flags & HA_USES_COMMENT) + key_comment_total_bytes += 2 + key_info[i].comment.length; + } + + key_length= keys * (8 + MAX_REF_PARTS * 9 + NAME_LEN + 1) + 16 + + key_comment_total_bytes; + length= next_io_size((ulong) (IO_SIZE+key_length+reclength+ create_info->extra_size)); int4store(fileinfo+10,length); @@ -3155,13 +3239,14 @@ File create_frm(THD *thd, const char *name, const char *db, fileinfo[32]=0; // No filename anymore fileinfo[33]=5; // Mark for 5.0 frm file int4store(fileinfo+34,create_info->avg_row_length); - fileinfo[38]= (create_info->default_table_charset ? - create_info->default_table_charset->number : 0); + csid= (create_info->default_table_charset ? + create_info->default_table_charset->number : 0); + fileinfo[38]= (uchar) csid; fileinfo[39]= (uchar) ((uint) create_info->transactional | ((uint) create_info->page_checksum << 2)); fileinfo[40]= (uchar) create_info->row_type; /* Next few bytes where for RAID support */ - fileinfo[41]= 0; + fileinfo[41]= (uchar) (csid >> 8); fileinfo[42]= 0; fileinfo[43]= 0; fileinfo[44]= 0; @@ -3179,11 +3264,11 @@ File create_frm(THD *thd, const char *name, const char *db, bzero(fill,IO_SIZE); for (; length > IO_SIZE ; length-= IO_SIZE) { - if (my_write(file,fill, IO_SIZE, MYF(MY_WME | MY_NABP))) + if (mysql_file_write(file, fill, IO_SIZE, MYF(MY_WME | MY_NABP))) { - VOID(my_close(file,MYF(0))); - VOID(my_delete(name,MYF(0))); - DBUG_RETURN(-1); + (void) mysql_file_close(file, MYF(0)); + (void) mysql_file_delete(key_file_frm, name, MYF(0)); + return(-1); } } } @@ -3222,9 +3307,9 @@ int rename_file_ext(const char * from,const char * to,const char * ext) { char from_b[FN_REFLEN],to_b[FN_REFLEN]; - VOID(strxmov(from_b,from,ext,NullS)); - VOID(strxmov(to_b,to,ext,NullS)); - return (my_rename(from_b,to_b,MYF(MY_WME))); + (void) strxmov(from_b,from,ext,NullS); + (void) strxmov(to_b,to,ext,NullS); + return (mysql_file_rename(key_file_frm, from_b, to_b, MYF(MY_WME))); } @@ -3357,9 +3442,10 @@ bool check_db_name(LEX_STRING *org_name) returns 1 on error */ -bool check_table_name(const char *name, uint length, bool check_for_path_chars) +bool check_table_name(const char *name, size_t length, bool check_for_path_chars) { - uint name_length= 0; // name length in symbols + // name length in symbols + size_t name_length= 0; const char *end= name+length; @@ -3401,18 +3487,19 @@ bool check_table_name(const char *name, uint length, bool check_for_path_chars) name_length++; } #if defined(USE_MB) && defined(USE_MB_IDENT) - return (last_char_is_space || name_length > NAME_CHAR_LEN) ; + return last_char_is_space || (name_length > NAME_CHAR_LEN); #else - return 0; + return FALSE; #endif } bool check_column_name(const char *name) { - uint name_length= 0; // name length in symbols + // name length in symbols + size_t name_length= 0; bool last_char_is_space= TRUE; - + while (*name) { #if defined(USE_MB) && defined(USE_MB_IDENT) @@ -3437,7 +3524,7 @@ bool check_column_name(const char *name) name_length++; } /* Error if empty or too long column name */ - return last_char_is_space || (uint) name_length > NAME_CHAR_LEN; + return last_char_is_space || (name_length > NAME_CHAR_LEN); } @@ -3482,7 +3569,8 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def) report_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE), table->alias.c_ptr(), table_def->count, table->s->fields, - (int) table->s->mysql_version, MYSQL_VERSION_ID); + static_cast<int>(table->s->mysql_version), + MYSQL_VERSION_ID); DBUG_RETURN(TRUE); } else if (MYSQL_VERSION_ID == table->s->mysql_version) @@ -3596,11 +3684,264 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def) } +/** + Traverse portion of wait-for graph which is reachable through edge + represented by this flush ticket in search for deadlocks. + + @retval TRUE A deadlock is found. A victim is remembered + by the visitor. + @retval FALSE Success, no deadlocks. +*/ + +bool Wait_for_flush::accept_visitor(MDL_wait_for_graph_visitor *gvisitor) +{ + return m_share->visit_subgraph(this, gvisitor); +} + + +uint Wait_for_flush::get_deadlock_weight() const +{ + return m_deadlock_weight; +} + + +/** + Traverse portion of wait-for graph which is reachable through this + table share in search for deadlocks. + + @param waiting_ticket Ticket representing wait for this share. + @param dvisitor Deadlock detection visitor. + + @retval TRUE A deadlock is found. A victim is remembered + by the visitor. + @retval FALSE No deadlocks, it's OK to begin wait. +*/ + +bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush, + MDL_wait_for_graph_visitor *gvisitor) +{ + TABLE *table; + MDL_context *src_ctx= wait_for_flush->get_ctx(); + bool result= TRUE; + + /* + To protect used_tables list from being concurrently modified + while we are iterating through it we acquire LOCK_open. + This does not introduce deadlocks in the deadlock detector + because we won't try to acquire LOCK_open while + holding a write-lock on MDL_lock::m_rwlock. + */ + if (gvisitor->m_lock_open_count++ == 0) + mysql_mutex_lock(&LOCK_open); + + I_P_List_iterator <TABLE, TABLE_share> tables_it(used_tables); + + /* + In case of multiple searches running in parallel, avoid going + over the same loop twice and shortcut the search. + Do it after taking the lock to weed out unnecessary races. + */ + if (src_ctx->m_wait.get_status() != MDL_wait::EMPTY) + { + result= FALSE; + goto end; + } + + if (gvisitor->enter_node(src_ctx)) + goto end; + + while ((table= tables_it++)) + { + if (gvisitor->inspect_edge(&table->in_use->mdl_context)) + { + goto end_leave_node; + } + } + + tables_it.rewind(); + while ((table= tables_it++)) + { + if (table->in_use->mdl_context.visit_subgraph(gvisitor)) + { + goto end_leave_node; + } + } + + result= FALSE; + +end_leave_node: + gvisitor->leave_node(src_ctx); + +end: + if (gvisitor->m_lock_open_count-- == 1) + mysql_mutex_unlock(&LOCK_open); + + return result; +} + + +/** + Wait until the subject share is removed from the table + definition cache and make sure it's destroyed. + + @param mdl_context MDL context for thread which is going to wait. + @param abstime Timeout for waiting as absolute time value. + @param deadlock_weight Weight of this wait for deadlock detector. + + @pre LOCK_open is write locked, the share is used (has + non-zero reference count), is marked for flush and + this connection does not reference the share. + LOCK_open will be unlocked temporarily during execution. + + @retval FALSE - Success. + @retval TRUE - Error (OOM, deadlock, timeout, etc...). +*/ + +bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime, + uint deadlock_weight) +{ + MDL_context *mdl_context= &thd->mdl_context; + Wait_for_flush ticket(mdl_context, this, deadlock_weight); + MDL_wait::enum_wait_status wait_status; + + mysql_mutex_assert_owner(&LOCK_open); + /* + We should enter this method only when share's version is not + up to date and the share is referenced. Otherwise our + thread will never be woken up from wait. + */ + DBUG_ASSERT(version != refresh_version && ref_count != 0); + + m_flush_tickets.push_front(&ticket); + + mdl_context->m_wait.reset_status(); + + mysql_mutex_unlock(&LOCK_open); + + mdl_context->will_wait_for(&ticket); + + mdl_context->find_deadlock(); + + wait_status= mdl_context->m_wait.timed_wait(thd, abstime, TRUE, + "Waiting for table flush"); + + mdl_context->done_waiting_for(); + + mysql_mutex_lock(&LOCK_open); + + m_flush_tickets.remove(&ticket); + + if (m_flush_tickets.is_empty() && ref_count == 0) + { + /* + If our thread was the last one using the share, + we must destroy it here. + */ + destroy(); + } + + /* + In cases when our wait was aborted by KILL statement, + a deadlock or a timeout, the share might still be referenced, + so we don't delete it. Note, that we can't determine this + condition by checking wait_status alone, since, for example, + a timeout can happen after all references to the table share + were released, but before the share is removed from the + cache and we receive the notification. This is why + we first destroy the share, and then look at + wait_status. + */ + switch (wait_status) + { + case MDL_wait::GRANTED: + return FALSE; + case MDL_wait::VICTIM: + my_error(ER_LOCK_DEADLOCK, MYF(0)); + return TRUE; + case MDL_wait::TIMEOUT: + my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); + return TRUE; + case MDL_wait::KILLED: + return TRUE; + default: + DBUG_ASSERT(0); + return TRUE; + } +} + + +/** + Initialize TABLE instance (newly created, or coming either from table + cache or THD::temporary_tables list) and prepare it for further use + during statement execution. Set the 'alias' attribute from the specified + TABLE_LIST element. Remember the TABLE_LIST element in the + TABLE::pos_in_table_list member. + + @param thd Thread context. + @param tl TABLE_LIST element. +*/ + +void TABLE::init(THD *thd, TABLE_LIST *tl) +{ + DBUG_ASSERT(s->ref_count > 0 || s->tmp_table != NO_TMP_TABLE); + + if (thd->lex->need_correct_ident()) + alias_name_used= my_strcasecmp(table_alias_charset, + s->table_name.str, + tl->alias); + /* Fix alias if table name changes. */ + if (strcmp(alias.c_ptr(), tl->alias)) + alias.copy(tl->alias, strlen(tl->alias), alias.charset()); + + tablenr= thd->current_tablenr++; + used_fields= 0; + const_table= 0; + null_row= 0; + maybe_null= 0; + force_index= 0; + force_index_order= 0; + force_index_group= 0; + status= STATUS_NO_RECORD; + insert_values= 0; + fulltext_searched= 0; + file->ha_start_of_new_statement(); + reginfo.impossible_range= 0; + created= TRUE; + + /* Catch wrong handling of the auto_increment_field_not_null. */ + DBUG_ASSERT(!auto_increment_field_not_null); + auto_increment_field_not_null= FALSE; + + if (timestamp_field) + timestamp_field_type= timestamp_field->get_auto_set_type(); + + pos_in_table_list= tl; + + clear_column_bitmaps(); + + DBUG_ASSERT(key_read == 0); + + /* mark the record[0] uninitialized */ + TRASH(record[0], s->reclength); + + /* + Initialize the null marker bits, to ensure that if we are doing a read + of only selected columns (like in keyread), all null markers are + initialized. + */ + memset(record[0], 255, s->null_bytes); + memset(record[1], 255, s->null_bytes); + + /* Tables may be reused in a sub statement. */ + DBUG_ASSERT(!file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN)); +} + + /* Create Item_field for each column in the table. SYNPOSIS - st_table::fill_item_list() + TABLE::fill_item_list() item_list a pointer to an empty list used to store items DESCRIPTION @@ -3613,7 +3954,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def) 1 out of memory */ -bool st_table::fill_item_list(List<Item> *item_list) const +bool TABLE::fill_item_list(List<Item> *item_list) const { /* All Item_field's created using a direct pointer to a field @@ -3633,7 +3974,7 @@ bool st_table::fill_item_list(List<Item> *item_list) const Fields of this table. SYNPOSIS - st_table::fill_item_list() + TABLE::fill_item_list() item_list a non-empty list with Item_fields DESCRIPTION @@ -3643,7 +3984,7 @@ bool st_table::fill_item_list(List<Item> *item_list) const is the same as the number of columns in the table. */ -void st_table::reset_item_list(List<Item> *item_list) const +void TABLE::reset_item_list(List<Item> *item_list) const { List_iterator_fast<Item> it(*item_list); for (Field **ptr= field; *ptr; ptr++) @@ -4041,20 +4382,20 @@ void TABLE_LIST::hide_view_error(THD *thd) /* Hide "Unknown column" or "Unknown function" error */ DBUG_ASSERT(thd->is_error()); - if (thd->main_da.sql_errno() == ER_BAD_FIELD_ERROR || - thd->main_da.sql_errno() == ER_SP_DOES_NOT_EXIST || - thd->main_da.sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION || - thd->main_da.sql_errno() == ER_PROCACCESS_DENIED_ERROR || - thd->main_da.sql_errno() == ER_COLUMNACCESS_DENIED_ERROR || - thd->main_da.sql_errno() == ER_TABLEACCESS_DENIED_ERROR || - thd->main_da.sql_errno() == ER_TABLE_NOT_LOCKED || - thd->main_da.sql_errno() == ER_NO_SUCH_TABLE) + if (thd->stmt_da->sql_errno() == ER_BAD_FIELD_ERROR || + thd->stmt_da->sql_errno() == ER_SP_DOES_NOT_EXIST || + thd->stmt_da->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION || + thd->stmt_da->sql_errno() == ER_PROCACCESS_DENIED_ERROR || + thd->stmt_da->sql_errno() == ER_COLUMNACCESS_DENIED_ERROR || + thd->stmt_da->sql_errno() == ER_TABLEACCESS_DENIED_ERROR || + thd->stmt_da->sql_errno() == ER_TABLE_NOT_LOCKED || + thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE) { TABLE_LIST *top= top_table(); thd->clear_error(); my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str); } - else if (thd->main_da.sql_errno() == ER_NO_DEFAULT_FOR_FIELD) + else if (thd->stmt_da->sql_errno() == ER_NO_DEFAULT_FOR_FIELD) { TABLE_LIST *top= top_table(); thd->clear_error(); @@ -4136,7 +4477,7 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure) TABLE_LIST *main_view= top_table(); if (ignore_failure) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_VIEW_CHECK_FAILED, ER(ER_VIEW_CHECK_FAILED), main_view->view_db.str, main_view->view_name.str); return(VIEW_CHECK_SKIP); @@ -4436,10 +4777,15 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd) } else { - my_error(ER_ACCESS_DENIED_ERROR, MYF(0), - thd->security_ctx->priv_user, - thd->security_ctx->priv_host, - (thd->password ? ER(ER_YES) : ER(ER_NO))); + if (thd->password == 2) + my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0), + thd->security_ctx->priv_user, + thd->security_ctx->priv_host); + else + my_error(ER_ACCESS_DENIED_ERROR, MYF(0), + thd->security_ctx->priv_user, + thd->security_ctx->priv_host, + (thd->password ? ER(ER_YES) : ER(ER_NO))); } DBUG_RETURN(TRUE); } @@ -4641,14 +4987,15 @@ const char *Natural_join_column::db_name() return table_ref->view_db.str; /* - Test that TABLE_LIST::db is the same as st_table_share::db to + Test that TABLE_LIST::db is the same as TABLE_SHARE::db to ensure consistency. An exception are I_S schema tables, which are inconsistent in this respect. */ DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db.str) || (table_ref->schema_table && - table_ref->table->s->db.str[0] == 0) || + is_infoschema_db(table_ref->table->s->db.str, + table_ref->table->s->db.length)) || table_ref->is_materialized_derived()); return table_ref->db; } @@ -4877,13 +5224,14 @@ const char *Field_iterator_table_ref::get_db_name() return natural_join_it.column_ref()->db_name(); /* - Test that TABLE_LIST::db is the same as st_table_share::db to + Test that TABLE_LIST::db is the same as TABLE_SHARE::db to ensure consistency. An exception are I_S schema tables, which are inconsistent in this respect. */ DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db.str) || (table_ref->schema_table && - table_ref->table->s->db.str[0] == 0)); + is_infoschema_db(table_ref->table->s->db.str, + table_ref->table->s->db.length))); return table_ref->db; } @@ -5053,7 +5401,7 @@ Field_iterator_table_ref::get_natural_column_ref() /* Reset all columns bitmaps */ -void st_table::clear_column_bitmaps() +void TABLE::clear_column_bitmaps() { /* Reset column read/write usage. It's identical to: @@ -5075,9 +5423,9 @@ void st_table::clear_column_bitmaps() key fields. */ -void st_table::prepare_for_position() +void TABLE::prepare_for_position() { - DBUG_ENTER("st_table::prepare_for_position"); + DBUG_ENTER("TABLE::prepare_for_position"); if ((file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && s->primary_key < MAX_KEY) @@ -5096,14 +5444,14 @@ void st_table::prepare_for_position() NOTE: This changes the bitmap to use the tmp bitmap After this, you can't access any other columns in the table until - bitmaps are reset, for example with st_table::clear_column_bitmaps() - or st_table::restore_column_maps_after_mark_index() + bitmaps are reset, for example with TABLE::clear_column_bitmaps() + or TABLE::restore_column_maps_after_mark_index() */ -void st_table::mark_columns_used_by_index(uint index) +void TABLE::mark_columns_used_by_index(uint index) { MY_BITMAP *bitmap= &tmp_set; - DBUG_ENTER("st_table::mark_columns_used_by_index"); + DBUG_ENTER("TABLE::mark_columns_used_by_index"); enable_keyread(); bitmap_clear_all(bitmap); @@ -5121,10 +5469,10 @@ void st_table::mark_columns_used_by_index(uint index) restore_column_maps_after_mark_index(). */ -void st_table::add_read_columns_used_by_index(uint index) +void TABLE::add_read_columns_used_by_index(uint index) { MY_BITMAP *bitmap= &tmp_set; - DBUG_ENTER("st_table::add_read_columns_used_by_index"); + DBUG_ENTER("TABLE::add_read_columns_used_by_index"); enable_keyread(); bitmap_copy(bitmap, read_set); @@ -5145,9 +5493,9 @@ void st_table::add_read_columns_used_by_index(uint index) when calling mark_columns_used_by_index */ -void st_table::restore_column_maps_after_mark_index() +void TABLE::restore_column_maps_after_mark_index() { - DBUG_ENTER("st_table::restore_column_maps_after_mark_index"); + DBUG_ENTER("TABLE::restore_column_maps_after_mark_index"); disable_keyread(); default_column_bitmaps(); @@ -5160,7 +5508,7 @@ void st_table::restore_column_maps_after_mark_index() mark columns used by key, but don't reset other fields */ -void st_table::mark_columns_used_by_index_no_reset(uint index, +void TABLE::mark_columns_used_by_index_no_reset(uint index, MY_BITMAP *bitmap) { KEY_PART_INFO *key_part= key_info[index].key_part; @@ -5186,7 +5534,7 @@ void st_table::mark_columns_used_by_index_no_reset(uint index, always set and sometimes read. */ -void st_table::mark_auto_increment_column() +void TABLE::mark_auto_increment_column() { DBUG_ASSERT(found_next_number_field); /* @@ -5219,7 +5567,7 @@ void st_table::mark_auto_increment_column() retrieve the row again. */ -void st_table::mark_columns_needed_for_delete() +void TABLE::mark_columns_needed_for_delete() { if (triggers) triggers->mark_fields_used(TRG_EVENT_DELETE); @@ -5269,7 +5617,7 @@ void st_table::mark_columns_needed_for_delete() retrieve the row again. */ -void st_table::mark_columns_needed_for_update() +void TABLE::mark_columns_needed_for_update() { DBUG_ENTER("mark_columns_needed_for_update"); if (triggers) @@ -5314,7 +5662,7 @@ void st_table::mark_columns_needed_for_update() as changed. */ -void st_table::mark_columns_needed_for_insert() +void TABLE::mark_columns_needed_for_insert() { if (triggers) { @@ -5352,7 +5700,7 @@ void st_table::mark_columns_needed_for_insert() FALSE otherwise */ -bool st_table::mark_virtual_col(Field *field) +bool TABLE::mark_virtual_col(Field *field) { bool res; DBUG_ASSERT(field->vcol_info); @@ -5394,7 +5742,7 @@ bool st_table::mark_virtual_col(Field *field) be added to read_set either. */ -void st_table::mark_virtual_columns_for_write(bool insert_fl) +void TABLE::mark_virtual_columns_for_write(bool insert_fl) { Field **vfield_ptr, *tmp_vfield; bool bitmap_updated= FALSE; @@ -5494,6 +5842,7 @@ void TABLE::create_key_part_by_field(KEY *keyinfo, keyinfo->key_length+= HA_KEY_NULL_LENGTH; } if (field->type() == MYSQL_TYPE_BLOB || + field->type() == MYSQL_TYPE_GEOMETRY || field->real_type() == MYSQL_TYPE_VARCHAR) { key_part_info->store_length+= HA_KEY_BLOB_LENGTH; @@ -5609,25 +5958,6 @@ void TABLE::use_index(int key_to_save) s->keys= i; } -/** - @brief Check if this is part of a MERGE table with attached children. - - @return status - @retval TRUE children are attached - @retval FALSE no MERGE part or children not attached - - @details - A MERGE table consists of a parent TABLE and zero or more child - TABLEs. Each of these TABLEs is called a part of a MERGE table. -*/ - -bool st_table::is_children_attached(void) -{ - return((child_l && children_attached) || - (parent && parent->children_attached)); -} - - /* Return TRUE if the table is filled at execution phase @@ -5635,7 +5965,7 @@ bool st_table::is_children_attached(void) the table, like range analysis or constant table detection) */ -bool st_table::is_filled_at_execution() +bool TABLE::is_filled_at_execution() { return test(pos_in_table_list->jtbm_subselect || pos_in_table_list->is_active_sjm()); @@ -5656,7 +5986,7 @@ bool st_table::is_filled_at_execution() @return number of considered key components */ -uint st_table::actual_n_key_parts(KEY *keyinfo) +uint TABLE::actual_n_key_parts(KEY *keyinfo) { return optimizer_flag(in_use, OPTIMIZER_SWITCH_EXTENDED_KEYS) ? keyinfo->ext_key_parts : keyinfo->key_parts; @@ -5676,7 +6006,7 @@ uint st_table::actual_n_key_parts(KEY *keyinfo) @return actual key flags */ -ulong st_table::actual_key_flags(KEY *keyinfo) +ulong TABLE::actual_key_flags(KEY *keyinfo) { return optimizer_flag(in_use, OPTIMIZER_SWITCH_EXTENDED_KEYS) ? keyinfo->ext_key_flags : keyinfo->flags; @@ -5711,6 +6041,8 @@ void TABLE_LIST::reinit_before_use(THD *thd) } while (parent_embedding && parent_embedding->nested_join->join_list.head() == embedded); + + mdl_request.ticket= NULL; } @@ -5742,10 +6074,10 @@ Item_subselect *TABLE_LIST::containing_subselect() DESCRIPTION The parser collects the index hints for each table in a "tagged list" (TABLE_LIST::index_hints). Using the information in this tagged list - this function sets the members st_table::keys_in_use_for_query, - st_table::keys_in_use_for_group_by, st_table::keys_in_use_for_order_by, - st_table::force_index, st_table::force_index_order, - st_table::force_index_group and st_table::covering_keys. + this function sets the members TABLE::keys_in_use_for_query, + TABLE::keys_in_use_for_group_by, TABLE::keys_in_use_for_order_by, + TABLE::force_index, TABLE::force_index_order, + TABLE::force_index_group and TABLE::covering_keys. Current implementation of the runtime does not allow mixing FORCE INDEX and USE INDEX, so this is checked here. Then the FORCE INDEX list @@ -5934,6 +6266,77 @@ size_t max_row_length(TABLE *table, const uchar *data) return length; } + +/** + Helper function which allows to allocate metadata lock request + objects for all elements of table list. +*/ + +void init_mdl_requests(TABLE_LIST *table_list) +{ + for ( ; table_list ; table_list= table_list->next_global) + table_list->mdl_request.init(MDL_key::TABLE, + table_list->db, table_list->table_name, + table_list->lock_type >= TL_WRITE_ALLOW_WRITE ? + MDL_SHARED_WRITE : MDL_SHARED_READ, + MDL_TRANSACTION); +} + + +/** + Update TABLE::const_key_parts for single table UPDATE/DELETE query + + @param conds WHERE clause expression + + @retval TRUE error (OOM) + @retval FALSE success + + @note + Set const_key_parts bits if key fields are equal to constants in + the WHERE expression. +*/ + +bool TABLE::update_const_key_parts(COND *conds) +{ + bzero((char*) const_key_parts, sizeof(key_part_map) * s->keys); + + if (conds == NULL) + return FALSE; + + for (uint index= 0; index < s->keys; index++) + { + KEY_PART_INFO *keyinfo= key_info[index].key_part; + KEY_PART_INFO *keyinfo_end= keyinfo + key_info[index].key_parts; + + for (key_part_map part_map= (key_part_map)1; + keyinfo < keyinfo_end; + keyinfo++, part_map<<= 1) + { + if (const_expression_in_where(conds, NULL, keyinfo->field)) + const_key_parts[index]|= part_map; + } + } + return FALSE; +} + +/** + Test if the order list consists of simple field expressions + + @param order Linked list of ORDER BY arguments + + @return TRUE if @a order is empty or consist of simple field expressions +*/ + +bool is_simple_order(ORDER *order) +{ + for (ORDER *ord= order; ord; ord= ord->next) + { + if (ord->item[0]->real_item()->type() != Item::FIELD_ITEM) + return FALSE; + } + return TRUE; +} + /* @brief Compute values for virtual columns used in query @@ -6023,7 +6426,7 @@ void TABLE_LIST::reset_const_table() @return FALSE ok */ -bool TABLE_LIST::handle_derived(struct st_lex *lex, uint phases) +bool TABLE_LIST::handle_derived(LEX *lex, uint phases) { SELECT_LEX_UNIT *unit= get_unit(); if (unit) |