summaryrefslogtreecommitdiff
path: root/sql/table.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/table.cc')
-rw-r--r--sql/table.cc1001
1 files changed, 744 insertions, 257 deletions
diff --git a/sql/table.cc b/sql/table.cc
index 4577778285f..3c46431078b 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -12,20 +12,37 @@
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-*/
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* Some general useful functions */
-#include "mysql_priv.h"
+#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_priv.h"
+#include "unireg.h" // REQUIRED: for other includes
+#include "table.h"
+#include "frm_crypt.h" // get_crypt_for_frm
+#include "key.h" // find_ref_key
+#include "sql_table.h" // build_table_filename,
+ // primary_key_name
#include "sql_trigger.h"
+#include "sql_parse.h" // free_items
+#include "strfunc.h" // unhex_type2
+#include "sql_partition.h" // mysql_unpack_partition,
+ // fix_partition_func, partition_info
+#include "sql_acl.h" // *_ACL, acl_getroot_no_password
+#include "sql_base.h" // release_table_share
#include <m_ctype.h>
#include "my_md5.h"
+#include "sql_select.h"
+#include "mdl.h" // MDL_wait_for_graph_visitor
/* INFORMATION_SCHEMA name */
LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")};
+/* PERFORMANCE_SCHEMA name */
+LEX_STRING PERFORMANCE_SCHEMA_DB_NAME= {C_STRING_WITH_LEN("performance_schema")};
+
/* MYSQL_SCHEMA name */
LEX_STRING MYSQL_SCHEMA_NAME= {C_STRING_WITH_LEN("mysql")};
@@ -216,36 +233,34 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name)
DBUG_ASSERT(db != NULL);
DBUG_ASSERT(name != NULL);
- if (is_schema_db(db->str, db->length))
- {
+ if (is_infoschema_db(db->str, db->length))
return TABLE_CATEGORY_INFORMATION;
- }
+
+ if ((db->length == PERFORMANCE_SCHEMA_DB_NAME.length) &&
+ (my_strcasecmp(system_charset_info,
+ PERFORMANCE_SCHEMA_DB_NAME.str,
+ db->str) == 0))
+ return TABLE_CATEGORY_PERFORMANCE;
if ((db->length == MYSQL_SCHEMA_NAME.length) &&
(my_strcasecmp(system_charset_info,
- MYSQL_SCHEMA_NAME.str,
- db->str) == 0))
+ MYSQL_SCHEMA_NAME.str,
+ db->str) == 0))
{
if (is_system_table_name(name->str, name->length))
- {
return TABLE_CATEGORY_SYSTEM;
- }
if ((name->length == GENERAL_LOG_NAME.length) &&
(my_strcasecmp(system_charset_info,
- GENERAL_LOG_NAME.str,
- name->str) == 0))
- {
- return TABLE_CATEGORY_PERFORMANCE;
- }
+ GENERAL_LOG_NAME.str,
+ name->str) == 0))
+ return TABLE_CATEGORY_LOG;
if ((name->length == SLOW_LOG_NAME.length) &&
(my_strcasecmp(system_charset_info,
- SLOW_LOG_NAME.str,
- name->str) == 0))
- {
- return TABLE_CATEGORY_PERFORMANCE;
- }
+ SLOW_LOG_NAME.str,
+ name->str) == 0))
+ return TABLE_CATEGORY_LOG;
}
return TABLE_CATEGORY_USER;
@@ -310,9 +325,13 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
share->table_map_id= ~0UL;
share->cached_row_logging_check= -1;
+ share->used_tables.empty();
+ share->free_tables.empty();
+ share->m_flush_tickets.empty();
+
memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root));
- pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
- pthread_cond_init(&share->cond, NULL);
+ mysql_mutex_init(key_TABLE_SHARE_LOCK_ha_data,
+ &share->LOCK_ha_data, MY_MUTEX_INIT_FAST);
}
DBUG_RETURN(share);
}
@@ -371,70 +390,106 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
*/
share->table_map_id= (ulong) thd->query_id;
+ share->used_tables.empty();
+ share->free_tables.empty();
+ share->m_flush_tickets.empty();
+
DBUG_VOID_RETURN;
}
+/**
+ Release resources (plugins) used by the share and free its memory.
+ TABLE_SHARE is self-contained -- it's stored in its own MEM_ROOT.
+ Free this MEM_ROOT.
+*/
+
+void TABLE_SHARE::destroy()
+{
+ uint idx;
+ KEY *info_it;
+
+ /* The mutex is initialized only for shares that are part of the TDC */
+ if (tmp_table == NO_TMP_TABLE)
+ mysql_mutex_destroy(&LOCK_ha_data);
+ my_hash_free(&name_hash);
+
+ plugin_unlock(NULL, db_plugin);
+ db_plugin= NULL;
+
+ /* Release fulltext parsers */
+ info_it= key_info;
+ for (idx= keys; idx; idx--, info_it++)
+ {
+ if (info_it->flags & HA_USES_PARSER)
+ {
+ plugin_unlock(NULL, info_it->parser);
+ info_it->flags= 0;
+ }
+ }
+ if (ha_data_destroy)
+ {
+ ha_data_destroy(ha_data);
+ ha_data_destroy= NULL;
+ }
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (ha_part_data_destroy)
+ {
+ ha_part_data_destroy(ha_part_data);
+ ha_part_data_destroy= NULL;
+ }
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
+ /*
+ Make a copy since the share is allocated in its own root,
+ and free_root() updates its argument after freeing the memory.
+ */
+ MEM_ROOT own_root= mem_root;
+ free_root(&own_root, MYF(0));
+}
+
/*
Free table share and memory used by it
SYNOPSIS
free_table_share()
share Table share
-
- NOTES
- share->mutex must be locked when we come here if it's not a temp table
*/
void free_table_share(TABLE_SHARE *share)
{
- MEM_ROOT mem_root;
- uint idx;
- KEY *key_info;
DBUG_ENTER("free_table_share");
DBUG_PRINT("enter", ("table: %s.%s", share->db.str, share->table_name.str));
DBUG_ASSERT(share->ref_count == 0);
- /*
- If someone is waiting for this to be deleted, inform it about this.
- Don't do a delete until we know that no one is refering to this anymore.
- */
- if (share->tmp_table == NO_TMP_TABLE)
+ if (share->m_flush_tickets.is_empty())
{
- /* share->mutex is locked in release_table_share() */
- while (share->waiting_on_cond)
- {
- pthread_cond_broadcast(&share->cond);
- pthread_cond_wait(&share->cond, &share->mutex);
- }
- /* No thread refers to this anymore */
- pthread_mutex_unlock(&share->mutex);
- pthread_mutex_destroy(&share->mutex);
- pthread_cond_destroy(&share->cond);
- }
- hash_free(&share->name_hash);
-
- plugin_unlock(NULL, share->db_plugin);
- share->db_plugin= NULL;
-
- /* Release fulltext parsers */
- key_info= share->key_info;
- for (idx= share->keys; idx; idx--, key_info++)
- {
- if (key_info->flags & HA_USES_PARSER)
- {
- plugin_unlock(NULL, key_info->parser);
- key_info->flags= 0;
- }
+ /*
+ No threads are waiting for this share to be flushed (the
+ share is not old, is for a temporary table, or just nobody
+ happens to be waiting for it). Destroy it.
+ */
+ share->destroy();
}
- if (share->ha_data_destroy)
+ else
{
- share->ha_data_destroy(share->ha_data);
- share->ha_data_destroy= NULL;
+ Wait_for_flush_list::Iterator it(share->m_flush_tickets);
+ Wait_for_flush *ticket;
+ /*
+ We're about to iterate over a list that is used
+ concurrently. Make sure this never happens without a lock.
+ */
+ mysql_mutex_assert_owner(&LOCK_open);
+
+ while ((ticket= it++))
+ (void) ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED);
+ /*
+ If there are threads waiting for this share to be flushed,
+ the last one to receive the notification will destroy the
+ share. At this point the share is removed from the table
+ definition cache, so is OK to proceed here without waiting
+ for this thread to do the work.
+ */
}
- /* We must copy mem_root from share because share is allocated through it */
- memcpy((char*) &mem_root, (char*) &share->mem_root, sizeof(mem_root));
- free_root(&mem_root, MYF(0)); // Free's share
DBUG_VOID_RETURN;
}
@@ -541,7 +596,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
int error, table_type;
bool error_given;
File file;
- uchar head[288];
+ uchar head[64];
char path[FN_REFLEN];
MEM_ROOT **root_ptr, *old_root;
DBUG_ENTER("open_table_def");
@@ -552,7 +607,8 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
error_given= 0;
strxmov(path, share->normalized_path.str, reg_ext, NullS);
- if ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0)
+ if ((file= mysql_file_open(key_file_frm,
+ path, O_RDONLY | O_SHARE, MYF(0))) < 0)
{
/*
We don't try to open 5.0 unencoded name, if
@@ -563,7 +619,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
- non-encoded db or table name contain "#mysql50#" prefix.
This kind of tables must have been opened only by the
- my_open() above.
+ mysql_file_open() above.
*/
if (has_disabled_path_chars(share->table_name.str) ||
has_disabled_path_chars(share->db.str) ||
@@ -590,7 +646,8 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
so no need to check the old file name.
*/
if (length == share->normalized_path.length ||
- ((file= my_open(path, O_RDONLY | O_SHARE, MYF(0))) < 0))
+ ((file= mysql_file_open(key_file_frm,
+ path, O_RDONLY | O_SHARE, MYF(0))) < 0))
goto err_not_open;
/* Unencoded 5.0 table name found */
@@ -600,7 +657,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
}
error= 4;
- if (my_read(file, head, 64, MYF(MY_NABP)))
+ if (mysql_file_read(file, head, 64, MYF(MY_NABP)))
goto err;
if (head[0] == (uchar) 254 && head[1] == 1)
@@ -653,7 +710,7 @@ int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
thd->status_var.opened_shares++;
err:
- my_close(file, MYF(MY_WME));
+ mysql_file_close(file, MYF(MY_WME));
err_not_open:
if (error && !error_given)
@@ -682,6 +739,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
uint i,j;
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, *rec_per_key, rec_buff_length;
@@ -693,6 +751,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
const char **interval_array;
enum legacy_db_type legacy_db_type;
my_bitmap_map *bitmaps;
+ uchar *extra_segment_buff= 0;
+ const uint format_section_header_size= 8;
+ uchar *format_section_fields= 0;
DBUG_ENTER("open_binary_frm");
new_field_pack_flag= head[27];
@@ -705,6 +766,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (!(pos= get_form_pos(file, head)))
goto err; /* purecov: inspected */
+ mysql_file_seek(file,pos,MY_SEEK_SET,MYF(0));
+ if (mysql_file_read(file, forminfo,288,MYF(MY_NABP)))
+ goto err;
share->frm_version= head[2];
/*
Check if .frm file created by MySQL 5.0. In this case we want to
@@ -740,7 +804,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{
share->avg_row_length= uint4korr(head+34);
share->row_type= (row_type) head[40];
- share->table_charset= get_charset((uint) head[38],MYF(0));
+ share->table_charset= get_charset((((uint) head[41]) << 8) +
+ (uint) head[38],MYF(0));
share->null_field_first= 1;
}
if (!share->table_charset)
@@ -767,7 +832,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
/* Read keyinformation */
key_info_length= (uint) uint2korr(head+28);
- VOID(my_seek(file,(ulong) uint2korr(head+6),MY_SEEK_SET,MYF(0)));
+ mysql_file_seek(file, (ulong) uint2korr(head+6), MY_SEEK_SET, MYF(0));
if (read_string(file,(uchar**) &disk_buff,key_info_length))
goto err; /* purecov: inspected */
if (disk_buff[0] & 0x80)
@@ -789,7 +854,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
goto err; /* purecov: inspected */
bzero((char*) keyinfo,n_length);
share->key_info= keyinfo;
- key_part= my_reinterpret_cast(KEY_PART_INFO*) (keyinfo+keys);
+ key_part= reinterpret_cast<KEY_PART_INFO*>(keyinfo+keys);
strpos=disk_buff+6;
if (!(rec_per_key= (ulong*) alloc_root(&share->mem_root,
@@ -849,6 +914,20 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
keynames=(char*) key_part;
strpos+= (strmov(keynames, (char *) strpos) - keynames)+1;
+ //reading index comments
+ for (keyinfo= share->key_info, i=0; i < keys; i++, keyinfo++)
+ {
+ if (keyinfo->flags & HA_USES_COMMENT)
+ {
+ keyinfo->comment.length= uint2korr(strpos);
+ keyinfo->comment.str= strmake_root(&share->mem_root, (char*) strpos+2,
+ keyinfo->comment.length);
+ strpos+= 2 + keyinfo->comment.length;
+ }
+ DBUG_ASSERT(test(keyinfo->flags & HA_USES_COMMENT) ==
+ (keyinfo->comment.length > 0));
+ }
+
share->reclength = uint2korr((head+16));
if (*(head+26) == 1)
share->system= 1; /* one-record-database */
@@ -867,27 +946,27 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if ((n_length= uint4korr(head+55)))
{
/* Read extra data segment */
- uchar *buff, *next_chunk, *buff_end;
+ uchar *next_chunk, *buff_end;
DBUG_PRINT("info", ("extra segment size is %u bytes", n_length));
- if (!(next_chunk= buff= (uchar*) my_malloc(n_length, MYF(MY_WME))))
+ if (!(extra_segment_buff= (uchar*) my_malloc(n_length, MYF(MY_WME))))
goto err;
- if (my_pread(file, buff, n_length, record_offset + share->reclength,
- MYF(MY_NABP)))
+ next_chunk= extra_segment_buff;
+ if (mysql_file_pread(file, extra_segment_buff,
+ n_length, record_offset + share->reclength,
+ MYF(MY_NABP)))
{
- my_free(buff, MYF(0));
goto err;
}
- share->connect_string.length= uint2korr(buff);
+ share->connect_string.length= uint2korr(next_chunk);
if (!(share->connect_string.str= strmake_root(&share->mem_root,
(char*) next_chunk + 2,
share->connect_string.
length)))
{
- my_free(buff, MYF(0));
goto err;
}
next_chunk+= share->connect_string.length + 2;
- buff_end= buff + n_length;
+ buff_end= extra_segment_buff + n_length;
if (next_chunk + 2 < buff_end)
{
uint str_db_type_length= uint2korr(next_chunk);
@@ -904,7 +983,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
plugin_data(tmp_plugin, handlerton *)))
{
/* bad file, legacy_db_type did not match the name */
- my_free(buff, MYF(0));
goto err;
}
/*
@@ -934,7 +1012,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
error= 8;
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
"--skip-partition");
- my_free(buff, MYF(0));
goto err;
}
plugin_unlock(NULL, share->db_plugin);
@@ -948,8 +1025,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{
/* purecov: begin inspected */
error= 8;
+ name.str[name.length]=0;
my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str);
- my_free(buff, MYF(0));
goto err;
/* purecov: end */
}
@@ -957,28 +1034,26 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
}
if (next_chunk + 5 < buff_end)
{
- uint32 partition_info_len = uint4korr(next_chunk);
+ uint32 partition_info_str_len = uint4korr(next_chunk);
#ifdef WITH_PARTITION_STORAGE_ENGINE
if ((share->partition_info_buffer_size=
- share->partition_info_len= partition_info_len))
+ share->partition_info_str_len= partition_info_str_len))
{
- if (!(share->partition_info= (char*)
+ if (!(share->partition_info_str= (char*)
memdup_root(&share->mem_root, next_chunk + 4,
- partition_info_len + 1)))
+ partition_info_str_len + 1)))
{
- my_free(buff, MYF(0));
goto err;
}
}
#else
- if (partition_info_len)
+ if (partition_info_str_len)
{
DBUG_PRINT("info", ("WITH_PARTITION_STORAGE_ENGINE is not defined"));
- my_free(buff, MYF(0));
goto err;
}
#endif
- next_chunk+= 5 + partition_info_len;
+ next_chunk+= 5 + partition_info_str_len;
}
#if MYSQL_VERSION_ID < 50200
if (share->mysql_version >= 50106 && share->mysql_version <= 50109)
@@ -992,8 +1067,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
*/
next_chunk+= 4;
}
- else if (share->mysql_version >= 50110)
+ else
#endif
+ if (share->mysql_version >= 50110)
{
/* New auto_partitioned indicator introduced in 5.1.11 */
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -1011,7 +1087,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{
DBUG_PRINT("error",
("fulltext key uses parser that is not defined in .frm"));
- my_free(buff, MYF(0));
goto err;
}
parser_name.str= (char*) next_chunk;
@@ -1022,12 +1097,79 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (! keyinfo->parser)
{
my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), parser_name.str);
- my_free(buff, MYF(0));
goto err;
}
}
}
- my_free(buff, MYF(0));
+ if (forminfo[46] == (uchar)255)
+ {
+ //reading long table comment
+ if (next_chunk + 2 > buff_end)
+ {
+ DBUG_PRINT("error",
+ ("long table comment is not defined in .frm"));
+ goto err;
+ }
+ share->comment.length = uint2korr(next_chunk);
+ if (! (share->comment.str= strmake_root(&share->mem_root,
+ (char*)next_chunk + 2, share->comment.length)))
+ {
+ goto err;
+ }
+ next_chunk+= 2 + share->comment.length;
+ }
+
+ if (next_chunk + format_section_header_size < buff_end)
+ {
+ /*
+ New extra data segment called "format section" with additional
+ table and column properties introduced by MySQL Cluster
+ based on 5.1.20
+
+ Table properties:
+ TABLESPACE <ts> and STORAGE [DISK|MEMORY]
+
+ Column properties:
+ COLUMN_FORMAT [DYNAMIC|FIXED] and STORAGE [DISK|MEMORY]
+ */
+ DBUG_PRINT("info", ("Found format section"));
+
+ /* header */
+ const uint format_section_length= uint2korr(next_chunk);
+ const uint format_section_flags= uint4korr(next_chunk+2);
+ /* 2 bytes unused */
+
+ if (next_chunk + format_section_length > buff_end)
+ {
+ DBUG_PRINT("error", ("format section length too long: %u",
+ format_section_length));
+ goto err;
+ }
+ DBUG_PRINT("info", ("format_section_length: %u, format_section_flags: %u",
+ format_section_length, format_section_flags));
+
+ share->default_storage_media=
+ (enum ha_storage_media) (format_section_flags & 0x7);
+
+ /* tablespace */
+ const char *tablespace=
+ (const char*)next_chunk + format_section_header_size;
+ const uint tablespace_length= strlen(tablespace);
+ if (tablespace_length &&
+ !(share->tablespace= strmake_root(&share->mem_root,
+ tablespace, tablespace_length+1)))
+ {
+ goto err;
+ }
+ DBUG_PRINT("info", ("tablespace: '%s'",
+ share->tablespace ? share->tablespace : "<null>"));
+
+ /* pointer to format section for fields */
+ format_section_fields=
+ next_chunk + format_section_header_size + tablespace_length + 1;
+
+ next_chunk+= format_section_length;
+ }
}
share->key_block_size= uint2korr(head+62);
@@ -1039,33 +1181,34 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
rec_buff_length)))
goto err; /* purecov: inspected */
share->default_values= record;
- if (my_pread(file, record, (size_t) share->reclength,
- record_offset, MYF(MY_NABP)))
+ if (mysql_file_pread(file, record, (size_t) share->reclength,
+ record_offset, MYF(MY_NABP)))
goto err; /* purecov: inspected */
- VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0)));
- if (my_read(file, head,288,MYF(MY_NABP)))
- goto err;
+ mysql_file_seek(file, pos+288, MY_SEEK_SET, MYF(0));
#ifdef HAVE_CRYPTED_FRM
if (crypted)
{
- crypted->decode((char*) head+256,288-256);
- if (sint2korr(head+284) != 0) // Should be 0
+ crypted->decode((char*) forminfo+256,288-256);
+ if (sint2korr(forminfo+284) != 0) // Should be 0
goto err; // Wrong password
}
#endif
- share->fields= uint2korr(head+258);
- pos= uint2korr(head+260); /* Length of all screens */
- n_length= uint2korr(head+268);
- interval_count= uint2korr(head+270);
- interval_parts= uint2korr(head+272);
- int_length= uint2korr(head+274);
- share->null_fields= uint2korr(head+282);
- com_length= uint2korr(head+284);
- share->comment.length= (int) (head[46]);
- share->comment.str= strmake_root(&share->mem_root, (char*) head+47,
- share->comment.length);
+ share->fields= uint2korr(forminfo+258);
+ pos= uint2korr(forminfo+260); /* Length of all screens */
+ n_length= uint2korr(forminfo+268);
+ interval_count= uint2korr(forminfo+270);
+ interval_parts= uint2korr(forminfo+272);
+ int_length= uint2korr(forminfo+274);
+ share->null_fields= uint2korr(forminfo+282);
+ com_length= uint2korr(forminfo+284);
+ if (forminfo[46] != (uchar)255)
+ {
+ share->comment.length= (int) (forminfo[46]);
+ share->comment.str= strmake_root(&share->mem_root, (char*) forminfo+47,
+ share->comment.length);
+ }
DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length));
@@ -1161,10 +1304,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
use_hash= share->fields >= MAX_FIELDS_BEFORE_HASH;
if (use_hash)
- use_hash= !hash_init(&share->name_hash,
- system_charset_info,
- share->fields,0,0,
- (hash_get_key) get_field_name,0,0);
+ use_hash= !my_hash_init(&share->name_hash,
+ system_charset_info,
+ share->fields,0,0,
+ (my_hash_get_key) get_field_name,0,0);
for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++)
{
@@ -1198,12 +1341,13 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
}
else
{
- if (!strpos[14])
+ uint csid= strpos[14] + (((uint) strpos[11]) << 8);
+ if (!csid)
charset= &my_charset_bin;
- else if (!(charset=get_charset((uint) strpos[14], MYF(0))))
+ else if (!(charset= get_charset(csid, MYF(0))))
{
error= 5; // Unknown or unavailable charset
- errarg= (int) strpos[14];
+ errarg= (int) csid;
goto err;
}
}
@@ -1276,7 +1420,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
"Please do \"ALTER TABLE '%s' FORCE\" to fix it!",
share->fieldnames.type_names[i], share->table_name.str,
share->table_name.str);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_CRASHED_ON_USAGE,
"Found incompatible DECIMAL field '%s' in %s; "
"Please do \"ALTER TABLE '%s' FORCE\" to fix it!",
@@ -1340,14 +1484,26 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
error= 8;
goto err;
}
+
+ if (format_section_fields)
+ {
+ const uchar field_flags= format_section_fields[i];
+ const uchar field_storage= (field_flags & STORAGE_TYPE_MASK);
+ const uchar field_column_format=
+ ((field_flags >> COLUMN_FORMAT_SHIFT)& COLUMN_FORMAT_MASK);
+ DBUG_PRINT("debug", ("field flags: %u, storage: %u, column_format: %u",
+ field_flags, field_storage, field_column_format));
+ (void)field_storage; /* Reserved by and used in MySQL Cluster */
+ (void)field_column_format; /* Reserved by and used in MySQL Cluster */
+ }
}
*field_ptr=0; // End marker
/* Fix key->name and key_part->field */
if (key_parts)
{
- uint primary_key=(uint) (find_type((char*) primary_key_name,
- &share->keynames, 3) - 1);
+ uint primary_key=(uint) (find_type(primary_key_name, &share->keynames,
+ FIND_TYPE_NO_PREFIX) - 1);
longlong ha_option= handler_file->ha_table_flags();
keyinfo= share->key_info;
key_part= keyinfo->key_part;
@@ -1418,12 +1574,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
keyinfo->extra_length+=HA_KEY_BLOB_LENGTH;
key_part->store_length+=HA_KEY_BLOB_LENGTH;
keyinfo->key_length+= HA_KEY_BLOB_LENGTH;
- /*
- Mark that there may be many matching values for one key
- combination ('a', 'a ', 'a '...)
- */
- if (!(field->flags & BINARY_FLAG))
- keyinfo->flags|= HA_END_SPACE_KEY;
}
if (field->type() == MYSQL_TYPE_BIT)
key_part->key_part_flag|= HA_BIT_PART;
@@ -1486,7 +1636,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
"Please do \"ALTER TABLE '%s' FORCE \" to fix it!",
share->table_name.str,
share->table_name.str);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_CRASHED_ON_USAGE,
"Found wrong key definition in %s; "
"Please do \"ALTER TABLE '%s' FORCE\" to fix "
@@ -1547,7 +1697,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
}
else
share->primary_key= MAX_KEY;
- x_free((uchar*) disk_buff);
+ my_free(disk_buff);
disk_buff=0;
if (new_field_pack_flag <= 1)
{
@@ -1611,23 +1761,32 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
delete handler_file;
#ifndef DBUG_OFF
if (use_hash)
- (void) hash_check(&share->name_hash);
+ (void) my_hash_check(&share->name_hash);
#endif
+ my_free(extra_segment_buff);
DBUG_RETURN (0);
err:
share->error= error;
share->open_errno= my_errno;
share->errarg= errarg;
- x_free((uchar*) disk_buff);
+ my_free(disk_buff);
+ my_free(extra_segment_buff);
delete crypted;
delete handler_file;
- hash_free(&share->name_hash);
+ my_hash_free(&share->name_hash);
if (share->ha_data_destroy)
{
share->ha_data_destroy(share->ha_data);
share->ha_data_destroy= NULL;
}
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (share->ha_part_data_destroy)
+ {
+ share->ha_part_data_destroy(share->ha_part_data);
+ share->ha_data_destroy= NULL;
+ }
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
open_table_error(share, error, share->open_errno, errarg);
DBUG_RETURN(error);
@@ -1672,9 +1831,6 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str,
share->table_name.str, (long) outparam));
- /* Parsing of partitioning information from .frm needs thd->lex set up. */
- DBUG_ASSERT(thd->lex->is_lex_started);
-
error= 1;
bzero((char*) outparam, sizeof(*outparam));
outparam->in_use= thd;
@@ -1785,8 +1941,8 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
if (!(key_info= (KEY*) alloc_root(&outparam->mem_root, n_length)))
goto err;
outparam->key_info= key_info;
- key_part= (my_reinterpret_cast(KEY_PART_INFO*) (key_info+share->keys));
-
+ key_part= (reinterpret_cast<KEY_PART_INFO*>(key_info+share->keys));
+
memcpy(key_info, share->key_info, sizeof(*key_info)*share->keys);
memcpy(key_part, share->key_info[0].key_part, (sizeof(*key_part) *
share->key_parts));
@@ -1822,7 +1978,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (share->partition_info_len && outparam->file)
+ if (share->partition_info_str_len && outparam->file)
{
/*
In this execution we must avoid calling thd->change_item_tree since
@@ -1837,16 +1993,15 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
Query_arena *backup_stmt_arena_ptr= thd->stmt_arena;
Query_arena backup_arena;
- Query_arena part_func_arena(&outparam->mem_root, Query_arena::INITIALIZED);
+ Query_arena part_func_arena(&outparam->mem_root,
+ Query_arena::STMT_INITIALIZED);
thd->set_n_backup_active_arena(&part_func_arena, &backup_arena);
thd->stmt_arena= &part_func_arena;
bool tmp;
bool work_part_info_used;
- tmp= mysql_unpack_partition(thd, share->partition_info,
- share->partition_info_len,
- share->part_state,
- share->part_state_len,
+ tmp= mysql_unpack_partition(thd, share->partition_info_str,
+ share->partition_info_str_len,
outparam, is_create_table,
share->default_part_db_type,
&work_part_info_used);
@@ -1975,7 +2130,7 @@ partititon_err:
outparam->file= 0; // For easier error checking
outparam->db_stat=0;
free_root(&outparam->mem_root, MYF(0)); // Safe to call on bzero'd root
- my_free((char*) outparam->alias, MYF(MY_ALLOW_ZERO_PTR));
+ my_free((void *) outparam->alias);
DBUG_RETURN (error);
}
@@ -1997,7 +2152,7 @@ int closefrm(register TABLE *table, bool free_share)
if (table->db_stat)
error=table->file->close();
- my_free((char*) table->alias, MYF(MY_ALLOW_ZERO_PTR));
+ my_free((void *) table->alias);
table->alias= 0;
if (table->field)
{
@@ -2018,7 +2173,7 @@ int closefrm(register TABLE *table, bool free_share)
if (free_share)
{
if (table->s->tmp_table == NO_TMP_TABLE)
- release_table_share(table->s, RELEASE_NORMAL);
+ release_table_share(table->s);
else
free_table_share(table->s);
}
@@ -2035,7 +2190,15 @@ void free_blobs(register TABLE *table)
for (ptr= table->s->blob_field, end=ptr + table->s->blob_fields ;
ptr != end ;
ptr++)
- ((Field_blob*) table->field[*ptr])->free();
+ {
+ /*
+ Reduced TABLE objects which are used by row-based replication for
+ type conversion might have some fields missing. Skip freeing BLOB
+ buffers for such missing fields.
+ */
+ if (table->field[*ptr])
+ ((Field_blob*) table->field[*ptr])->free();
+ }
}
@@ -2085,21 +2248,21 @@ static ulong get_form_pos(File file, uchar *head)
length= uint2korr(head+4);
- my_seek(file, 64L, MY_SEEK_SET, MYF(0));
+ mysql_file_seek(file, 64L, MY_SEEK_SET, MYF(0));
if (!(buf= (uchar*) my_malloc(length+names*4, MYF(MY_WME))))
DBUG_RETURN(0);
- if (my_read(file, buf, length+names*4, MYF(MY_NABP)))
+ if (mysql_file_read(file, buf, length+names*4, MYF(MY_NABP)))
{
- x_free(buf);
+ my_free(buf);
DBUG_RETURN(0);
}
pos= buf+length;
ret_value= uint4korr(pos);
- my_free(buf, MYF(0));
+ my_free(buf);
DBUG_RETURN(ret_value);
}
@@ -2116,11 +2279,11 @@ int read_string(File file, uchar**to, size_t length)
{
DBUG_ENTER("read_string");
- x_free(*to);
+ my_free(*to);
if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) ||
- my_read(file, *to, length,MYF(MY_NABP)))
+ mysql_file_read(file, *to, length, MYF(MY_NABP)))
{
- x_free(*to); /* purecov: inspected */
+ my_free(*to); /* purecov: inspected */
*to= 0; /* purecov: inspected */
DBUG_RETURN(1); /* purecov: inspected */
}
@@ -2150,23 +2313,24 @@ ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
{ /* Expand file */
newpos+=IO_SIZE;
int4store(fileinfo+10,newpos);
- endpos=(ulong) my_seek(file,0L,MY_SEEK_END,MYF(0));/* Copy from file-end */
+ /* Copy from file-end */
+ endpos= (ulong) mysql_file_seek(file, 0L, MY_SEEK_END, MYF(0));
bufflength= (uint) (endpos & (IO_SIZE-1)); /* IO_SIZE is a power of 2 */
while (endpos > maxlength)
{
- VOID(my_seek(file,(ulong) (endpos-bufflength),MY_SEEK_SET,MYF(0)));
- if (my_read(file, buff, bufflength, MYF(MY_NABP+MY_WME)))
+ mysql_file_seek(file, (ulong) (endpos-bufflength), MY_SEEK_SET, MYF(0));
+ if (mysql_file_read(file, buff, bufflength, MYF(MY_NABP+MY_WME)))
DBUG_RETURN(0L);
- VOID(my_seek(file,(ulong) (endpos-bufflength+IO_SIZE),MY_SEEK_SET,
- MYF(0)));
- if ((my_write(file, buff,bufflength,MYF(MY_NABP+MY_WME))))
+ mysql_file_seek(file, (ulong) (endpos-bufflength+IO_SIZE), MY_SEEK_SET,
+ MYF(0));
+ if ((mysql_file_write(file, buff, bufflength, MYF(MY_NABP+MY_WME))))
DBUG_RETURN(0);
endpos-=bufflength; bufflength=IO_SIZE;
}
bzero(buff,IO_SIZE); /* Null new block */
- VOID(my_seek(file,(ulong) maxlength,MY_SEEK_SET,MYF(0)));
- if (my_write(file,buff,bufflength,MYF(MY_NABP+MY_WME)))
+ mysql_file_seek(file, (ulong) maxlength, MY_SEEK_SET, MYF(0));
+ if (mysql_file_write(file, buff, bufflength, MYF(MY_NABP+MY_WME)))
DBUG_RETURN(0L);
maxlength+=IO_SIZE; /* Fix old ref */
int2store(fileinfo+6,maxlength);
@@ -2181,20 +2345,21 @@ ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
if (n_length == 1 )
{ /* First name */
length++;
- VOID(strxmov((char*) buff,"/",newname,"/",NullS));
+ (void) strxmov((char*) buff,"/",newname,"/",NullS);
}
else
- VOID(strxmov((char*) buff,newname,"/",NullS)); /* purecov: inspected */
- VOID(my_seek(file,63L+(ulong) n_length,MY_SEEK_SET,MYF(0)));
- if (my_write(file, buff, (size_t) length+1,MYF(MY_NABP+MY_WME)) ||
- (names && my_write(file,(uchar*) (*formnames->type_names+n_length-1),
- names*4, MYF(MY_NABP+MY_WME))) ||
- my_write(file, fileinfo+10, 4,MYF(MY_NABP+MY_WME)))
+ (void) strxmov((char*) buff,newname,"/",NullS); /* purecov: inspected */
+ mysql_file_seek(file, 63L+(ulong) n_length, MY_SEEK_SET, MYF(0));
+ if (mysql_file_write(file, buff, (size_t) length+1, MYF(MY_NABP+MY_WME)) ||
+ (names && mysql_file_write(file,
+ (uchar*) (*formnames->type_names+n_length-1),
+ names*4, MYF(MY_NABP+MY_WME))) ||
+ mysql_file_write(file, fileinfo+10, 4, MYF(MY_NABP+MY_WME)))
DBUG_RETURN(0L); /* purecov: inspected */
int2store(fileinfo+8,names+1);
int2store(fileinfo+4,n_length+length);
- VOID(my_chsize(file, newpos, 0, MYF(MY_WME)));/* Append file with '\0' */
+ (void) mysql_file_chsize(file, newpos, 0, MYF(MY_WME));/* Append file with '\0' */
DBUG_RETURN(newpos);
} /* make_new_entry */
@@ -2460,12 +2625,14 @@ void append_unescaped(String *res, const char *pos, uint length)
File create_frm(THD *thd, const char *name, const char *db,
const char *table, uint reclength, uchar *fileinfo,
- HA_CREATE_INFO *create_info, uint keys)
+ HA_CREATE_INFO *create_info, uint keys, KEY *key_info)
{
register File file;
ulong length;
uchar fill[IO_SIZE];
int create_flags= O_RDWR | O_TRUNC;
+ ulong key_comment_total_bytes= 0;
+ uint i;
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
create_flags|= O_EXCL | O_NOFOLLOW;
@@ -2476,10 +2643,10 @@ File create_frm(THD *thd, const char *name, const char *db,
if (create_info->min_rows > UINT_MAX32)
create_info->min_rows= UINT_MAX32;
- if ((file= my_create(name, CREATE_MODE, create_flags, MYF(0))) >= 0)
+ if ((file= mysql_file_create(key_file_frm,
+ name, CREATE_MODE, create_flags, MYF(0))) >= 0)
{
- uint key_length, tmp_key_length;
- uint tmp;
+ uint key_length, tmp_key_length, tmp, csid;
bzero((char*) fileinfo,64);
/* header */
fileinfo[0]=(uchar) 254;
@@ -2502,7 +2669,17 @@ File create_frm(THD *thd, const char *name, const char *db,
1 byte for the NAMES_SEP_CHAR (after the last name)
9 extra bytes (padding for safety? alignment?)
*/
- key_length= keys * (8 + MAX_REF_PARTS * 9 + NAME_LEN + 1) + 16;
+ for (i= 0; i < keys; i++)
+ {
+ DBUG_ASSERT(test(key_info[i].flags & HA_USES_COMMENT) ==
+ (key_info[i].comment.length > 0));
+ if (key_info[i].flags & HA_USES_COMMENT)
+ key_comment_total_bytes += 2 + key_info[i].comment.length;
+ }
+
+ key_length= keys * (8 + MAX_REF_PARTS * 9 + NAME_LEN + 1) + 16
+ + key_comment_total_bytes;
+
length= next_io_size((ulong) (IO_SIZE+key_length+reclength+
create_info->extra_size));
int4store(fileinfo+10,length);
@@ -2519,8 +2696,9 @@ File create_frm(THD *thd, const char *name, const char *db,
fileinfo[32]=0; // No filename anymore
fileinfo[33]=5; // Mark for 5.0 frm file
int4store(fileinfo+34,create_info->avg_row_length);
- fileinfo[38]= (create_info->default_table_charset ?
- create_info->default_table_charset->number : 0);
+ csid= (create_info->default_table_charset ?
+ create_info->default_table_charset->number : 0);
+ fileinfo[38]= (uchar) csid;
/*
In future versions, we will store in fileinfo[39] the values of the
TRANSACTIONAL and PAGE_CHECKSUM clauses of CREATE TABLE.
@@ -2528,7 +2706,7 @@ File create_frm(THD *thd, const char *name, const char *db,
fileinfo[39]= 0;
fileinfo[40]= (uchar) create_info->row_type;
/* Next few bytes where for RAID support */
- fileinfo[41]= 0;
+ fileinfo[41]= (uchar) (csid >> 8);
fileinfo[42]= 0;
fileinfo[43]= 0;
fileinfo[44]= 0;
@@ -2546,10 +2724,10 @@ File create_frm(THD *thd, const char *name, const char *db,
bzero(fill,IO_SIZE);
for (; length > IO_SIZE ; length-= IO_SIZE)
{
- if (my_write(file,fill, IO_SIZE, MYF(MY_WME | MY_NABP)))
+ if (mysql_file_write(file, fill, IO_SIZE, MYF(MY_WME | MY_NABP)))
{
- VOID(my_close(file,MYF(0)));
- VOID(my_delete(name,MYF(0)));
+ (void) mysql_file_close(file, MYF(0));
+ (void) mysql_file_delete(key_file_frm, name, MYF(0));
return(-1);
}
}
@@ -2578,6 +2756,8 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
create_info->default_table_charset= share->table_charset;
create_info->table_charset= 0;
create_info->comment= share->comment;
+ create_info->storage_media= share->default_storage_media;
+ create_info->tablespace= share->tablespace;
DBUG_VOID_RETURN;
}
@@ -2586,9 +2766,9 @@ int
rename_file_ext(const char * from,const char * to,const char * ext)
{
char from_b[FN_REFLEN],to_b[FN_REFLEN];
- VOID(strxmov(from_b,from,ext,NullS));
- VOID(strxmov(to_b,to,ext,NullS));
- return (my_rename(from_b,to_b,MYF(MY_WME)));
+ (void) strxmov(from_b,from,ext,NullS);
+ (void) strxmov(to_b,to,ext,NullS);
+ return (mysql_file_rename(key_file_frm, from_b, to_b, MYF(MY_WME)));
}
@@ -2721,9 +2901,10 @@ bool check_db_name(LEX_STRING *org_name)
returns 1 on error
*/
-bool check_table_name(const char *name, uint length, bool check_for_path_chars)
+bool check_table_name(const char *name, size_t length, bool check_for_path_chars)
{
- uint name_length= 0; // name length in symbols
+ // name length in symbols
+ size_t name_length= 0;
const char *end= name+length;
if (!length || length > NAME_LEN)
return 1;
@@ -2756,18 +2937,19 @@ bool check_table_name(const char *name, uint length, bool check_for_path_chars)
name_length++;
}
#if defined(USE_MB) && defined(USE_MB_IDENT)
- return (last_char_is_space || name_length > NAME_CHAR_LEN) ;
+ return last_char_is_space || (name_length > NAME_CHAR_LEN);
#else
- return 0;
+ return FALSE;
#endif
}
bool check_column_name(const char *name)
{
- uint name_length= 0; // name length in symbols
+ // name length in symbols
+ size_t name_length= 0;
bool last_char_is_space= TRUE;
-
+
while (*name)
{
#if defined(USE_MB) && defined(USE_MB_IDENT)
@@ -2792,7 +2974,7 @@ bool check_column_name(const char *name)
name_length++;
}
/* Error if empty or too long column name */
- return last_char_is_space || (uint) name_length > NAME_CHAR_LEN;
+ return last_char_is_space || (name_length > NAME_CHAR_LEN);
}
@@ -2945,11 +3127,256 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
}
+/**
+ Traverse portion of wait-for graph which is reachable through edge
+ represented by this flush ticket in search for deadlocks.
+
+ @retval TRUE A deadlock is found. A victim is remembered
+ by the visitor.
+ @retval FALSE Success, no deadlocks.
+*/
+
+bool Wait_for_flush::accept_visitor(MDL_wait_for_graph_visitor *gvisitor)
+{
+ return m_share->visit_subgraph(this, gvisitor);
+}
+
+
+uint Wait_for_flush::get_deadlock_weight() const
+{
+ return m_deadlock_weight;
+}
+
+
+/**
+ Traverse portion of wait-for graph which is reachable through this
+ table share in search for deadlocks.
+
+ @param waiting_ticket Ticket representing wait for this share.
+ @param dvisitor Deadlock detection visitor.
+
+ @retval TRUE A deadlock is found. A victim is remembered
+ by the visitor.
+ @retval FALSE No deadlocks, it's OK to begin wait.
+*/
+
+bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
+ MDL_wait_for_graph_visitor *gvisitor)
+{
+ TABLE *table;
+ MDL_context *src_ctx= wait_for_flush->get_ctx();
+ bool result= TRUE;
+
+ /*
+ To protect used_tables list from being concurrently modified
+ while we are iterating through it we acquire LOCK_open.
+ This does not introduce deadlocks in the deadlock detector
+ because we won't try to acquire LOCK_open while
+ holding a write-lock on MDL_lock::m_rwlock.
+ */
+ if (gvisitor->m_lock_open_count++ == 0)
+ mysql_mutex_lock(&LOCK_open);
+
+ I_P_List_iterator <TABLE, TABLE_share> tables_it(used_tables);
+
+ /*
+ In case of multiple searches running in parallel, avoid going
+ over the same loop twice and shortcut the search.
+ Do it after taking the lock to weed out unnecessary races.
+ */
+ if (src_ctx->m_wait.get_status() != MDL_wait::EMPTY)
+ {
+ result= FALSE;
+ goto end;
+ }
+
+ if (gvisitor->enter_node(src_ctx))
+ goto end;
+
+ while ((table= tables_it++))
+ {
+ if (gvisitor->inspect_edge(&table->in_use->mdl_context))
+ {
+ goto end_leave_node;
+ }
+ }
+
+ tables_it.rewind();
+ while ((table= tables_it++))
+ {
+ if (table->in_use->mdl_context.visit_subgraph(gvisitor))
+ {
+ goto end_leave_node;
+ }
+ }
+
+ result= FALSE;
+
+end_leave_node:
+ gvisitor->leave_node(src_ctx);
+
+end:
+ if (gvisitor->m_lock_open_count-- == 1)
+ mysql_mutex_unlock(&LOCK_open);
+
+ return result;
+}
+
+
+/**
+ Wait until the subject share is removed from the table
+ definition cache and make sure it's destroyed.
+
+ @param mdl_context MDL context for thread which is going to wait.
+ @param abstime Timeout for waiting as absolute time value.
+ @param deadlock_weight Weight of this wait for deadlock detector.
+
+ @pre LOCK_open is write locked, the share is used (has
+ non-zero reference count), is marked for flush and
+ this connection does not reference the share.
+ LOCK_open will be unlocked temporarily during execution.
+
+ @retval FALSE - Success.
+ @retval TRUE - Error (OOM, deadlock, timeout, etc...).
+*/
+
+bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime,
+ uint deadlock_weight)
+{
+ MDL_context *mdl_context= &thd->mdl_context;
+ Wait_for_flush ticket(mdl_context, this, deadlock_weight);
+ MDL_wait::enum_wait_status wait_status;
+
+ mysql_mutex_assert_owner(&LOCK_open);
+ /*
+ We should enter this method only when share's version is not
+ up to date and the share is referenced. Otherwise our
+ thread will never be woken up from wait.
+ */
+ DBUG_ASSERT(version != refresh_version && ref_count != 0);
+
+ m_flush_tickets.push_front(&ticket);
+
+ mdl_context->m_wait.reset_status();
+
+ mysql_mutex_unlock(&LOCK_open);
+
+ mdl_context->will_wait_for(&ticket);
+
+ mdl_context->find_deadlock();
+
+ wait_status= mdl_context->m_wait.timed_wait(thd, abstime, TRUE,
+ "Waiting for table flush");
+
+ mdl_context->done_waiting_for();
+
+ mysql_mutex_lock(&LOCK_open);
+
+ m_flush_tickets.remove(&ticket);
+
+ if (m_flush_tickets.is_empty() && ref_count == 0)
+ {
+ /*
+ If our thread was the last one using the share,
+ we must destroy it here.
+ */
+ destroy();
+ }
+
+ /*
+ In cases when our wait was aborted by KILL statement,
+ a deadlock or a timeout, the share might still be referenced,
+ so we don't delete it. Note, that we can't determine this
+ condition by checking wait_status alone, since, for example,
+ a timeout can happen after all references to the table share
+ were released, but before the share is removed from the
+ cache and we receive the notification. This is why
+ we first destroy the share, and then look at
+ wait_status.
+ */
+ switch (wait_status)
+ {
+ case MDL_wait::GRANTED:
+ return FALSE;
+ case MDL_wait::VICTIM:
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ return TRUE;
+ case MDL_wait::TIMEOUT:
+ my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
+ return TRUE;
+ case MDL_wait::KILLED:
+ return TRUE;
+ default:
+ DBUG_ASSERT(0);
+ return TRUE;
+ }
+}
+
+
+/**
+ Initialize TABLE instance (newly created, or coming either from table
+ cache or THD::temporary_tables list) and prepare it for further use
+ during statement execution. Set the 'alias' attribute from the specified
+ TABLE_LIST element. Remember the TABLE_LIST element in the
+ TABLE::pos_in_table_list member.
+
+ @param thd Thread context.
+ @param tl TABLE_LIST element.
+*/
+
+void TABLE::init(THD *thd, TABLE_LIST *tl)
+{
+ DBUG_ASSERT(s->ref_count > 0 || s->tmp_table != NO_TMP_TABLE);
+
+ if (thd->lex->need_correct_ident())
+ alias_name_used= my_strcasecmp(table_alias_charset,
+ s->table_name.str,
+ tl->alias);
+ /* Fix alias if table name changes. */
+ if (strcmp(alias, tl->alias))
+ {
+ uint length= (uint) strlen(tl->alias)+1;
+ alias= (char*) my_realloc((char*) alias, length, MYF(MY_WME));
+ memcpy((char*) alias, tl->alias, length);
+ }
+
+ tablenr= thd->current_tablenr++;
+ used_fields= 0;
+ const_table= 0;
+ null_row= 0;
+ maybe_null= 0;
+ force_index= 0;
+ force_index_order= 0;
+ force_index_group= 0;
+ status= STATUS_NO_RECORD;
+ insert_values= 0;
+ fulltext_searched= 0;
+ file->ft_handler= 0;
+ reginfo.impossible_range= 0;
+
+ /* Catch wrong handling of the auto_increment_field_not_null. */
+ DBUG_ASSERT(!auto_increment_field_not_null);
+ auto_increment_field_not_null= FALSE;
+
+ if (timestamp_field)
+ timestamp_field_type= timestamp_field->get_auto_set_type();
+
+ pos_in_table_list= tl;
+
+ clear_column_bitmaps();
+
+ DBUG_ASSERT(key_read == 0);
+
+ /* Tables may be reused in a sub statement. */
+ DBUG_ASSERT(!file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
+}
+
+
/*
Create Item_field for each column in the table.
SYNPOSIS
- st_table::fill_item_list()
+ TABLE::fill_item_list()
item_list a pointer to an empty list used to store items
DESCRIPTION
@@ -2962,7 +3389,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
1 out of memory
*/
-bool st_table::fill_item_list(List<Item> *item_list) const
+bool TABLE::fill_item_list(List<Item> *item_list) const
{
/*
All Item_field's created using a direct pointer to a field
@@ -2982,7 +3409,7 @@ bool st_table::fill_item_list(List<Item> *item_list) const
Fields of this table.
SYNPOSIS
- st_table::fill_item_list()
+ TABLE::fill_item_list()
item_list a non-empty list with Item_fields
DESCRIPTION
@@ -2992,7 +3419,7 @@ bool st_table::fill_item_list(List<Item> *item_list) const
is the same as the number of columns in the table.
*/
-void st_table::reset_item_list(List<Item> *item_list) const
+void TABLE::reset_item_list(List<Item> *item_list) const
{
List_iterator_fast<Item> it(*item_list);
for (Field **ptr= field; *ptr; ptr++)
@@ -3376,20 +3803,20 @@ void TABLE_LIST::hide_view_error(THD *thd)
/* Hide "Unknown column" or "Unknown function" error */
DBUG_ASSERT(thd->is_error());
- if (thd->main_da.sql_errno() == ER_BAD_FIELD_ERROR ||
- thd->main_da.sql_errno() == ER_SP_DOES_NOT_EXIST ||
- thd->main_da.sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION ||
- thd->main_da.sql_errno() == ER_PROCACCESS_DENIED_ERROR ||
- thd->main_da.sql_errno() == ER_COLUMNACCESS_DENIED_ERROR ||
- thd->main_da.sql_errno() == ER_TABLEACCESS_DENIED_ERROR ||
- thd->main_da.sql_errno() == ER_TABLE_NOT_LOCKED ||
- thd->main_da.sql_errno() == ER_NO_SUCH_TABLE)
+ if (thd->stmt_da->sql_errno() == ER_BAD_FIELD_ERROR ||
+ thd->stmt_da->sql_errno() == ER_SP_DOES_NOT_EXIST ||
+ thd->stmt_da->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION ||
+ thd->stmt_da->sql_errno() == ER_PROCACCESS_DENIED_ERROR ||
+ thd->stmt_da->sql_errno() == ER_COLUMNACCESS_DENIED_ERROR ||
+ thd->stmt_da->sql_errno() == ER_TABLEACCESS_DENIED_ERROR ||
+ thd->stmt_da->sql_errno() == ER_TABLE_NOT_LOCKED ||
+ thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE)
{
TABLE_LIST *top= top_table();
thd->clear_error();
my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str);
}
- else if (thd->main_da.sql_errno() == ER_NO_DEFAULT_FOR_FIELD)
+ else if (thd->stmt_da->sql_errno() == ER_NO_DEFAULT_FOR_FIELD)
{
TABLE_LIST *top= top_table();
thd->clear_error();
@@ -3467,7 +3894,7 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure)
TABLE_LIST *main_view= top_table();
if (ignore_failure)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_VIEW_CHECK_FAILED, ER(ER_VIEW_CHECK_FAILED),
main_view->view_db.str, main_view->view_name.str);
return(VIEW_CHECK_SKIP);
@@ -3736,11 +4163,8 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd)
{
DBUG_PRINT("info", ("This table is suid view => load contest"));
DBUG_ASSERT(view && view_sctx);
- if (acl_getroot_no_password(view_sctx,
- definer.user.str,
- definer.host.str,
- definer.host.str,
- thd->db))
+ if (acl_getroot(view_sctx, definer.user.str, definer.host.str,
+ definer.host.str, thd->db))
{
if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) ||
(thd->lex->sql_command == SQLCOM_SHOW_FIELDS))
@@ -3759,10 +4183,15 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd)
}
else
{
- my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
- thd->security_ctx->priv_user,
- thd->security_ctx->priv_host,
- (thd->password ? ER(ER_YES) : ER(ER_NO)));
+ if (thd->password == 2)
+ my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+ thd->security_ctx->priv_user,
+ thd->security_ctx->priv_host);
+ else
+ my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+ thd->security_ctx->priv_user,
+ thd->security_ctx->priv_host,
+ (thd->password ? ER(ER_YES) : ER(ER_NO)));
}
DBUG_RETURN(TRUE);
}
@@ -3934,14 +4363,15 @@ const char *Natural_join_column::db_name()
return table_ref->view_db.str;
/*
- Test that TABLE_LIST::db is the same as st_table_share::db to
+ Test that TABLE_LIST::db is the same as TABLE_SHARE::db to
ensure consistency. An exception are I_S schema tables, which
are inconsistent in this respect.
*/
DBUG_ASSERT(!strcmp(table_ref->db,
table_ref->table->s->db.str) ||
(table_ref->schema_table &&
- table_ref->table->s->db.str[0] == 0));
+ is_infoschema_db(table_ref->table->s->db.str,
+ table_ref->table->s->db.length)));
return table_ref->db;
}
@@ -4152,13 +4582,14 @@ const char *Field_iterator_table_ref::get_db_name()
return natural_join_it.column_ref()->db_name();
/*
- Test that TABLE_LIST::db is the same as st_table_share::db to
+ Test that TABLE_LIST::db is the same as TABLE_SHARE::db to
ensure consistency. An exception are I_S schema tables, which
are inconsistent in this respect.
*/
DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db.str) ||
(table_ref->schema_table &&
- table_ref->table->s->db.str[0] == 0));
+ is_infoschema_db(table_ref->table->s->db.str,
+ table_ref->table->s->db.length)));
return table_ref->db;
}
@@ -4328,7 +4759,7 @@ Field_iterator_table_ref::get_natural_column_ref()
/* Reset all columns bitmaps */
-void st_table::clear_column_bitmaps()
+void TABLE::clear_column_bitmaps()
{
/*
Reset column read/write usage. It's identical to:
@@ -4349,9 +4780,9 @@ void st_table::clear_column_bitmaps()
key fields.
*/
-void st_table::prepare_for_position()
+void TABLE::prepare_for_position()
{
- DBUG_ENTER("st_table::prepare_for_position");
+ DBUG_ENTER("TABLE::prepare_for_position");
if ((file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) &&
s->primary_key < MAX_KEY)
@@ -4370,14 +4801,14 @@ void st_table::prepare_for_position()
NOTE:
This changes the bitmap to use the tmp bitmap
After this, you can't access any other columns in the table until
- bitmaps are reset, for example with st_table::clear_column_bitmaps()
- or st_table::restore_column_maps_after_mark_index()
+ bitmaps are reset, for example with TABLE::clear_column_bitmaps()
+ or TABLE::restore_column_maps_after_mark_index()
*/
-void st_table::mark_columns_used_by_index(uint index)
+void TABLE::mark_columns_used_by_index(uint index)
{
MY_BITMAP *bitmap= &tmp_set;
- DBUG_ENTER("st_table::mark_columns_used_by_index");
+ DBUG_ENTER("TABLE::mark_columns_used_by_index");
set_keyread(TRUE);
bitmap_clear_all(bitmap);
@@ -4395,10 +4826,10 @@ void st_table::mark_columns_used_by_index(uint index)
restore_column_maps_after_mark_index().
*/
-void st_table::add_read_columns_used_by_index(uint index)
+void TABLE::add_read_columns_used_by_index(uint index)
{
MY_BITMAP *bitmap= &tmp_set;
- DBUG_ENTER("st_table::add_read_columns_used_by_index");
+ DBUG_ENTER("TABLE::add_read_columns_used_by_index");
set_keyread(TRUE);
bitmap_copy(bitmap, read_set);
@@ -4419,9 +4850,9 @@ void st_table::add_read_columns_used_by_index(uint index)
when calling mark_columns_used_by_index
*/
-void st_table::restore_column_maps_after_mark_index()
+void TABLE::restore_column_maps_after_mark_index()
{
- DBUG_ENTER("st_table::restore_column_maps_after_mark_index");
+ DBUG_ENTER("TABLE::restore_column_maps_after_mark_index");
set_keyread(FALSE);
default_column_bitmaps();
@@ -4434,7 +4865,7 @@ void st_table::restore_column_maps_after_mark_index()
mark columns used by key, but don't reset other fields
*/
-void st_table::mark_columns_used_by_index_no_reset(uint index,
+void TABLE::mark_columns_used_by_index_no_reset(uint index,
MY_BITMAP *bitmap)
{
KEY_PART_INFO *key_part= key_info[index].key_part;
@@ -4453,7 +4884,7 @@ void st_table::mark_columns_used_by_index_no_reset(uint index,
always set and sometimes read.
*/
-void st_table::mark_auto_increment_column()
+void TABLE::mark_auto_increment_column()
{
DBUG_ASSERT(found_next_number_field);
/*
@@ -4486,7 +4917,7 @@ void st_table::mark_auto_increment_column()
retrieve the row again.
*/
-void st_table::mark_columns_needed_for_delete()
+void TABLE::mark_columns_needed_for_delete()
{
if (triggers)
triggers->mark_fields_used(TRG_EVENT_DELETE);
@@ -4536,7 +4967,7 @@ void st_table::mark_columns_needed_for_delete()
retrieve the row again.
*/
-void st_table::mark_columns_needed_for_update()
+void TABLE::mark_columns_needed_for_update()
{
DBUG_ENTER("mark_columns_needed_for_update");
if (triggers)
@@ -4579,7 +5010,7 @@ void st_table::mark_columns_needed_for_update()
as changed.
*/
-void st_table::mark_columns_needed_for_insert()
+void TABLE::mark_columns_needed_for_insert()
{
if (triggers)
{
@@ -4597,24 +5028,6 @@ void st_table::mark_columns_needed_for_insert()
}
-/**
- @brief Check if this is part of a MERGE table with attached children.
-
- @return status
- @retval TRUE children are attached
- @retval FALSE no MERGE part or children not attached
-
- @detail
- A MERGE table consists of a parent TABLE and zero or more child
- TABLEs. Each of these TABLEs is called a part of a MERGE table.
-*/
-
-bool st_table::is_children_attached(void)
-{
- return((child_l && children_attached) ||
- (parent && parent->children_attached));
-}
-
/*
Cleanup this table for re-execution.
@@ -4643,6 +5056,8 @@ void TABLE_LIST::reinit_before_use(THD *thd)
}
while (parent_embedding &&
parent_embedding->nested_join->join_list.head() == embedded);
+
+ mdl_request.ticket= NULL;
}
/*
@@ -4673,9 +5088,9 @@ Item_subselect *TABLE_LIST::containing_subselect()
DESCRIPTION
The parser collects the index hints for each table in a "tagged list"
(TABLE_LIST::index_hints). Using the information in this tagged list
- this function sets the members st_table::keys_in_use_for_query,
+ this function sets the members st_table::keys_in_use_for_query,
st_table::keys_in_use_for_group_by, st_table::keys_in_use_for_order_by,
- st_table::force_index, st_table::force_index_order,
+ st_table::force_index, st_table::force_index_order,
st_table::force_index_group and st_table::covering_keys.
Current implementation of the runtime does not allow mixing FORCE INDEX
@@ -4865,6 +5280,78 @@ size_t max_row_length(TABLE *table, const uchar *data)
return length;
}
+
+/**
+ Helper function which allows to allocate metadata lock request
+ objects for all elements of table list.
+*/
+
+void init_mdl_requests(TABLE_LIST *table_list)
+{
+ for ( ; table_list ; table_list= table_list->next_global)
+ table_list->mdl_request.init(MDL_key::TABLE,
+ table_list->db, table_list->table_name,
+ table_list->lock_type >= TL_WRITE_ALLOW_WRITE ?
+ MDL_SHARED_WRITE : MDL_SHARED_READ,
+ MDL_TRANSACTION);
+}
+
+
+/**
+ Update TABLE::const_key_parts for single table UPDATE/DELETE query
+
+ @param conds WHERE clause expression
+
+ @retval TRUE error (OOM)
+ @retval FALSE success
+
+ @note
+ Set const_key_parts bits if key fields are equal to constants in
+ the WHERE expression.
+*/
+
+bool TABLE::update_const_key_parts(COND *conds)
+{
+ bzero((char*) const_key_parts, sizeof(key_part_map) * s->keys);
+
+ if (conds == NULL)
+ return FALSE;
+
+ for (uint index= 0; index < s->keys; index++)
+ {
+ KEY_PART_INFO *keyinfo= key_info[index].key_part;
+ KEY_PART_INFO *keyinfo_end= keyinfo + key_info[index].key_parts;
+
+ for (key_part_map part_map= (key_part_map)1;
+ keyinfo < keyinfo_end;
+ keyinfo++, part_map<<= 1)
+ {
+ if (const_expression_in_where(conds, NULL, keyinfo->field))
+ const_key_parts[index]|= part_map;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ Test if the order list consists of simple field expressions
+
+ @param order Linked list of ORDER BY arguments
+
+ @return TRUE if @a order is empty or consist of simple field expressions
+*/
+
+bool is_simple_order(ORDER *order)
+{
+ for (ORDER *ord= order; ord; ord= ord->next)
+ {
+ if (ord->item[0]->real_item()->type() != Item::FIELD_ITEM)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
/*****************************************************************************
** Instansiate templates
*****************************************************************************/