summaryrefslogtreecommitdiff
path: root/storage/innobase/dict/dict0mem.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/dict/dict0mem.cc')
-rw-r--r--storage/innobase/dict/dict0mem.cc367
1 files changed, 364 insertions, 3 deletions
diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc
index 86a2e025c8f..132e7960541 100644
--- a/storage/innobase/dict/dict0mem.cc
+++ b/storage/innobase/dict/dict0mem.cc
@@ -136,8 +136,7 @@ dict_mem_table_create(
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->n_t_cols = (unsigned int) (n_cols +
- dict_table_get_n_sys_cols(table));
+ 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;
@@ -312,6 +311,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.
@@ -369,7 +378,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;
@@ -675,6 +684,8 @@ dict_mem_fill_column_struct(
column->mtype = (unsigned int) mtype;
column->prtype = (unsigned int) prtype;
column->len = (unsigned int) col_len;
+ column->def_val.data = NULL;
+ column->def_val.len = UNIV_SQL_DEFAULT;
dtype_get_mblen(mtype, prtype, &mbminlen, &mbmaxlen);
dict_col_set_mbminmaxlen(column, mbminlen, mbmaxlen);
@@ -1183,3 +1194,353 @@ dict_mem_table_is_system(
return true;
}
}
+
+/** 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)
+{
+ DBUG_ASSERT(is_clust());
+ DBUG_ASSERT(instant.is_clust());
+ 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());
+ }
+
+ 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,
+ 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 = 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 = 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];
+ }
+ }
+ }
+
+ 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++) {
+ index->n_nullable -= index->fields[i].col->is_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(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++) {
+ index->n_nullable -= index->fields[i].col->is_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 ulint* offsets)
+{
+ ut_a(is_clust());
+
+ 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.mtype == DATA_FIXBINARY) {
+ ut_ad(len == sizeof timestamp_max_bytes);
+ return 0 != memcmp(data, timestamp_max_bytes, len);
+ } else {
+ ut_ad(col.mtype == DATA_INT);
+ ut_ad(len == sizeof trx_id_max_bytes);
+ return 0 != memcmp(data, trx_id_max_bytes, len);
+ }
+ ut_ad(0);
+ return false;
+}
+
+/** 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_clust());
+
+ bool error = false;
+ mem_heap_t* heap = NULL;
+ dict_index_t* clust_index = NULL;
+ ulint offsets_[REC_OFFS_NORMAL_SIZE];
+ ulint* 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);
+}