diff options
Diffstat (limited to 'sql/table.cc')
-rw-r--r-- | sql/table.cc | 2246 |
1 files changed, 1261 insertions, 985 deletions
diff --git a/sql/table.cc b/sql/table.cc index 87a249defa0..85dffeb9167 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -17,11 +17,9 @@ /* Some general useful functions */ -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ +#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 @@ -31,13 +29,15 @@ #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 "sql_base.h" #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 "sql_statistics.h" +#include "discover.h" #include "mdl.h" // MDL_wait_for_graph_visitor /* INFORMATION_SCHEMA name */ @@ -63,18 +63,12 @@ LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") }; /* Functions defined in this file */ -void open_table_error(TABLE_SHARE *share, int error, int db_errno, - myf errortype, int errarg); -static int open_binary_frm(THD *thd, TABLE_SHARE *share, - uchar *head, File file); static void fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types, char **names); static uint find_field(Field **fields, uchar *record, uint start, uint length); inline bool is_system_table_name(const char *name, uint length); -static ulong get_form_pos(File file, uchar *head); - /************************************************************************** Object_creation_ctx implementation. **************************************************************************/ @@ -152,7 +146,7 @@ View_creation_ctx * View_creation_ctx::create(THD *thd, if (!view->view_client_cs_name.str || !view->view_connection_cl_name.str) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_VIEW_NO_CREATION_CTX, ER(ER_VIEW_NO_CREATION_CTX), (const char *) view->db, @@ -186,7 +180,7 @@ View_creation_ctx * View_creation_ctx::create(THD *thd, (const char *) view->view_client_cs_name.str, (const char *) view->view_connection_cl_name.str); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_VIEW_INVALID_CREATION_CTX, ER(ER_VIEW_INVALID_CREATION_CTX), (const char *) view->db, @@ -277,7 +271,7 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name) /* - Allocate a setup TABLE_SHARE structure + Allocate and setup a TABLE_SHARE structure SYNOPSIS alloc_table_share() @@ -290,8 +284,8 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name) # Share */ -TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, - uint key_length) +TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, + const char *key, uint key_length) { MEM_ROOT mem_root; TABLE_SHARE *share; @@ -299,13 +293,11 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, char path[FN_REFLEN]; uint path_length; DBUG_ENTER("alloc_table_share"); - DBUG_PRINT("enter", ("table: '%s'.'%s'", - table_list->db, table_list->table_name)); + DBUG_PRINT("enter", ("table: '%s'.'%s'", db, table_name)); path_length= build_table_filename(path, sizeof(path) - 1, - table_list->db, - table_list->table_name, "", 0); - init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); + db, table_name, "", 0); + init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); if (multi_alloc_root(&mem_root, &share, sizeof(*share), &key_buff, key_length, @@ -321,26 +313,18 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, strmov(share->path.str, path); share->normalized_path.str= share->path.str; share->normalized_path.length= path_length; - - share->set_refresh_version(); - - /* - Since alloc_table_share() can be called without any locking (for - example, ha_create_table... functions), we do not assign a table - map id here. Instead we assign a value that is not used - elsewhere, and then assign a table map id inside open_table() - under the protection of the LOCK_open mutex. - */ - share->table_map_id= ~0UL; + share->table_category= get_table_category(& share->db, & share->table_name); + share->open_errno= ENOENT; share->cached_row_logging_check= -1; - share->used_tables.empty(); - share->free_tables.empty(); - share->m_flush_tickets.empty(); + init_sql_alloc(&share->stats_cb.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root)); + mysql_mutex_init(key_TABLE_SHARE_LOCK_share, + &share->LOCK_share, MY_MUTEX_INIT_SLOW); mysql_mutex_init(key_TABLE_SHARE_LOCK_ha_data, &share->LOCK_ha_data, MY_MUTEX_INIT_FAST); + tdc_init_share(share); } DBUG_RETURN(share); } @@ -353,7 +337,7 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, init_tmp_table_share() thd thread handle share Share to fill - key Table_cache_key, as generated from create_table_def_key. + key Table_cache_key, as generated from tdc_create_key. must start with db name. key_length Length of key table_name Table name @@ -377,7 +361,12 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, DBUG_PRINT("enter", ("table: '%s'.'%s'", key, table_name)); bzero((char*) share, sizeof(*share)); - init_sql_alloc(&share->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); + /* + This can't be MY_THREAD_SPECIFIC for slaves as they are freed + during cleanup() from Relay_log_info::close_temporary_tables() + */ + init_sql_alloc(&share->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, + MYF(thd->slave_thread ? 0 : MY_THREAD_SPECIFIC)); share->table_category= TABLE_CATEGORY_TEMPORARY; share->tmp_table= INTERNAL_TMP_TABLE; share->db.str= (char*) key; @@ -398,11 +387,6 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, compatibility checks. */ share->table_map_id= (ulong) thd->query_id; - - share->used_tables.empty(); - share->free_tables.empty(); - share->m_flush_tickets.empty(); - DBUG_VOID_RETURN; } @@ -417,10 +401,28 @@ void TABLE_SHARE::destroy() { uint idx; KEY *info_it; + DBUG_ENTER("TABLE_SHARE::destroy"); + DBUG_PRINT("info", ("db: %s table: %s", db.str, table_name.str)); + + if (ha_share) + { + delete ha_share; + ha_share= NULL; // Safety + } + + free_root(&stats_cb.mem_root, MYF(0)); + stats_cb.stats_can_be_read= FALSE; + stats_cb.stats_is_read= FALSE; + stats_cb.histograms_can_be_read= FALSE; + stats_cb.histograms_are_read= FALSE; - /* The mutex is initialized only for shares that are part of the TDC */ + /* The mutexes are initialized only for shares that are part of the TDC */ if (tmp_table == NO_TMP_TABLE) + { + mysql_mutex_destroy(&LOCK_share); mysql_mutex_destroy(&LOCK_ha_data); + tdc_deinit_share(this); + } my_hash_free(&name_hash); plugin_unlock(NULL, db_plugin); @@ -436,24 +438,20 @@ void TABLE_SHARE::destroy() 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; - } + plugin_unlock(NULL, default_part_plugin); #endif /* WITH_PARTITION_STORAGE_ENGINE */ + + PSI_CALL_release_table_share(m_psi); + /* 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)); + DBUG_VOID_RETURN; } /* @@ -468,37 +466,7 @@ void free_table_share(TABLE_SHARE *share) { 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 (share->m_flush_tickets.is_empty()) - { - /* - 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(); - } - else - { - 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. - */ - } + share->destroy(); DBUG_VOID_RETURN; } @@ -543,6 +511,17 @@ inline bool is_system_table_name(const char *name, uint length) my_tolower(ci, name[2]) == 'm' && my_tolower(ci, name[3]) == 'e') || + /* one of mysql.*_stat tables, but not mysql.innodb* tables*/ + ((my_tolower(ci, name[length-5]) == 's' && + my_tolower(ci, name[length-4]) == 't' && + my_tolower(ci, name[length-3]) == 'a' && + my_tolower(ci, name[length-2]) == 't' && + my_tolower(ci, name[length-1]) == 's') && + !(my_tolower(ci, name[0]) == 'i' && + my_tolower(ci, name[1]) == 'n' && + my_tolower(ci, name[2]) == 'n' && + my_tolower(ci, name[3]) == 'o')) || + /* mysql.event table */ (my_tolower(ci, name[0]) == 'e' && my_tolower(ci, name[1]) == 'v' && @@ -555,27 +534,6 @@ inline bool is_system_table_name(const char *name, uint length) } -/** - Check if a string contains path elements -*/ - -static bool has_disabled_path_chars(const char *str) -{ - for (; *str; str++) - { - switch (*str) { - case FN_EXTCHAR: - case '/': - case '\\': - case '~': - case '@': - return TRUE; - } - } - return FALSE; -} - - /* Read table definition from a binary / text based .frm file @@ -587,155 +545,114 @@ static bool has_disabled_path_chars(const char *str) NOTES This function is called when the table definition is not cached in - table_def_cache + table definition cache The data is returned in 'share', which is alloced by alloc_table_share().. The code assumes that share is initialized. - - RETURN VALUES - 0 ok - 1 Error (see open_table_error) - 2 Error (see open_table_error) - 3 Wrong data in .frm file - 4 Error (see open_table_error) - 5 Error (see open_table_error: charset unavailable) - 6 Unknown .frm version */ -int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags) +enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags) { - int error, table_type; - bool error_given; + bool error_given= false; File file; - uchar head[64]; + uchar *buf; + uchar head[FRM_HEADER_SIZE]; char path[FN_REFLEN]; - MEM_ROOT **root_ptr, *old_root; + size_t frmlen, read_length; DBUG_ENTER("open_table_def"); DBUG_PRINT("enter", ("table: '%s'.'%s' path: '%s'", share->db.str, share->table_name.str, share->normalized_path.str)); - error= 1; - error_given= 0; + share->error= OPEN_FRM_OPEN_ERROR; strxmov(path, share->normalized_path.str, reg_ext, NullS); - if ((file= mysql_file_open(key_file_frm, - path, O_RDONLY | O_SHARE, MYF(0))) < 0) + if (flags & GTS_FORCE_DISCOVERY) { - /* - We don't try to open 5.0 unencoded name, if - - non-encoded name contains '@' signs, - because '@' can be misinterpreted. - It is not clear if '@' is escape character in 5.1, - or a normal character in 5.0. - - - non-encoded db or table name contain "#mysql50#" prefix. - This kind of tables must have been opened only by the - mysql_file_open() above. - */ - if (has_disabled_path_chars(share->table_name.str) || - has_disabled_path_chars(share->db.str) || - !strncmp(share->db.str, MYSQL50_TABLE_NAME_PREFIX, - MYSQL50_TABLE_NAME_PREFIX_LENGTH) || - !strncmp(share->table_name.str, MYSQL50_TABLE_NAME_PREFIX, - MYSQL50_TABLE_NAME_PREFIX_LENGTH)) - goto err_not_open; - - /* Try unencoded 5.0 name */ - uint length; - strxnmov(path, sizeof(path)-1, - mysql_data_home, "/", share->db.str, "/", - share->table_name.str, reg_ext, NullS); - length= unpack_filename(path, path) - reg_ext_length; - /* - The following is a safety test and should never fail - as the old file name should never be longer than the new one. - */ - DBUG_ASSERT(length <= share->normalized_path.length); - /* - If the old and the new names have the same length, - then table name does not have tricky characters, - so no need to check the old file name. - */ - if (length == share->normalized_path.length || - ((file= mysql_file_open(key_file_frm, - path, O_RDONLY | O_SHARE, MYF(0))) < 0)) - goto err_not_open; + DBUG_ASSERT(flags & GTS_TABLE); + DBUG_ASSERT(flags & GTS_USE_DISCOVERY); + mysql_file_delete_with_symlink(key_file_frm, path, "", MYF(0)); + file= -1; + } + else + file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0)); - /* Unencoded 5.0 table name found */ - path[length]= '\0'; // Remove .frm extension - strmov(share->normalized_path.str, path); - share->normalized_path.length= length; + if (file < 0) + { + if ((flags & GTS_TABLE) && (flags & GTS_USE_DISCOVERY)) + { + ha_discover_table(thd, share); + error_given= true; + } + goto err_not_open; } - error= 4; - if (mysql_file_read(file, head, 64, MYF(MY_NABP))) + if (mysql_file_read(file, head, sizeof(head), MYF(MY_NABP))) + { + share->error = my_errno == HA_ERR_FILE_TOO_SHORT + ? OPEN_FRM_CORRUPTED : OPEN_FRM_READ_ERROR; goto err; + } - if (head[0] == (uchar) 254 && head[1] == 1) + if (memcmp(head, STRING_WITH_LEN("TYPE=VIEW\n")) == 0) { - if (head[2] == FRM_VER || head[2] == FRM_VER+1 || - (head[2] >= FRM_VER+3 && head[2] <= FRM_VER+4)) - { - /* Open view only */ - if (db_flags & OPEN_VIEW_ONLY) - { - error_given= 1; - goto err; - } - table_type= 1; - } - else - { - error= 6; // Unkown .frm version - goto err; - } + share->is_view= 1; + share->error= flags & GTS_VIEW ? OPEN_FRM_OK : OPEN_FRM_NOT_A_TABLE; + goto err; } - else if (memcmp(head, STRING_WITH_LEN("TYPE=")) == 0) + if (!is_binary_frm_header(head)) { - error= 5; - if (memcmp(head+5,"VIEW",4) == 0) - { - share->is_view= 1; - if (db_flags & OPEN_VIEW) - error= 0; - } + /* No handling of text based files yet */ + share->error = OPEN_FRM_CORRUPTED; goto err; } - else + if (!(flags & GTS_TABLE)) + { + share->error = OPEN_FRM_NOT_A_VIEW; + goto err; + } + + frmlen= uint4korr(head+10); + set_if_smaller(frmlen, FRM_MAX_SIZE); // safety + + if (!(buf= (uchar*)my_malloc(frmlen, MYF(MY_THREAD_SPECIFIC|MY_WME)))) goto err; - /* No handling of text based files yet */ - if (table_type == 1) + memcpy(buf, head, sizeof(head)); + + read_length= mysql_file_read(file, buf + sizeof(head), + frmlen - sizeof(head), MYF(MY_WME)); + if (read_length == 0 || read_length == (size_t)-1) { - root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC); - old_root= *root_ptr; - *root_ptr= &share->mem_root; - error= open_binary_frm(thd, share, head, file); - *root_ptr= old_root; - error_given= 1; + share->error = OPEN_FRM_READ_ERROR; + my_free(buf); + goto err; } + mysql_file_close(file, MYF(MY_WME)); - share->table_category= get_table_category(& share->db, & share->table_name); + frmlen= read_length + sizeof(head); - if (!error) - thd->status_var.opened_shares++; + share->init_from_binary_frm_image(thd, false, buf, frmlen); + error_given= true; // init_from_binary_frm_image has already called my_error() + my_free(buf); + + goto err_not_open; err: mysql_file_close(file, MYF(MY_WME)); err_not_open: - if (error && !error_given) + if (share->error && !error_given) { - share->error= error; - open_table_error(share, error, (share->open_errno= my_errno), 0); + share->open_errno= my_errno; + open_table_error(share, share->error, share->open_errno); } - DBUG_RETURN(error); + DBUG_RETURN(share->error); } - -static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo, - uint new_frm_ver, - uint &ext_key_parts, TABLE_SHARE *share, uint len, +static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end, + uint keys, KEY *keyinfo, + uint new_frm_ver, uint &ext_key_parts, + TABLE_SHARE *share, uint len, KEY *first_keyinfo, char* &keynames) { uint i, j, n_length; @@ -770,25 +687,29 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo, { if (new_frm_ver >= 3) { + if (strpos + 8 >= frm_image_end) + return 1; keyinfo->flags= (uint) uint2korr(strpos) ^ HA_NOSAME; keyinfo->key_length= (uint) uint2korr(strpos+2); - keyinfo->key_parts= (uint) strpos[4]; + keyinfo->user_defined_key_parts= (uint) strpos[4]; keyinfo->algorithm= (enum ha_key_alg) strpos[5]; keyinfo->block_size= uint2korr(strpos+6); strpos+=8; } else { + if (strpos + 4 >= frm_image_end) + return 1; keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME; keyinfo->key_length= (uint) uint2korr(strpos+1); - keyinfo->key_parts= (uint) strpos[3]; + keyinfo->user_defined_key_parts= (uint) strpos[3]; keyinfo->algorithm= HA_KEY_ALG_UNDEF; strpos+=4; } if (i == 0) { - ext_key_parts+= (share->use_ext_keys ? first_keyinfo->key_parts*(keys-1) : 0); + ext_key_parts+= (share->use_ext_keys ? first_keyinfo->user_defined_key_parts*(keys-1) : 0); n_length=keys * sizeof(KEY) + ext_key_parts * sizeof(KEY_PART_INFO); if (!(keyinfo= (KEY*) alloc_root(&share->mem_root, n_length + len))) @@ -801,10 +722,10 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo, sizeof(ulong) * ext_key_parts))) return 1; first_key_part= key_part; - first_key_parts= first_keyinfo->key_parts; + first_key_parts= first_keyinfo->user_defined_key_parts; keyinfo->flags= first_keyinfo->flags; keyinfo->key_length= first_keyinfo->key_length; - keyinfo->key_parts= first_keyinfo->key_parts; + keyinfo->user_defined_key_parts= first_keyinfo->user_defined_key_parts; keyinfo->algorithm= first_keyinfo->algorithm; if (new_frm_ver >= 3) keyinfo->block_size= first_keyinfo->block_size; @@ -812,8 +733,10 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo, keyinfo->key_part= key_part; keyinfo->rec_per_key= rec_per_key; - for (j=keyinfo->key_parts ; j-- ; key_part++) + for (j=keyinfo->user_defined_key_parts ; j-- ; key_part++) { + if (strpos + (new_frm_ver >= 1 ? 9 : 7) >= frm_image_end) + return 1; *rec_per_key++=0; key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK); key_part->offset= (uint) uint2korr(strpos+2)-1; @@ -838,16 +761,21 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo, } key_part->store_length=key_part->length; } - keyinfo->ext_key_parts= keyinfo->key_parts; + + /* + Add primary key to end of extended keys for non unique keys for + storage engines that supports it. + */ + keyinfo->ext_key_parts= keyinfo->user_defined_key_parts; keyinfo->ext_key_flags= keyinfo->flags; keyinfo->ext_key_part_map= 0; - if (share->use_ext_keys && i) + if (share->use_ext_keys && i && !(keyinfo->flags & HA_NOSAME)) { for (j= 0; j < first_key_parts && keyinfo->ext_key_parts < MAX_REF_PARTS; j++) { - uint key_parts= keyinfo->key_parts; + uint key_parts= keyinfo->user_defined_key_parts; KEY_PART_INFO* curr_key_part= keyinfo->key_part; KEY_PART_INFO* curr_key_part_end= curr_key_part+key_parts; for ( ; curr_key_part < curr_key_part_end; curr_key_part++) @@ -869,20 +797,28 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo, share->ext_key_parts+= keyinfo->ext_key_parts; } keynames=(char*) key_part; - strpos+= (strmov(keynames, (char *) strpos) - keynames)+1; + strpos+= strnmov(keynames, (char *) strpos, frm_image_end - strpos) - keynames; + if (*strpos++) // key names are \0-terminated + return 1; //reading index comments for (keyinfo= share->key_info, i=0; i < keys; i++, keyinfo++) { if (keyinfo->flags & HA_USES_COMMENT) { + if (strpos + 2 >= frm_image_end) + return 1; keyinfo->comment.length= uint2korr(strpos); - keyinfo->comment.str= strmake_root(&share->mem_root, (char*) strpos+2, + strpos+= 2; + + if (strpos + keyinfo->comment.length >= frm_image_end) + return 1; + keyinfo->comment.str= strmake_root(&share->mem_root, (char*) strpos, keyinfo->comment.length); - strpos+= 2 + keyinfo->comment.length; + strpos+= keyinfo->comment.length; } - DBUG_ASSERT(test(keyinfo->flags & HA_USES_COMMENT) == - (keyinfo->comment.length > 0)); + DBUG_ASSERT(MY_TEST(keyinfo->flags & HA_USES_COMMENT) == + (keyinfo->comment.length > 0)); } share->keys= keys; // do it *after* all key_info's are initialized @@ -890,11 +826,13 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo, return 0; } + /** ensures that the enum value (read from frm) is within limits if not - issues a warning and resets the value to 0 (that is, 0 is assumed to be a default value) */ + static uint enum_value_with_check(THD *thd, TABLE_SHARE *share, const char *name, uint value, uint limit) { @@ -907,31 +845,77 @@ static uint enum_value_with_check(THD *thd, TABLE_SHARE *share, } -/* - Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE +/** + Check if a collation has changed number + + @param mysql_version + @param current collation number + + @retval new collation number (same as current collation number of no change) */ -static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, - File file) +static uint upgrade_collation(ulong mysql_version, uint cs_number) { - int error, errarg= 0; + if (mysql_version >= 50300 && mysql_version <= 50399) + { + switch (cs_number) { + case 149: return MY_PAGE2_COLLATION_ID_UCS2; // ucs2_crotian_ci + case 213: return MY_PAGE2_COLLATION_ID_UTF8; // utf8_crotian_ci + } + } + if ((mysql_version >= 50500 && mysql_version <= 50599) || + (mysql_version >= 100000 && mysql_version <= 100005)) + { + switch (cs_number) { + case 149: return MY_PAGE2_COLLATION_ID_UCS2; // ucs2_crotian_ci + case 213: return MY_PAGE2_COLLATION_ID_UTF8; // utf8_crotian_ci + case 214: return MY_PAGE2_COLLATION_ID_UTF32; // utf32_croatian_ci + case 215: return MY_PAGE2_COLLATION_ID_UTF16; // utf16_croatian_ci + case 245: return MY_PAGE2_COLLATION_ID_UTF8MB4;// utf8mb4_croatian_ci + } + } + return cs_number; +} + + +/** + Read data from a binary .frm file image into a TABLE_SHARE + + @note + frm bytes at the following offsets are unused in MariaDB 10.0: + + 8..9 (used to be the number of "form names") + 28..29 (used to be key_info_length) + + They're still set, for compatibility reasons, but never read. + + 42..46 are unused since 5.0 (were for RAID support) + Also, there're few unused bytes in forminfo. + +*/ + +int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, + const uchar *frm_image, + size_t frm_length) +{ + TABLE_SHARE *share= this; uint new_frm_ver, field_pack_length, new_field_pack_flag; 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 com_length, null_bit_pos; uint extra_rec_buf_length; uint i; bool use_hash; char *keynames, *names, *comment_pos; - uchar forminfo[288]; - uchar *record; - uchar *disk_buff, *strpos, *null_flags, *null_pos; - ulong pos, record_offset; + const uchar *forminfo, *extra2; + const uchar *frm_image_end = frm_image + frm_length; + uchar *record, *null_flags, *null_pos; + const uchar *disk_buff, *strpos; + ulong pos, record_offset; ulong rec_buff_length; handler *handler_file= 0; KEY *keyinfo; KEY_PART_INFO *key_part= NULL; - SQL_CRYPT *crypted=0; Field **field_ptr, *reg_field; const char **interval_array; enum legacy_db_type legacy_db_type; @@ -939,80 +923,160 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, 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; + const uchar *options= 0; KEY first_keyinfo; uint len; uint ext_key_parts= 0; + plugin_ref se_plugin= 0; keyinfo= &first_keyinfo; share->ext_key_parts= 0; - DBUG_ENTER("open_binary_frm"); + MEM_ROOT **root_ptr, *old_root; + DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image"); + + root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC); + old_root= *root_ptr; + *root_ptr= &share->mem_root; + + if (write && write_frm_image(frm_image, frm_length)) + goto err; + + if (frm_length < FRM_HEADER_SIZE + FRM_FORMINFO_SIZE) + goto err; - new_field_pack_flag= head[27]; - new_frm_ver= (head[2] - FRM_VER); + new_field_pack_flag= frm_image[27]; + new_frm_ver= (frm_image[2] - FRM_VER); field_pack_length= new_frm_ver < 2 ? 11 : 17; - disk_buff= 0; - error= 3; - /* Position of the form in the form file. */ - if (!(pos= get_form_pos(file, head))) - goto err; /* purecov: inspected */ + /* Length of the MariaDB extra2 segment in the form file. */ + len = uint2korr(frm_image+4); + extra2= frm_image + 64; + + if (*extra2 != '/') // old frm had '/' there + { + const uchar *e2end= extra2 + len; + while (extra2 + 3 < e2end) + { + uchar type= *extra2++; + size_t length= *extra2++; + if (!length) + { + if (extra2 + 2 >= e2end) + goto err; + length= uint2korr(extra2); + extra2+= 2; + if (length < 256) + goto err; + } + if (extra2 + length > e2end) + goto err; + switch (type) { + case EXTRA2_TABLEDEF_VERSION: + if (tabledef_version.str) // see init_from_sql_statement_string() + { + if (length != tabledef_version.length || + memcmp(extra2, tabledef_version.str, length)) + goto err; + } + else + { + tabledef_version.length= length; + tabledef_version.str= (uchar*)memdup_root(&mem_root, extra2, length); + if (!tabledef_version.str) + goto err; + } + break; + case EXTRA2_ENGINE_TABLEOPTS: + if (options) + goto err; + /* remember but delay parsing until we have read fields and keys */ + options= extra2; + options_len= length; + break; + case EXTRA2_DEFAULT_PART_ENGINE: +#ifdef WITH_PARTITION_STORAGE_ENGINE + { + LEX_STRING name= { (char*)extra2, length }; + share->default_part_plugin= ha_resolve_by_name(NULL, &name); + if (!share->default_part_plugin) + goto err; + } +#endif + break; + default: + /* abort frm parsing if it's an unknown but important extra2 value */ + if (type >= EXTRA2_ENGINE_IMPORTANT) + goto err; + } + extra2+= length; + } + if (extra2 != e2end) + goto err; + } + + if (frm_length < FRM_HEADER_SIZE + len || + !(pos= uint4korr(frm_image + FRM_HEADER_SIZE + len))) + goto err; - mysql_file_seek(file,pos,MY_SEEK_SET,MYF(0)); - if (mysql_file_read(file, forminfo,288,MYF(MY_NABP))) + forminfo= frm_image + pos; + if (forminfo + FRM_FORMINFO_SIZE >= frm_image_end) goto err; - share->frm_version= head[2]; + + share->frm_version= frm_image[2]; /* Check if .frm file created by MySQL 5.0. In this case we want to display CHAR fields as CHAR and not as VARCHAR. We do it this way as we want to keep the old frm version to enable MySQL 4.1 to read these files. */ - if (share->frm_version == FRM_VER_TRUE_VARCHAR -1 && head[33] == 5) + if (share->frm_version == FRM_VER_TRUE_VARCHAR -1 && frm_image[33] == 5) share->frm_version= FRM_VER_TRUE_VARCHAR; #ifdef WITH_PARTITION_STORAGE_ENGINE - /* - Yuck! Double-bad. Doesn't work with dynamic engine codes. - And doesn't lock the plugin. Fixed in 10.0.4 - */ - compile_time_assert(MYSQL_VERSION_ID < 100000); - if (*(head+61) && - !(share->default_part_db_type= - ha_checktype(thd, (enum legacy_db_type) (uint) *(head+61), 1, 0))) - goto err; - DBUG_PRINT("info", ("default_part_db_type = %u", head[61])); + if (frm_image[61] && !share->default_part_plugin) + { + enum legacy_db_type db_type= (enum legacy_db_type) (uint) frm_image[61]; + share->default_part_plugin= + ha_lock_engine(NULL, ha_checktype(thd, db_type, 1, 0)); + if (!share->default_part_plugin) + goto err; + } #endif - legacy_db_type= (enum legacy_db_type) (uint) *(head+3); - DBUG_ASSERT(share->db_plugin == NULL); + legacy_db_type= (enum legacy_db_type) (uint) frm_image[3]; /* if the storage engine is dynamic, no point in resolving it by its dynamically allocated legacy_db_type. We will resolve it later by name. */ if (legacy_db_type > DB_TYPE_UNKNOWN && legacy_db_type < DB_TYPE_FIRST_DYNAMIC) - share->db_plugin= ha_lock_engine(NULL, - ha_checktype(thd, legacy_db_type, 0, 0)); - share->db_create_options= db_create_options= uint2korr(head+30); + se_plugin= ha_lock_engine(NULL, ha_checktype(thd, legacy_db_type, 0, 0)); + share->db_create_options= db_create_options= uint2korr(frm_image+30); share->db_options_in_use= share->db_create_options; - share->mysql_version= uint4korr(head+51); + share->mysql_version= uint4korr(frm_image+51); share->null_field_first= 0; - if (!head[32]) // New frm file in 3.23 + if (!frm_image[32]) // New frm file in 3.23 { - share->avg_row_length= uint4korr(head+34); + uint cs_org= (((uint) frm_image[41]) << 8) + (uint) frm_image[38]; + uint cs_new= upgrade_collation(share->mysql_version, cs_org); + if (cs_org != cs_new) + share->incompatible_version|= HA_CREATE_USED_CHARSET; + + share->avg_row_length= uint4korr(frm_image+34); share->transactional= (ha_choice) - enum_value_with_check(thd, share, "transactional", (head[39] & 3), HA_CHOICE_MAX); + enum_value_with_check(thd, share, "transactional", frm_image[39] & 3, HA_CHOICE_MAX); share->page_checksum= (ha_choice) - enum_value_with_check(thd, share, "page_checksum", (head[39] >> 2) & 3, HA_CHOICE_MAX); - share->row_type= (row_type) - enum_value_with_check(thd, share, "row_format", head[40], ROW_TYPE_MAX); - share->table_charset= get_charset((((uint) head[41]) << 8) + - (uint) head[38],MYF(0)); + enum_value_with_check(thd, share, "page_checksum", (frm_image[39] >> 2) & 3, HA_CHOICE_MAX); + share->row_type= (enum row_type) + enum_value_with_check(thd, share, "row_format", frm_image[40], ROW_TYPE_MAX); + + if (cs_new && !(share->table_charset= get_charset(cs_new, MYF(MY_WME)))) + goto err; share->null_field_first= 1; + share->stats_sample_pages= uint2korr(frm_image+42); + share->stats_auto_recalc= (enum_stats_auto_recalc)(frm_image[44]); } if (!share->table_charset) { - /* unknown charset in head[38] or pre-3.23 frm */ + /* unknown charset in frm_image[38] or pre-3.23 frm */ if (use_mb(default_charset_info)) { /* Warn that we may be changing the size of character columns */ @@ -1023,18 +1087,17 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, } share->table_charset= default_charset_info; } + share->db_record_offset= 1; - if (db_create_options & HA_OPTION_LONG_BLOB_PTR) - share->blob_ptr_size= portable_sizeof_char_ptr; - error=4; - share->max_rows= uint4korr(head+18); - share->min_rows= uint4korr(head+22); + share->max_rows= uint4korr(frm_image+18); + share->min_rows= uint4korr(frm_image+22); /* Read keyinformation */ - key_info_length= (uint) uint2korr(head+28); - 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 */ + disk_buff= frm_image + uint2korr(frm_image+6); + + if (disk_buff + 6 >= frm_image_end) + goto err; + if (disk_buff[0] & 0x80) { keys= (disk_buff[1] << 7) | (disk_buff[0] & 0x7f); @@ -1051,36 +1114,29 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, len= (uint) uint2korr(disk_buff+4); - share->reclength = uint2korr((head+16)); + share->reclength = uint2korr(frm_image+16); share->stored_rec_length= share->reclength; - if (*(head+26) == 1) + if (frm_image[26] == 1) share->system= 1; /* one-record-database */ -#ifdef HAVE_CRYPTED_FRM - else if (*(head+26) == 2) - { - crypted= get_crypt_for_frm(); - share->crypted= 1; - } -#endif - record_offset= (ulong) (uint2korr(head+6)+ - ((uint2korr(head+14) == 0xffff ? - uint4korr(head+47) : uint2korr(head+14)))); + record_offset= (ulong) (uint2korr(frm_image+6)+ + ((uint2korr(frm_image+14) == 0xffff ? + uint4korr(frm_image+47) : uint2korr(frm_image+14)))); + + if (record_offset + share->reclength >= frm_length) + goto err; - if ((n_length= uint4korr(head+55))) + if ((n_length= uint4korr(frm_image+55))) { /* Read extra data segment */ - uchar *next_chunk, *buff_end; + const uchar *next_chunk, *buff_end; DBUG_PRINT("info", ("extra segment size is %u bytes", n_length)); - if (!(extra_segment_buff= (uchar*) my_malloc(n_length + 1, MYF(MY_WME)))) - goto err; - next_chunk= extra_segment_buff; - if (mysql_file_pread(file, extra_segment_buff, - n_length, record_offset + share->reclength, - MYF(MY_NABP))) - { + next_chunk= frm_image + record_offset + share->reclength; + buff_end= next_chunk + n_length; + + if (buff_end >= frm_image_end) goto err; - } + share->connect_string.length= uint2korr(next_chunk); if (!(share->connect_string.str= strmake_root(&share->mem_root, (char*) next_chunk + 2, @@ -1090,7 +1146,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, goto err; } next_chunk+= share->connect_string.length + 2; - buff_end= extra_segment_buff + n_length; if (next_chunk + 2 < buff_end) { uint str_db_type_length= uint2korr(next_chunk); @@ -1099,12 +1154,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, name.length= str_db_type_length; plugin_ref tmp_plugin= ha_resolve_by_name(thd, &name); - if (tmp_plugin != NULL && !plugin_equals(tmp_plugin, share->db_plugin)) + if (tmp_plugin != NULL && !plugin_equals(tmp_plugin, se_plugin)) { - if (legacy_db_type > DB_TYPE_UNKNOWN && - legacy_db_type < DB_TYPE_FIRST_DYNAMIC && - legacy_db_type != ha_legacy_type( - plugin_data(tmp_plugin, handlerton *))) + if (se_plugin) { /* bad file, legacy_db_type did not match the name */ sql_print_warning("%s.frm is inconsistent: engine typecode %d, engine name %s (%d)", @@ -1114,14 +1166,11 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, } /* tmp_plugin is locked with a local lock. - we unlock the old value of share->db_plugin before + we unlock the old value of se_plugin before replacing it with a globally locked version of tmp_plugin */ - plugin_unlock(NULL, share->db_plugin); - share->db_plugin= my_plugin_lock(NULL, tmp_plugin); - DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)", - str_db_type_length, next_chunk + 2, - ha_legacy_type(share->db_type()))); + plugin_unlock(NULL, se_plugin); + se_plugin= plugin_lock(NULL, tmp_plugin); } #ifdef WITH_PARTITION_STORAGE_ENGINE else if (str_db_type_length == 9 && @@ -1130,28 +1179,23 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, /* Use partition handler tmp_plugin is locked with a local lock. - we unlock the old value of share->db_plugin before + we unlock the old value of se_plugin before replacing it with a globally locked version of tmp_plugin */ /* Check if the partitioning engine is ready */ if (!plugin_is_ready(&name, MYSQL_STORAGE_ENGINE_PLUGIN)) { - error= 8; my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-partition"); goto err; } - plugin_unlock(NULL, share->db_plugin); - share->db_plugin= ha_lock_engine(NULL, partition_hton); - DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)", - str_db_type_length, next_chunk + 2, - ha_legacy_type(share->db_type()))); + plugin_unlock(NULL, se_plugin); + se_plugin= ha_lock_engine(NULL, partition_hton); } #endif else if (!tmp_plugin) { /* purecov: begin inspected */ - error= 8; name.str[name.length]=0; my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str); goto err; @@ -1160,10 +1204,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, next_chunk+= str_db_type_length + 2; } - share->set_use_ext_keys_flag(share->db_type()->flags & HTON_EXTENDED_KEYS); + share->set_use_ext_keys_flag(plugin_hton(se_plugin)->flags & HTON_SUPPORTS_EXTENDED_KEYS); - if (create_key_infos(disk_buff + 6, keys, keyinfo, new_frm_ver, - ext_key_parts, + if (create_key_infos(disk_buff + 6, frm_image_end, keys, keyinfo, + new_frm_ver, ext_key_parts, share, len, &first_keyinfo, keynames)) goto err; @@ -1243,12 +1287,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, DBUG_ASSERT(next_chunk <= buff_end); - if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS) + if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS_legacy) { - /* - store options position, but skip till the time we will - know number of fields - */ + if (options) + goto err; options_len= uint4korr(next_chunk); options= next_chunk + 4; next_chunk+= options_len + 4; @@ -1257,16 +1299,18 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, } else { - if (create_key_infos(disk_buff + 6, keys, keyinfo, new_frm_ver, - ext_key_parts, + if (create_key_infos(disk_buff + 6, frm_image_end, keys, keyinfo, + new_frm_ver, ext_key_parts, share, len, &first_keyinfo, keynames)) goto err; } - share->key_block_size= uint2korr(head+62); + share->key_block_size= uint2korr(frm_image+62); + + if (share->db_plugin && !plugin_equals(share->db_plugin, se_plugin)) + goto err; // wrong engine (someone changed the frm under our feet?) - error=4; - extra_rec_buf_length= uint2korr(head+59); + extra_rec_buf_length= uint2korr(frm_image+59); rec_buff_length= ALIGN_SIZE(share->reclength + 1 + extra_rec_buf_length); share->rec_buff_length= rec_buff_length; if (!(record= (uchar *) alloc_root(&share->mem_root, rec_buff_length))) @@ -1274,19 +1318,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, MEM_NOACCESS(record, rec_buff_length); MEM_UNDEFINED(record, share->reclength); share->default_values= record; - if (mysql_file_pread(file, record, (size_t) share->reclength, - record_offset, MYF(MY_NABP))) - goto err; /* purecov: inspected */ + memcpy(record, frm_image + record_offset, share->reclength); - mysql_file_seek(file, pos+288, MY_SEEK_SET, MYF(0)); -#ifdef HAVE_CRYPTED_FRM - if (crypted) - { - crypted->decode((char*) forminfo+256,288-256); - if (sint2korr(forminfo+284) != 0) // Should be 0 - goto err; // Wrong password - } -#endif + disk_buff= frm_image + pos + FRM_FORMINFO_SIZE; share->fields= uint2korr(forminfo+258); pos= uint2korr(forminfo+260); /* Length of all screens */ @@ -1298,6 +1332,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, com_length= uint2korr(forminfo+284); vcol_screen_length= uint2korr(forminfo+286); share->vfields= 0; + share->default_fields= 0; share->stored_fields= share->fields; if (forminfo[46] != (uchar)255) { @@ -1323,16 +1358,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, 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 err; /* purecov: inspected */ -#ifdef HAVE_CRYPTED_FRM - if (crypted) - { - crypted->decode((char*) disk_buff,read_length); - delete crypted; - crypted=0; - } -#endif strpos= disk_buff+pos; share->intervals= (TYPELIB*) (field_ptr+share->fields+1); @@ -1380,14 +1405,17 @@ 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()))) + plugin_hton(se_plugin)))) + goto err; + + if (handler_file->set_ha_share_ref(&share->ha_share)) goto err; record= share->default_values-1; /* Fieldstart = 1 */ null_bits_are_used= share->null_fields != 0; if (share->null_field_first) { - null_flags= null_pos= (uchar*) record+1; + null_flags= null_pos= record+1; null_bit_pos= (db_create_options & HA_OPTION_PACK_RECORD) ? 0 : 1; /* null_bytes below is only correct under the condition that @@ -1400,8 +1428,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, else { share->null_bytes= (share->null_fields+7)/8; - null_flags= null_pos= (uchar*) (record + 1 +share->reclength - - share->null_bytes); + null_flags= null_pos= record + 1 + share->reclength - share->null_bytes; null_bit_pos= 0; } #endif @@ -1443,19 +1470,29 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, geom_type= (Field::geometry_type) strpos[14]; charset= &my_charset_bin; #else - error= 4; // unsupported field type goto err; #endif } else { - uint csid= strpos[14] + (((uint) strpos[11]) << 8); - if (!csid) + uint cs_org= strpos[14] + (((uint) strpos[11]) << 8); + uint cs_new= upgrade_collation(share->mysql_version, cs_org); + if (cs_org != cs_new) + share->incompatible_version|= HA_CREATE_USED_CHARSET; + if (!cs_new) charset= &my_charset_bin; - else if (!(charset= get_charset(csid, MYF(0)))) + else if (!(charset= get_charset(cs_new, MYF(0)))) { - error= 5; // Unknown or unavailable charset - errarg= (int) csid; + const char *csname= get_charset_name((uint) cs_new); + char tmp[10]; + if (!csname || csname[0] =='?') + { + my_snprintf(tmp, sizeof(tmp), "#%d", cs_new); + csname= tmp; + } + my_printf_error(ER_UNKNOWN_COLLATION, + "Unknown collation '%s' in table '%-.64s' definition", + MYF(0), csname, share->table_name.str); goto err; } } @@ -1463,10 +1500,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if ((uchar)field_type == (uchar)MYSQL_TYPE_VIRTUAL) { if (!interval_nr) // Expect non-null expression - { - error= 4; goto err; - } /* The interval_id byte in the .frm file stores the length of the expression statement for a virtual column. @@ -1503,10 +1537,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (opt_interval_id) interval_nr= (uint)vcol_screen_pos[3]; else if ((uint)vcol_screen_pos[0] != 1) - { - error= 4; goto err; - } + fld_stored_in_db= (bool) (uint) vcol_screen_pos[2]; vcol_expr_length= vcol_info_length - (uint)(FRM_VCOL_HEADER_SIZE(opt_interval_id)); @@ -1580,7 +1612,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_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_CRASHED_ON_USAGE, "Found incompatible DECIMAL field '%s' in %s; " "Please do \"ALTER TABLE '%s' FORCE\" to fix it!", @@ -1605,10 +1637,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, (TYPELIB*) 0), share->fieldnames.type_names[i]); if (!reg_field) // Not supported field type - { - error= 4; - goto err; /* purecov: inspected */ - } + goto err; + reg_field->field_index= i; reg_field->comment=comment; @@ -1633,29 +1663,18 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (reg_field->unireg_check == Field::NEXT_NUMBER) share->found_next_number_field= field_ptr; - if (share->timestamp_field == reg_field) - share->timestamp_field_offset= i; - if (use_hash) - { - if (my_hash_insert(&share->name_hash, - (uchar*) field_ptr)) - { - /* - Set return code 8 here to indicate that an error has - occurred but that the error message already has been - sent (OOM). - */ - error= 8; - goto err; - } - } + if (use_hash && my_hash_insert(&share->name_hash, (uchar*) field_ptr)) + goto err; if (!reg_field->stored_in_db) { share->stored_fields--; if (share->stored_rec_length>=recpos) share->stored_rec_length= recpos-1; } + if (reg_field->has_insert_default_function() || + reg_field->has_update_default_function()) + ++share->default_fields; } *field_ptr=0; // End marker /* Sanity checks: */ @@ -1666,10 +1685,44 @@ 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(primary_key_name, &share->keynames, - FIND_TYPE_NO_PREFIX) - 1); longlong ha_option= handler_file->ha_table_flags(); keyinfo= share->key_info; + uint primary_key= my_strcasecmp(system_charset_info, share->keynames.type_names[0], + primary_key_name) ? MAX_KEY : 0; + + if (primary_key >= MAX_KEY && keyinfo->flags & HA_NOSAME) + { + /* + If the UNIQUE key doesn't have NULL columns and is not a part key + declare this as a primary key. + */ + primary_key= 0; + key_part= keyinfo->key_part; + for (i=0 ; i < keyinfo->user_defined_key_parts ;i++) + { + DBUG_ASSERT(key_part[i].fieldnr > 0); + // Table field corresponding to the i'th key part. + Field *table_field= share->field[key_part[i].fieldnr - 1]; + + /* + If the key column is of NOT NULL BLOB type, then it + will definitly have key prefix. And if key part prefix size + is equal to the BLOB column max size, then we can promote + it to primary key. + */ + if (!table_field->real_maybe_null() && + table_field->type() == MYSQL_TYPE_BLOB && + table_field->field_length == key_part[i].length) + continue; + + if (table_field->real_maybe_null() || + table_field->key_length() != key_part[i].length) + { + primary_key= MAX_KEY; // Can't be used + break; + } + } + } if (share->use_ext_keys) { @@ -1680,12 +1733,12 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, } else { - add_first_key_parts= first_keyinfo.key_parts; + add_first_key_parts= first_keyinfo.user_defined_key_parts; /* Do not add components of the primary key starting from the major component defined over the beginning of a field. */ - for (i= 0; i < first_keyinfo.key_parts; i++) + for (i= 0; i < first_keyinfo.user_defined_key_parts; i++) { uint fieldnr= keyinfo[0].key_part[i].fieldnr; if (share->field[fieldnr-1]->key_length() != @@ -1724,7 +1777,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, Do not extend the key that contains a component defined over the beginning of a field. */ - for (i= 0; i < keyinfo->key_parts; i++) + for (i= 0; i < keyinfo->user_defined_key_parts; i++) { uint fieldnr= keyinfo->key_part[i].fieldnr; if (share->field[fieldnr-1]->key_length() != @@ -1735,11 +1788,11 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, } } - if (add_first_key_parts < keyinfo->ext_key_parts-keyinfo->key_parts) + if (add_first_key_parts < keyinfo->ext_key_parts-keyinfo->user_defined_key_parts) { share->ext_key_parts-= keyinfo->ext_key_parts; key_part_map ext_key_part_map= keyinfo->ext_key_part_map; - keyinfo->ext_key_parts= keyinfo->key_parts; + keyinfo->ext_key_parts= keyinfo->user_defined_key_parts; keyinfo->ext_key_flags= keyinfo->flags; keyinfo->ext_key_part_map= 0; for (i= 0; i < add_first_key_parts; i++) @@ -1764,43 +1817,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (share->key_info[key].flags & HA_FULLTEXT) share->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT; - if (primary_key >= MAX_KEY && (keyinfo->flags & HA_NOSAME)) - { - /* - If the UNIQUE key doesn't have NULL columns and is not a part key - declare this as a primary key. - */ - primary_key=key; - key_part= keyinfo->key_part; - for (i=0 ; i < keyinfo->key_parts ;i++) - { - DBUG_ASSERT(key_part[i].fieldnr > 0); - // Table field corresponding to the i'th key part. - Field *table_field= share->field[key_part[i].fieldnr - 1]; - - /* - If the key column is of NOT NULL BLOB type, then it - will definitly have key prefix. And if key part prefix size - is equal to the BLOB column max size, then we can promote - it to primary key. - */ - if (!table_field->real_maybe_null() && - table_field->type() == MYSQL_TYPE_BLOB && - table_field->field_length == key_part[i].length) - continue; - - if (table_field->real_maybe_null() || - table_field->key_length() != key_part[i].length) - { - primary_key= MAX_KEY; // Can't be used - break; - } - } - } - key_part= keyinfo->key_part; uint key_parts= share->use_ext_keys ? keyinfo->ext_key_parts : - keyinfo->key_parts; + keyinfo->user_defined_key_parts; for (i=0; i < key_parts; key_part++, i++) { Field *field; @@ -1810,10 +1829,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, (uint) key_part->offset, (uint) key_part->length); if (!key_part->fieldnr) - { - error= 4; // Wrong file goto err; - } + field= key_part->field= share->field[key_part->fieldnr-1]; key_part->type= field->key_type(); if (field->null_ptr) @@ -1842,7 +1859,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (i == 0 && key != primary_key) field->flags |= (((keyinfo->flags & HA_NOSAME) && - (keyinfo->key_parts == 1)) ? + (keyinfo->user_defined_key_parts == 1)) ? UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG); if (i == 0) field->key_start.set_bit(key); @@ -1853,7 +1870,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { share->keys_for_keyread.set_bit(key); field->part_of_key.set_bit(key); - if (i < keyinfo->key_parts) + if (i < keyinfo->user_defined_key_parts) field->part_of_key_not_clustered.set_bit(key); } if (handler_file->index_flags(key, i, 1) & HA_READ_ORDER) @@ -1899,7 +1916,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_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_CRASHED_ON_USAGE, "Found wrong key definition in %s; " "Please do \"ALTER TABLE '%s' FORCE\" to fix " @@ -1927,7 +1944,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, keyinfo->usable_key_parts= usable_parts; // Filesort set_if_bigger(share->max_key_length,keyinfo->key_length+ - keyinfo->key_parts); + keyinfo->user_defined_key_parts); share->total_key_length+= keyinfo->key_length; /* MERGE tables do not have unique indexes. But every key could be @@ -1945,7 +1962,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, If we are using an integer as the primary key then allow the user to refer to it as '_rowid' */ - if (share->key_info[primary_key].key_parts == 1) + if (share->key_info[primary_key].user_defined_key_parts == 1) { Field *field= share->key_info[primary_key].key_part[0].field; if (field && field->result_type() == INT_RESULT) @@ -1961,8 +1978,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, } else share->primary_key= MAX_KEY; - my_free(disk_buff); - disk_buff=0; if (new_field_pack_flag <= 1) { /* Old file format with default as not null */ @@ -1971,7 +1986,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, null_length, 255); } - if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS) + if (options) { DBUG_ASSERT(options_len); if (engine_table_options_frm_read(options, options_len, share)) @@ -1988,13 +2003,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, share->default_values, reg_field, &share->next_number_key_offset, &share->next_number_keypart)) < 0) - { - /* Wrong field definition */ - error= 4; - goto err; - } - else - reg_field->flags |= AUTO_INCREMENT_FLAG; + goto err; // Wrong field definition + reg_field->flags |= AUTO_INCREMENT_FLAG; } if (share->blob_fields) @@ -2030,7 +2040,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (!(bitmaps= (my_bitmap_map*) alloc_root(&share->mem_root, share->column_bitmap_size))) goto err; - bitmap_init(&share->all_set, bitmaps, share->fields, FALSE); + my_bitmap_init(&share->all_set, bitmaps, share->fields, FALSE); bitmap_set_all(&share->all_set); delete handler_file; @@ -2038,34 +2048,187 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (use_hash) (void) my_hash_check(&share->name_hash); #endif - my_free(extra_segment_buff); - DBUG_RETURN (0); + + share->db_plugin= se_plugin; + share->error= OPEN_FRM_OK; + thd->status_var.opened_shares++; + *root_ptr= old_root; + DBUG_RETURN(0); err: - share->error= error; + share->error= OPEN_FRM_CORRUPTED; share->open_errno= my_errno; - share->errarg= errarg; - my_free(disk_buff); - my_free(extra_segment_buff); - delete crypted; delete handler_file; + plugin_unlock(0, se_plugin); my_hash_free(&share->name_hash); - if (share->ha_data_destroy) + + if (!thd->is_error()) + open_table_error(share, OPEN_FRM_CORRUPTED, share->open_errno); + + *root_ptr= old_root; + DBUG_RETURN(HA_ERR_NOT_A_TABLE); +} + + +static bool sql_unusable_for_discovery(THD *thd, handlerton *engine, + const char *sql) +{ + LEX *lex= thd->lex; + HA_CREATE_INFO *create_info= &lex->create_info; + + // ... not CREATE TABLE + if (lex->sql_command != SQLCOM_CREATE_TABLE) + return 1; + // ... create like + if (create_info->options & HA_LEX_CREATE_TABLE_LIKE) + return 1; + // ... create select + if (lex->select_lex.item_list.elements) + return 1; + // ... temporary + if (create_info->tmp_table()) + return 1; + // ... if exists + if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) + return 1; + + // XXX error out or rather ignore the following: + // ... partitioning + if (lex->part_info) + return 1; + // ... union + if (create_info->used_fields & HA_CREATE_USED_UNION) + return 1; + // ... index/data directory + if (create_info->data_file_name || create_info->index_file_name) + return 1; + // ... engine + if (create_info->db_type && create_info->db_type != engine) + return 1; + + return 0; +} + +int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, + const char *sql, size_t sql_length) +{ + ulonglong saved_mode= thd->variables.sql_mode; + CHARSET_INFO *old_cs= thd->variables.character_set_client; + Parser_state parser_state; + bool error; + char *sql_copy; + handler *file; + LEX *old_lex; + Query_arena *arena, backup; + LEX tmp_lex; + KEY *unused1; + uint unused2; + handlerton *hton= plugin_hton(db_plugin); + LEX_CUSTRING frm= {0,0}; + + DBUG_ENTER("TABLE_SHARE::init_from_sql_statement_string"); + + /* + Ouch. Parser may *change* the string it's working on. + Currently (2013-02-26) it is used to permanently disable + conditional comments. + Anyway, let's copy the caller's string... + */ + if (!(sql_copy= thd->strmake(sql, sql_length))) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + + if (parser_state.init(thd, sql_copy, sql_length)) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + + thd->variables.sql_mode= MODE_NO_ENGINE_SUBSTITUTION | MODE_NO_DIR_IN_CREATE; + thd->variables.character_set_client= system_charset_info; + tmp_disable_binlog(thd); + old_lex= thd->lex; + thd->lex= &tmp_lex; + + arena= thd->stmt_arena; + if (arena->is_conventional()) + arena= 0; + else + thd->set_n_backup_active_arena(arena, &backup); + + lex_start(thd); + + if ((error= parse_sql(thd, & parser_state, NULL) || + sql_unusable_for_discovery(thd, hton, sql_copy))) + goto ret; + + thd->lex->create_info.db_type= hton; + + if (tabledef_version.str) + thd->lex->create_info.tabledef_version= tabledef_version; + + promote_first_timestamp_column(&thd->lex->alter_info.create_list); + file= mysql_create_frm_image(thd, db.str, table_name.str, + &thd->lex->create_info, &thd->lex->alter_info, + C_ORDINARY_CREATE, &unused1, &unused2, &frm); + error|= file == 0; + delete file; + + if (frm.str) { - share->ha_data_destroy(share->ha_data); - share->ha_data_destroy= NULL; + option_list= 0; // cleanup existing options ... + option_struct= 0; // ... if it's an assisted discovery + error= init_from_binary_frm_image(thd, write, frm.str, frm.length); } -#ifdef WITH_PARTITION_STORAGE_ENGINE - if (share->ha_part_data_destroy) + +ret: + my_free(const_cast<uchar*>(frm.str)); + lex_end(thd->lex); + thd->lex= old_lex; + if (arena) + thd->restore_active_arena(arena, &backup); + reenable_binlog(thd); + thd->variables.sql_mode= saved_mode; + thd->variables.character_set_client= old_cs; + if (thd->is_error() || error) { - share->ha_part_data_destroy(share->ha_part_data); - share->ha_data_destroy= NULL; + thd->clear_error(); + my_error(ER_SQL_DISCOVER_ERROR, MYF(0), + plugin_name(db_plugin)->str, db.str, table_name.str, + sql_copy); + DBUG_RETURN(HA_ERR_GENERIC); } -#endif /* WITH_PARTITION_STORAGE_ENGINE */ + DBUG_RETURN(0); +} + +bool TABLE_SHARE::write_frm_image(const uchar *frm, size_t len) +{ + return writefrm(normalized_path.str, db.str, table_name.str, false, frm, len); +} + + +bool TABLE_SHARE::read_frm_image(const uchar **frm, size_t *len) +{ + if (IF_PARTITIONING(partition_info_str, 0)) // cannot discover a partition + { + DBUG_ASSERT(db_type()->discover_table == 0); + return 1; + } + + if (frm_image) + { + *frm= frm_image->str; + *len= frm_image->length; + frm_image->str= 0; // pass the ownership to the caller + frm_image= 0; + return 0; + } + return readfrm(normalized_path.str, frm, len); +} + + +void TABLE_SHARE::free_frm_image(const uchar *frm) +{ + if (frm) + my_free(const_cast<uchar*>(frm)); +} - open_table_error(share, error, share->open_errno, errarg); - DBUG_RETURN(error); -} /* open_binary_frm */ /* @brief @@ -2378,15 +2541,16 @@ end: 7 Table definition has changed in engine */ -int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, - uint db_stat, uint prgflag, uint ha_open_flags, - TABLE *outparam, bool is_create_table) +enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, + const char *alias, uint db_stat, uint prgflag, + uint ha_open_flags, TABLE *outparam, + bool is_create_table) { - int error; + enum open_frm_error error; uint records, i, bitmap_size; bool error_reported= FALSE; uchar *record, *bitmaps; - Field **field_ptr, **vfield_ptr; + Field **field_ptr, **UNINIT_VAR(vfield_ptr), **UNINIT_VAR(dfield_ptr); uint8 save_context_analysis_only= thd->lex->context_analysis_only; DBUG_ENTER("open_table_from_share"); DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str, @@ -2394,14 +2558,21 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, thd->lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_VIEW; // not a view - error= 1; + error= OPEN_FRM_ERROR_ALREADY_ISSUED; // for OOM errors below bzero((char*) outparam, sizeof(*outparam)); outparam->in_use= thd; outparam->s= share; outparam->db_stat= db_stat; outparam->write_row_record= NULL; - init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); + if (share->incompatible_version && + !(ha_open_flags & (HA_OPEN_FOR_ALTER | HA_OPEN_FOR_REPAIR))) + { + /* one needs to run mysql_upgrade on the table */ + error= OPEN_FRM_NEEDS_REBUILD; + goto err; + } + init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); if (outparam->alias.copy(alias, strlen(alias), table_alias_charset)) goto err; @@ -2417,13 +2588,15 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, if (!(outparam->file= get_new_handler(share, &outparam->mem_root, share->db_type()))) goto err; + + if (outparam->file->set_ha_share_ref(&share->ha_share)) + goto err; } else { DBUG_ASSERT(!db_stat); } - error= 4; outparam->reginfo.lock_type= TL_UNLOCK; outparam->current_lock= F_UNLCK; records=0; @@ -2478,9 +2651,6 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, if (share->found_next_number_field) outparam->found_next_number_field= outparam->field[(uint) (share->found_next_number_field - share->field)]; - if (share->timestamp_field) - outparam->timestamp_field= (Field_timestamp*) outparam->field[share->timestamp_field_offset]; - /* Fix key->name and key_part->field */ if (share->key_parts) @@ -2508,7 +2678,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, key_info->key_part= key_part; key_part_end= key_part + (share->use_ext_keys ? key_info->ext_key_parts : - key_info->key_parts) ; + key_info->user_defined_key_parts) ; for ( ; key_part < key_part_end; key_part++) { Field *field= key_part->field= outparam->field[key_part->fieldnr - 1]; @@ -2526,16 +2696,14 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, } } if (!share->use_ext_keys) - key_part+= key_info->ext_key_parts - key_info->key_parts; + key_part+= key_info->ext_key_parts - key_info->user_defined_key_parts; } } /* - Process virtual columns, if any. + Process virtual and default columns, if any. */ - if (!share->vfields) - outparam->vfield= NULL; - else + if (share->vfields) { if (!(vfield_ptr = (Field **) alloc_root(&outparam->mem_root, (uint) ((share->vfields+1)* @@ -2543,10 +2711,24 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, goto err; outparam->vfield= vfield_ptr; + } + + if (share->default_fields) + { + if (!(dfield_ptr = (Field **) alloc_root(&outparam->mem_root, + (uint) ((share->default_fields+1)* + sizeof(Field*))))) + goto err; + outparam->default_field= dfield_ptr; + } + + if (share->vfields || share->default_fields) + { + /* Reuse the same loop both for virtual and default fields. */ for (field_ptr= outparam->field; *field_ptr; field_ptr++) { - if ((*field_ptr)->vcol_info) + if (share->vfields && (*field_ptr)->vcol_info) { if (unpack_vcol_info_from_frm(thd, &outparam->mem_root, @@ -2555,13 +2737,20 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, &(*field_ptr)->vcol_info->expr_str, &error_reported)) { - error= 4; // in case no error is reported + error= OPEN_FRM_CORRUPTED; goto err; } *(vfield_ptr++)= *field_ptr; } + if (share->default_fields && + ((*field_ptr)->has_insert_default_function() || + (*field_ptr)->has_update_default_function())) + *(dfield_ptr++)= *field_ptr; } - *vfield_ptr= 0; // End marker + if (share->vfields) + *vfield_ptr= 0; // End marker + if (share->default_fields) + *dfield_ptr= 0; // End marker } #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -2590,7 +2779,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, tmp= mysql_unpack_partition(thd, share->partition_info_str, share->partition_info_str_len, outparam, is_create_table, - share->default_part_db_type, + plugin_hton(share->default_part_plugin), &work_part_info_used); if (tmp) { @@ -2600,8 +2789,9 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, } outparam->part_info->is_auto_partitioned= share->auto_partitioned; DBUG_PRINT("info", ("autopartitioned: %u", share->auto_partitioned)); - /* we should perform the fix_partition_func in either local or - caller's arena depending on work_part_info_used value + /* + We should perform the fix_partition_func in either local or + caller's arena depending on work_part_info_used value. */ if (!work_part_info_used) tmp= fix_partition_func(thd, outparam, is_create_table); @@ -2644,68 +2834,65 @@ partititon_err: /* Allocate bitmaps */ bitmap_size= share->column_bitmap_size; - if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*5))) + if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*6))) goto err; - bitmap_init(&outparam->def_read_set, + my_bitmap_init(&outparam->def_read_set, (my_bitmap_map*) bitmaps, share->fields, FALSE); - bitmap_init(&outparam->def_write_set, + my_bitmap_init(&outparam->def_write_set, (my_bitmap_map*) (bitmaps+bitmap_size), share->fields, FALSE); - bitmap_init(&outparam->def_vcol_set, + my_bitmap_init(&outparam->def_vcol_set, (my_bitmap_map*) (bitmaps+bitmap_size*2), share->fields, FALSE); - bitmap_init(&outparam->tmp_set, + my_bitmap_init(&outparam->tmp_set, (my_bitmap_map*) (bitmaps+bitmap_size*3), share->fields, FALSE); - bitmap_init(&outparam->eq_join_set, + my_bitmap_init(&outparam->eq_join_set, (my_bitmap_map*) (bitmaps+bitmap_size*4), share->fields, FALSE); + my_bitmap_init(&outparam->cond_set, + (my_bitmap_map*) (bitmaps+bitmap_size*5), share->fields, FALSE); outparam->default_column_bitmaps(); + outparam->cond_selectivity= 1.0; + /* The table struct is now initialized; Open the table */ - error= 2; if (db_stat) { - int ha_err; - if ((ha_err= (outparam->file-> - ha_open(outparam, share->normalized_path.str, - (db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR), - (db_stat & HA_OPEN_TEMPORARY ? HA_OPEN_TMP_TABLE : - ((db_stat & HA_WAIT_IF_LOCKED) || - (specialflag & SPECIAL_WAIT_IF_LOCKED)) ? - HA_OPEN_WAIT_IF_LOCKED : - (db_stat & (HA_ABORT_IF_LOCKED | HA_GET_INFO)) ? - HA_OPEN_ABORT_IF_LOCKED : - HA_OPEN_IGNORE_IF_LOCKED) | ha_open_flags)))) + if (db_stat & HA_OPEN_TEMPORARY) + ha_open_flags|= HA_OPEN_TMP_TABLE; + else if ((db_stat & HA_WAIT_IF_LOCKED) || + (specialflag & SPECIAL_WAIT_IF_LOCKED)) + ha_open_flags|= HA_OPEN_WAIT_IF_LOCKED; + else if (db_stat & (HA_ABORT_IF_LOCKED | HA_GET_INFO)) + ha_open_flags|= HA_OPEN_ABORT_IF_LOCKED; + else + ha_open_flags|= HA_OPEN_IGNORE_IF_LOCKED; + + int ha_err= outparam->file->ha_open(outparam, share->normalized_path.str, + (db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR), + ha_open_flags); + if (ha_err) { + share->open_errno= ha_err; /* Set a flag if the table is crashed and it can be auto. repaired */ share->crashed= (outparam->file->auto_repair(ha_err) && !(ha_open_flags & HA_OPEN_FOR_REPAIR)); + outparam->file->print_error(ha_err, MYF(0)); + error_reported= TRUE; - switch (ha_err) - { - case HA_ERR_NO_SUCH_TABLE: - /* - The table did not exists in storage engine, use same error message - as if the .frm file didn't exist - */ - error= 1; - my_errno= ENOENT; - break; - case EMFILE: - /* - Too many files opened, use same error message as if the .frm - file can't open - */ - DBUG_PRINT("error", ("open file: %s failed, too many files opened (errno: %d)", - share->normalized_path.str, ha_err)); - error= 1; - my_errno= EMFILE; - break; - default: - outparam->file->print_error(ha_err, MYF(0)); - error_reported= TRUE; - if (ha_err == HA_ERR_TABLE_DEF_CHANGED) - error= 7; - break; - } - goto err; /* purecov: inspected */ + if (ha_err == HA_ERR_TABLE_DEF_CHANGED) + error= OPEN_FRM_DISCOVER; + + /* + We're here, because .frm file was successfully opened. + + But if the table doesn't exist in the engine and the engine + supports discovery, we force rediscover to discover + the fact that table doesn't in fact exist and remove + the stray .frm file. + */ + if (share->db_type()->discover_table && + (ha_err == ENOENT || ha_err == HA_ERR_NO_SUCH_TABLE)) + error= OPEN_FRM_DISCOVER; + + goto err; } } @@ -2713,19 +2900,32 @@ partititon_err: bzero((char*) bitmaps, bitmap_size*3); #endif - outparam->no_replicate= outparam->file && - test(outparam->file->ha_table_flags() & - HA_HAS_OWN_BINLOGGING); + if (share->table_category == TABLE_CATEGORY_LOG) + { + outparam->no_replicate= TRUE; + } + else if (outparam->file) + { + handler::Table_flags flags= outparam->file->ha_table_flags(); + outparam->no_replicate= ! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE + | HA_BINLOG_ROW_CAPABLE)) + || MY_TEST(flags & HA_HAS_OWN_BINLOGGING); + } + else + { + outparam->no_replicate= FALSE; + } + /* Increment the opened_tables counter, only when open flags set. */ if (db_stat) thd->status_var.opened_tables++; thd->lex->context_analysis_only= save_context_analysis_only; - DBUG_RETURN (0); + DBUG_RETURN (OPEN_FRM_OK); err: if (! error_reported) - open_table_error(share, error, my_errno, 0); + open_table_error(share, error, my_errno); delete outparam->file; #ifdef WITH_PARTITION_STORAGE_ENGINE if (outparam->part_info) @@ -2777,6 +2977,7 @@ int closefrm(register TABLE *table, bool free_share) #ifdef WITH_PARTITION_STORAGE_ENGINE if (table->part_info) { + /* Allocated through table->mem_root, freed below */ free_items(table->part_info->item_free_list); table->part_info->item_free_list= 0; table->part_info= 0; @@ -2785,7 +2986,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); + tdc_release_share(table->s); else free_table_share(table->s); } @@ -2836,158 +3037,18 @@ void free_field_buffers_larger_than(TABLE *table, uint32 size) } } -/** - Find where a form starts. - - @param head The start of the form file. - - @remark If formname is NULL then only formnames is read. - - @retval The form position. -*/ - -static ulong get_form_pos(File file, uchar *head) -{ - uchar *pos, *buf; - uint names, length; - ulong ret_value=0; - DBUG_ENTER("get_form_pos"); - - names= uint2korr(head+8); - - if (!(names= uint2korr(head+8))) - DBUG_RETURN(0); - - length= uint2korr(head+4); - - 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 (mysql_file_read(file, buf, length+names*4, MYF(MY_NABP))) - { - my_free(buf); - DBUG_RETURN(0); - } - - pos= buf+length; - ret_value= uint4korr(pos); - - my_free(buf); - - DBUG_RETURN(ret_value); -} - - -/* - Read string from a file with malloc +/* error message when opening a form file */ - NOTES: - We add an \0 at end of the read string to make reading of C strings easier -*/ - -int read_string(File file, uchar**to, size_t length) +void open_table_error(TABLE_SHARE *share, enum open_frm_error error, + int db_errno) { - DBUG_ENTER("read_string"); - - my_free(*to); - if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) || - mysql_file_read(file, *to, length, MYF(MY_NABP))) - { - my_free(*to); /* purecov: inspected */ - *to= 0; /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ - } - *((char*) *to+length)= '\0'; - DBUG_RETURN (0); -} /* read_string */ - - - /* Add a new form to a form file */ - -ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames, - const char *newname) -{ - uint i,bufflength,maxlength,n_length,length,names; - ulong endpos,newpos; - uchar buff[IO_SIZE]; - uchar *pos; - DBUG_ENTER("make_new_entry"); - - length=(uint) strlen(newname)+1; - n_length=uint2korr(fileinfo+4); - maxlength=uint2korr(fileinfo+6); - names=uint2korr(fileinfo+8); - newpos=uint4korr(fileinfo+10); - - if (64+length+n_length+(names+1)*4 > maxlength) - { /* Expand file */ - newpos+=IO_SIZE; - int4store(fileinfo+10,newpos); - /* 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) - { - 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); - 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 */ - 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); - for (i=names, pos= (uchar*) *formnames->type_names+n_length-1; i-- ; - pos+=4) - { - endpos=uint4korr(pos)+IO_SIZE; - int4store(pos,endpos); - } - } - - if (n_length == 1 ) - { /* First name */ - length++; - (void) strxmov((char*) buff,"/",newname,"/",NullS); - } - else - (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) mysql_file_chsize(file, newpos, 0, MYF(MY_WME));/* Append file with '\0' */ - DBUG_RETURN(newpos); -} /* make_new_entry */ - - - /* error message when opening a form file */ - -void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg) -{ - int err_no; char buff[FN_REFLEN]; - myf errortype= ME_ERROR+ME_WAITTANG; // Write fatals error to log + const myf errortype= ME_ERROR+ME_WAITTANG; // Write fatals error to log DBUG_ENTER("open_table_error"); + DBUG_PRINT("info", ("error: %d db_errno: %d", error, db_errno)); switch (error) { - case 7: - case 1: + case OPEN_FRM_OPEN_ERROR: /* Test if file didn't exists. We have to also test for EINVAL as this may happen on windows when opening a file with a not legal file name @@ -3001,55 +3062,35 @@ void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg) errortype, buff, db_errno); } break; - case 2: - { - handler *file= 0; - const char *datext= ""; - - if (share->db_type() != NULL) - { - if ((file= get_new_handler(share, current_thd->mem_root, - share->db_type()))) - { - if (!(datext= *file->bas_ext())) - datext= ""; - } - } - err_no= (db_errno == ENOENT) ? ER_FILE_NOT_FOUND : (db_errno == EAGAIN) ? - ER_FILE_USED : ER_CANT_OPEN_FILE; - strxmov(buff, share->normalized_path.str, datext, NullS); - my_error(err_no,errortype, buff, db_errno); - delete file; + case OPEN_FRM_OK: + DBUG_ASSERT(0); // open_table_error() is never called for this one break; - } - case 5: - { - const char *csname= get_charset_name((uint) errarg); - char tmp[10]; - if (!csname || csname[0] =='?') - { - my_snprintf(tmp, sizeof(tmp), "#%d", errarg); - csname= tmp; - } - my_printf_error(ER_UNKNOWN_COLLATION, - "Unknown collation '%s' in table '%-.64s' definition", - MYF(0), csname, share->table_name.str); + case OPEN_FRM_ERROR_ALREADY_ISSUED: break; - } - case 6: - strxmov(buff, share->normalized_path.str, reg_ext, NullS); - my_printf_error(ER_NOT_FORM_FILE, - "Table '%-.64s' was created with a different version " - "of MySQL and cannot be read", - MYF(0), buff); + case OPEN_FRM_NOT_A_VIEW: + my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, + share->table_name.str, "VIEW"); + break; + case OPEN_FRM_NOT_A_TABLE: + my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, + share->table_name.str, "TABLE"); break; - case 8: + case OPEN_FRM_DISCOVER: + DBUG_ASSERT(0); // open_table_error() is never called for this one break; - default: /* Better wrong error than none */ - case 4: + case OPEN_FRM_CORRUPTED: strxmov(buff, share->normalized_path.str, reg_ext, NullS); my_error(ER_NOT_FORM_FILE, errortype, buff); break; + case OPEN_FRM_READ_ERROR: + strxmov(buff, share->normalized_path.str, reg_ext, NullS); + my_error(ER_ERROR_ON_READ, errortype, buff, db_errno); + break; + case OPEN_FRM_NEEDS_REBUILD: + strxnmov(buff, sizeof(buff)-1, + share->db.str, ".", share->table_name.str, NullS); + my_error(ER_TABLE_NEEDS_REBUILD, errortype, buff); + break; } DBUG_VOID_RETURN; } /* open_table_error */ @@ -3153,28 +3194,6 @@ static uint find_field(Field **fields, uchar *record, uint start, uint length) } - /* Check that the integer is in the internal */ - -int set_zone(register int nr, int min_zone, int max_zone) -{ - if (nr<=min_zone) - return (min_zone); - if (nr>=max_zone) - return (max_zone); - return (nr); -} /* set_zone */ - - /* Adjust number to next larger disk buffer */ - -ulong next_io_size(register ulong pos) -{ - reg2 ulong offset; - if ((offset= pos & (IO_SIZE-1))) - return pos-offset+IO_SIZE; - return pos; -} /* next_io_size */ - - /* Store an SQL quoted string. @@ -3237,22 +3256,12 @@ void append_unescaped(String *res, const char *pos, uint length) } - /* Create a .frm file */ - -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, KEY *key_info) +void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, + 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) - create_flags|= O_EXCL | O_NOFOLLOW; + DBUG_ENTER("prepare_frm_header"); /* Fix this when we have new .frm files; Current limit is 4G rows (TODO) */ if (create_info->max_rows > UINT_MAX32) @@ -3260,101 +3269,76 @@ 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= mysql_file_create(key_file_frm, - name, CREATE_MODE, create_flags, MYF(0))) >= 0) - { - uint key_length, tmp_key_length, tmp, csid; - bzero((char*) fileinfo,64); - /* header */ - fileinfo[0]=(uchar) 254; - fileinfo[1]= 1; - fileinfo[2]= FRM_VER+3+ test(create_info->varchar); + uint key_length, tmp_key_length, tmp, csid; + bzero((char*) fileinfo, FRM_HEADER_SIZE); + /* header */ + fileinfo[0]=(uchar) 254; + fileinfo[1]= 1; + fileinfo[2]= FRM_VER + 3 + MY_TEST(create_info->varchar); - fileinfo[3]= (uchar) ha_legacy_type( - ha_checktype(thd,ha_legacy_type(create_info->db_type),0,0)); - fileinfo[4]=1; - int2store(fileinfo+6,IO_SIZE); /* Next block starts here */ - /* - Keep in sync with pack_keys() in unireg.cc - For each key: - 8 bytes for the key header - 9 bytes for each key-part (MAX_REF_PARTS) - NAME_LEN bytes for the name - 1 byte for the NAMES_SEP_CHAR (before the name) - For all keys: - 6 bytes for the header - 1 byte for the NAMES_SEP_CHAR (after the last name) - 9 extra bytes (padding for safety? alignment?) - */ - 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; - } + fileinfo[3]= (uchar) ha_legacy_type( + ha_checktype(thd,ha_legacy_type(create_info->db_type),0,0)); - 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); - tmp_key_length= (key_length < 0xffff) ? key_length : 0xffff; - int2store(fileinfo+14,tmp_key_length); - int2store(fileinfo+16,reclength); - int4store(fileinfo+18,create_info->max_rows); - int4store(fileinfo+22,create_info->min_rows); - /* fileinfo[26] is set in mysql_create_frm() */ - fileinfo[27]=2; // Use long pack-fields - /* fileinfo[28 & 29] is set to key_info_length in mysql_create_frm() */ - create_info->table_options|=HA_OPTION_LONG_BLOB_PTR; // Use portable blob pointers - int2store(fileinfo+30,create_info->table_options); - fileinfo[32]=0; // No filename anymore - fileinfo[33]=5; // Mark for 5.0 frm file - int4store(fileinfo+34,create_info->avg_row_length); - 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]= (uchar) (csid >> 8); - fileinfo[42]= 0; - fileinfo[43]= 0; - fileinfo[44]= 0; - fileinfo[45]= 0; - fileinfo[46]= 0; - int4store(fileinfo+47, key_length); - tmp= MYSQL_VERSION_ID; // Store to avoid warning from int4store - int4store(fileinfo+51, tmp); - int4store(fileinfo+55, create_info->extra_size); - /* - 59-60 is reserved for extra_rec_buf_length, - 61 for default_part_db_type - */ - int2store(fileinfo+62, create_info->key_block_size); - bzero(fill,IO_SIZE); - for (; length > IO_SIZE ; length-= IO_SIZE) - { - if (mysql_file_write(file, fill, IO_SIZE, MYF(MY_WME | MY_NABP))) - { - (void) mysql_file_close(file, MYF(0)); - (void) mysql_file_delete(key_file_frm, name, MYF(0)); - return(-1); - } - } - } - else - { - if (my_errno == ENOENT) - my_error(ER_BAD_DB_ERROR,MYF(0),db); - else - my_error(ER_CANT_CREATE_TABLE,MYF(0),table,my_errno); - } - DBUG_RETURN(file); -} /* create_frm */ + /* + Keep in sync with pack_keys() in unireg.cc + For each key: + 8 bytes for the key header + 9 bytes for each key-part (MAX_REF_PARTS) + NAME_LEN bytes for the name + 1 byte for the NAMES_SEP_CHAR (before the name) + For all keys: + 6 bytes for the header + 1 byte for the NAMES_SEP_CHAR (after the last name) + 9 extra bytes (padding for safety? alignment?) + */ + for (i= 0; i < keys; i++) + { + DBUG_ASSERT(MY_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; + + int2store(fileinfo+8,1); + tmp_key_length= (key_length < 0xffff) ? key_length : 0xffff; + int2store(fileinfo+14,tmp_key_length); + int2store(fileinfo+16,reclength); + int4store(fileinfo+18,create_info->max_rows); + int4store(fileinfo+22,create_info->min_rows); + /* fileinfo[26] is set in mysql_create_frm() */ + fileinfo[27]=2; // Use long pack-fields + /* fileinfo[28 & 29] is set to key_info_length in mysql_create_frm() */ + create_info->table_options|=HA_OPTION_LONG_BLOB_PTR; // Use portable blob pointers + int2store(fileinfo+30,create_info->table_options); + fileinfo[32]=0; // No filename anymore + fileinfo[33]=5; // Mark for 5.0 frm file + int4store(fileinfo+34,create_info->avg_row_length); + 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; + /* Bytes 41-46 were for RAID support; now reused for other purposes */ + fileinfo[41]= (uchar) (csid >> 8); + int2store(fileinfo+42, create_info->stats_sample_pages & 0xffff); + fileinfo[44]= (uchar) create_info->stats_auto_recalc; + fileinfo[45]= 0; + fileinfo[46]= 0; + int4store(fileinfo+47, key_length); + tmp= MYSQL_VERSION_ID; // Store to avoid warning from int4store + int4store(fileinfo+51, tmp); + int4store(fileinfo+55, create_info->extra_size); + /* + 59-60 is reserved for extra_rec_buf_length, + 61 for default_part_db_type + */ + int2store(fileinfo+62, create_info->key_block_size); + DBUG_VOID_RETURN; +} /* prepare_fileinfo */ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) @@ -3383,7 +3367,7 @@ 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 (mysql_file_rename(key_file_frm, from_b, to_b, MYF(MY_WME))); + return mysql_file_rename(key_file_frm, from_b, to_b, MYF(0)); } @@ -3405,18 +3389,23 @@ bool get_field(MEM_ROOT *mem, Field *field, String *res) { char buff[MAX_FIELD_WIDTH], *to; String str(buff,sizeof(buff),&my_charset_bin); - uint length; + bool rc; + THD *thd= field->get_thd(); + ulonglong sql_mode_backup= thd->variables.sql_mode; + thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; field->val_str(&str); - if (!(length= str.length())) + if ((rc= !str.length() || + !(to= strmake_root(mem, str.ptr(), str.length())))) { res->length(0); - return 1; + goto ex; } - if (!(to= strmake_root(mem, str.ptr(), length))) - length= 0; // Safety fix - res->set(to, length, ((Field_str*)field)->charset()); - return 0; + res->set(to, str.length(), field->charset()); + +ex: + thd->variables.sql_mode= sql_mode_backup; + return rc; } @@ -3435,17 +3424,10 @@ bool get_field(MEM_ROOT *mem, Field *field, String *res) char *get_field(MEM_ROOT *mem, Field *field) { - char buff[MAX_FIELD_WIDTH], *to; - String str(buff,sizeof(buff),&my_charset_bin); - uint length; - - field->val_str(&str); - length= str.length(); - if (!length || !(to= (char*) alloc_root(mem,length+1))) - return NullS; - memcpy(to,str.ptr(),(uint) length); - to[length]=0; - return to; + String str; + bool rc= get_field(mem, field, &str); + DBUG_ASSERT(rc || str.ptr()[str.length()] == '\0'); + return rc ? NullS : (char *) str.ptr(); } /* @@ -3473,15 +3455,34 @@ uint calculate_key_len(TABLE *table, uint key, const uchar *buf, return length; } +#ifndef DBUG_OFF +/** + Verifies that database/table name is in lowercase, when it should be + + This is supposed to be used only inside DBUG_ASSERT() +*/ +bool ok_for_lower_case_names(const char *name) +{ + if (!lower_case_table_names || !name) + return true; + + char buf[SAFE_NAME_LEN]; + strmake_buf(buf, name); + my_casedn_str(files_charset_info, buf); + return strcmp(name, buf) == 0; +} +#endif + /* Check if database name is valid SYNPOSIS check_db_name() - org_name Name of database and length + org_name Name of database NOTES - If lower_case_table_names is set then database is converted to lower case + If lower_case_table_names is set to 1 then database name is converted + to lower case RETURN 0 ok @@ -3503,9 +3504,12 @@ bool check_db_name(LEX_STRING *org_name) if (!name_length || name_length > NAME_LEN) return 1; - if (lower_case_table_names && name != any_db) - my_casedn_str(files_charset_info, name); - + if (lower_case_table_names == 1 && name != any_db) + { + org_name->length= name_length= my_casedn_str(files_charset_info, name); + if (check_for_path_chars) + org_name->length+= MYSQL50_TABLE_NAME_PREFIX_LENGTH; + } if (db_name_is_in_ignore_db_dirs_list(name)) return 1; @@ -3652,9 +3656,9 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def) } else if (MYSQL_VERSION_ID == table->s->mysql_version) { - report_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED, - ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED), - table->alias.c_ptr(), + report_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2, + ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2), + table->s->db.str, table->s->table_name.str, table_def->count, table->s->fields); DBUG_RETURN(TRUE); } @@ -3754,6 +3758,46 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def) } } + if (table_def->primary_key_parts) + { + if (table->s->primary_key == MAX_KEY) + { + report_error(0, "Incorrect definition of table %s.%s: " + "missing primary key.", table->s->db.str, + table->alias.c_ptr()); + error= TRUE; + } + else + { + KEY *pk= &table->s->key_info[table->s->primary_key]; + if (pk->user_defined_key_parts != table_def->primary_key_parts) + { + report_error(0, "Incorrect definition of table %s.%s: " + "Expected primary key to have %u columns, but instead " + "found %u columns.", table->s->db.str, + table->alias.c_ptr(), table_def->primary_key_parts, + pk->user_defined_key_parts); + error= TRUE; + } + else + { + for (i= 0; i < pk->user_defined_key_parts; ++i) + { + if (table_def->primary_key_columns[i] + 1 != pk->key_part[i].fieldnr) + { + report_error(0, "Incorrect definition of table %s.%s: Expected " + "primary key part %u to refer to column %u, but " + "instead found column %u.", table->s->db.str, + table->alias.c_ptr(), i + 1, + table_def->primary_key_columns[i] + 1, + pk->key_part[i].fieldnr); + error= TRUE; + } + } + } + } + } + if (! error) table->s->table_field_def_cache= table_def; @@ -3812,16 +3856,17 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush, bool result= TRUE; /* - To protect used_tables list from being concurrently modified - while we are iterating through it we acquire LOCK_open. + To protect all_tables list from being concurrently modified + while we are iterating through it we increment tdc.all_tables_refs. This does not introduce deadlocks in the deadlock detector - because we won't try to acquire LOCK_open while + because we won't try to acquire tdc.LOCK_table_share while holding a write-lock on MDL_lock::m_rwlock. */ - if (gvisitor->m_lock_open_count++ == 0) - mysql_mutex_lock(&LOCK_open); + mysql_mutex_lock(&tdc.LOCK_table_share); + tdc.all_tables_refs++; + mysql_mutex_unlock(&tdc.LOCK_table_share); - I_P_List_iterator <TABLE, TABLE_share> tables_it(used_tables); + All_share_tables_list::Iterator tables_it(tdc.all_tables); /* In case of multiple searches running in parallel, avoid going @@ -3839,6 +3884,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush, while ((table= tables_it++)) { + DBUG_ASSERT(table->in_use && tdc.flushed); if (gvisitor->inspect_edge(&table->in_use->mdl_context)) { goto end_leave_node; @@ -3848,6 +3894,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush, tables_it.rewind(); while ((table= tables_it++)) { + DBUG_ASSERT(table->in_use && tdc.flushed); if (table->in_use->mdl_context.visit_subgraph(gvisitor)) { goto end_leave_node; @@ -3860,8 +3907,10 @@ end_leave_node: gvisitor->leave_node(src_ctx); end: - if (gvisitor->m_lock_open_count-- == 1) - mysql_mutex_unlock(&LOCK_open); + mysql_mutex_lock(&tdc.LOCK_table_share); + if (!--tdc.all_tables_refs) + mysql_cond_broadcast(&tdc.COND_release); + mysql_mutex_unlock(&tdc.LOCK_table_share); return result; } @@ -3875,10 +3924,15 @@ end: @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 + @pre LOCK_table_share is locked, the share is marked for flush and this connection does not reference the share. - LOCK_open will be unlocked temporarily during execution. + LOCK_table_share will be unlocked temporarily during execution. + + It may happen that another FLUSH TABLES thread marked this share + for flush, but didn't yet purge it from table definition cache. + In this case we may start waiting for a table share that has no + references (ref_count == 0). We do this with assumption that this + another FLUSH TABLES thread is about to purge this share. @retval FALSE - Success. @retval TRUE - Error (OOM, deadlock, timeout, etc...). @@ -3891,41 +3945,40 @@ bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime, 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); + mysql_mutex_assert_owner(&tdc.LOCK_table_share); + DBUG_ASSERT(tdc.flushed); - m_flush_tickets.push_front(&ticket); + tdc.m_flush_tickets.push_front(&ticket); mdl_context->m_wait.reset_status(); - mysql_mutex_unlock(&LOCK_open); + mysql_mutex_unlock(&tdc.LOCK_table_share); 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"); + &stage_waiting_for_table_flush); mdl_context->done_waiting_for(); - mysql_mutex_lock(&LOCK_open); + mysql_mutex_lock(&tdc.LOCK_table_share); - m_flush_tickets.remove(&ticket); + tdc.m_flush_tickets.remove(&ticket); - if (m_flush_tickets.is_empty() && ref_count == 0) + if (tdc.m_flush_tickets.is_empty() && tdc.ref_count == 0) { /* If our thread was the last one using the share, we must destroy it here. */ + mysql_mutex_unlock(&tdc.LOCK_table_share); destroy(); } + else + mysql_mutex_unlock(&tdc.LOCK_table_share); + /* In cases when our wait was aborted by KILL statement, @@ -3970,7 +4023,7 @@ bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime, void TABLE::init(THD *thd, TABLE_LIST *tl) { - DBUG_ASSERT(s->ref_count > 0 || s->tmp_table != NO_TMP_TABLE); + DBUG_ASSERT(s->tdc.ref_count > 0 || s->tmp_table != NO_TMP_TABLE); if (thd->lex->need_correct_ident()) alias_name_used= my_strcasecmp(table_alias_charset, @@ -3994,17 +4047,25 @@ void TABLE::init(THD *thd, TABLE_LIST *tl) file->ft_handler= 0; reginfo.impossible_range= 0; created= TRUE; + cond_selectivity= 1.0; + cond_selectivity_sampling_explain= NULL; +#ifdef HAVE_REPLICATION + /* used in RBR Triggers */ + master_had_triggers= 0; +#endif /* 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(); + for (Field **f_ptr= field ; *f_ptr ; f_ptr++) + { + (*f_ptr)->next_equal_field= NULL; + (*f_ptr)->cond_selectivity= 1.0; + } DBUG_ASSERT(key_read == 0); @@ -4093,7 +4154,8 @@ void TABLE::reset_item_list(List<Item> *item_list) const void TABLE_LIST::calc_md5(char *buffer) { uchar digest[16]; - MY_MD5_HASH(digest, (uchar *) select_stmt.str, select_stmt.length); + compute_md5_hash((char*) digest, select_stmt.str, + select_stmt.length); 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], @@ -4488,27 +4550,32 @@ void TABLE_LIST::hide_view_error(THD *thd) return; /* Hide "Unknown column" or "Unknown function" error */ DBUG_ASSERT(thd->is_error()); + switch (thd->get_stmt_da()->sql_errno()) { + case ER_BAD_FIELD_ERROR: + case ER_SP_DOES_NOT_EXIST: + case ER_FUNC_INEXISTENT_NAME_COLLISION: + case ER_PROCACCESS_DENIED_ERROR: + case ER_COLUMNACCESS_DENIED_ERROR: + case ER_TABLEACCESS_DENIED_ERROR: + case ER_TABLE_NOT_LOCKED: + case 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); + break; + } - 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->stmt_da->sql_errno() == ER_NO_DEFAULT_FOR_FIELD) - { - TABLE_LIST *top= top_table(); - thd->clear_error(); - // TODO: make correct error message - my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0), - top->view_db.str, top->view_name.str); + case ER_NO_DEFAULT_FOR_FIELD: + { + TABLE_LIST *top= top_table(); + thd->clear_error(); + // TODO: make correct error message + my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0), + top->view_db.str, top->view_name.str); + break; + } } } @@ -4579,19 +4646,28 @@ void TABLE_LIST::cleanup_items() int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure) { - if (check_option && check_option->val_int() == 0) + if (check_option) { - TABLE_LIST *main_view= top_table(); - if (ignore_failure) + Counting_error_handler ceh; + thd->push_internal_handler(&ceh); + bool res= check_option->val_int() == 0; + thd->pop_internal_handler(); + if (ceh.errors) + return(VIEW_CHECK_ERROR); + if (res) { - 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); + TABLE_LIST *main_view= top_table(); + if (ignore_failure) + { + push_warning_printf(thd, Sql_condition::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); + } + my_error(ER_VIEW_CHECK_FAILED, MYF(0), main_view->view_db.str, + main_view->view_name.str); + return(VIEW_CHECK_ERROR); } - my_error(ER_VIEW_CHECK_FAILED, MYF(0), main_view->view_db.str, - main_view->view_name.str); - return(VIEW_CHECK_ERROR); } return(VIEW_CHECK_OK); } @@ -4663,23 +4739,26 @@ bool TABLE_LIST::check_single_table(TABLE_LIST **table_arg, bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root) { + DBUG_ENTER("set_insert_values"); if (table) { + DBUG_PRINT("info", ("setting insert_value for table")); if (!table->insert_values && !(table->insert_values= (uchar *)alloc_root(mem_root, table->s->rec_buff_length))) - return TRUE; + DBUG_RETURN(TRUE); } else { + DBUG_PRINT("info", ("setting insert_value for view")); DBUG_ASSERT(is_view_or_derived() && is_merged_derived()); for (TABLE_LIST *tbl= (TABLE_LIST*)view->select_lex.table_list.first; tbl; tbl= tbl->next_local) if (tbl->set_insert_values(mem_root)) - return TRUE; + DBUG_RETURN(TRUE); } - return FALSE; + DBUG_RETURN(FALSE); } @@ -4852,7 +4931,7 @@ void TABLE_LIST::register_want_access(ulong want_access) Load security context information for this view SYNOPSIS - TABLE_LIST::prepare_view_securety_context() + TABLE_LIST::prepare_view_security_context() thd [in] thread handler RETURN @@ -4861,9 +4940,9 @@ void TABLE_LIST::register_want_access(ulong want_access) */ #ifndef NO_EMBEDDED_ACCESS_CHECKS -bool TABLE_LIST::prepare_view_securety_context(THD *thd) +bool TABLE_LIST::prepare_view_security_context(THD *thd) { - DBUG_ENTER("TABLE_LIST::prepare_view_securety_context"); + DBUG_ENTER("TABLE_LIST::prepare_view_security_context"); DBUG_PRINT("enter", ("table: %s", alias)); DBUG_ASSERT(!prelocking_placeholder && view); @@ -4877,7 +4956,7 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd) if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) || (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_NO_SUCH_USER, ER(ER_NO_SUCH_USER), definer.user.str, definer.host.str); @@ -4906,6 +4985,7 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd) } } DBUG_RETURN(FALSE); + } #endif @@ -4970,7 +5050,7 @@ bool TABLE_LIST::prepare_security(THD *thd) Security_context *save_security_ctx= thd->security_ctx; DBUG_ASSERT(!prelocking_placeholder); - if (prepare_view_securety_context(thd)) + if (prepare_view_security_context(thd)) DBUG_RETURN(TRUE); thd->security_ctx= find_view_security_context(thd); while ((tbl= tb++)) @@ -5198,7 +5278,8 @@ Item *Field_iterator_table::create_item(THD *thd) Item_field *item= new Item_field(thd, &select->context, *ptr); if (item && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && - !thd->lex->in_sum_func && select->cur_pos_in_select_list != UNDEF_POS) + !thd->lex->in_sum_func && select->cur_pos_in_select_list != UNDEF_POS && + select->join) { select->join->non_agg_fields.push_back(item); item->marker= select->cur_pos_in_select_list; @@ -5266,6 +5347,12 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, item->maybe_null= TRUE; /* Save item in case we will need to fall back to materialization. */ view->used_items.push_front(item); + /* + If we create this reference on persistent memory then it should be + present in persistent list + */ + if (thd->mem_root == thd->stmt_arena->mem_root) + view->persistent_used_items.push_front(item); DBUG_RETURN(item); } @@ -5675,7 +5762,7 @@ void TABLE::mark_columns_used_by_index_no_reset(uint index, { KEY_PART_INFO *key_part= key_info[index].key_part; KEY_PART_INFO *key_part_end= (key_part + - key_info[index].key_parts); + key_info[index].user_defined_key_parts); for (;key_part != key_part_end; key_part++) { bitmap_set_bit(bitmap, key_part->fieldnr-1); @@ -5950,6 +6037,51 @@ void TABLE::mark_virtual_columns_for_write(bool insert_fl) /** + Check if a table has a default function either for INSERT or UPDATE-like + operation + @retval true there is a default function + @retval false there is no default function +*/ + +bool TABLE::has_default_function(bool is_update) +{ + Field **dfield_ptr, *dfield; + bool res= false; + for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++) + { + dfield= (*dfield_ptr); + if (is_update) + res= dfield->has_update_default_function(); + else + res= dfield->has_insert_default_function(); + if (res) + return res; + } + return res; +} + + +/** + Add all fields that have a default function to the table write set. +*/ + +void TABLE::mark_default_fields_for_write() +{ + Field **dfield_ptr, *dfield; + enum_sql_command cmd= in_use->lex->sql_command; + for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++) + { + dfield= (*dfield_ptr); + if (((sql_command_flags[cmd] & CF_INSERTS_DATA) && + dfield->has_insert_default_function()) || + ((sql_command_flags[cmd] & CF_UPDATES_DATA) && + dfield->has_update_default_function())) + bitmap_set_bit(write_set, dfield->field_index); + } +} + + +/** @brief Allocate space for keys @@ -5989,6 +6121,7 @@ bool TABLE::alloc_keys(uint key_count) void TABLE::create_key_part_by_field(KEY_PART_INFO *key_part_info, Field *field, uint fieldnr) { + DBUG_ASSERT(field->field_index + 1 == (int)fieldnr); key_part_info->null_bit= field->null_bit; key_part_info->null_offset= (uint) (field->null_ptr - (uchar*) record[0]); @@ -6131,12 +6264,13 @@ bool TABLE::add_tmp_key(uint key, uint key_parts, return TRUE; keyinfo= key_info + key; keyinfo->key_part= key_part_info; - keyinfo->usable_key_parts= keyinfo->key_parts = key_parts; - keyinfo->ext_key_parts= keyinfo->key_parts; + keyinfo->usable_key_parts= keyinfo->user_defined_key_parts = key_parts; + keyinfo->ext_key_parts= keyinfo->user_defined_key_parts; keyinfo->key_length=0; keyinfo->algorithm= HA_KEY_ALG_UNDEF; keyinfo->flags= HA_GENERATED_KEY; keyinfo->ext_key_flags= keyinfo->flags; + keyinfo->is_statistics_from_stat_tables= FALSE; if (unique) keyinfo->flags|= HA_NOSAME; sprintf(buf, "key%i", key); @@ -6147,6 +6281,8 @@ bool TABLE::add_tmp_key(uint key, uint key_parts, if (!keyinfo->rec_per_key) return TRUE; bzero(keyinfo->rec_per_key, sizeof(ulong)*key_parts); + keyinfo->read_stats= NULL; + keyinfo->collected_stats= NULL; for (i= 0; i < key_parts; i++) { @@ -6207,9 +6343,9 @@ bool TABLE::is_filled_at_execution() do not have a corresponding table reference. Such tables are filled during execution. */ - return test(!pos_in_table_list || - pos_in_table_list->jtbm_subselect || - pos_in_table_list->is_active_sjm()); + return MY_TEST(!pos_in_table_list || + pos_in_table_list->jtbm_subselect || + pos_in_table_list->is_active_sjm()); } @@ -6230,7 +6366,7 @@ bool TABLE::is_filled_at_execution() uint TABLE::actual_n_key_parts(KEY *keyinfo) { return optimizer_flag(in_use, OPTIMIZER_SWITCH_EXTENDED_KEYS) ? - keyinfo->ext_key_parts : keyinfo->key_parts; + keyinfo->ext_key_parts : keyinfo->user_defined_key_parts; } @@ -6547,7 +6683,7 @@ bool TABLE::update_const_key_parts(COND *conds) 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; + KEY_PART_INFO *keyinfo_end= keyinfo + key_info[index].user_defined_key_parts; for (key_part_map part_map= (key_part_map)1; keyinfo < keyinfo_end; @@ -6630,6 +6766,135 @@ int update_virtual_fields(THD *thd, TABLE *table, DBUG_RETURN(0); } + +/** + Update all DEFAULT and/or ON INSERT fields. + + @details + Compute and set the default value of all fields with a default function. + There are two kinds of default functions - one is used for INSERT-like + operations, the other for UPDATE-like operations. Depending on the field + definition and the current operation one or the other kind of update + function is evaluated. + + @retval + 0 Success + @retval + >0 Error occurred when storing a virtual field value +*/ + +int TABLE::update_default_fields() +{ + DBUG_ENTER("update_default_fields"); + Field **dfield_ptr, *dfield; + int res= 0; + enum_sql_command cmd= in_use->lex->sql_command; + + DBUG_ASSERT(default_field); + + /* Iterate over fields with default functions in the table */ + for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++) + { + dfield= (*dfield_ptr); + /* + If an explicit default value for a filed overrides the default, + do not update the field with its automatic default value. + */ + if (!(dfield->flags & HAS_EXPLICIT_VALUE)) + { + if (sql_command_flags[cmd] & CF_INSERTS_DATA) + res= dfield->evaluate_insert_default_function(); + if (sql_command_flags[cmd] & CF_UPDATES_DATA) + res= dfield->evaluate_update_default_function(); + if (res) + DBUG_RETURN(res); + } + } + DBUG_RETURN(res); +} + +void TABLE::reset_default_fields() +{ + if (default_field) + for (Field **df= default_field; *df; df++) + (*df)->flags&= ~HAS_EXPLICIT_VALUE; +} + +/* + Prepare triggers for INSERT-like statement. + + SYNOPSIS + prepare_triggers_for_insert_stmt_or_event() + + NOTE + Prepare triggers for INSERT-like statement by marking fields + used by triggers and inform handlers that batching of UPDATE/DELETE + cannot be done if there are BEFORE UPDATE/DELETE triggers. +*/ + +void TABLE::prepare_triggers_for_insert_stmt_or_event() +{ + if (triggers) + { + if (triggers->has_triggers(TRG_EVENT_DELETE, + TRG_ACTION_AFTER)) + { + /* + The table has AFTER DELETE triggers that might access to + subject table and therefore might need delete to be done + immediately. So we turn-off the batching. + */ + (void) file->extra(HA_EXTRA_DELETE_CANNOT_BATCH); + } + if (triggers->has_triggers(TRG_EVENT_UPDATE, + TRG_ACTION_AFTER)) + { + /* + The table has AFTER UPDATE triggers that might access to subject + table and therefore might need update to be done immediately. + So we turn-off the batching. + */ + (void) file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH); + } + } +} + + +bool TABLE::prepare_triggers_for_delete_stmt_or_event() +{ + if (triggers && + triggers->has_triggers(TRG_EVENT_DELETE, + TRG_ACTION_AFTER)) + { + /* + The table has AFTER DELETE triggers that might access to subject table + and therefore might need delete to be done immediately. So we turn-off + the batching. + */ + (void) file->extra(HA_EXTRA_DELETE_CANNOT_BATCH); + return TRUE; + } + return FALSE; +} + + +bool TABLE::prepare_triggers_for_update_stmt_or_event() +{ + if (triggers && + triggers->has_triggers(TRG_EVENT_UPDATE, + TRG_ACTION_AFTER)) + { + /* + The table has AFTER UPDATE triggers that might access to subject + table and therefore might need update to be done immediately. + So we turn-off the batching. + */ + (void) file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH); + return TRUE; + } + return FALSE; +} + /* @brief Reset const_table flag @@ -6670,15 +6935,17 @@ void TABLE_LIST::reset_const_table() bool TABLE_LIST::handle_derived(LEX *lex, uint phases) { - SELECT_LEX_UNIT *unit= get_unit(); - if (unit) + SELECT_LEX_UNIT *unit; + DBUG_ENTER("handle_derived"); + DBUG_PRINT("enter", ("phases: 0x%x", phases)); + if ((unit= get_unit())) { for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) if (sl->handle_derived(lex, phases)) - return TRUE; - return mysql_handle_single_derived(lex, this, phases); + DBUG_RETURN(TRUE); + DBUG_RETURN(mysql_handle_single_derived(lex, this, phases)); } - return FALSE; + DBUG_RETURN(FALSE); } @@ -6835,12 +7102,21 @@ int TABLE_LIST::fetch_number_of_rows() { int error= 0; if (jtbm_subselect) + { + if (jtbm_subselect->is_jtbm_merged) + { + table->file->stats.records= jtbm_subselect->jtbm_record_count; + set_if_bigger(table->file->stats.records, 2); + table->used_stat_records= table->file->stats.records; + } return 0; + } if (is_materialized_derived() && !fill_me) { table->file->stats.records= ((select_union*)derived->result)->records; set_if_bigger(table->file->stats.records, 2); + table->used_stat_records= table->file->stats.records; } else error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); @@ -6958,11 +7234,11 @@ uint TABLE_SHARE::actual_n_key_parts(THD *thd) } -/***************************************************************************** -** Instansiate templates -*****************************************************************************/ +double KEY::actual_rec_per_key(uint i) +{ + if (rec_per_key == 0) + return 0; + return (is_statistics_from_stat_tables ? + read_stats->get_avg_frequency(i) : (double) rec_per_key[i]); +} -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION -template class List<String>; -template class List_iterator<String>; -#endif |