sql/handler.h | 26 ++- sql/sql_table.cc | 19 ++- storage/innobase/api/api0api.cc | 4 +- storage/innobase/btr/btr0btr.cc | 7 +- storage/innobase/btr/btr0cur.cc | 2 +- storage/innobase/btr/btr0sea.cc | 19 +- storage/innobase/data/data0data.cc | 1 + storage/innobase/data/data0type.cc | 2 +- storage/innobase/dict/dict0boot.cc | 8 +- storage/innobase/dict/dict0crea.cc | 102 ++++++++- storage/innobase/dict/dict0dict.cc | 13 +- storage/innobase/dict/dict0load.cc | 262 +++++++++++++++++++- storage/innobase/dict/dict0mem.cc | 44 ++++- storage/innobase/fts/fts0fts.cc | 11 +- storage/innobase/handler/ha_innodb.cc | 21 ++- storage/innobase/handler/ha_innodb.h | 10 + storage/innobase/handler/handler0alter.cc | 383 ++++++++++++++++++++++++++++- storage/innobase/handler/i_s.cc | 240 ++++++++++++++++++ storage/innobase/handler/i_s.h | 1 + storage/innobase/ibuf/ibuf0ibuf.cc | 4 +- storage/innobase/include/data0data.ic | 2 + storage/innobase/include/dict0boot.h | 17 ++ storage/innobase/include/dict0crea.h | 8 + storage/innobase/include/dict0dict.h | 63 +++++ storage/innobase/include/dict0dict.ic | 125 ++++++++++ storage/innobase/include/dict0load.h | 20 ++ storage/innobase/include/dict0mem.h | 49 ++++ storage/innobase/include/dict0mem.ic | 3 +- storage/innobase/include/gis0rtree.ic | 2 +- storage/innobase/include/rem0rec.h | 136 ++++++++++- storage/innobase/include/rem0rec.ic | 247 ++++++++++++++++++- storage/innobase/include/row0row.h | 4 +- storage/innobase/include/univ.i | 6 + storage/innobase/mtr/mtr0log.cc | 55 ++++- storage/innobase/page/page0cur.cc | 11 +- storage/innobase/page/page0zip.cc | 9 +- storage/innobase/pars/pars0pars.cc | 2 +- storage/innobase/rem/rem0cmp.cc | 22 ++- storage/innobase/rem/rem0rec.cc | 309 ++++++++++++++++++++---- storage/innobase/row/row0log.cc | 45 +++- storage/innobase/row/row0mysql.cc | 5 + storage/innobase/row/row0row.cc | 17 +- storage/innobase/row/row0sel.cc | 35 ++- storage/innobase/row/row0trunc.cc | 3 + storage/innobase/row/row0umod.cc | 30 ++- storage/innobase/row/row0upd.cc | 52 +++-- storage/innobase/srv/srv0start.cc | 6 + storage/innobase/trx/trx0i_s.cc | 3 + storage/innobase/trx/trx0rec.cc | 7 +- 49 files changed, 2296 insertions(+), 176 deletions(-) diff --git a/sql/handler.h b/sql/handler.h index 8b26821..3fd6a0d 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1211,4 +1210,0 @@ public: - // Add generic column (convience constant). - static const HA_ALTER_FLAGS ADD_COLUMN= ADD_VIRTUAL_COLUMN | - ADD_STORED_BASE_COLUMN | - ADD_STORED_GENERATED_COLUMN; @@ -1323,0 +1320,8 @@ public: + // Instant generated column + static const HA_ALTER_FLAGS ADD_INSTANT_COLUMN = 1ULL << 42; + // Add generic column (convience constant). + static const HA_ALTER_FLAGS ADD_COLUMN= ADD_VIRTUAL_COLUMN | + ADD_STORED_BASE_COLUMN | + ADD_STORED_GENERATED_COLUMN | + ADD_INSTANT_COLUMN; + @@ -3420,0 +3425,14 @@ public: + /** + Check the storage engine support instant alter table + @param ha_alter_info Structure describing changes to be done + by ALTER TABLE and holding data used + during in-place alter. + + @retval false Not supported. + @retval true Supported. + */ + /* return false, mean it can't instant alter default */ + virtual bool check_instant_alter(const Alter_inplace_info* inplace_info) const + { + return false; + } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b1bf21a..60888a2 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6392,0 +6393 @@ static bool fill_alter_inplace_info(THD *thd, + bool has_expr_default = false; @@ -6668,3 +6669,7 @@ static bool fill_alter_inplace_info(THD *thd, - else - ha_alter_info->handler_flags|= - Alter_inplace_info::ADD_STORED_BASE_COLUMN; + else { + //if (new_field->has_default_expression() && !new_field->has_default_now_unireg_check()) { + // has_expr_default = true; + //} + ha_alter_info->handler_flags |= + Alter_inplace_info::ADD_STORED_BASE_COLUMN; + } @@ -6866,0 +6872,8 @@ static bool fill_alter_inplace_info(THD *thd, + // If ADD_STORED_BASE_COLUMN only, we can change to ADD_INSTANT_COLUMN in some cases + if (ha_alter_info->handler_flags == Alter_inplace_info::ADD_STORED_BASE_COLUMN && + !has_expr_default && + table->file->check_instant_alter(ha_alter_info)) { + + ha_alter_info->handler_flags = Alter_inplace_info::ADD_INSTANT_COLUMN; + } + diff --git a/storage/innobase/api/api0api.cc b/storage/innobase/api/api0api.cc index 5e08b32..e453ec9 100644 --- a/storage/innobase/api/api0api.cc +++ b/storage/innobase/api/api0api.cc @@ -321 +321 @@ ib_read_tuple( - ulint* len) /*!< in/out: buffer len */ + ulint* len) /*!< in/out: buffer len */ @@ -380 +380 @@ ib_read_tuple( - data = rec_get_nth_field(copy, offsets, i, &len); + data = rec_get_nth_cfield(copy, offsets, i, index, tuple->heap, &len); diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index a7bc8a2..1da01af 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -4364 +4364 @@ btr_index_rec_validate( - && len != UNIV_SQL_NULL && fixed_size + && len_is_stored(len) && fixed_size @@ -4367,3 +4367,2 @@ btr_index_rec_validate( - && len != UNIV_SQL_NULL - && len - > field->prefix_len)) { + && len_is_stored(len) + && len > field->prefix_len)) { diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index 54429a3..b602044 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -6274 +6274 @@ btr_rec_get_field_ref_offs( - ut_a(local_len != UNIV_SQL_NULL); + ut_a(len_is_stored(local_len)); diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc index ba0e702..8a697b5 100644 --- a/storage/innobase/btr/btr0sea.cc +++ b/storage/innobase/btr/btr0sea.cc @@ -691 +691 @@ btr_search_update_hash_ref( - block->curr_n_bytes, index->id); + block->curr_n_bytes, index); @@ -1279 +1279 @@ retry: - fold = rec_fold(rec, offsets, n_fields, n_bytes, index_id); + fold = rec_fold(rec, offsets, n_fields, n_bytes, index); @@ -1494 +1494 @@ btr_search_build_page_hash_index( - fold = rec_fold(rec, offsets, n_fields, n_bytes, index->id); + fold = rec_fold(rec, offsets, n_fields, n_bytes, index); @@ -1522 +1522 @@ btr_search_build_page_hash_index( - n_bytes, index->id); + n_bytes, index); @@ -1702 +1702 @@ btr_search_update_hash_on_delete(btr_cur_t* cursor) - block->curr_n_fields, block->curr_n_bytes, index->id); + block->curr_n_fields, block->curr_n_bytes, index); @@ -1852 +1852 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor) - ins_fold = rec_fold(ins_rec, offsets, n_fields, n_bytes, index->id); + ins_fold = rec_fold(ins_rec, offsets, n_fields, n_bytes, index); @@ -1859 +1859 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor) - n_bytes, index->id); + n_bytes, index); @@ -1866 +1866 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor) - fold = rec_fold(rec, offsets, n_fields, n_bytes, index->id); + fold = rec_fold(rec, offsets, n_fields, n_bytes, index); @@ -2067,0 +2068 @@ btr_search_hash_table_validate(ulint hash_table_id) + ut_ad(page_index_id == block->index->id); @@ -2072 +2073 @@ btr_search_hash_table_validate(ulint hash_table_id) - page_index_id); + block->index); diff --git a/storage/innobase/data/data0data.cc b/storage/innobase/data/data0data.cc index 4b788c8..287ba73 100644 --- a/storage/innobase/data/data0data.cc +++ b/storage/innobase/data/data0data.cc @@ -836,0 +837 @@ dfield_t::clone( + ut_ad(len != UNIV_SQL_DEFAULT); diff --git a/storage/innobase/data/data0type.cc b/storage/innobase/data/data0type.cc index 8fb3761..5dde2f3 100644 --- a/storage/innobase/data/data0type.cc +++ b/storage/innobase/data/data0type.cc @@ -63 +63 @@ dtype_get_at_most_n_mbchars( - ut_a(data_len != UNIV_SQL_NULL); + ut_a(len_is_stored(data_len)); diff --git a/storage/innobase/dict/dict0boot.cc b/storage/innobase/dict/dict0boot.cc index 5c07d05..441dbec 100644 --- a/storage/innobase/dict/dict0boot.cc +++ b/storage/innobase/dict/dict0boot.cc @@ -337 +337 @@ dict_boot(void) - table = dict_mem_table_create("SYS_TABLES", DICT_HDR_SPACE, 8, 0, 0, 0); + table = dict_mem_table_create("SYS_TABLES", DICT_HDR_SPACE, 8, 0, 0, 0, 0); @@ -391 +391 @@ dict_boot(void) - 7, 0, 0, 0); + 7, 0, 0, 0, 0); @@ -424 +424 @@ dict_boot(void) - DICT_NUM_COLS__SYS_INDEXES, 0, 0, 0); + DICT_NUM_COLS__SYS_INDEXES, 0, 0, 0, 0); @@ -457 +457 @@ dict_boot(void) - table = dict_mem_table_create("SYS_FIELDS", DICT_HDR_SPACE, 3, 0, 0, 0); + table = dict_mem_table_create("SYS_FIELDS", DICT_HDR_SPACE, 3, 0, 0, 0, 0); diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc index 63e4436..8bbbbb9 100644 --- a/storage/innobase/dict/dict0crea.cc +++ b/storage/innobase/dict/dict0crea.cc @@ -141 +141,2 @@ dict_create_sys_tables_tuple( - mach_write_to_4(ptr, table->flags2); + /* When creating a new table, there is no history of instant ADD COLUMN. */ + mach_write_to_4(ptr, dict_table_encode_mix_len(table->flags2, 0)); @@ -2521,0 +2523,99 @@ dict_create_or_check_sys_tablespace(void) +/****************************************************************//** +Creates the sys_columns_added system tables inside InnoDB at server +bootstrap or server start if they are not found or are not of the +right form. +@return DB_SUCCESS or error code */ +dberr_t +dict_create_or_check_sys_columns_added(void) +/*=====================================*/ +{ + trx_t* trx; + my_bool srv_file_per_table_backup; + dberr_t err; + dberr_t sys_columns_added_err; + + ut_a(srv_get_active_thread_type() == SRV_NONE); + + /* Note: The master thread has not been started at this point. */ + sys_columns_added_err = dict_check_if_system_table_exists( + "SYS_COLUMNS_ADDED", DICT_NUM_FIELDS__SYS_COLUMNS_ADDED + 1, 1); + + if (sys_columns_added_err == DB_SUCCESS) { + return(DB_SUCCESS); + } + + if (high_level_read_only + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO) { + return(DB_READ_ONLY); + } + + trx = trx_allocate_for_mysql(); + + trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); + + trx->op_info = "creating sys_columns_added sys tables"; + + row_mysql_lock_data_dictionary(trx); + + /* Check which incomplete table definition to drop. */ + if (sys_columns_added_err == DB_CORRUPTION) { + ib::warn() << "Dropping incompletely created" + " SYS_COLUMNS_ADDED table."; + + row_drop_table_for_mysql("SYS_COLUMNS_ADDED", trx, TRUE, TRUE); + } + + ib::info() << "Creating sys_columns_added system tables."; + + /* We always want SYSTEM tables to be created inside the system + tablespace. */ + srv_file_per_table_backup = srv_file_per_table; + srv_file_per_table = 0; + + err = que_eval_sql( + NULL, + "PROCEDURE CREATE_SYS_COLUMNS_ADDED_PROC () IS\n" + "BEGIN\n" + "CREATE TABLE SYS_COLUMNS_ADDED(\n" + "TABLE_ID BIGINT UNSIGNED NOT NULL,\n" + "POS INT NOT NULL,\n" + "DEFAULT_VALUE CHAR);\n" + "CREATE UNIQUE CLUSTERED INDEX COL_IND ON SYS_COLUMNS_ADDED (TABLE_ID, POS);\n" + "END;\n", + FALSE, trx); + + if (err != DB_SUCCESS) { + + ib::error() << "Creation of SYS_COLUMNS_ADDED" + " has failed with error " << ut_strerr(err) + << ". Dropping incompletely created tables."; + + ut_a(err == DB_OUT_OF_FILE_SPACE + || err == DB_TOO_MANY_CONCURRENT_TRXS); + + row_drop_table_for_mysql("SYS_COLUMNS_ADDED", trx, TRUE, TRUE); + + if (err == DB_OUT_OF_FILE_SPACE) { + err = DB_MUST_GET_MORE_FILE_SPACE; + } + } + + trx_commit_for_mysql(trx); + + row_mysql_unlock_data_dictionary(trx); + + trx_free_for_mysql(trx); + + srv_file_per_table = srv_file_per_table_backup; + + if (err == DB_SUCCESS) { + /* Note: The master thread has not been started at this point. */ + /* Confirm and move to the non-LRU part of the table LRU list. */ + sys_columns_added_err = dict_check_if_system_table_exists( + "SYS_COLUMNS_ADDED", DICT_NUM_FIELDS__SYS_COLUMNS_ADDED + 1, 1); + ut_a(sys_columns_added_err == DB_SUCCESS); + } + + return(err); +} + diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 51a8f9d..ad350c7 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -2688,0 +2689,11 @@ dict_index_add_to_cache_w_vcol( + if (new_index->is_instant()) { + ut_a(new_index->table->n_cols - new_index->table->n_core_cols >= 0); + + new_index->n_core_fields = new_index->n_fields - + (new_index->table->n_cols - new_index->table->n_core_cols); + new_index->n_core_nullable = dict_index_get_first_n_field_n_nullable(new_index, new_index->n_core_fields); + } else { + new_index->n_core_nullable = new_index->n_nullable; + new_index->n_core_fields = new_index->n_fields; + } + @@ -6011 +6022 @@ dict_ind_init(void) - table = dict_mem_table_create("SYS_DUMMY1", DICT_HDR_SPACE, 1, 0, 0, 0); + table = dict_mem_table_create("SYS_DUMMY1", DICT_HDR_SPACE, 1, 0, 0, 0, 0); diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index 96a30a7..deefbaf 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -65 +65,2 @@ static const char* SYSTEM_TABLE_NAME[] = { - "SYS_VIRTUAL" + "SYS_VIRTUAL", + "SYS_COLUMNS_ADDED" @@ -1259,0 +1261 @@ dict_sys_tables_rec_read( + ulint* n_cols_core, @@ -1314 +1316,3 @@ dict_sys_tables_rec_read( - *flags2 = mach_read_from_4(field); + ulint mix_len = mach_read_from_4(field); + dict_table_decode_mix_len(mix_len, flags2, n_cols_core); + ut_ad(*n_cols_core <= *n_cols); @@ -1367,0 +1372 @@ dict_check_sys_tables( + ulint n_cols_core; @@ -1387 +1392 @@ dict_check_sys_tables( - &n_cols, &flags, &flags2); + &n_cols, &n_cols_core, &flags, &flags2); @@ -1793,0 +1799,244 @@ err_len: + +/** Error message for a delete-marked record in dict_load_column_added_low() */ +static const char* dict_load_column_added_del = "delete-marked record in SYS_COLUMNS_ADDED"; + +/** Load a table added column definition from a SYS_COLUMNS_ADDED +record. +@param[in,out] table table +@param[in,out] heap memory heap +@param[in] rec current SYS_COLUMNS_ADDED rec +@param[in] sys_index cluster index of SYS_COLUMNS_ADDED +@param[in,out] table_id_out table id +@param[in,out] pos_out added column position +@param[in,out] def_val_out the pointer of default value +@param[in,out] def_val_len_out the length of default value +@return error message +@retval NULL on success */ +static +const char* +dict_load_column_added_low( +dict_table_t* table, +mem_heap_t* heap, +const rec_t* rec, +dict_index_t* sys_index, +table_id_t* table_id_out, +ulint* pos_out, +char** def_val_out, +ulint* def_val_len_out) +{ + const byte* field; + const byte* def_val = NULL; + ulint def_val_len = 0; + table_id_t table_id; + ulint len; + ulint pos; + + if (rec_get_deleted_flag(rec, 0)) { + return(dict_load_column_added_del); + } + + if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_COLUMNS_ADDED) { + return("wrong number of columns in SYS_COLUMNS_ADDED record"); + } + + field = rec_get_nth_field_old( + rec, DICT_FLD__SYS_COLUMNS_ADDED__TABLE_ID, &len); + if (len != 8) { + err_len: + return("incorrect column length in SYS_COLUMNS_ADDED"); + } + + table_id = mach_read_from_8(field); + if (table && table->id != mach_read_from_8(field)) { + return("SYS_COLUMNS_ADDED.TABLE_ID mismatch"); + } + + field = rec_get_nth_field_old( + rec, DICT_FLD__SYS_COLUMNS_ADDED__POS, &len); + if (len != 4) { + goto err_len; + } + + pos = mach_read_from_4(field); + + rec_get_nth_field_offs_old( + rec, DICT_FLD__SYS_COLUMNS_ADDED__DB_TRX_ID, &len); + if (len != DATA_TRX_ID_LEN && len != UNIV_SQL_NULL) { + goto err_len; + } + rec_get_nth_field_offs_old( + rec, DICT_FLD__SYS_COLUMNS_ADDED__DB_ROLL_PTR, &len); + if (len != DATA_ROLL_PTR_LEN && len != UNIV_SQL_NULL) { + goto err_len; + } + + if (UNIV_UNLIKELY(rec_offs_nth_extern_old(rec, + DICT_FLD__SYS_COLUMNS_ADDED__DEFAULT_VALUE))) { + ulint offsets_[REC_OFFS_NORMAL_SIZE]; + ulint * offsets = offsets_; + + ut_a(rec); + rec_offs_init(offsets_); + + offsets = rec_get_offsets(rec, sys_index, offsets, ULINT_UNDEFINED, &heap); + + def_val = btr_rec_copy_externally_stored_field( + rec, offsets, + dict_table_page_size(table), + DICT_FLD__SYS_COLUMNS_ADDED__DEFAULT_VALUE, &def_val_len, heap); + ut_ad(def_val_len); + ut_a(def_val); + + } + else { + field = rec_get_nth_field_old( + rec, DICT_FLD__SYS_COLUMNS_ADDED__DEFAULT_VALUE, &len); + + if (len == UNIV_SQL_NULL) { + def_val = NULL; + def_val_len = UNIV_SQL_NULL; + } + else { + def_val = field; + def_val_len = len; + } + } + + if (table) { + dict_col_t* col = dict_table_get_nth_col(table, pos); + + dict_col_set_added_column_default(col, def_val, def_val_len, table->heap); + } + + *table_id_out = table_id; + *pos_out = pos; + if (def_val_out && def_val_len_out) { + *def_val_out = def_val ? + (char*)mem_heap_strdupl(heap, (char*)def_val, def_val_len) : + (char*)def_val; + *def_val_len_out = def_val_len; + } + return(NULL); +} + +static +void +dict_load_columns_added( +dict_table_t* table, +mem_heap_t* heap) +{ + dict_table_t* sys_columns_added; + dict_index_t* sys_index; + btr_pcur_t pcur; + dtuple_t* tuple; + dfield_t* dfield; + const rec_t* rec; + byte* buf; + ulint i; + mtr_t mtr; + + if (!table->is_instant()) + return; + + ut_ad(mutex_own(&dict_sys->mutex)); + mtr_start(&mtr); + + sys_columns_added = dict_table_get_low("SYS_COLUMNS_ADDED"); + sys_index = UT_LIST_GET_FIRST(sys_columns_added->indexes); + ut_ad(!dict_table_is_comp(sys_columns_added)); + + ut_ad(name_of_col_is(sys_columns_added, sys_index, + DICT_FLD__SYS_COLUMNS_ADDED__TABLE_ID, "TABLE_ID")); + ut_ad(name_of_col_is(sys_columns_added, sys_index, + DICT_FLD__SYS_COLUMNS_ADDED__POS, "POS")); + + tuple = dtuple_create(heap, 2); + + /* TABLE_ID */ + dfield = dtuple_get_nth_field(tuple, 0); + buf = static_cast(mem_heap_alloc(heap, 8)); + mach_write_to_8(buf, table->id); + dfield_set_data(dfield, buf, 8); + + ulint first_added_pos = table->n_core_cols - dict_table_get_n_sys_cols(table); + ulint last_added_pos = table->n_cols - dict_table_get_n_sys_cols(table); + + /* POS */ + dfield = dtuple_get_nth_field(tuple, 1); + buf = static_cast(mem_heap_alloc(heap, 4)); + // first instant add columns + mach_write_to_4(buf, first_added_pos); + dfield_set_data(dfield, buf, 4); + + dict_index_copy_types(tuple, sys_index, 2); + + btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr); + + for (i = first_added_pos; + i < last_added_pos;) + { + const char* err_msg; + table_id_t table_id; + ulint pos; + + ut_a(btr_pcur_is_on_user_rec(&pcur)); + rec = btr_pcur_get_rec(&pcur); + + err_msg = dict_load_column_added_low(table, heap, rec, sys_index, &table_id, &pos, NULL, NULL); + + if (!err_msg) { + if (table_id != table->id) { + err_msg = "SYS_COLUMNS_ADDED.TABLE_ID mismatch"; + } + else if (pos != i){ + err_msg = "SYS_COLUMNS_ADDED.POS mismatch"; + } + } + + if (err_msg == dict_load_column_added_del) { + goto next_rec; + } + else if (err_msg) { + ib::fatal() << err_msg; + } + + i++; + + next_rec: + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + } + + btr_pcur_close(&pcur); + mtr_commit(&mtr); +} + +/** This function parses a SYS_COLUMNS_ADDED record and extracts added column +information +@param[in,out] heap heap memory +@param[in] rec current SYS_COLUMNS_ADDED rec +@param[in] index cluster index of SYS_COLUMNS_ADDED +@param[in,out] table_id table id +@param[in,out] pos column position +@param[in,out] def_val default value of column +@param[in,out] def_val_len the length of default value +@return error message, or NULL on success */ +const char* +dict_process_sys_columns_added_rec( +mem_heap_t* heap, +const rec_t* rec, +dict_index_t* index, +table_id_t* table_id, +ulint* pos, +char** def_val, +ulint* def_val_len) +{ + const char* err_msg; + + /* Parse the record, and get "dict_col_def_t" struct filled */ + err_msg = dict_load_column_added_low(NULL, heap, rec, index, + table_id, pos, def_val, def_val_len); + + return(err_msg); +} + @@ -2662,0 +2912 @@ dict_load_table_low( + ulint n_cols_core; @@ -2674 +2924 @@ dict_load_table_low( - &t_num, &flags, &flags2); + &t_num, &n_cols_core, &flags, &flags2); @@ -2683 +2933 @@ dict_load_table_low( - name.m_name, space_id, n_cols + n_v_col, n_v_col, flags, flags2); + name.m_name, space_id, n_cols + n_v_col, n_cols_core, n_v_col, flags, flags2); @@ -3113,0 +3364,2 @@ err_exit: + dict_load_columns_added(table, heap); + diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index ab11826..9917d0d 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -103,0 +104,3 @@ dict_mem_table_create( + ulint n_cols_core, /*!< in: total number of columns before + first time instant add column. + If zero, means non-instant */ @@ -135,0 +139,4 @@ dict_mem_table_create( + ut_ad(n_cols_core <= n_cols); + table->n_core_cols = n_cols_core ? + (n_cols_core + dict_table_get_n_sys_cols(table)) : + table->n_cols; @@ -138 +145 @@ dict_mem_table_create( - mem_heap_alloc(heap, table->n_cols * sizeof(dict_col_t))); + mem_heap_zalloc(heap, table->n_cols * sizeof(dict_col_t))); @@ -140 +147 @@ dict_mem_table_create( - mem_heap_alloc(heap, n_v_cols * sizeof(*table->v_cols))); + mem_heap_zalloc(heap, n_v_cols * sizeof(*table->v_cols))); @@ -279,0 +287,33 @@ dict_add_col_name( +/****************************************************************//** +Fake column default values for recovery*/ +void +dict_mem_table_fake_nth_col_default( +/*==============*/ + dict_table_t* table, /*!< in/out: table, set the default values + for the nth columns */ + ulint pos, /*!< in: the position of column in table */ + mem_heap_t* heap /*!< in: mem_heap for default value */ +) +{ + ulint fixed_size = 0; + ulint def_val_len = 0; + dict_col_t* col = NULL; + + ut_ad(table->is_instant()); + + col = dict_table_get_nth_col(table, pos); + fixed_size = dict_col_get_fixed_size(col, dict_table_is_comp(table)); + + /* For non-fixed size columns, we fake one byte default value */ + def_val_len = fixed_size ? fixed_size : 1; + + col->def_val = (dict_col_def_t*)mem_heap_alloc(heap, sizeof(*col->def_val)); + col->def_val->col = col; + col->def_val->def_val_len = def_val_len; + col->def_val->def_val = (unsigned char*)mem_heap_zalloc(heap, def_val_len + 1); + + /* For string, we use space */ + if(dtype_is_string_type(col->mtype)) + memset(col->def_val->def_val, ' ', def_val_len); +} + diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index 747e618..8ebb3a3 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -1780 +1780 @@ fts_create_in_mem_aux_table( - aux_table_name, table->space, n_cols, 0, table->flags, + aux_table_name, table->space, n_cols, 0, 0, table->flags, @@ -3462,2 +3462,7 @@ fts_fetch_doc_from_rec( - doc->text.f_str = (byte*) rec_get_nth_field( - clust_rec, offsets, clust_pos, + ut_ad(rec_offs_validate(clust_rec, clust_index, offsets)); + /* Instant ADD COLUMN is never invoked on + the internal tables that are created for implementing FULLTEXT INDEX + for InnoDB tables. Use the low-level physical access to the fields. */ + doc->text.f_str = (byte*) rec_get_nth_cfield( + clust_rec, offsets, clust_pos, clust_index, + static_cast(doc->self_heap->arg), diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 7497bb1..5b7a2f0 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -9927 +9927 @@ create_table_info_t::create_table_def() - actual_n_cols, num_v, m_flags, m_flags2); + actual_n_cols, 0, num_v, m_flags, m_flags2); @@ -10322,0 +10323,18 @@ error_ret: +/****************************************************************//** +whether the table can do the instant alter. */ +bool ha_innobase::check_instant_alter( +/*===========================*/ + const Alter_inplace_info* inplace_info /*!< in: in-place alter */ +) const +{ + if (inplace_info->handler_flags == Alter_inplace_info::ADD_STORED_BASE_COLUMN + || inplace_info->handler_flags == Alter_inplace_info::ADD_INSTANT_COLUMN) { + + dict_table_t* table = this->m_prebuilt->table; + if (table && dict_table_is_comp(table) && !DICT_TF_GET_ZIP_SSIZE(table->flags)) { + return true; + } + } + return false; +} + @@ -20393,0 +20412 @@ i_s_innodb_sys_columns, +i_s_innodb_sys_columns_added, diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index b83b70c..4925320 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -302,0 +303,10 @@ public: + /** Check whether the table can do the instant alter. + + @param ha_alter_info Structure describing changes to be done + by ALTER TABLE and holding data used during in-place alter. + + @retval true Failure + @retval false Success + */ + virtual bool check_instant_alter(const Alter_inplace_info* inplace_info) const; + diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 6eff265..adadfaa 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -60,0 +61,3 @@ Smart ALTER TABLE +#include "sql_time.h" +#include "sql_class.h" + @@ -112,0 +116 @@ static const Alter_inplace_info::HA_ALTER_FLAGS INNOBASE_ALTER_NOREBUILD + | Alter_inplace_info::ADD_INSTANT_COLUMN @@ -179,0 +184,2 @@ struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx + /** number of instant columns to be added */ + ulint num_to_add_instant_col; @@ -223,0 +230 @@ struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx + num_to_add_instant_col(0), @@ -1634 +1641 @@ null_field: - ifield = rec_get_nth_field(rec, offsets, ipos, &ilen); + ifield = rec_get_nth_cfield(rec, offsets, ipos, index, NULL, &ilen); @@ -3400,0 +3408,27 @@ innobase_check_gis_columns( +/** Collect instant column info for its addition +@param[in] ha_alter_info Data used during in-place alter +@param[in] altered_table MySQL table that is being altered to +@param[in] table MySQL table as it is before the ALTER operation +@retval true Failure +@retval false Success */ +static +bool +prepare_inplace_add_instant( +Alter_inplace_info* ha_alter_info, +const TABLE* altered_table, +const TABLE* table) +{ + ha_innobase_inplace_ctx* ctx; + + ctx = static_cast + (ha_alter_info->handler_ctx); + + ut_ad(altered_table->s->fields > table->s->fields); + + ctx->num_to_add_instant_col = altered_table->s->fields - + table->s->fields; + + return false; + +} + @@ -3857,0 +3892,318 @@ innobase_add_virtual_try( +/** Get the default value +@param[in] table MySQL table +@param[in] col dict_col_t +@param[in] pos_in_mysql The field position in MySQL table +@param[in] comp true: compact table +@param[in] heap memory heap for def +@param[in/out] def the default value, maybe NULL +@param[out] def_length the length of default value +@retval DB_SUCCESS Success */ +static +dberr_t +innobase_get_field_def_value( +const TABLE* table, +dict_col_t* col, +ulint pos_in_mysql, +ulint comp, +mem_heap_t* heap, +byte** def, +ulint* def_length +) +{ + Field* field; + + DBUG_ENTER("innobase_get_field_def_value"); + ut_ad(pos_in_mysql < table->s->fields); + + field = table->field[pos_in_mysql]; + + ut_ad(field->stored_in_db); + + if (field->is_real_null()) { + // DEFAULT NULL + *def = NULL; + *def_length = UNIV_SQL_NULL; + } else { + dfield_t dfield; +#define DATA_INT_MAX_LEN 8 + unsigned char int_buff[DATA_INT_MAX_LEN + 1]; + + ulint len = field->pack_length(); + byte* ptr = field->ptr; + + dict_col_copy_type(col, dfield_get_type(&dfield)); + ut_a(dfield.type.mtype != DATA_INT || len <= DATA_INT_MAX_LEN); + + row_mysql_store_col_in_innobase_format(&dfield, (unsigned char *)&int_buff[0], TRUE, + ptr, len, comp); + + // maybe default value is '' + ut_ad(dfield.len <= field->pack_length()); + + *def = (byte*)mem_heap_dup(heap, dfield.data, dfield.len); + *def_length = dfield.len; + + } + + DBUG_RETURN(DB_SUCCESS); +} + +/** Update INNODB SYS_COLUMNS on new virtual columns +@param[in] table InnoDB table +@param[in] altered_table MySQL table +@param[in] field added field +@param[in] pos position of table +@param[in] trx transaction +@return DB_SUCCESS if successful, otherwise error code */ +static +dberr_t +innobase_add_one_instant( +const dict_table_t* table, +const TABLE* altered_table, +const Field* field, +uint pos_in_innodb, +trx_t* trx) +{ + ulint col_len; + ulint is_unsigned; + ulint field_type; + ulint charset_no; + dberr_t error; + mem_heap_t* heap = NULL; + dict_col_t tmp_col; + ulint prtype; + pars_info_t* info; + byte* def_val = NULL; + ulint def_val_len = 0; + + + ulint col_type + = get_innobase_type_from_mysql_type( + &is_unsigned, field); + + ut_ad(!innobase_is_v_fld(field)); + + /* First check whether the column to be added has a + system reserved name. */ + if (dict_col_name_is_reserved(field->field_name)){ + my_error(ER_WRONG_COLUMN_NAME, MYF(0), + field->field_name); + + error = DB_ERROR; + goto err_exit; + } + + col_len = field->pack_length(); + field_type = (ulint)field->type(); + + if (!field->real_maybe_null()) { + field_type |= DATA_NOT_NULL; + } + + if (field->binary()) { + field_type |= DATA_BINARY_TYPE; + } + + if (is_unsigned) { + field_type |= DATA_UNSIGNED; + } + + if (dtype_is_string_type(col_type)) { + charset_no = (ulint)field->charset()->number; + + if (charset_no > MAX_CHAR_COLL_NUM) { + my_error(ER_WRONG_KEY_COLUMN, MYF(0), "InnoDB", + field->field_name); + error = DB_ERROR; + goto err_exit; + } + } + else { + charset_no = 0; + } + + if (field->type() == MYSQL_TYPE_VARCHAR) { + uint32 length_bytes + = static_cast( + field)->length_bytes; + + col_len -= length_bytes; + + if (length_bytes == 2) { + field_type |= DATA_LONG_TRUE_VARCHAR; + } + } + + + prtype = dtype_form_prtype(field_type, charset_no); + + info = pars_info_create(); + + pars_info_add_ull_literal(info, "id", table->id); + pars_info_add_int4_literal(info, "pos", pos_in_innodb); + pars_info_add_str_literal(info, "name", field->field_name); + pars_info_add_int4_literal(info, "mtype", col_type); + pars_info_add_int4_literal(info, "prtype", prtype); + pars_info_add_int4_literal(info, "len", col_len); + pars_info_add_int4_literal(info, "prec", 0); + + error = que_eval_sql( + info, + "PROCEDURE P () IS\n" + "BEGIN\n" + "INSERT INTO SYS_COLUMNS VALUES" + "(:id, :pos, :name, :mtype, :prtype, :len, :prec);\n" + "END;\n", + FALSE, trx); + + if (error != DB_SUCCESS) { + goto err_exit; + } + + memset((byte*)&tmp_col, 0, sizeof(dict_col_t)); + dict_mem_fill_column_struct(&tmp_col, pos_in_innodb, col_type, prtype, col_len); + + heap = mem_heap_create(1024); + + // Note: field->field_index is not always equal to pos(because of virtual columns) + error = innobase_get_field_def_value(altered_table, &tmp_col, field->field_index, dict_table_is_comp(table), heap, &def_val, &def_val_len); + if (error != DB_SUCCESS) { + goto err_exit; + } + + info = pars_info_create(); + + pars_info_add_ull_literal(info, "id", table->id); + pars_info_add_int4_literal(info, "pos", pos_in_innodb); + pars_info_add_literal(info, "default_value", def_val, def_val_len, DATA_BLOB, DATA_BINARY_TYPE); + + error = que_eval_sql( + info, + "PROCEDURE P () IS\n" + "BEGIN\n" + "INSERT INTO SYS_COLUMNS_ADDED VALUES" + "(:id, :pos, :default_value);\n" + "END;\n", + FALSE, trx); + + if (error != DB_SUCCESS) { + goto err_exit; + } + + +err_exit: + if (heap) { + mem_heap_free(heap); + } + return(error); +} + +/** Update INNODB SYS_TABLES on number of instant added columns +@param[in] user_table InnoDB table +@param[in] n_col number of columns +@param[in] trx transaction +@return DB_SUCCESS if successful, otherwise error code */ +static +dberr_t +innobase_update_n_instant( +const dict_table_t* table, +ulint n_col, +trx_t* trx) +{ + dberr_t err = DB_SUCCESS; + pars_info_t* info = pars_info_create(); + + ulint mix_len = dict_table_encode_mix_len(table->flags2, + table->n_core_cols - dict_table_get_n_sys_cols(table)); + + pars_info_add_int4_literal(info, "num_col", n_col); + pars_info_add_int4_literal(info, "mix_len", mix_len); + pars_info_add_ull_literal(info, "id", table->id); + + err = que_eval_sql( + info, + "PROCEDURE RENUMBER_TABLE_ID_PROC () IS\n" + "BEGIN\n" + "UPDATE SYS_TABLES" + " SET N_COLS = :num_col,\n" + " MIX_LEN = :mix_len\n" + " WHERE ID = :id;\n" + "END;\n", FALSE, trx); + + return(err); +} + +/** Update system table for instant adding column(s) +@param[in] ha_alter_info Data used during in-place alter +@param[in] altered_table MySQL table that is being altered +@param[in] table MySQL table as it is before the ALTER operation +@param[in] user_table InnoDB table +@param[in] trx transaction +@retval true Failure +@retval false Success */ +static +bool +innobase_add_instant_try( +Alter_inplace_info* ha_alter_info, +const TABLE* altered_table, +const TABLE* table, +const dict_table_t* user_table, +trx_t* trx) +{ + dberr_t err = DB_SUCCESS; + + ut_ad(altered_table->s->fields > table->s->fields); + + Field *field = NULL; + ulint i = 0, j = 0; + ulint pos_in_innodb = 0; + ulint added_column = 0; + + for (i = 0; i < altered_table->s->fields; i++) + { + field = altered_table->field[i]; + // ignore virtual field + if (innobase_is_v_fld(field)) { + // It doesn't support add virtual columns when instant adding columns. + ut_ad(i < table->s->fields); + continue; + } + + pos_in_innodb = j++; + // find the first instant add columns + if (i < table->s->fields) { + continue; + } + + // instant add columns + err = innobase_add_one_instant(user_table, + altered_table, field, pos_in_innodb, trx); + if (err != DB_SUCCESS) { + my_error(ER_INTERNAL_ERROR, MYF(0), + "InnoDB: ADD COLUMN...INSTANT"); + return(true); + } + added_column++; + } + + ulint n_col = user_table->n_cols; + ulint n_v_col = user_table->n_v_cols; + + n_col -= dict_table_get_n_sys_cols(user_table); + + n_col += added_column; + + ulint new_n = dict_table_encode_n_col(n_col, n_v_col) + + ((user_table->flags & DICT_TF_COMPACT) << 31); + + err = innobase_update_n_instant(user_table, new_n, trx); + + if (err != DB_SUCCESS) { + my_error(ER_INTERNAL_ERROR, MYF(0), + "InnoDB: ADD COLUMN...INSTANT"); + return(true); + } + + return(false); +} + @@ -4261,0 +4614,8 @@ prepare_inplace_alter_table_dict( + if (ha_alter_info->handler_flags + & Alter_inplace_info::ADD_INSTANT_COLUMN) { + if (prepare_inplace_add_instant( + ha_alter_info, altered_table, old_table)) { + DBUG_RETURN(true); + } + } + @@ -4437 +4797 @@ prepare_inplace_alter_table_dict( - new_table_name, space_id, n_cols + n_v_cols, n_v_cols, + new_table_name, space_id, n_cols + n_v_cols, 0, n_v_cols, @@ -5971,0 +6332,8 @@ err_exit: + if (ha_alter_info->handler_flags + & Alter_inplace_info::ADD_INSTANT_COLUMN) { + if (prepare_inplace_add_instant( + ha_alter_info, altered_table, table)) { + DBUG_RETURN(true); + } + } + @@ -7876,0 +8245,8 @@ commit_try_norebuild( + if ((ha_alter_info->handler_flags + & Alter_inplace_info::ADD_INSTANT_COLUMN) + && innobase_add_instant_try( + ha_alter_info, altered_table, old_table, + ctx->old_table, trx)) { + DBUG_RETURN(true); + } + @@ -8633 +9009,2 @@ foreign_fail: - if (ctx0->num_to_drop_vcol || ctx0->num_to_add_vcol) { + if (ctx0->num_to_drop_vcol || ctx0->num_to_add_vcol + || ctx0->num_to_add_instant_col) { diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc index 8f8a722..539ca4b 100644 --- a/storage/innobase/handler/i_s.cc +++ b/storage/innobase/handler/i_s.cc @@ -290,0 +291,25 @@ field_store_string( +Auxiliary function to store char* value in MYSQL_TYPE_BLOB field. +@return 0 on success */ +static +int +field_store_blob( +/*===============*/ + Field* field, /*!< in/out: target field for storage */ + const char* str, /*!< in: blob string, or NULL */ + ulint str_len)/*!< in: length of blob string */ +{ + int ret; + + if (str != NULL) { + ret = field->store(str, str_len, + field->charset()); + field->set_notnull(); + } else { + ret = 0; /* success */ + field->set_null(); + } + + return(ret); +} + +/*******************************************************************//** @@ -9041,0 +9067,215 @@ i_s_files_table_fill( + +/** SYS_COLUMNS_ADDED **************************************************/ +/** Fields of the dynamic table INFORMATION_SCHEMA.INNODB_SYS_COLUMNS_ADDED */ +static ST_FIELD_INFO innodb_sys_columns_added_fields_info[] = +{ +#define SYS_COLUMNS_ADDED_TABLE_ID 0 + {STRUCT_FLD(field_name, "TABLE_ID"), + STRUCT_FLD(field_length, MY_INT64_NUM_DECIMAL_DIGITS), + STRUCT_FLD(field_type, MYSQL_TYPE_LONGLONG), + STRUCT_FLD(value, 0), + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), + STRUCT_FLD(old_name, ""), + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, + +#define SYS_COLUMNS_ADDED_POS 1 + {STRUCT_FLD(field_name, "POS"), + STRUCT_FLD(field_length, MY_INT32_NUM_DECIMAL_DIGITS), + STRUCT_FLD(field_type, MYSQL_TYPE_LONG), + STRUCT_FLD(value, 0), + STRUCT_FLD(field_flags, MY_I_S_UNSIGNED), + STRUCT_FLD(old_name, ""), + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, + +#define SYS_COLUMNS_ADDED_DEFAULT_VALUE 2 + {STRUCT_FLD(field_name, "DEFAULT_VALUE"), + STRUCT_FLD(field_length, 65535), + STRUCT_FLD(field_type, MYSQL_TYPE_BLOB), + STRUCT_FLD(value, 0), + STRUCT_FLD(field_flags, MY_I_S_MAYBE_NULL), + STRUCT_FLD(old_name, ""), + STRUCT_FLD(open_method, SKIP_OPEN_TABLE)}, + + END_OF_ST_FIELD_INFO +}; + +/** Function to populate the information_schema.innodb_sys_columns_added with +related information +param[in] thd thread +param[in] table_id table ID +param[in] pos column position +param[in] def_val default value of columns +param[in,out] table_to_fill fill this table +@return 0 on success */ +static +int +i_s_dict_fill_sys_columns_added( + THD* thd, + table_id_t table_id, + ulint pos, + const char* def_val, + ulint def_val_len, + TABLE* table_to_fill) +{ + Field** fields; + + DBUG_ENTER("i_s_dict_fill_sys_columns_added"); + + fields = table_to_fill->field; + + OK(fields[SYS_COLUMNS_ADDED_TABLE_ID]->store((longlong) table_id, TRUE)); + + OK(fields[SYS_COLUMNS_ADDED_POS]->store(pos)); + + OK(field_store_blob(fields[SYS_COLUMNS_ADDED_DEFAULT_VALUE], def_val, def_val_len)); + + OK(schema_table_store_record(thd, table_to_fill)); + + DBUG_RETURN(0); +} + +/** Function to fill information_schema.innodb_sys_columns_added with information +collected by scanning SYS_COLUMNS_ADDED table. +param[in] thd thread +param[in,out] tables tables to fill +param[in] item condition (not used) +@return 0 on success */ +static +int +i_s_sys_columns_added_fill_table( + THD* thd, + TABLE_LIST* tables, + Item* ) +{ + btr_pcur_t pcur; + const rec_t* rec; + ulint pos; + mem_heap_t* heap; + mtr_t mtr; + + DBUG_ENTER("i_s_sys_columns_added_fill_table"); + RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name); + + /* deny access to user without PROCESS_ACL privilege */ + if (check_global_access(thd, PROCESS_ACL)) { + DBUG_RETURN(0); + } + + heap = mem_heap_create(1000); + mutex_enter(&dict_sys->mutex); + mtr_start(&mtr); + + rec = dict_startscan_system(&pcur, &mtr, SYS_COLUMNS_ADDED); + + while (rec) { + const char* err_msg; + table_id_t table_id; + char* def_val; + ulint def_val_len = 0; + + + /* populate a dict_col_t structure with information from + a SYS_COLUMNS_ADDED row */ + err_msg = dict_process_sys_columns_added_rec(heap, rec, pcur.index(), + &table_id, &pos, + &def_val, &def_val_len); + + mtr_commit(&mtr); + mutex_exit(&dict_sys->mutex); + + if (!err_msg) { + i_s_dict_fill_sys_columns_added(thd, table_id, pos, def_val, def_val_len, + tables->table); + } else { + push_warning_printf(thd, Sql_condition::SL_WARNING, + ER_CANT_FIND_SYSTEM_REC, "%s", + err_msg); + } + + mem_heap_empty(heap); + + /* Get the next record */ + mutex_enter(&dict_sys->mutex); + mtr_start(&mtr); + rec = dict_getnext_system(&pcur, &mtr); + } + + mtr_commit(&mtr); + mutex_exit(&dict_sys->mutex); + mem_heap_free(heap); + + DBUG_RETURN(0); +} + +/** Bind the dynamic table INFORMATION_SCHEMA.innodb_sys_columns_added +param[in,out] p table schema object +@return 0 on success */ +static +int +innodb_sys_columns_added_init( + void* p) +{ + ST_SCHEMA_TABLE* schema; + + DBUG_ENTER("innodb_sys_columns_added_init"); + + schema = (ST_SCHEMA_TABLE*) p; + + schema->fields_info = innodb_sys_columns_added_fields_info; + schema->fill_table = i_s_sys_columns_added_fill_table; + + DBUG_RETURN(0); +} + +struct st_mysql_plugin i_s_innodb_sys_columns_added = +{ + /* the plugin type (a MYSQL_XXX_PLUGIN value) */ + /* int */ + STRUCT_FLD(type, MYSQL_INFORMATION_SCHEMA_PLUGIN), + + /* pointer to type-specific plugin descriptor */ + /* void* */ + STRUCT_FLD(info, &i_s_info), + + /* plugin name */ + /* const char* */ + STRUCT_FLD(name, "INNODB_SYS_COLUMNS_ADDED"), + + /* plugin author (for SHOW PLUGINS) */ + /* const char* */ + STRUCT_FLD(author, plugin_author), + + /* general descriptive text (for SHOW PLUGINS) */ + /* const char* */ + STRUCT_FLD(descr, "InnoDB SYS_COLUMNS_ADDED"), + + /* the plugin license (PLUGIN_LICENSE_XXX) */ + /* int */ + STRUCT_FLD(license, PLUGIN_LICENSE_GPL), + + /* the function to invoke when plugin is loaded */ + /* int (*)(void*); */ + STRUCT_FLD(init, innodb_sys_columns_added_init), + + /* the function to invoke when plugin is unloaded */ + /* int (*)(void*); */ + STRUCT_FLD(deinit, i_s_common_deinit), + + /* plugin version (for SHOW PLUGINS) */ + /* unsigned int */ + STRUCT_FLD(version, INNODB_VERSION_SHORT), + + /* struct st_mysql_show_var* */ + STRUCT_FLD(status_vars, NULL), + + /* struct st_mysql_sys_var** */ + STRUCT_FLD(system_vars, NULL), + + /* reserved for dependency checking */ + /* void* */ + STRUCT_FLD(__reserved1, NULL), + + /* Plugin flags */ + /* unsigned long */ + STRUCT_FLD(flags, 0UL) +}; diff --git a/storage/innobase/handler/i_s.h b/storage/innobase/handler/i_s.h index 4b95ae1..932ce05 100644 --- a/storage/innobase/handler/i_s.h +++ b/storage/innobase/handler/i_s.h @@ -60,0 +61 @@ extern struct st_mysql_plugin i_s_innodb_sys_virtual; +extern struct st_mysql_plugin i_s_innodb_sys_columns_added; diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index 07c698e..c6de88a 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -559 +559 @@ ibuf_init_at_db_start(void) - "innodb_change_buffer", IBUF_SPACE_ID, 1, 0, 0, 0); + "innodb_change_buffer", IBUF_SPACE_ID, 1, 0, 0, 0, 0); @@ -1473 +1473 @@ ibuf_dummy_index_create( - comp ? DICT_TF_COMPACT : 0, 0); + comp ? DICT_TF_COMPACT : 0, 0, 0); diff --git a/storage/innobase/include/data0data.ic b/storage/innobase/include/data0data.ic index dc51735..a5c8616 100644 --- a/storage/innobase/include/data0data.ic +++ b/storage/innobase/include/data0data.ic @@ -95,0 +96 @@ dfield_get_len( + ut_ad(field->len != UNIV_SQL_DEFAULT); @@ -109,0 +111 @@ dfield_set_len( + ut_ad(len != UNIV_SQL_DEFAULT); diff --git a/storage/innobase/include/dict0boot.h b/storage/innobase/include/dict0boot.h index 5884ba4..7eda02e 100644 --- a/storage/innobase/include/dict0boot.h +++ b/storage/innobase/include/dict0boot.h @@ -345,0 +346,17 @@ enum dict_fld_sys_virtual_enum { +/* The columns in SYS_COLUMNS_ADDED */ +enum dict_col_sys_columns_added_enum { + DICT_COL__SYS_COLUMNS_ADDED__TABLE_ID = 0, + DICT_COL__SYS_COLUMNS_ADDED__POS = 1, + DICT_COL__SYS_COLUMNS_ADDED__DEFAULT_VALUE = 2, + DICT_NUM_COLS__SYS_COLUMNS_ADDED = 3 +}; +/* The field numbers in the SYS_COLUMNS_ADDED clustered index */ +enum dict_fld_sys_columns_added_enum { + DICT_FLD__SYS_COLUMNS_ADDED__TABLE_ID = 0, + DICT_FLD__SYS_COLUMNS_ADDED__POS = 1, + DICT_FLD__SYS_COLUMNS_ADDED__DB_TRX_ID = 2, + DICT_FLD__SYS_COLUMNS_ADDED__DB_ROLL_PTR = 3, + DICT_FLD__SYS_COLUMNS_ADDED__DEFAULT_VALUE = 4, + DICT_NUM_FIELDS__SYS_COLUMNS_ADDED = 5 +}; + diff --git a/storage/innobase/include/dict0crea.h b/storage/innobase/include/dict0crea.h index 0fd1c41..a687b4a 100644 --- a/storage/innobase/include/dict0crea.h +++ b/storage/innobase/include/dict0crea.h @@ -224,0 +225,8 @@ dict_foreigns_has_s_base_col( +Creates the sys_columns_added system tables inside InnoDB at server +bootstrap or server start if they are not found or are not of the +right form. +@return DB_SUCCESS or error code */ +dberr_t +dict_create_or_check_sys_columns_added(); + +/****************************************************************//** diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 1f94773..5634490 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -2120,0 +2121,63 @@ dict_allocate_mem_intrinsic_cache( +/*********************************************************************//** +Sets default value of added columns */ +UNIV_INLINE +void +dict_col_set_added_column_default( +/*===============*/ + dict_col_t* col, /*!< in/out: column */ + const byte* def_val, /*!< in: pointer of default value */ + ulint def_val_len, /*!< in: length of default value */ + mem_heap_t* heap ); /*!< in: mem_heap_t for default value in col */ + +/********************************************************************//** +@return nullable count of n_core_fields */ +UNIV_INLINE +ulint +dict_index_get_first_n_field_n_nullable( +/*================*/ + const dict_index_t* index, /*!< in: index */ + ulint first_n_fields) /*!< in: Precede n fields */ + MY_ATTRIBUTE((nonnull, warn_unused_result)); + +/********************************************************************//** +Get the default value of column from dict +@return default value*/ +UNIV_INLINE +const byte* +dict_index_get_nth_field_def( +/*===================*/ + const dict_index_t* index, /*!< in: index */ + ulint pos, /*!< in: position of the field */ + ulint* len ); /*!< out: length of default value */ + +/*********************************************************************//** +Gets the column is nullable. +@return TRUE if nullable */ +UNIV_INLINE +ibool +dict_col_is_nullable( + const dict_col_t* col); /*!< in: column */ + +/** encode flags2 and number of core columns in one +4 bytes value. We could do this because the number of columns in +InnoDB is limited to 1017 +@param[in] flags2 +@param[in] n_cols_core column number before first time instant add column +@return encoded value */ +UNIV_INLINE +ulint +dict_table_encode_mix_len( + ulint flags2, + ulint n_cols_core); + +/** Decode number of flags2 and number of core columns in one 4 bytes value. +@param[in] encoded encoded value +@param[in,out] flags2 +@param[in,out] n_cols_core column number before first time instant add column*/ +UNIV_INLINE +void +dict_table_decode_mix_len( + ulint encoded, + ulint* flags2, + ulint* n_cols_core); + diff --git a/storage/innobase/include/dict0dict.ic b/storage/innobase/include/dict0dict.ic index d95b206..c542763 100644 --- a/storage/innobase/include/dict0dict.ic +++ b/storage/innobase/include/dict0dict.ic @@ -1312,0 +1313,125 @@ dict_index_get_nth_col_pos( +/*********************************************************************//** +Sets default value of added columns */ +UNIV_INLINE +void +dict_col_set_added_column_default( + dict_col_t* col, /*!< in: column */ + const byte* def_val, /*!< in: default value */ + ulint def_val_len, /*!< in: the length of default value */ + mem_heap_t* heap ) /*!< in: memory heap for dict object */ +{ + ut_a(col); + + col->def_val = (dict_col_def_t*)mem_heap_alloc(heap, sizeof(*col->def_val)); + col->def_val->col = col; + col->def_val->def_val_len = def_val_len; + if (def_val) { + col->def_val->def_val = (byte*)mem_heap_strdupl(heap, (char*)def_val, def_val_len); + } else { + col->def_val->def_val = NULL; + } +} + +/*********************************************************************//** +Gets the column is nullable. +@return TRUE if nullable */ +UNIV_INLINE +ibool +dict_col_is_nullable( +/*============*/ + const dict_col_t* col) /*!< in: column */ +{ + return (col->prtype & DATA_NOT_NULL) == 0; +} + +/** @return whether the index is the clustered index of instant table. +@retval true if clustered index of instant table +@retval false otherwise */ +inline +bool +dict_index_t::is_instant() const +{ + return dict_index_is_clust(this) && table->is_instant(); +} + +/********************************************************************//** +Determine how many fields of a given prefix can be set NULL. */ +UNIV_INLINE +ulint +dict_index_get_first_n_field_n_nullable( +/*================*/ + const dict_index_t* index, /*!< in: index */ + ulint first_n_fields) /*!< in: prefix n fields */ +{ + ulint n_nullable = index->n_nullable; + const dict_field_t* ifield; + ulint i = 0; + ulint n_fields; + + n_fields = dict_index_get_n_fields(index); + ut_a(first_n_fields <= n_fields); + for (i = first_n_fields ; i < n_fields; ++i) { + ifield = dict_index_get_nth_field(index, i); + + if (dict_col_is_nullable(dict_field_get_col(ifield))) + n_nullable--; + } + + return n_nullable; +} + +/********************************************************************//** +Get the default value of column from dict +@return default value*/ +UNIV_INLINE +const byte* +dict_index_get_nth_field_def( +/*===================*/ + const dict_index_t* index, /*!< in: index */ + ulint pos, /*!< in: position of the field */ + ulint* len ) /*!< out: length of default value */ +{ + const dict_col_t* col; + + col = dict_index_get_nth_col(index, pos); + ut_ad(col->def_val); + *len = col->def_val->def_val_len; + return col->def_val->def_val; + +} + +/** encode flags2 and number of core columns in one +4 bytes value. We could do this because the number of columns in +InnoDB is limited to 1017 +@param[in] flags2 +@param[in] n_cols_core column number before first time instant add column +@return encoded value */ +UNIV_INLINE +ulint +dict_table_encode_mix_len( +ulint flags2, +ulint n_cols_core) +{ + /* Be sure all non-used bits are zero. */ + ut_a(!(flags2 & DICT_TF2_UNUSED_BIT_MASK)); + ut_a(!(flags2 & 0xFFFF0000)); + + return (flags2 + (n_cols_core << 16)); +} + +/** Decode number of flags2 and number of core columns in one 4 bytes value. +@param[in] encoded encoded value +@param[in,out] flags2 +@param[in,out] n_cols_core column number before first time instant add column*/ +UNIV_INLINE +void +dict_table_decode_mix_len( +ulint encoded, +ulint* flags2, +ulint* n_cols_core) +{ + *flags2 = encoded & 0xFFFF; + + *n_cols_core = encoded >> 16; +} + diff --git a/storage/innobase/include/dict0load.h b/storage/innobase/include/dict0load.h index 6d01c38..c4e14a3 100644 --- a/storage/innobase/include/dict0load.h +++ b/storage/innobase/include/dict0load.h @@ -53,0 +54 @@ enum dict_system_id_t { + SYS_COLUMNS_ADDED, @@ -440,0 +442,19 @@ dict_replace_tablespace_and_filepath( +/** This function parses a SYS_COLUMNS_ADDED record and extracts added column +information +@param[in,out] heap heap memory +@param[in] rec current SYS_COLUMNS_ADDED rec +@param[in] index cluster index of SYS_COLUMNS_ADDED +@param[in,out] table_id table id +@param[in,out] pos column position +@param[in,out] def_val default value of column +@return error message, or NULL on success */ +const char* +dict_process_sys_columns_added_rec( + mem_heap_t* heap, + const rec_t* rec, + dict_index_t* index, + table_id_t* table_id, + ulint* pos, + char** def_val, + ulint* def_val_len); + diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 9c16081..705029a 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -225,0 +226,3 @@ for unknown bits in order to protect backward incompatibility. */ +/* The higher two bytes of SYS_TABLES.MIX_LEN is used by +instant add columns */ +#define DICT_TF2_MAX_BITS 16 @@ -230,0 +234,4 @@ for unknown bits in order to protect backward incompatibility. */ +#if DICT_TF2_BITS > DICT_TF2_MAX_BITS +#error "DICT_TF2_BITS > DICT_TF2_MAX_BITS" +#endif + @@ -302,0 +310,3 @@ dict_mem_table_create( + ulint n_cols_core, /*!< in: total number of columns before + first time instant add column. + If zero, means non-instant */ @@ -312,0 +323,10 @@ dict_mem_table_free( +Fake column default values for recovery */ +void +dict_mem_table_fake_nth_col_default( +/*================*/ + dict_table_t* table, /*!< in/out: table, set the default values + for the nth columns */ + ulint pos, /*!< in: the position of column in table */ + mem_heap_t* heap /*!< in: mem_heap for default value */ +); +/**********************************************************************//** @@ -549,0 +570,7 @@ struct table_name_t +/** Data structure for a column added default value */ +struct dict_col_def_t { + dict_col_t* col; + byte* def_val; + unsigned def_val_len; +}; + @@ -592,0 +620,2 @@ struct dict_col_t{ + + dict_col_def_t* def_val;/*!< default value of added columns */ @@ -904,0 +934,4 @@ struct dict_index_t{ + unsigned n_core_fields:10;/*!< number of fields in the index + (before the first time of instant add columns) */ + unsigned n_core_nullable:10;/*!< number of nullable fields + (before the first time of instant add columns) */ @@ -1016,0 +1050,4 @@ struct dict_index_t{ + /** @return whether the index is the clustered index of instant table. + @retval true if clustered index of instant table + @retval false otherwise */ + inline bool is_instant() const; @@ -1326,0 +1364,8 @@ struct dict_table_t { + /** @return whether the table has been instant added columns. + @retval true if table has been instant added columns + @retval false if table hasn't been instant added columns */ + bool is_instant() const + { + return (n_core_cols < n_cols); + } + @@ -1403,0 +1449,4 @@ struct dict_table_t { + /** Number of non-virtual columns before the first time instant add columns. + If n_cols_core < n_cols, means dict_table_is_instant return TRUE */ + unsigned n_core_cols : 10; + diff --git a/storage/innobase/include/dict0mem.ic b/storage/innobase/include/dict0mem.ic index 393211d..8573fbd 100644 --- a/storage/innobase/include/dict0mem.ic +++ b/storage/innobase/include/dict0mem.ic @@ -69 +69,2 @@ dict_mem_fill_index_struct( - index->n_fields = (unsigned int) n_fields; + index->n_fields = (unsigned int) n_fields; + index->n_core_fields = (unsigned int)n_fields; diff --git a/storage/innobase/include/gis0rtree.ic b/storage/innobase/include/gis0rtree.ic index a30db12..ecbc231 100644 --- a/storage/innobase/include/gis0rtree.ic +++ b/storage/innobase/include/gis0rtree.ic @@ -40 +40 @@ rtr_page_cal_mbr( - byte* field; + const byte* field; diff --git a/storage/innobase/include/rem0rec.h b/storage/innobase/include/rem0rec.h index c99a63c..24d611d 100644 --- a/storage/innobase/include/rem0rec.h +++ b/storage/innobase/include/rem0rec.h @@ -45,0 +46,2 @@ B-tree page that is the leftmost page on its level +#define REC_INFO_ADDED_FLAG 0x80UL /* when bit is set to 1, it means the + record has been instant added columns */ @@ -52,0 +55,3 @@ in addition to the data and the offsets */ +/* Number of extra bytes in a new-style temp record, +in addition to the data and the offsets(after instant add columns) */ +#define REC_N_TMP_EXTRA_BYTES 1 @@ -59,0 +65,5 @@ in addition to the data and the offsets */ +/* REC_FLAG for instant add columns */ +#define REC_FLAG_NONE 0x00 +#define REC_FLAG_INSTANT 0x01 +#define REC_FLAG_NODE_PTR 0x02 + @@ -548,0 +559,18 @@ rec_get_nth_field_offs( + +/************************************************************//** +Get the nth field from cluster index +@return pointer of field(Maybe get the pointer of default value of dictionary) */ +const byte* +rec_get_nth_cfield( + const rec_t* rec, /*!< in: rec */ + const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ + ulint n, /*!< in: index of the field */ + const dict_index_t* index, /*!< in: dict_index of rec */ + mem_heap_t* heap, /*!< in: mem_heap for default + value of instant added columns */ + ulint* len); /*!< out: length of the field; UNIV_SQL_NULL + if SQL null */ + +/* Never return DEFAULT value(UNIV_SQL_DEFAULT) + It always return pointer inside the record +*/ @@ -550 +578,2 @@ rec_get_nth_field_offs( -((rec) + rec_get_nth_field_offs(offsets, n, len)) +(byte*)rec_get_nth_cfield(rec, offsets, n, NULL, NULL, len) + @@ -571,0 +601,10 @@ rec_offs_any_extern( +Determine if the offsets are for a record containing +default value for instant added columns. +@return nonzero if externally stored */ +UNIV_INLINE +ulint +rec_offs_any_default( +/*================*/ + const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ + MY_ATTRIBUTE((warn_unused_result)); +/******************************************************//** @@ -591,0 +631,11 @@ rec_offs_nth_extern( +/******************************************************//** +Returns nonzero if the extern bit is set in nth field of rec. +@return nonzero if externally stored */ +UNIV_INLINE +ulint +rec_offs_nth_extern_old( +/*================*/ + const rec_t* rec, /*!< in: record */ + ulint n /*!< in: index of the field */) + MY_ATTRIBUTE((warn_unused_result)); + @@ -831 +881 @@ rec_fold( - index_id_t tree_id) + const dict_index_t* index) @@ -960,0 +1011,82 @@ rec_print( +/******************************************************//** +Returns nonzero if the default bit is set in nth field of rec. +@return nonzero if default bit is set */ +UNIV_INLINE +ulint +rec_offs_nth_default( +/*================*/ + const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ + ulint n); /*!< in: nth field */ + +/******************************************************//** +set instant flag */ +UNIV_INLINE +void +rec_set_instant_flag( +/*=====================*/ + rec_t* rec, /*!< in/out: new-style physical record */ + ulint flag); /*!< in: nonzero if instant marked */ + +/******************************************************//** +@return TRUE if instant record type */ +UNIV_INLINE +ibool +rec_is_instant_func( +/*=====================*/ +#ifdef UNIV_DEBUG + const dict_index_t* index, /*!< in: index of rec */ +#endif + const rec_t* rec, /*!< in: new-style physical record */ + const ulint extra_bytes);/*!< in: extra_bytes */ + +#ifdef UNIV_DEBUG +# define rec_is_instant(index, rec) rec_is_instant_func(index, rec, REC_N_NEW_EXTRA_BYTES) +#else +# define rec_is_instant(index, rec) rec_is_instant_func(rec, REC_N_NEW_EXTRA_BYTES) +#endif + +#ifdef UNIV_DEBUG +# define rec_is_instant_tmp(index, rec) rec_is_instant_func(index, rec, REC_N_TMP_EXTRA_BYTES) +#else +# define rec_is_instant_tmp(index, rec) rec_is_instant_func(rec, REC_N_TMP_EXTRA_BYTES) +#endif + +/**********************************************************//** +Returns length of field count input +@return size */ +UNIV_INLINE +ulint rec_get_field_count_len ( +/*==========*/ + ulint field_count ); /*!< in: field count*/ + +/**********************************************************//** +Returns field count of instant record +@return size */ +UNIV_INLINE +ulint +rec_get_field_count( +/*==========*/ + const rec_t* rec, /*!< in: the record ptr */ + ulint* field_count_len); /*!< out: occupy size of field count */ + +/**********************************************************//** +Returns field count of ptr +@return size */ +UNIV_INLINE +ulint +rec_get_field_count_low( +/*==========*/ + const rec_t* rec, /*!< in: the record ptr */ + const ulint extra_size, /*!< in: the extra size */ + ulint* field_count_len); /*!< out: occupy size of field count */ + +/**********************************************************//** +Set field count of instant record +@return the occupy size of field count */ +UNIV_INLINE +ulint +rec_set_field_count( +/*==========*/ + rec_t* rec, /*!< in: the record ptr */ + const ulint n_fields); /*!< in: field count of instant record */ + diff --git a/storage/innobase/include/rem0rec.ic b/storage/innobase/include/rem0rec.ic index b855a39..0c795e3 100644 --- a/storage/innobase/include/rem0rec.ic +++ b/storage/innobase/include/rem0rec.ic @@ -37,0 +38,2 @@ Created 5/30/1994 Heikki Tuuri +/* Default value flag in offsets returned by rec_get_offsets() */ +#define REC_OFFS_DEFAULT ((ulint) 1 << 29) @@ -39 +41 @@ Created 5/30/1994 Heikki Tuuri -#define REC_OFFS_MASK (REC_OFFS_EXTERNAL - 1) +#define REC_OFFS_MASK (REC_OFFS_DEFAULT - 1) @@ -118,0 +121 @@ and the shift needed to obtain each bit-field of the record. */ +#define REC_TMP_INFO_BITS 1 /* This is single byte bit-field */ @@ -599 +602 @@ rec_info_bits_valid( - return(0 == (bits & ~(REC_INFO_DELETED_FLAG | REC_INFO_MIN_REC_FLAG))); + return(0 == (bits & ~(REC_INFO_DELETED_FLAG | REC_INFO_MIN_REC_FLAG | REC_INFO_ADDED_FLAG))); @@ -620,0 +624,17 @@ rec_get_info_bits( +The following function is used to retrieve the info bits of a record. +@return info bits */ +UNIV_INLINE +ulint +rec_get_info_bits_tmp( +/*==============*/ + const rec_t* rec, /*!< in: physical record */ + ulint comp) /*!< in: nonzero=compact page format */ +{ + const ulint val = rec_get_bit_field_1( + rec, REC_TMP_INFO_BITS, + REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT); + ut_ad(rec_info_bits_valid(val)); + return(val); +} + +/******************************************************//** @@ -1074 +1094 @@ rec_get_nth_field_offs( - if SQL null */ + if SQL null; UNIV_SQL_DEFAULT is default value */ @@ -1090,0 +1111,2 @@ rec_get_nth_field_offs( + } else if (length & REC_OFFS_DEFAULT) { + length = UNIV_SQL_DEFAULT; @@ -1128,0 +1151,14 @@ rec_offs_any_extern( +Determine if the offsets are for a record containing +default value for instant added columns. +@return nonzero if externally stored */ +UNIV_INLINE +ulint +rec_offs_any_default( +/*================*/ + const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ +{ + ut_ad(rec_offs_validate(NULL, NULL, offsets)); + return(*rec_offs_base(offsets) & REC_OFFS_DEFAULT); +} + +/******************************************************//** @@ -1191 +1227,33 @@ rec_offs_nth_sql_null( - return(rec_offs_base(offsets)[1 + n] & REC_OFFS_SQL_NULL); + ulint ret = rec_offs_base(offsets)[1 + n] & REC_OFFS_SQL_NULL; + ut_ad(!ret || !rec_offs_nth_default(offsets, n)); + return ret; +} + +/******************************************************//** +Returns nonzero if the extern bit is set in nth field of rec. +@return nonzero if externally stored */ +UNIV_INLINE +ulint +rec_offs_nth_extern_old( +/*================*/ + const rec_t* rec, /*!< in: record */ + ulint n /*!< in: index of the field */) +{ + if(rec_get_1byte_offs_flag(rec)) + return 0; + return (rec_2_get_field_end_info(rec,n) & REC_2BYTE_EXTERN_MASK); +} + +/******************************************************//** +Returns nonzero if the default bit is set in nth field of rec. +@return nonzero if default bit is set */ +UNIV_INLINE +ulint +rec_offs_nth_default( +/*================*/ + const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ + ulint n) /*!< in: nth field */ +{ + ut_ad(rec_offs_validate(NULL, NULL, offsets)); + ut_ad(n < rec_offs_n_fields(offsets)); + return(rec_offs_base(offsets)[1 + n] & REC_OFFS_DEFAULT); @@ -1439 +1507,2 @@ rec_set_nth_field( - data2 = rec_get_nth_field(rec, offsets, n, &len2); + ut_ad(!rec_offs_nth_default(offsets, n)); + data2 = (byte*)rec_get_nth_field(rec, offsets, n, &len2); @@ -1520 +1589 @@ rec_offs_extra_size( - size = *rec_offs_base(offsets) & ~(REC_OFFS_COMPACT | REC_OFFS_EXTERNAL); + size = *rec_offs_base(offsets) & REC_OFFS_MASK; @@ -1536,0 +1606,157 @@ rec_offs_size( +/******************************************************//** +set instant flag */ +UNIV_INLINE +void +rec_set_instant_flag( +/*=====================*/ + rec_t* rec, /*!< in/out: new-style physical record */ + ulint flag) /*!< in: nonzero if instant marked */ +{ + ulint val; + + val = rec_get_info_bits(rec, TRUE); + + if (flag) { + val |= REC_INFO_ADDED_FLAG; + } else { + val &= ~REC_INFO_ADDED_FLAG; + } + + rec_set_info_bits_new(rec, val); +} + +/******************************************************//** +@return TRUE if instant record type */ +UNIV_INLINE +ibool +rec_is_instant_func( +/*=====================*/ +#ifdef UNIV_DEBUG + const dict_index_t* index, /*!< in: index of rec */ +#endif + const rec_t* rec, /*!< in: new-style physical record */ + const ulint extra_bytes) /*!< in: extra_bytes */ +{ + ulint val; + + switch (extra_bytes) + { + case REC_N_NEW_EXTRA_BYTES: + val = rec_get_info_bits(rec, TRUE); + break; + case REC_N_TMP_EXTRA_BYTES: + val = rec_get_info_bits_tmp(rec, TRUE); + break; + default: + ut_ad(!extra_bytes); + return FALSE; + } + + val = ((val & REC_INFO_ADDED_FLAG) != 0); + +#ifdef UNIV_DEBUG + ut_ad(!val || (index->is_instant() && !dict_index_is_ibuf(index))); +#endif + + return val; +} + +#define REC_FIELD_COUNT_TWO_BYTES_FLAG 0x80 +#define REC_FIELD_COUNT_ONE_BYTE_MAX 0x7F +#define REC_FIELD_COUNT_HIGHER_BYTE_MASK 0x7F + +/**********************************************************//** +Returns field count of instant record +@return size */ +UNIV_INLINE +ulint +rec_get_field_count( +/*==========*/ + const rec_t* rec, /*!< in: the record ptr */ + ulint* field_count_len) /*!< out: occupy size of field count */ +{ + return rec_get_field_count_low(rec, REC_N_NEW_EXTRA_BYTES, field_count_len); +} + +/**********************************************************//** +Returns field count of ptr +@return size */ +UNIV_INLINE +ulint +rec_get_field_count_low( +/*==========*/ + const rec_t* rec, /*!< in: the record ptr */ + const ulint extra_size, /*!< in: the extra size */ + ulint* field_count_len) /*!< out: occupy size of field count */ +{ + ulint ret; + byte* ptr; + + ptr = (byte*)rec - (extra_size + 1); + + if ((*ptr & REC_FIELD_COUNT_TWO_BYTES_FLAG) == 0) { + if (field_count_len != NULL) { + *field_count_len = 1; + } + + ret = *ptr; + + return ret; + } + + if (field_count_len != NULL) { + *field_count_len = 2; + } + + ret = ((*ptr-- & REC_FIELD_COUNT_HIGHER_BYTE_MASK) << 8); // higher byte + ret |= *ptr; // lower byte + ut_ad(ret < REC_MAX_N_FIELDS && ret > REC_FIELD_COUNT_ONE_BYTE_MAX); //TODO + + return ret; +} + +/**********************************************************//** +Returns length of field count input +@return size */ +UNIV_INLINE +ulint rec_get_field_count_len ( +/*==========*/ + ulint field_count ) /*!< in: field count*/ +{ + if (field_count > REC_FIELD_COUNT_ONE_BYTE_MAX) + return 2; + + return 1; +} + +#define rec_get_feild_count_len(n_feilds) ((n_feilds > 127) ? 2 : 1) + +/**********************************************************//** +Set field count of instant record +@return the occupy size of field count */ +UNIV_INLINE +ulint +rec_set_field_count( +/*==========*/ + rec_t* rec, /*!< in: the record ptr */ + const ulint n_fields) /*!< in: field count of instant record */ +{ + byte* ptr; + + ut_ad(n_fields < REC_MAX_N_FIELDS); + + if (n_fields <= REC_FIELD_COUNT_ONE_BYTE_MAX) { + ptr = rec - (REC_N_NEW_EXTRA_BYTES + 1); + *ptr = (byte)n_fields; + return 1; + } + + ptr = rec - (REC_N_NEW_EXTRA_BYTES + 2); + *ptr++ = (byte)(n_fields & 0xFF); /** lower byte */ + *ptr = (byte)(n_fields >> 8); /** higher byte */ + ut_ad((*ptr & 0x80) == 0); + *ptr |= REC_FIELD_COUNT_TWO_BYTES_FLAG; /** flag */ + + return 2; +} + @@ -1705 +1931 @@ rec_get_converted_size( -@param[in] index_id index tree ID +@param[in] index index @@ -1714 +1940 @@ rec_fold( - index_id_t tree_id) + const dict_index_t* index) @@ -1720,0 +1947 @@ rec_fold( + index_id_t tree_id = index->id; @@ -1741 +1968 @@ rec_fold( - data = rec_get_nth_field(rec, offsets, i, &len); + data = rec_get_nth_cfield(rec, offsets, i, index, NULL, &len); @@ -1750 +1977 @@ rec_fold( - data = rec_get_nth_field(rec, offsets, i, &len); + data = rec_get_nth_cfield(rec, offsets, i, index, NULL, &len); diff --git a/storage/innobase/include/row0row.h b/storage/innobase/include/row0row.h index 903d7d1..fff5df8 100644 --- a/storage/innobase/include/row0row.h +++ b/storage/innobase/include/row0row.h @@ -286,2 +286,2 @@ row_build_row_ref_fast( - const rec_t* rec, /*!< in: record in the index; must be - preserved while ref is used, as we do + const rec_t* rec, /*!< in: record in the non-cluster index; + must be preserved while ref is used, as we do diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 72ab814..c878be4 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -494,0 +495,6 @@ computers! */ +/** Flag for default value of instant added column */ +#define UNIV_SQL_DEFAULT (UNIV_SQL_NULL - 1) + +/** Check the record whether store in the row */ +#define len_is_stored(len) (len != UNIV_SQL_NULL && len != UNIV_SQL_DEFAULT) + diff --git a/storage/innobase/mtr/mtr0log.cc b/storage/innobase/mtr/mtr0log.cc index b64e700..6989381 100644 --- a/storage/innobase/mtr/mtr0log.cc +++ b/storage/innobase/mtr/mtr0log.cc @@ -448,0 +449 @@ mlog_open_and_write_index( + ibool is_instant = index->is_instant(); @@ -450 +451 @@ mlog_open_and_write_index( - ulint total = 11 + size + (n + 2) * 2; + ulint total = 11 + (is_instant ? 2 : 0) + size + (n + 2) * 2; @@ -475 +476,14 @@ mlog_open_and_write_index( - mach_write_to_2(log_ptr, n); + if (is_instant) { + // marked as instant index + mach_write_to_2(log_ptr, n | 0x8000); + + log_ptr += 2; + + // record the n_core_fields + mach_write_to_2(log_ptr, index->n_core_fields); + + } + else { + mach_write_to_2(log_ptr, n); + } + @@ -552,0 +567,2 @@ mlog_parse_index( + ulint n_core_fields = 0; + ibool is_instant = FALSE; @@ -561,0 +578,12 @@ mlog_parse_index( + if (n & 0x8000) { // instant record + is_instant = TRUE; + n &= 0x7FFF; + + n_core_fields = mach_read_from_2(ptr); + ptr += 2; + ut_ad(n_core_fields <= n && n_core_fields > 0); + + if (end_ptr < ptr + 2) { + return(NULL); + } + } @@ -571 +599 @@ mlog_parse_index( - table = dict_mem_table_create("LOG_DUMMY", DICT_HDR_SPACE, n, 0, + table = dict_mem_table_create("LOG_DUMMY", DICT_HDR_SPACE, n, n_core_fields, 0, @@ -594,0 +623,8 @@ mlog_parse_index( + if (is_instant && + n_core_fields >0 && n_core_fields <= i) { + + /* For recovery, it does not need the true default values. + We fake it!*/ + dict_mem_table_fake_nth_col_default(table, i, table->heap); + } + @@ -612,0 +649,13 @@ mlog_parse_index( + + if (ind->is_instant()) { + ut_ad(table->n_cols == table->n_def); + ut_ad(table->n_core_cols > 0 && + table->n_core_cols <= table->n_cols); + + ind->n_core_fields = n_core_fields; + ind->n_core_nullable = dict_index_get_first_n_field_n_nullable(ind, ind->n_core_fields); + } + else { + ind->n_core_nullable = ind->n_nullable; + ind->n_core_fields = ind->n_fields; + } diff --git a/storage/innobase/page/page0cur.cc b/storage/innobase/page/page0cur.cc index d3f39b6..14ac64f 100644 --- a/storage/innobase/page/page0cur.cc +++ b/storage/innobase/page/page0cur.cc @@ -264,0 +265 @@ page_cur_rec_field_extends( + const dict_index_t* index, /*!< in: index of rec */ @@ -277 +278 @@ page_cur_rec_field_extends( - rec_f = rec_get_nth_field(rec, offsets, n, &rec_f_len); + rec_f = rec_get_nth_cfield(rec, offsets, n, index, NULL, &rec_f_len); @@ -549 +550 @@ low_slot_match: - tuple, mid_rec, offsets, + tuple, mid_rec, offsets, index, @@ -611 +612 @@ low_rec_match: - tuple, mid_rec, offsets, + tuple, mid_rec, offsets, index, @@ -805 +806 @@ low_slot_match: - tuple, mid_rec, offsets, + tuple, mid_rec, offsets, index, @@ -862 +863 @@ low_rec_match: - tuple, mid_rec, offsets, + tuple, mid_rec, offsets, index diff --git a/storage/innobase/page/page0zip.cc b/storage/innobase/page/page0zip.cc index cb5fe4c..625f4e9 100644 --- a/storage/innobase/page/page0zip.cc +++ b/storage/innobase/page/page0zip.cc @@ -1705 +1705 @@ page_zip_fields_decode( - table = dict_mem_table_create("ZIP_DUMMY", DICT_HDR_SPACE, n, 0, + table = dict_mem_table_create("ZIP_DUMMY", DICT_HDR_SPACE, n, 0, 0, @@ -1772,0 +1773,4 @@ page_zip_fields_decode( + /* set core fields for internal index */ + index->n_core_fields = index->n_fields; + index->n_core_nullable = index->n_nullable; + @@ -2285,0 +2290,3 @@ page_zip_decompress_heap_no( + + /* Set the info bit to zero for instant add columns */ + rec[-REC_N_NEW_EXTRA_BYTES] = 0; diff --git a/storage/innobase/pars/pars0pars.cc b/storage/innobase/pars/pars0pars.cc index 6651475..9d50f04 100644 --- a/storage/innobase/pars/pars0pars.cc +++ b/storage/innobase/pars/pars0pars.cc @@ -1932 +1932 @@ pars_create_table( - table_sym->name, 0, n_cols, 0, flags, flags2); + table_sym->name, 0, n_cols, 0, 0, flags, flags2); diff --git a/storage/innobase/rem/rem0cmp.cc b/storage/innobase/rem/rem0cmp.cc index 21a1c1d..6a44490 100644 --- a/storage/innobase/rem/rem0cmp.cc +++ b/storage/innobase/rem/rem0cmp.cc @@ -418,0 +419,2 @@ cmp_data( + ut_ad(len1 != UNIV_SQL_DEFAULT && len2 != UNIV_SQL_DEFAULT); + @@ -723,0 +726,6 @@ cmp_dtuple_rec_with_match_low( + /* We should never compare against instant add columns. + Columns can only be instantly added to clustered index + leaf page records, and the first fields (primary key fields) + should already differ.*/ + ut_ad(!rec_offs_nth_default(offsets, cur_field)); + @@ -838,0 +847,2 @@ cmp_dtuple_rec_with_match_bytes( + + ut_ad(!rec_offs_nth_default(offsets, cur_field)); @@ -1025,2 +1035,2 @@ cmp_rec_rec_simple_field( - rec1_b_ptr = rec_get_nth_field(rec1, offsets1, n, &rec1_f_len); - rec2_b_ptr = rec_get_nth_field(rec2, offsets2, n, &rec2_f_len); + rec1_b_ptr = rec_get_nth_cfield(rec1, offsets1, n, index, NULL, &rec1_f_len); + rec2_b_ptr = rec_get_nth_cfield(rec2, offsets2, n, index, NULL, &rec2_f_len); @@ -1214,4 +1224,4 @@ cmp_rec_rec_with_match( - rec1_b_ptr = rec_get_nth_field(rec1, offsets1, - cur_field, &rec1_f_len); - rec2_b_ptr = rec_get_nth_field(rec2, offsets2, - cur_field, &rec2_f_len); + rec1_b_ptr = rec_get_nth_cfield(rec1, offsets1, + cur_field, index, NULL, &rec1_f_len); + rec2_b_ptr = rec_get_nth_cfield(rec2, offsets2, + cur_field, index, NULL, &rec2_f_len); diff --git a/storage/innobase/rem/rem0rec.cc b/storage/innobase/rem/rem0rec.cc index c6b5bd1..9137f0d 100644 --- a/storage/innobase/rem/rem0rec.cc +++ b/storage/innobase/rem/rem0rec.cc @@ -240,0 +241,12 @@ rec_get_n_extern_new( +/*******************************************************************//** +Get the bit number of nullable bitmap. */ +ulint +rec_get_n_nullable( +/*=======================*/ + const rec_t* rec, /*!< in: instant record */ + const dict_index_t* index, /*!< in: clustered index */ + const ulint field_count)/*!< in: field count */ +{ + return dict_index_get_first_n_field_n_nullable(index, field_count); +} + @@ -261,5 +273,5 @@ rec_init_offsets_comp_ordinary( - ulint n_null = index->n_nullable; - const byte* nulls = temp - ? rec - 1 - : rec - (1 + REC_N_NEW_EXTRA_BYTES); - const byte* lens = nulls - UT_BITS_IN_BYTES(n_null); + ulint any_def = 0; + const byte* nulls = NULL; + const byte* lens = NULL; + ulint field_count = ULINT_UNDEFINED; /* Init as a big value */ + ulint n_null = ULINT_UNDEFINED; @@ -266,0 +279,39 @@ rec_init_offsets_comp_ordinary( + ulint extra_bytes = 0; + bool is_instant = false; + if (temp) { + if (index->is_instant()) { + extra_bytes = REC_N_TMP_EXTRA_BYTES; + is_instant = rec_is_instant_tmp(index, rec); + } + } else { + extra_bytes = REC_N_NEW_EXTRA_BYTES; + is_instant = rec_is_instant(index, rec); + } + + if (is_instant) { + ulint field_count_len; + + ut_ad(index->is_instant()); + field_count = rec_get_field_count_low(rec, extra_bytes, &field_count_len); + + nulls = (byte*)rec - (1 + extra_bytes + field_count_len); + + n_null = rec_get_n_nullable(rec, index, field_count); + lens = nulls - UT_BITS_IN_BYTES(n_null); + + } else if (index->is_instant()) { + ut_ad(index->n_core_fields > 0); + + n_null = index->n_core_nullable; + field_count = index->n_core_fields; + + nulls = rec - (1 + extra_bytes); + + lens = nulls - UT_BITS_IN_BYTES(n_null); + } else { + n_null = index->n_nullable; + + nulls = rec - (1 + extra_bytes); + + lens = nulls - UT_BITS_IN_BYTES(n_null); + } @@ -291,0 +343,16 @@ rec_init_offsets_comp_ordinary( + /* set default value flag */ + if (i >= field_count) { + ulint def_len; + + ut_ad(is_instant || index->is_instant()); + if (!dict_index_get_nth_field_def(index, i, &def_len)) { + len = offs | REC_OFFS_SQL_NULL; + ut_ad(def_len == UNIV_SQL_NULL); + } else { + len = offs | REC_OFFS_DEFAULT; + any_def = REC_OFFS_DEFAULT; + } + + goto resolved; + } + @@ -356 +423 @@ resolved: - = (rec - (lens + 1)) | REC_OFFS_COMPACT | any_ext; + = (rec - (lens + 1)) | REC_OFFS_COMPACT | any_ext | any_def; @@ -413,0 +481,12 @@ rec_init_offsets( + /** The n_nullable flags in the clustered index node pointer + records in ROW_FORMAT=COMPACT or ROW_FORMAT=DYNAMIC must + reflect the number of 'core columns'. These flags are + useless garbage, and they are only reserved because of + file format compatibility. + (Clustered index node pointer records only contain the + PRIMARY KEY columns, which are always NOT NULL, + so we should have used n_nullable=0.) + */ + ut_ad(!rec_is_instant(index, rec)); + ut_ad(index->n_core_fields > 0); + @@ -415 +494 @@ rec_init_offsets( - lens = nulls - UT_BITS_IN_BYTES(index->n_nullable); + lens = nulls - UT_BITS_IN_BYTES(index->n_core_nullable); @@ -731,0 +811,36 @@ resolved: +Get the nth field from cluster index +@return pointer of field(Maybe get the pointer of default value of dictionary) */ +const byte* +rec_get_nth_cfield( + const rec_t* rec, /*!< in: rec */ + const ulint* offsets, /*!< in: array returned by rec_get_offsets() */ + ulint n, /*!< in: index of the field */ + const dict_index_t* index, /*!< in: dict_index of rec. */ + mem_heap_t* heap, /*!< in: mem_heap for default value of instant added columns + IF NULL, return the dictionary memory directly. + It must be read only when heap = NULL. */ + ulint* len) /*!< out: length of the field; UNIV_SQL_NULL if SQL null */ +{ + const byte* field; + ulint off = rec_get_nth_field_offs(offsets, n, len); + + if (*len != UNIV_SQL_DEFAULT) { + return rec + off; + } + + ut_a(index); + ut_ad(dict_index_is_clust(index)); + + /* If heap = NULL, return the dictionary memory directly. */ + field = dict_index_get_nth_field_def(index, n, len); + + ut_ad(*len != UNIV_SQL_DEFAULT); + if (heap && field) { + ut_a(*len != UNIV_SQL_NULL); + field = static_cast(mem_heap_dup(heap, field, (*len))); + } + + return field; +} + +/************************************************************//** @@ -799,0 +915 @@ rec_get_converted_size_comp_prefix_low( + ulint rec_flag, /*!< in: REC_FLAG_* */ @@ -803 +919 @@ rec_get_converted_size_comp_prefix_low( - ulint extra_size; + ulint extra_size = 0; @@ -816,4 +932,18 @@ rec_get_converted_size_comp_prefix_low( - extra_size = temp - ? UT_BITS_IN_BYTES(n_null) - : REC_N_NEW_EXTRA_BYTES - + UT_BITS_IN_BYTES(n_null); + extra_size += temp ? 0 : REC_N_NEW_EXTRA_BYTES; + + if (index->is_instant()) { + if (rec_flag & REC_FLAG_NODE_PTR) { + ut_ad(!temp && n_fields > 0 && !n_v_fields); + + /* Always use n_core_nullable for NODE_PTR */ + n_null = index->n_core_nullable; + + } + else if (rec_flag & REC_FLAG_INSTANT) { + ut_ad(!temp && n_fields > 0 && !n_v_fields); + + extra_size += rec_get_field_count_len(n_fields); + } + } + + extra_size += UT_BITS_IN_BYTES(n_null); @@ -974 +1104 @@ rec_get_converted_size_comp_prefix( - index, fields, n_fields, NULL, extra, false)); + index, fields, n_fields, NULL, extra, REC_FLAG_NONE, false)); @@ -993,0 +1124 @@ rec_get_converted_size_comp( + ulint rec_flag = REC_FLAG_NONE; @@ -998,0 +1130 @@ rec_get_converted_size_comp( + rec_flag |= REC_FLAG_INSTANT; @@ -1005,0 +1138 @@ rec_get_converted_size_comp( + rec_flag |= REC_FLAG_NODE_PTR; @@ -1020 +1153 @@ rec_get_converted_size_comp( - index, fields, n_fields, NULL, extra, false)); + index, fields, n_fields, NULL, extra, rec_flag, false)); @@ -1194 +1327,3 @@ rec_convert_dtuple_to_rec_old( -Builds a ROW_FORMAT=COMPACT record out of a data tuple. */ +Builds a ROW_FORMAT=COMPACT record out of a data tuple. +@return TRUE if instant record. +*/ @@ -1196 +1331 @@ UNIV_INLINE -void +ibool @@ -1221,0 +1357 @@ rec_convert_dtuple_to_rec_comp( + ibool is_instant = FALSE; @@ -1223,0 +1360 @@ rec_convert_dtuple_to_rec_comp( + n_null = index->n_nullable; @@ -1242,0 +1380,13 @@ rec_convert_dtuple_to_rec_comp( + if (index->is_instant()) { + ulint field_count_len; + + n_null = index->n_nullable; + ut_ad(n_fields == dict_index_get_n_fields(index)); + + field_count_len = rec_set_field_count(rec, n_fields); + nulls = rec - (REC_N_NEW_EXTRA_BYTES + field_count_len + 1); + is_instant = TRUE; + } else { + n_null = index->n_nullable; + } + @@ -1248,0 +1399 @@ rec_convert_dtuple_to_rec_comp( + n_null = index->n_core_nullable; @@ -1258 +1409 @@ rec_convert_dtuple_to_rec_comp( - return; + return FALSE; @@ -1366 +1517 @@ rec_convert_dtuple_to_rec_comp( - return; + return is_instant; @@ -1413,0 +1565,3 @@ rec_convert_dtuple_to_rec_comp( + + ut_ad(!is_instant); + return is_instant; @@ -1438 +1592 @@ rec_convert_dtuple_to_rec_new( - rec_convert_dtuple_to_rec_comp( + if (rec_convert_dtuple_to_rec_comp( @@ -1440 +1594,3 @@ rec_convert_dtuple_to_rec_new( - status, false); + status, false)) { + /* Set the info bits of the record */ + rec_set_info_and_status_bits(rec, dtuple_get_info_bits(dtuple)); @@ -1442,2 +1598,8 @@ rec_convert_dtuple_to_rec_new( - /* Set the info bits of the record */ - rec_set_info_and_status_bits(rec, dtuple_get_info_bits(dtuple)); + // INSTANT RECORD + rec_set_instant_flag(rec, TRUE); + } + else { + /* Set the info bits of the record */ + rec_set_info_and_status_bits(rec, dtuple_get_info_bits(dtuple)); + rec_set_instant_flag(rec, FALSE); + } @@ -1516,0 +1679 @@ rec_get_converted_size_temp( + ut_ad(!index->is_instant()); @@ -1518 +1681 @@ rec_get_converted_size_temp( - index, fields, n_fields, v_entry, extra, true)); + index, fields, n_fields, v_entry, extra, REC_FLAG_NONE, true)); @@ -1547,0 +1711 @@ rec_convert_dtuple_to_temp( + ut_ad(!index->is_instant()); @@ -1584 +1748 @@ rec_copy_prefix_to_dtuple( - data = rec_get_nth_field(rec, offsets, i, &len); + data = rec_get_nth_cfield(rec, offsets, i, index, NULL, &len); @@ -1698,2 +1862,27 @@ rec_copy_prefix_to_buf( - nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1); - lens = nulls - UT_BITS_IN_BYTES(index->n_nullable); + if (rec_is_instant(index, rec)) { + ulint field_count_len = 0; + ulint field_count = 0; + ulint n_nullable = 0; + + ut_a(index->is_instant()); + + field_count = rec_get_field_count(rec, &field_count_len); + ut_ad(field_count_len == rec_get_field_count_len(field_count)); + ut_ad(field_count >= n_fields); + + n_nullable = rec_get_n_nullable(rec, index, field_count); + ut_ad(n_nullable <= index->n_nullable); + + nulls = rec - (REC_N_NEW_EXTRA_BYTES + field_count_len + 1); + lens = nulls - UT_BITS_IN_BYTES(n_nullable); + + } + else if (index->is_instant()) { + nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1); + lens = nulls - UT_BITS_IN_BYTES(index->n_core_nullable); + } + else { + nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1); + lens = nulls - UT_BITS_IN_BYTES(index->n_nullable); + } + @@ -1839,0 +2029 @@ rec_validate( + ulint offs; @@ -1852,8 +2042,15 @@ rec_validate( - data = rec_get_nth_field(rec, offsets, i, &len); - - if (!((len < UNIV_PAGE_SIZE) || (len == UNIV_SQL_NULL))) { - ib::error() << "Record field " << i << " len " << len; - return(FALSE); - } - - if (len != UNIV_SQL_NULL) { + offs = rec_get_nth_field_offs(offsets, i, &len); + switch (len) { + case UNIV_SQL_DEFAULT: + break; + case UNIV_SQL_NULL: + if (!rec_offs_comp(offsets)) { + len_sum += rec_get_nth_field_size(rec, i); + } + break; + default: + if (len >= UNIV_PAGE_SIZE) { + ib::error() << "Record field " << i << " len " << len; + return(FALSE); + } + data = rec + offs; @@ -1861,6 +2058,5 @@ rec_validate( - sum += *(data + len -1); /* dereference the - end of the field to - cause a memory trap - if possible */ - } else if (!rec_offs_comp(offsets)) { - len_sum += rec_get_nth_field_size(rec, i); + sum += *(data + len - 1); /* dereference the + end of the field to + cause a memory trap + if possible */ + break; @@ -1953 +2149,5 @@ rec_print_comp( - data = rec_get_nth_field(rec, offsets, i, &len); + if (rec_offs_nth_default(offsets, i)) { + len = UNIV_SQL_DEFAULT; + } else { + data = rec_get_nth_field(rec, offsets, i, &len); + } @@ -1957 +2157,5 @@ rec_print_comp( - if (len != UNIV_SQL_NULL) { + if (len == UNIV_SQL_NULL) { + fputs(" SQL NULL", file); + } else if (len == UNIV_SQL_DEFAULT) { + fputs(" SQL DEFAULT", file); + } else { @@ -1974,2 +2177,0 @@ rec_print_comp( - } else { - fputs(" SQL NULL", file); @@ -2078 +2280,5 @@ rec_print_mbr_rec( - data = rec_get_nth_field(rec, offsets, i, &len); + if (rec_offs_nth_default(offsets, i)) { + len = UNIV_SQL_DEFAULT; + } else { + data = rec_get_nth_field(rec, offsets, i, &len); + } @@ -2096 +2302,5 @@ rec_print_mbr_rec( - if (len != UNIV_SQL_NULL) { + if (len == UNIV_SQL_NULL) { + fputs(" SQL NULL", file); + } else if (len == UNIV_SQL_DEFAULT) { + fputs(" SQL DEFAULT", file); + } else { @@ -2106,2 +2315,0 @@ rec_print_mbr_rec( - } else { - fputs(" SQL NULL", file); @@ -2217,0 +2426,5 @@ rec_print( + if (rec_offs_nth_default(offsets, i)) { + o << "DEFAULT"; + continue; + } + diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index c8a56b2..c48f5b2 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -352,0 +353,2 @@ row_log_online_op( + // secondary index never be instant + ut_ad(!index->is_instant() && !dict_index_is_clust(index)); @@ -690,0 +693,3 @@ row_log_table_delete( + /* The ROW_T_DELETE record was converted by + rec_convert_dtuple_to_temp() using new_index. */ + ut_ad(!new_index->is_instant()); @@ -726,4 +731,4 @@ row_log_table_delete( - rec_convert_dtuple_to_temp( - b, new_index, NULL, 0, ventry); - b += mach_read_from_2(b); - } + rec_convert_dtuple_to_temp( + b, new_index, NULL, 0, ventry); + b += mach_read_from_2(b); + } @@ -962,0 +968,4 @@ row_log_table_low( + if (index->is_instant()) { + // it need to copy the info_bit also + omit_size -= REC_N_TMP_EXTRA_BYTES; + } @@ -995,0 +1005,3 @@ row_log_table_low( + /* The old_pk prefix was converted by + rec_convert_dtuple_to_temp() using new_index. */ + ut_ad(!new_index->is_instant()); @@ -1007,0 +1020 @@ row_log_table_low( + ut_ad(!insert); @@ -1450 +1463,2 @@ Converts a log record to a table row. -static MY_ATTRIBUTE((nonnull, warn_unused_result)) +static +MY_ATTRIBUTE((nonnull, warn_unused_result)) @@ -1549 +1563 @@ blob_done: - data = rec_get_nth_field(mrec, offsets, i, &len); + data = rec_get_nth_cfield(mrec, offsets, i, index, heap, &len); @@ -1885,2 +1899,2 @@ row_log_table_apply_delete( - dict_table_copy_v_types(old_pk, index->table); - } + dict_table_copy_v_types(old_pk, index->table); + } @@ -1892 +1906 @@ row_log_table_apply_delete( - ut_ad(len != UNIV_SQL_NULL); + ut_ad(len_is_stored(len)); @@ -2354 +2368,2 @@ pointer to next record on success */ -static MY_ATTRIBUTE((nonnull, warn_unused_result)) +static +MY_ATTRIBUTE((nonnull, warn_unused_result)) @@ -2459,0 +2475,3 @@ row_log_table_apply_op( + /* The ROW_T_DELETE record was converted by + rec_convert_dtuple_to_temp() using new_index. */ + ut_ad(!new_index->is_instant()); @@ -2562 +2580 @@ row_log_table_apply_op( - ut_ad(len != UNIV_SQL_NULL); + ut_ad(len_is_stored(len)); @@ -2577,0 +2596,3 @@ row_log_table_apply_op( + /* The old_pk prefix was converted by + rec_convert_dtuple_to_temp() using new_index. */ + ut_ad(!new_index->is_instant()); @@ -2609 +2630 @@ row_log_table_apply_op( - ut_ad(len != UNIV_SQL_NULL); + ut_ad(len_is_stored(len)); diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 5882f50..78bbcf7 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -3689,0 +3690,2 @@ row_mysql_table_id_reassign( + "UPDATE SYS_COLUMNS_ADDED SET TABLE_ID = :new_id\n" + " WHERE TABLE_ID = :old_id;\n" @@ -4691,0 +4694,3 @@ row_drop_table_for_mysql( + sql += "DELETE FROM SYS_COLUMNS_ADDED\n" + "WHERE TABLE_ID = table_id;\n"; + diff --git a/storage/innobase/row/row0row.cc b/storage/innobase/row/row0row.cc index 8d37428..16467ab 100644 --- a/storage/innobase/row/row0row.cc +++ b/storage/innobase/row/row0row.cc @@ -514,2 +514,11 @@ row_build_low( - const byte* field = rec_get_nth_field( - copy, offsets, i, &len); + const byte* field ; + + if (rec_offs_nth_default(offsets, i)) { + field = rec_get_nth_cfield(copy, offsets, i, index, NULL, &len); + + if (len != UNIV_SQL_NULL && type != ROW_COPY_POINTERS) { + field = static_cast(mem_heap_dup(heap, field, len)); + } + } else { + field = rec_get_nth_field(copy, offsets, i, &len); + } @@ -700 +709 @@ row_rec_to_index_entry_low( - field = rec_get_nth_field(rec, offsets, i, &len); + field = rec_get_nth_cfield(rec, offsets, i, index, heap, &len); @@ -1233,0 +1243,2 @@ row_raw_format( + ut_ad(data_len != UNIV_SQL_DEFAULT); + diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 0684e3c..1e6ed3d 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -249,2 +249,2 @@ row_sel_sec_rec_is_for_clust_rec( - clust_field = rec_get_nth_field( - clust_rec, clust_offs, clust_pos, &clust_len); + clust_field = rec_get_nth_cfield( + clust_rec, clust_offs, clust_pos, clust_index, NULL, &clust_len); @@ -539,4 +539,7 @@ row_sel_fetch_columns( - data = rec_get_nth_field(rec, offsets, - field_no, &len); - - needs_copy = column->copy_val; + data = rec_get_nth_cfield(rec, offsets, + field_no, index, NULL, &len); + if (rec_offs_nth_default(offsets, field_no)) { + needs_copy = TRUE; + } else { + needs_copy = column->copy_val; + } @@ -2887 +2890 @@ row_sel_field_store_in_mysql_format_func( - ut_ad(len != UNIV_SQL_NULL); + ut_ad(len_is_stored(len)); @@ -3162 +3165 @@ row_sel_store_mysql_field_func( - ut_a(len != UNIV_SQL_NULL); + ut_a(len_is_stored(len)); @@ -3174 +3177,11 @@ row_sel_store_mysql_field_func( - data = rec_get_nth_field(rec, offsets, field_no, &len); + if (rec_offs_nth_default(offsets, field_no)) { + /* There is default value of added column only in + cluster index */ + ut_ad(dict_index_is_clust(index)); + ut_ad(index->is_instant()); + + data = rec_get_nth_cfield(rec, offsets, field_no, index, + NULL, &len); + } else { + data = rec_get_nth_field(rec, offsets, field_no, &len); + } @@ -3230 +3243 @@ row_sel_store_mysql_field_func( - ut_ad(len != UNIV_SQL_NULL); + ut_ad(len_is_stored(len)); @@ -4552 +4565 @@ row_sel_fill_vrow( - const dict_v_col_t* vcol = reinterpret_cast< + const dict_v_col_t* vcol = reinterpret_cast< diff --git a/storage/innobase/row/row0trunc.cc b/storage/innobase/row/row0trunc.cc index 73bed52..e6ac9f6 100644 --- a/storage/innobase/row/row0trunc.cc +++ b/storage/innobase/row/row0trunc.cc @@ -1420,0 +1421,3 @@ row_truncate_update_table_id( + "UPDATE SYS_COLUMNS_ADDED" + " SET TABLE_ID = :new_id\n" + " WHERE TABLE_ID = :old_id;\n" diff --git a/storage/innobase/row/row0umod.cc b/storage/innobase/row/row0umod.cc index 3d50bb0..65251e8 100644 --- a/storage/innobase/row/row0umod.cc +++ b/storage/innobase/row/row0umod.cc @@ -139,2 +139,4 @@ row_undo_mod_clust_low( - err = btr_cur_pessimistic_update( - BTR_NO_LOCKING_FLAG + ulint upd_flag; + dict_index_t* index = btr_cur->index; + + upd_flag = BTR_NO_LOCKING_FLAG @@ -142 +144,13 @@ row_undo_mod_clust_low( - | BTR_KEEP_SYS_FLAG, + | BTR_KEEP_SYS_FLAG; + + /* For instant table, undo update maybe create new big_rec. + Because the default value maybe didn't store in the row before. + After rollback, the default value should store in row(always newest form), + it maybe create new external columns. + */ + if (index->is_instant()) { + upd_flag |= BTR_KEEP_POS_FLAG; + } + + err = btr_cur_pessimistic_update( + upd_flag, @@ -147 +161,9 @@ row_undo_mod_clust_low( - ut_a(!dummy_big_rec); + if (dummy_big_rec) { + ut_a(err == DB_SUCCESS); + ut_ad(index->is_instant()); + + err = btr_store_big_rec_extern_fields(pcur, node->update, *offsets, + dummy_big_rec, mtr, BTR_STORE_UPDATE); + + dtuple_big_rec_free(dummy_big_rec); + } diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index 637daf4..9c076e3 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -402 +402 @@ Returns TRUE if row update changes size of some field in index or if some -field to be updated is stored externally in rec or update. +field to be updated is stored externally in rec or added columns or update. @@ -404 +404 @@ field to be updated is stored externally in rec or update. -the field is external in rec or update */ +the field is external in rec or added columns or update */ @@ -449,11 +449,15 @@ row_upd_changes_field_size_or_external( - if (rec_offs_comp(offsets) - && rec_offs_nth_sql_null(offsets, - upd_field->field_no)) { - /* Note that in the compact table format, for a - variable length field, an SQL NULL will use zero - bytes in the offset array at the start of the physical - record, but a zero-length value (empty string) will - use one byte! Thus, we cannot use update-in-place - if we update an SQL NULL varchar to an empty string! */ - - old_len = UNIV_SQL_NULL; + if (rec_offs_comp(offsets)){ + if (rec_offs_nth_sql_null(offsets, + upd_field->field_no)) { + /* Note that in the compact table format, for a + variable length field, an SQL NULL will use zero + bytes in the offset array at the start of the physical + record, but a zero-length value (empty string) will + use one byte! Thus, we cannot use update-in-place + if we update an SQL NULL varchar to an empty string! */ + + old_len = UNIV_SQL_NULL; + } else if (rec_offs_nth_default(offsets, upd_field->field_no)) { + /* If update the added columns, use pessimistic update */ + old_len = UNIV_SQL_DEFAULT; + } @@ -536,0 +541,2 @@ row_upd_rec_in_place( + ulint is_instant = rec_is_instant(index, rec); + @@ -537,0 +544,6 @@ row_upd_rec_in_place( + + if (is_instant) + rec_set_instant_flag(rec, TRUE); + else + rec_set_instant_flag(rec, FALSE); + @@ -556,0 +569,4 @@ row_upd_rec_in_place( + /* If the added columns is been updated, it always use + pessimistic update. So we assert here. Detailed in + row_upd_changes_field_size_or_external() */ + ut_ad(!rec_offs_nth_default(offsets, upd_field->field_no)); @@ -923 +939 @@ row_upd_build_difference_binary( - data = rec_get_nth_field(rec, offsets, i, &len); + data = rec_get_nth_cfield(rec, offsets, i, index, NULL, &len); @@ -1895,0 +1912 @@ row_upd_copy_columns( + const dict_index_t* index, /*!< in: index of rec */ @@ -1899 +1916 @@ row_upd_copy_columns( - byte* data; + const byte* data; @@ -1903 +1920 @@ row_upd_copy_columns( - data = rec_get_nth_field(rec, offsets, + data = rec_get_nth_cfield(rec, offsets, @@ -1904,0 +1922 @@ row_upd_copy_columns( + index, NULL, @@ -2931 +2949 @@ row_upd_clust_step( - row_upd_copy_columns(rec, offsets, + row_upd_copy_columns(rec, offsets, index, diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 6f9b58e..08fd500 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -2529,0 +2530,6 @@ files_checked: + /* Create the SYS_COLUMNS_ADDED system tables */ + err = dict_create_or_check_sys_columns_added(); + if (err != DB_SUCCESS) { + return(srv_init_abort(err)); + } + diff --git a/storage/innobase/trx/trx0i_s.cc b/storage/innobase/trx/trx0i_s.cc index 05bf6ff..c562786 100644 --- a/storage/innobase/trx/trx0i_s.cc +++ b/storage/innobase/trx/trx0i_s.cc @@ -648,0 +649,3 @@ put_nth_field( + ut_ad(!rec_offs_nth_default(offsets, n)); + /* Key columns can never be instantly added, and the only caller + fill_lock_data() is restricting the n_fields to the key columns. */ diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc index e789a6f..2dc39a5 100644 --- a/storage/innobase/trx/trx0rec.cc +++ b/storage/innobase/trx/trx0rec.cc @@ -974,0 +975 @@ trx_undo_page_report_modify( + ut_ad(!rec_offs_nth_default(offsets, i)); @@ -1087,2 +1088,2 @@ trx_undo_page_report_modify( - field = rec_get_nth_field(rec, offsets, - pos, &flen); + field = rec_get_nth_cfield(rec, offsets, + pos, index, NULL, &flen); @@ -1222 +1223 @@ trx_undo_page_report_modify( - field = rec_get_nth_field(rec, offsets, pos, + field = rec_get_nth_cfield(rec, offsets, pos, index, NULL,