summaryrefslogtreecommitdiff
path: root/storage/innobase/include/dict0mem.h
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/include/dict0mem.h')
-rw-r--r--storage/innobase/include/dict0mem.h279
1 files changed, 235 insertions, 44 deletions
diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h
index 6ed3b105da2..1521fb74970 100644
--- a/storage/innobase/include/dict0mem.h
+++ b/storage/innobase/include/dict0mem.h
@@ -587,13 +587,17 @@ struct dict_col_t{
this column. Our current max limit is
3072 (REC_VERSION_56_MAX_INDEX_COL_LEN)
bytes. */
+private:
+ /** Special value of ind for a dropped column */
+ static const unsigned DROPPED = 1023;
+public:
/** Detach the column from an index.
@param[in] index index to be detached from */
inline void detach(const dict_index_t& index);
/** Data for instantly added columns */
- struct {
+ struct def_t {
/** original default value of instantly added column */
const void* data;
/** len of data, or UNIV_SQL_DEFAULT if unavailable */
@@ -601,7 +605,7 @@ struct dict_col_t{
} def_val;
/** Retrieve the column name.
- @param[in] table table name */
+ @param[in] table the table of this column */
const char* name(const dict_table_t& table) const;
/** @return whether this is a virtual column */
@@ -630,26 +634,55 @@ struct dict_col_t{
}
/** @return whether this is an instantly-added column */
- bool is_instant() const
+ bool is_added() const
{
DBUG_ASSERT(def_val.len != UNIV_SQL_DEFAULT || !def_val.data);
return def_val.len != UNIV_SQL_DEFAULT;
}
+ /** Flag the column instantly dropped */
+ void set_dropped() { ind = DROPPED; }
+ /** Flag the column instantly dropped.
+ @param[in] not_null whether the column was NOT NULL
+ @param[in] len2 whether the length exceeds 255 bytes
+ @param[in] fixed_len the fixed length in bytes, or 0 */
+ void set_dropped(bool not_null, bool len2, unsigned fixed)
+ {
+ DBUG_ASSERT(!len2 || !fixed);
+ prtype = not_null
+ ? DATA_NOT_NULL | DATA_BINARY_TYPE
+ : DATA_BINARY_TYPE;
+ if (fixed) {
+ mtype = DATA_FIXBINARY;
+ len = fixed;
+ } else {
+ mtype = DATA_BINARY;
+ len = len2 ? 65535 : 255;
+ }
+ mbminlen = mbmaxlen = 0;
+ ind = DROPPED;
+ ord_part = 0;
+ max_prefix = 0;
+ }
+ /** @return whether the column was instantly dropped */
+ bool is_dropped() const { return ind == DROPPED; }
+ /** @return whether the column was instantly dropped
+ @param[in] index the clustered index */
+ inline bool is_dropped(const dict_index_t& index) const;
+
/** Get the default value of an instantly-added column.
@param[out] len value length (in bytes), or UNIV_SQL_NULL
@return default value
@retval NULL if the default value is SQL NULL (len=UNIV_SQL_NULL) */
const byte* instant_value(ulint* len) const
{
- DBUG_ASSERT(is_instant());
+ DBUG_ASSERT(is_added());
*len = def_val.len;
return static_cast<const byte*>(def_val.data);
}
/** Remove the 'instant ADD' status of the column */
- void remove_instant()
+ void clear_instant()
{
- DBUG_ASSERT(is_instant());
def_val.len = UNIV_SQL_DEFAULT;
def_val.data = NULL;
}
@@ -771,6 +804,9 @@ struct dict_field_t{
column if smaller than
DICT_ANTELOPE_MAX_INDEX_COL_LEN */
+ /** Zero-initialize all fields */
+ dict_field_t() : col(NULL), name(NULL), prefix_len(0), fixed_len(0) {}
+
/** Check whether two index fields are equivalent.
@param[in] old the other index field
@return whether the index fields are equivalent */
@@ -850,7 +886,7 @@ to start with. */
/** Data structure for an index. Most fields will be
initialized to 0, NULL or FALSE in dict_mem_index_create(). */
-struct dict_index_t{
+struct dict_index_t {
index_id_t id; /*!< id of the index */
mem_heap_t* heap; /*!< memory heap */
id_name_t name; /*!< index name */
@@ -1039,7 +1075,7 @@ struct dict_index_t{
page cannot be read or decrypted */
inline bool is_readable() const;
- /** @return whether instant ADD COLUMN is in effect */
+ /** @return whether instant ALTER TABLE is in effect */
inline bool is_instant() const;
/** @return whether the index is the primary key index
@@ -1052,6 +1088,13 @@ struct dict_index_t{
/** @return whether the index includes virtual columns */
bool has_virtual() const { return type & DICT_VIRTUAL; }
+ /** @return the offset of the metadata BLOB field,
+ or the first user field after the PRIMARY KEY,DB_TRX_ID,DB_ROLL_PTR */
+ unsigned first_user_field() const {
+ ut_ad(is_primary());
+ return n_uniq + 2;
+ }
+
/** @return whether the index is corrupted */
inline bool is_corrupted() const;
@@ -1097,24 +1140,20 @@ struct dict_index_t{
return fields[n].col->instant_value(len);
}
- /** Adjust clustered index metadata for instant ADD COLUMN.
- @param[in] clustered index definition after instant ADD COLUMN */
- void instant_add_field(const dict_index_t& instant);
-
- /** Remove the 'instant ADD' status of a clustered index.
- Protected by index root page x-latch or table X-lock. */
- void remove_instant()
- {
- DBUG_ASSERT(is_primary());
- if (!is_instant()) {
- return;
- }
- for (unsigned i = n_core_fields; i < n_fields; i++) {
- fields[i].col->remove_instant();
- }
- n_core_fields = n_fields;
- n_core_null_bytes = UT_BITS_IN_BYTES(unsigned(n_nullable));
- }
+ /** Adjust index metadata for instant ADD/DROP/reorder COLUMN.
+ @param[in] clustered index definition after instant ALTER TABLE */
+ inline void instant_add_field(const dict_index_t& instant);
+ /** Remove instant ADD COLUMN metadata. */
+ inline void clear_instant_add();
+ /** Remove instant ALTER TABLE metadata. */
+ inline void clear_instant_alter();
+
+ /** Construct the metadata record for instant ALTER TABLE.
+ @param[in] row dummy or default values for existing columns
+ @param[in,out] heap memory heap for allocations
+ @return metadata record */
+ inline dtuple_t*
+ instant_metadata(const dtuple_t& row, mem_heap_t* heap) const;
/** Check if record in clustered index is historical row.
@param[in] rec clustered row
@@ -1129,6 +1168,9 @@ struct dict_index_t{
@return true on error */
bool
vers_history_row(const rec_t* rec, bool &history_row);
+
+ /** Reconstruct the clustered index fields. */
+ inline void reconstruct_fields();
};
/** Detach a column from an index.
@@ -1463,6 +1505,17 @@ struct dict_vcol_templ_t {
dict_vcol_templ_t() : vtempl(0), mysql_table_query_id(~0ULL) {}
};
+/** Instantly dropped or reordered columns */
+struct dict_instant_t
+{
+ /** Number of dropped columns */
+ unsigned n_dropped;
+ /** Dropped columns */
+ dict_col_t* dropped;
+ /** Mapping the non-pk field to column of the table. */
+ uint16_t* non_pk_col_map;
+};
+
/** These are used when MySQL FRM and InnoDB data dictionary are
in inconsistent state. */
typedef enum {
@@ -1516,35 +1569,89 @@ struct dict_table_t {
return(UNIV_LIKELY(!file_unreadable));
}
- /** @return whether instant ADD COLUMN is in effect */
+ /** @return whether instant ALTER TABLE is in effect */
bool is_instant() const
{
return(UT_LIST_GET_FIRST(indexes)->is_instant());
}
- /** @return whether the table supports instant ADD COLUMN */
+ /** @return whether the table supports instant ALTER TABLE */
bool supports_instant() const
{
return(!(flags & DICT_TF_MASK_ZIP_SSIZE));
}
- /** Adjust metadata for instant ADD COLUMN.
- @param[in] table table definition after instant ADD COLUMN */
- void instant_add_column(const dict_table_t& table);
+ /** @return the number of instantly dropped columns */
+ unsigned n_dropped() const { return instant ? instant->n_dropped : 0; }
+
+ /** Look up an old column.
+ @param[in] cols the old columns of the table
+ @param[in] col_map map from old table columns to altered ones
+ @param[in] n_cols number of old columns
+ @param[in] i the number of the new column
+ @return old column
+ @retval NULL if column i was added to the table */
+ static const dict_col_t* find(const dict_col_t* cols,
+ const ulint* col_map, ulint n_cols,
+ ulint i)
+ {
+ for (ulint o = n_cols; o--; ) {
+ if (col_map[o] == i) {
+ return &cols[o];
+ }
+ }
+ return NULL;
+ }
- /** 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 rollback_instant(
+ /** Serialise metadata of dropped or reordered columns.
+ @param[in,out] heap memory heap for allocation
+ @param[out] field data field with the metadata */
+ inline void serialise_columns(mem_heap_t* heap, dfield_t* field) const;
+
+ /** Reconstruct dropped or reordered columns.
+ @param[in] metadata data from serialise_columns()
+ @param[in] len length of the metadata, in bytes
+ @return whether parsing the metadata failed */
+ bool deserialise_columns(const byte* metadata, ulint len);
+
+ /** Set is_instant() before instant_column().
+ @param[in] old previous table definition
+ @param[in] col_map map from old.cols[]
+ and old.v_cols[] to this
+ @param[out] first_alter_pos 0, or
+ 1 + first changed column position */
+ inline void prepare_instant(const dict_table_t& old,
+ const ulint* col_map,
+ unsigned& first_alter_pos);
+
+ /** Adjust table metadata for instant ADD/DROP/reorder COLUMN.
+ @param[in] table table on which prepare_instant() was invoked
+ @param[in] col_map mapping from cols[] and v_cols[] to table */
+ inline void instant_column(const dict_table_t& table,
+ const ulint* col_map);
+
+ /** Roll back instant_column().
+ @param[in] old_n_cols original n_cols
+ @param[in] old_cols original cols
+ @param[in] old_col_names original col_names
+ @param[in] old_instant original instant structure
+ @param[in] old_fields original fields
+ @param[in] old_n_fields original number of fields
+ @param[in] old_n_v_cols original n_v_cols
+ @param[in] old_v_cols original v_cols
+ @param[in] old_v_col_names original v_col_names
+ @param[in] col_map column map */
+ inline void rollback_instant(
unsigned old_n_cols,
dict_col_t* old_cols,
- const char* old_col_names);
-
- /** 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 rollback_instant(unsigned n);
+ const char* old_col_names,
+ dict_instant_t* old_instant,
+ dict_field_t* old_fields,
+ unsigned old_n_fields,
+ unsigned old_n_v_cols,
+ dict_v_col_t* old_v_cols,
+ const char* old_v_col_names,
+ const ulint* col_map);
/** Add the table definition to the data dictionary cache */
void add_to_cache();
@@ -1686,6 +1793,9 @@ struct dict_table_t {
reason s_cols is a part of dict_table_t */
dict_s_col_list* s_cols;
+ /** Instantly dropped or reordered columns, or NULL if none */
+ dict_instant_t* instant;
+
/** Column names packed in a character string
"name1\0name2\0...nameN\0". Until the string contains n_cols, it will
be allocated from a temporary heap. The final string will be allocated
@@ -1967,12 +2077,15 @@ inline bool dict_index_t::is_readable() const { return table->is_readable(); }
inline bool dict_index_t::is_instant() const
{
ut_ad(n_core_fields > 0);
- ut_ad(n_core_fields <= n_fields);
+ ut_ad(n_core_fields <= n_fields || table->n_dropped());
ut_ad(n_core_fields == n_fields
|| (type & ~(DICT_UNIQUE | DICT_CORRUPT)) == DICT_CLUSTERED);
ut_ad(n_core_fields == n_fields || table->supports_instant());
ut_ad(n_core_fields == n_fields || !table->is_temporary());
- return(n_core_fields != n_fields);
+ ut_ad(!table->instant || !table->is_temporary());
+
+ return n_core_fields != n_fields
+ || (is_primary() && table->instant);
}
inline bool dict_index_t::is_corrupted() const
@@ -1982,6 +2095,84 @@ inline bool dict_index_t::is_corrupted() const
|| (table && table->corrupted));
}
+inline void dict_index_t::clear_instant_add()
+{
+ DBUG_ASSERT(is_primary());
+ DBUG_ASSERT(is_instant());
+ DBUG_ASSERT(!table->instant);
+ for (unsigned i = n_core_fields; i < n_fields; i++) {
+ fields[i].col->clear_instant();
+ }
+ n_core_fields = n_fields;
+ n_core_null_bytes = UT_BITS_IN_BYTES(unsigned(n_nullable));
+}
+
+inline void dict_index_t::clear_instant_alter()
+{
+ DBUG_ASSERT(is_primary());
+ DBUG_ASSERT(n_fields == n_def);
+
+ if (!table->instant) {
+ if (is_instant()) {
+ clear_instant_add();
+ }
+ return;
+ }
+
+#ifndef DBUG_OFF
+ for (unsigned i = first_user_field(); i--; ) {
+ DBUG_ASSERT(!fields[i].col->is_dropped());
+ DBUG_ASSERT(!fields[i].col->is_nullable());
+ }
+#endif
+ dict_field_t* end = &fields[n_fields];
+
+ for (dict_field_t* d = &fields[first_user_field()]; d < end; d++) {
+ /* Move fields for dropped columns to the end. */
+ while (d->col->is_dropped()) {
+ if (d->col->is_nullable()) {
+ n_nullable--;
+ }
+
+ std::swap(*d, *--end);
+
+ if (d == end) {
+ goto done;
+ }
+ }
+
+ /* Ensure that the surviving fields are sorted by
+ ascending order of columns. */
+ const unsigned c = d->col->ind;
+
+ for (dict_field_t* s = d + 1; s < end; s++) {
+ if (s->col->ind < c) {
+ std::swap(*d, *s);
+ break;
+ }
+ }
+ }
+
+done:
+ DBUG_ASSERT(&fields[n_fields - table->n_dropped()] == end);
+
+ n_core_fields = n_fields = n_def = end - fields;
+ n_core_null_bytes = UT_BITS_IN_BYTES(n_nullable);
+ table->instant = NULL;
+}
+
+/** @return whether the column was instantly dropped
+@param[in] index the clustered index */
+inline bool dict_col_t::is_dropped(const dict_index_t& index) const
+{
+ DBUG_ASSERT(index.is_primary());
+ DBUG_ASSERT(!is_dropped() == !index.table->instant);
+ DBUG_ASSERT(!is_dropped() || (this >= index.table->instant->dropped
+ && this < index.table->instant->dropped
+ + index.table->instant->n_dropped));
+ return is_dropped();
+}
+
/*******************************************************************//**
Initialise the table lock list. */
void