diff options
Diffstat (limited to 'storage/innobase/dict/dict0mem.cc')
-rw-r--r-- | storage/innobase/dict/dict0mem.cc | 485 |
1 files changed, 405 insertions, 80 deletions
diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index 8327318e46c..f380feff7ef 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -34,9 +34,9 @@ Created 1/8/1996 Heikki Tuuri #include "mach0data.h" #include "dict0dict.h" #include "fts0priv.h" -#include "ut0crc32.h" #include "lock0lock.h" #include "sync0sync.h" +#include "row0row.h" #include <iostream> #define DICT_HEAP_SIZE 100 /*!< initial memory heap size when @@ -50,6 +50,29 @@ static const char* innobase_system_databases[] = { NullS }; +/** Determine if a table belongs to innobase_system_databases[] +@param[in] name database_name/table_name +@return whether the database_name is in innobase_system_databases[] */ +static bool dict_mem_table_is_system(const char *name) +{ + /* table has the following format: database/table + and some system table are of the form SYS_* */ + if (!strchr(name, '/')) { + return true; + } + size_t table_len = strlen(name); + const char *system_db; + int i = 0; + while ((system_db = innobase_system_databases[i++]) + && (system_db != NullS)) { + size_t len = strlen(system_db); + if (table_len > len && !strncmp(name, system_db, len)) { + return true; + } + } + return false; +} + /** The start of the table basename suffix for partitioned tables */ const char table_name_t::part_suffix[4] #ifdef _WIN32 @@ -58,10 +81,6 @@ const char table_name_t::part_suffix[4] = "#P#"; #endif -/** An interger randomly initialized at startup used to make a temporary -table name as unuique as possible. */ -static ib_uint32_t dict_temp_file_num; - /** Display an identifier. @param[in,out] s output stream @param[in] id_name SQL identifier (other than table name) @@ -103,8 +122,7 @@ dict_table_t* dict_mem_table_create( /*==================*/ const char* name, /*!< in: table name */ - ulint space, /*!< in: space where the clustered index of - the table is placed */ + fil_space_t* space, /*!< in: tablespace */ ulint n_cols, /*!< in: total number of columns including virtual and non-virtual columns */ ulint n_v_cols,/*!< in: number of virtual columns */ @@ -115,6 +133,10 @@ dict_mem_table_create( mem_heap_t* heap; ut_ad(name); + ut_ad(!space + || space->purpose == FIL_TYPE_TABLESPACE + || space->purpose == FIL_TYPE_TEMPORARY + || space->purpose == FIL_TYPE_IMPORT); ut_a(dict_tf2_is_valid(flags, flags2)); ut_a(!(flags2 & DICT_TF2_UNUSED_BIT_MASK)); @@ -138,10 +160,11 @@ dict_mem_table_create( table->flags2 = (unsigned int) flags2; table->name.m_name = mem_strdup(name); table->is_system_db = dict_mem_table_is_system(table->name.m_name); - table->space = (unsigned int) space; + table->space = space; + table->space_id = space ? space->id : ULINT_UNDEFINED; table->n_t_cols = unsigned(n_cols + DATA_N_SYS_COLS); table->n_v_cols = (unsigned int) (n_v_cols); - table->n_cols = table->n_t_cols - table->n_v_cols; + table->n_cols = unsigned(table->n_t_cols - table->n_v_cols); table->cols = static_cast<dict_col_t*>( mem_heap_alloc(heap, table->n_cols * sizeof(dict_col_t))); @@ -255,7 +278,7 @@ dict_add_col_name( s += strlen(s) + 1; } - old_len = s - col_names; + old_len = unsigned(s - col_names); } else { old_len = 0; } @@ -317,6 +340,16 @@ dict_mem_table_add_col( col = dict_table_get_nth_col(table, i); dict_mem_fill_column_struct(col, i, mtype, prtype, len); + + switch (prtype & DATA_VERSIONED) { + case DATA_VERS_START: + ut_ad(!table->vers_start); + table->vers_start = i; + break; + case DATA_VERS_END: + ut_ad(!table->vers_end); + table->vers_end = i; + } } /** Adds a virtual column definition to a table. @@ -374,7 +407,7 @@ dict_mem_table_add_v_col( i, name, heap); } - v_col = dict_table_get_nth_v_col(table, i); + v_col = &table->v_cols[i]; dict_mem_fill_column_struct(&v_col->m_col, pos, mtype, prtype, len); v_col->v_pos = i; @@ -403,7 +436,7 @@ dict_mem_table_add_s_col( dict_table_t* table, ulint num_base) { - ulint i = table->n_def - 1; + unsigned i = unsigned(table->n_def) - 1; dict_col_t* col = dict_table_get_nth_col(table, i); dict_s_col_t s_col; @@ -464,13 +497,13 @@ dict_mem_table_col_rename_low( /* We need to adjust all affected index->field pointers, as in dict_index_add_col(). First, copy table->col_names. */ - ulint prefix_len = s - t_col_names; + ulint prefix_len = ulint(s - t_col_names); for (; i < n_col; i++) { s += strlen(s) + 1; } - ulint full_len = s - t_col_names; + ulint full_len = ulint(s - t_col_names); char* col_names; if (to_len > from_len) { @@ -503,12 +536,12 @@ dict_mem_table_col_rename_low( /* if is_virtual and that in field->col does not match, continue */ if ((!is_virtual) != - (!dict_col_is_virtual(field->col))) { + (!field->col->is_virtual())) { continue; } ulint name_ofs - = field->name - t_col_names; + = ulint(field->name - t_col_names); if (name_ofs <= prefix_len) { field->name = col_names + name_ofs; } else { @@ -712,9 +745,11 @@ dict_mem_fill_column_struct( column->mtype = (unsigned int) mtype; column->prtype = (unsigned int) prtype; column->len = (unsigned int) col_len; - dtype_get_mblen(mtype, prtype, &mbminlen, &mbmaxlen); + dtype_get_mblen(mtype, prtype, &mbminlen, &mbmaxlen); column->mbminlen = mbminlen; column->mbmaxlen = mbmaxlen; + column->def_val.data = NULL; + column->def_val.len = UNIV_SQL_DEFAULT; } /**********************************************************************//** @@ -723,11 +758,8 @@ Creates an index memory object. dict_index_t* dict_mem_index_create( /*==================*/ - const char* table_name, /*!< in: table name */ + dict_table_t* table, /*!< in: table */ const char* index_name, /*!< in: index name */ - ulint space, /*!< in: space where the index tree is - placed, ignored if the index is of - the clustered type */ ulint type, /*!< in: DICT_UNIQUE, DICT_CLUSTERED, ... ORed */ ulint n_fields) /*!< in: number of fields */ @@ -735,15 +767,16 @@ dict_mem_index_create( dict_index_t* index; mem_heap_t* heap; - ut_ad(table_name && index_name); + ut_ad(!table || table->magic_n == DICT_TABLE_MAGIC_N); + ut_ad(index_name); heap = mem_heap_create(DICT_HEAP_SIZE); index = static_cast<dict_index_t*>( mem_heap_zalloc(heap, sizeof(*index))); + index->table = table; - dict_mem_fill_index_struct(index, heap, table_name, index_name, - space, type, n_fields); + dict_mem_fill_index_struct(index, heap, index_name, type, n_fields); dict_index_zip_pad_mutex_create_lazy(index); @@ -1044,7 +1077,7 @@ dict_mem_index_add_field( index->n_def++; - field = dict_index_get_nth_field(index, index->n_def - 1); + field = dict_index_get_nth_field(index, unsigned(index->n_def) - 1); field->name = name; field->prefix_len = (unsigned int) prefix_len; @@ -1098,46 +1131,15 @@ dict_mem_create_temporary_tablename( ut_ad(dbend); size_t dblen = size_t(dbend - dbtab) + 1; - if (srv_safe_truncate) { - /* InnoDB will drop all #sql tables at startup. - Therefore, the id alone should generate a unique - and previously non-existent name. */ - size = dblen + (sizeof(TEMP_FILE_PREFIX) + 3 + 20); - name = static_cast<char*>(mem_heap_alloc(heap, size)); - memcpy(name, dbtab, dblen); - snprintf(name + dblen, size - dblen, - TEMP_FILE_PREFIX_INNODB UINT64PF, id); - return name; - } - /* Increment a randomly initialized number for each temp file. */ - my_atomic_add32((int32*) &dict_temp_file_num, 1); - - size = dblen + (sizeof(TEMP_FILE_PREFIX) + 3 + 20 + 1 + 10); + size = dblen + (sizeof(TEMP_FILE_PREFIX) + 3 + 20); name = static_cast<char*>(mem_heap_alloc(heap, size)); memcpy(name, dbtab, dblen); snprintf(name + dblen, size - dblen, - TEMP_FILE_PREFIX_INNODB UINT64PF "-" UINT32PF, - id, dict_temp_file_num); + TEMP_FILE_PREFIX_INNODB UINT64PF, id); return(name); } -/** Initialize dict memory variables */ -void -dict_mem_init(void) -{ - /* Initialize a randomly distributed temporary file number */ - ib_uint32_t now = static_cast<ib_uint32_t>(time(NULL)); - - const byte* buf = reinterpret_cast<const byte*>(&now); - - dict_temp_file_num = ut_crc32(buf, sizeof(now)); - - DBUG_PRINT("dict_mem_init", - ("Starting Temporary file number is " UINT32PF, - dict_temp_file_num)); -} - /** Validate the search order in the foreign key set. @param[in] fk_set the foreign key set to be validated @return true if search order is fine in the set, false otherwise. */ @@ -1195,31 +1197,354 @@ operator<< (std::ostream& out, const dict_foreign_set& fk_set) return(out); } -/****************************************************************//** -Determines if a table belongs to a system database -@return */ -bool -dict_mem_table_is_system( -/*================*/ - char *name) /*!< in: table name */ +/** Adjust clustered index metadata for instant ADD COLUMN. +@param[in] clustered index definition after instant ADD COLUMN */ +inline void dict_index_t::instant_add_field(const dict_index_t& instant) { - ut_ad(name); + DBUG_ASSERT(is_primary()); + DBUG_ASSERT(instant.is_primary()); + DBUG_ASSERT(!instant.is_instant()); + DBUG_ASSERT(n_def == n_fields); + DBUG_ASSERT(instant.n_def == instant.n_fields); + + DBUG_ASSERT(type == instant.type); + DBUG_ASSERT(trx_id_offset == instant.trx_id_offset); + DBUG_ASSERT(n_user_defined_cols == instant.n_user_defined_cols); + DBUG_ASSERT(n_uniq == instant.n_uniq); + DBUG_ASSERT(instant.n_fields > n_fields); + DBUG_ASSERT(instant.n_def > n_def); + DBUG_ASSERT(instant.n_nullable >= n_nullable); + DBUG_ASSERT(instant.n_core_fields >= n_core_fields); + DBUG_ASSERT(instant.n_core_null_bytes >= n_core_null_bytes); + + n_fields = instant.n_fields; + n_def = instant.n_def; + n_nullable = instant.n_nullable; + fields = static_cast<dict_field_t*>( + mem_heap_dup(heap, instant.fields, n_fields * sizeof *fields)); + + ut_d(unsigned n_null = 0); + + for (unsigned i = 0; i < n_fields; i++) { + DBUG_ASSERT(fields[i].same(instant.fields[i])); + const dict_col_t* icol = instant.fields[i].col; + DBUG_ASSERT(!icol->is_virtual()); + dict_col_t* col = fields[i].col = &table->cols[ + icol - instant.table->cols]; + fields[i].name = col->name(*table); + ut_d(n_null += col->is_nullable()); + } - /* table has the following format: database/table - and some system table are of the form SYS_* */ - if (strchr(name, '/')) { - size_t table_len = strlen(name); - const char *system_db; - int i = 0; - while ((system_db = innobase_system_databases[i++]) - && (system_db != NullS)) { - size_t len = strlen(system_db); - if (table_len > len && !strncmp(name, system_db, len)) { - return true; + ut_ad(n_null == n_nullable); +} + +/** Adjust metadata for instant ADD COLUMN. +@param[in] table table definition after instant ADD COLUMN */ +void dict_table_t::instant_add_column(const dict_table_t& table) +{ + DBUG_ASSERT(!table.cached); + DBUG_ASSERT(table.n_def == table.n_cols); + DBUG_ASSERT(table.n_t_def == table.n_t_cols); + DBUG_ASSERT(n_def == n_cols); + DBUG_ASSERT(n_t_def == n_t_cols); + DBUG_ASSERT(table.n_cols > n_cols); + ut_ad(mutex_own(&dict_sys->mutex)); + + const char* end = table.col_names; + for (unsigned i = table.n_cols; i--; ) end += strlen(end) + 1; + + col_names = static_cast<char*>( + mem_heap_dup(heap, table.col_names, + ulint(end - table.col_names))); + const dict_col_t* const old_cols = cols; + const dict_col_t* const old_cols_end = cols + n_cols; + cols = static_cast<dict_col_t*>(mem_heap_dup(heap, table.cols, + table.n_cols + * sizeof *cols)); + + /* Preserve the default values of previously instantly + added columns. */ + for (unsigned i = unsigned(n_cols) - DATA_N_SYS_COLS; i--; ) { + cols[i].def_val = old_cols[i].def_val; + } + + /* Copy the new default values to this->heap. */ + for (unsigned i = n_cols; i < table.n_cols; i++) { + dict_col_t& c = cols[i - DATA_N_SYS_COLS]; + DBUG_ASSERT(c.is_instant()); + if (c.def_val.len == 0) { + c.def_val.data = field_ref_zero; + } else if (const void*& d = c.def_val.data) { + d = mem_heap_dup(heap, d, c.def_val.len); + } else { + DBUG_ASSERT(c.def_val.len == UNIV_SQL_NULL); + } + } + + const unsigned old_n_cols = n_cols; + const unsigned n_add = unsigned(table.n_cols - n_cols); + + n_t_def += n_add; + n_t_cols += n_add; + n_cols = table.n_cols; + n_def = n_cols; + + for (unsigned i = n_v_def; i--; ) { + const dict_v_col_t& v = v_cols[i]; + for (ulint n = v.num_base; n--; ) { + dict_col_t*& base = v.base_col[n]; + if (!base->is_virtual()) { + DBUG_ASSERT(base >= old_cols); + size_t n = size_t(base - old_cols); + DBUG_ASSERT(n + DATA_N_SYS_COLS < old_n_cols); + base = &cols[n]; } } - return false; - } else { - return true; } + + dict_index_t* index = dict_table_get_first_index(this); + + index->instant_add_field(*dict_table_get_first_index(&table)); + + while ((index = dict_table_get_next_index(index)) != NULL) { + for (unsigned i = 0; i < index->n_fields; i++) { + dict_field_t& field = index->fields[i]; + if (field.col < old_cols + || field.col >= old_cols_end) { + DBUG_ASSERT(field.col->is_virtual()); + } else { + /* Secondary indexes may contain user + columns and DB_ROW_ID (if there is + GEN_CLUST_INDEX instead of PRIMARY KEY), + but not DB_TRX_ID,DB_ROLL_PTR. */ + DBUG_ASSERT(field.col >= old_cols); + size_t n = size_t(field.col - old_cols); + DBUG_ASSERT(n + DATA_N_SYS_COLS <= old_n_cols); + if (n + DATA_N_SYS_COLS >= old_n_cols) { + /* Replace DB_ROW_ID */ + n += n_add; + } + field.col = &cols[n]; + DBUG_ASSERT(!field.col->is_virtual()); + field.name = field.col->name(*this); + } + } + } +} + +/** Roll back instant_add_column(). +@param[in] old_n_cols original n_cols +@param[in] old_cols original cols +@param[in] old_col_names original col_names */ +void +dict_table_t::rollback_instant( + unsigned old_n_cols, + dict_col_t* old_cols, + const char* old_col_names) +{ + ut_ad(mutex_own(&dict_sys->mutex)); + dict_index_t* index = indexes.start; + /* index->is_instant() does not necessarily hold here, because + the table may have been emptied */ + DBUG_ASSERT(old_n_cols >= DATA_N_SYS_COLS); + DBUG_ASSERT(n_cols >= old_n_cols); + DBUG_ASSERT(n_cols == n_def); + DBUG_ASSERT(index->n_def == index->n_fields); + + const unsigned n_remove = n_cols - old_n_cols; + + for (unsigned i = index->n_fields - n_remove; i < index->n_fields; + i++) { + if (index->fields[i].col->is_nullable()) { + index->n_nullable--; + } + } + + index->n_fields -= n_remove; + index->n_def = index->n_fields; + if (index->n_core_fields > index->n_fields) { + index->n_core_fields = index->n_fields; + index->n_core_null_bytes + = UT_BITS_IN_BYTES(unsigned(index->n_nullable)); + } + + const dict_col_t* const new_cols = cols; + const dict_col_t* const new_cols_end = cols + n_cols; + + cols = old_cols; + col_names = old_col_names; + n_cols = old_n_cols; + n_def = old_n_cols; + n_t_def -= n_remove; + n_t_cols -= n_remove; + + for (unsigned i = n_v_def; i--; ) { + const dict_v_col_t& v = v_cols[i]; + for (ulint n = v.num_base; n--; ) { + dict_col_t*& base = v.base_col[n]; + if (!base->is_virtual()) { + base = &cols[base - new_cols]; + } + } + } + + do { + for (unsigned i = 0; i < index->n_fields; i++) { + dict_field_t& field = index->fields[i]; + if (field.col < new_cols + || field.col >= new_cols_end) { + DBUG_ASSERT(field.col->is_virtual()); + } else { + DBUG_ASSERT(field.col >= new_cols); + size_t n = size_t(field.col - new_cols); + DBUG_ASSERT(n <= n_cols); + if (n + DATA_N_SYS_COLS >= n_cols) { + n -= n_remove; + } + field.col = &cols[n]; + DBUG_ASSERT(!field.col->is_virtual()); + field.name = field.col->name(*this); + } + } + } while ((index = dict_table_get_next_index(index)) != NULL); +} + +/** Trim the instantly added columns when an insert into SYS_COLUMNS +is rolled back during ALTER TABLE or recovery. +@param[in] n number of surviving non-system columns */ +void dict_table_t::rollback_instant(unsigned n) +{ + ut_ad(mutex_own(&dict_sys->mutex)); + dict_index_t* index = indexes.start; + DBUG_ASSERT(index->is_instant()); + DBUG_ASSERT(index->n_def == index->n_fields); + DBUG_ASSERT(n_cols == n_def); + DBUG_ASSERT(n >= index->n_uniq); + DBUG_ASSERT(n_cols > n + DATA_N_SYS_COLS); + const unsigned n_remove = n_cols - n - DATA_N_SYS_COLS; + + char* names = const_cast<char*>(dict_table_get_col_name(this, n)); + const char* sys = names; + for (unsigned i = n_remove; i--; ) { + sys += strlen(sys) + 1; + } + static const char system[] = "DB_ROW_ID\0DB_TRX_ID\0DB_ROLL_PTR"; + DBUG_ASSERT(!memcmp(sys, system, sizeof system)); + for (unsigned i = index->n_fields - n_remove; i < index->n_fields; + i++) { + if (index->fields[i].col->is_nullable()) { + index->n_nullable--; + } + } + index->n_fields -= n_remove; + index->n_def = index->n_fields; + memmove(names, sys, sizeof system); + memmove(cols + n, cols + n_cols - DATA_N_SYS_COLS, + DATA_N_SYS_COLS * sizeof *cols); + n_cols -= n_remove; + n_def = n_cols; + n_t_cols -= n_remove; + n_t_def -= n_remove; + + for (unsigned i = DATA_N_SYS_COLS; i--; ) { + cols[n_cols - i].ind--; + } + + if (dict_index_is_auto_gen_clust(index)) { + DBUG_ASSERT(index->n_uniq == 1); + dict_field_t* field = index->fields; + field->name = sys; + field->col = dict_table_get_sys_col(this, DATA_ROW_ID); + field++; + field->name = sys + sizeof "DB_ROW_ID"; + field->col = dict_table_get_sys_col(this, DATA_TRX_ID); + field++; + field->name = sys + sizeof "DB_ROW_ID\0DB_TRX_ID"; + field->col = dict_table_get_sys_col(this, DATA_ROLL_PTR); + + /* Replace the DB_ROW_ID column in secondary indexes. */ + while ((index = dict_table_get_next_index(index)) != NULL) { + field = &index->fields[index->n_fields - 1]; + DBUG_ASSERT(field->col->mtype == DATA_SYS); + DBUG_ASSERT(field->col->prtype + == DATA_NOT_NULL + DATA_TRX_ID); + field->col--; + field->name = sys; + } + + return; + } + + dict_field_t* field = &index->fields[index->n_uniq]; + field->name = sys + sizeof "DB_ROW_ID"; + field->col = dict_table_get_sys_col(this, DATA_TRX_ID); + field++; + field->name = sys + sizeof "DB_ROW_ID\0DB_TRX_ID"; + field->col = dict_table_get_sys_col(this, DATA_ROLL_PTR); +} + + +/** Check if record in clustered index is historical row. +@param[in] rec clustered row +@param[in] offsets offsets +@return true if row is historical */ +bool +dict_index_t::vers_history_row( + const rec_t* rec, + const rec_offs* offsets) +{ + ut_ad(is_primary()); + + ulint len; + dict_col_t& col= table->cols[table->vers_end]; + ut_ad(col.vers_sys_end()); + ulint nfield = dict_col_get_clust_pos(&col, this); + const byte *data = rec_get_nth_field(rec, offsets, nfield, &len); + if (col.vers_native()) { + ut_ad(len == sizeof trx_id_max_bytes); + return 0 != memcmp(data, trx_id_max_bytes, len); + } + ut_ad(len == sizeof timestamp_max_bytes); + return 0 != memcmp(data, timestamp_max_bytes, len); +} + +/** Check if record in secondary index is historical row. +@param[in] rec record in a secondary index +@param[out] history_row true if row is historical +@return true on error */ +bool +dict_index_t::vers_history_row( + const rec_t* rec, + bool &history_row) +{ + ut_ad(!is_primary()); + + bool error = false; + mem_heap_t* heap = NULL; + dict_index_t* clust_index = NULL; + rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; + rec_offs* offsets = offsets_; + rec_offs_init(offsets_); + + mtr_t mtr; + mtr.start(); + + rec_t* clust_rec = + row_get_clust_rec(BTR_SEARCH_LEAF, rec, this, &clust_index, &mtr); + if (clust_rec) { + offsets = rec_get_offsets(clust_rec, clust_index, offsets, true, + ULINT_UNDEFINED, &heap); + + history_row = clust_index->vers_history_row(clust_rec, offsets); + } else { + ib::error() << "foreign constraints: secondary index is out of " + "sync"; + ut_ad(!"secondary index is out of sync"); + error = true; + } + mtr.commit(); + if (heap) { + mem_heap_free(heap); + } + return(error); } |