diff options
Diffstat (limited to 'storage')
-rw-r--r-- | storage/innobase/btr/btr0btr.cc | 65 | ||||
-rw-r--r-- | storage/innobase/btr/btr0cur.cc | 43 | ||||
-rw-r--r-- | storage/innobase/dict/dict0mem.cc | 37 | ||||
-rw-r--r-- | storage/innobase/handler/ha_innodb.cc | 6 | ||||
-rw-r--r-- | storage/innobase/handler/handler0alter.cc | 86 | ||||
-rw-r--r-- | storage/innobase/include/btr0btr.h | 15 | ||||
-rw-r--r-- | storage/innobase/include/dict0mem.h | 19 | ||||
-rw-r--r-- | storage/innobase/include/row0log.h | 8 | ||||
-rw-r--r-- | storage/innobase/include/trx0rec.h | 4 | ||||
-rw-r--r-- | storage/innobase/row/row0ins.cc | 51 | ||||
-rw-r--r-- | storage/innobase/row/row0log.cc | 93 | ||||
-rw-r--r-- | storage/innobase/row/row0merge.cc | 9 | ||||
-rw-r--r-- | storage/innobase/row/row0purge.cc | 6 | ||||
-rw-r--r-- | storage/innobase/row/row0sel.cc | 10 | ||||
-rw-r--r-- | storage/innobase/row/row0uins.cc | 9 | ||||
-rw-r--r-- | storage/innobase/row/row0undo.cc | 1 | ||||
-rw-r--r-- | storage/innobase/trx/trx0rec.cc | 13 | ||||
-rw-r--r-- | storage/innobase/trx/trx0trx.cc | 8 |
18 files changed, 428 insertions, 55 deletions
diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index b2de0ad33b2..4945ef48d73 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -996,6 +996,39 @@ btr_free_root_check( return(block); } +void +btr_root_page_init(buf_block_t *block, index_id_t index_id, + dict_index_t *index, mtr_t *mtr) +{ + constexpr uint16_t field = PAGE_HEADER + PAGE_INDEX_ID; + byte* page_index_id = my_assume_aligned<2>(field + block->frame); + + /* Create a new index page on the allocated segment page */ + if (UNIV_LIKELY_NULL(block->page.zip.data)) + { + mach_write_to_8(page_index_id, index_id); + ut_ad(!page_has_siblings(block->page.zip.data)); + page_create_zip(block, index, 0, 0, mtr); + } + else + { + page_create(block, mtr, index && index->table->not_redundant()); + if (index && index->is_spatial()) + { + static_assert(((FIL_PAGE_INDEX & 0xff00) | byte(FIL_PAGE_RTREE)) + == FIL_PAGE_RTREE, "compatibility"); + mtr->write<1>(*block, FIL_PAGE_TYPE + 1 + block->frame, + byte(FIL_PAGE_RTREE)); + if (mach_read_from_8(block->frame + FIL_RTREE_SPLIT_SEQ_NUM)) + mtr->memset(block, FIL_RTREE_SPLIT_SEQ_NUM, 8, 0); + } + /* Set the level of the new index page */ + mtr->write<2,mtr_t::MAYBE_NOP>( + *block, PAGE_HEADER + PAGE_LEVEL + block->frame, 0U); + mtr->write<8,mtr_t::MAYBE_NOP>(*block, page_index_id, index_id); + } +} + /** Create the root node for a new index tree. @param[in] type type of the index @param[in] index_id index id @@ -1079,36 +1112,7 @@ btr_create( ut_ad(!page_has_siblings(block->frame)); - constexpr uint16_t field = PAGE_HEADER + PAGE_INDEX_ID; - - byte* page_index_id = my_assume_aligned<2>(field + block->frame); - - /* Create a new index page on the allocated segment page */ - if (UNIV_LIKELY_NULL(block->page.zip.data)) { - mach_write_to_8(page_index_id, index_id); - ut_ad(!page_has_siblings(block->page.zip.data)); - page_create_zip(block, index, 0, 0, mtr); - } else { - page_create(block, mtr, - index && index->table->not_redundant()); - if (index && index->is_spatial()) { - static_assert(((FIL_PAGE_INDEX & 0xff00) - | byte(FIL_PAGE_RTREE)) - == FIL_PAGE_RTREE, "compatibility"); - mtr->write<1>(*block, FIL_PAGE_TYPE + 1 + block->frame, - byte(FIL_PAGE_RTREE)); - if (mach_read_from_8(block->frame - + FIL_RTREE_SPLIT_SEQ_NUM)) { - mtr->memset(block, FIL_RTREE_SPLIT_SEQ_NUM, - 8, 0); - } - } - /* Set the level of the new index page */ - mtr->write<2,mtr_t::MAYBE_NOP>(*block, PAGE_HEADER + PAGE_LEVEL - + block->frame, 0U); - mtr->write<8,mtr_t::MAYBE_NOP>(*block, page_index_id, - index_id); - } + btr_root_page_init(block, index_id, index, mtr); /* We reset the free bits for the page in a separate mini-transaction to allow creation of several trees in the @@ -1136,7 +1140,6 @@ btr_create( this by calling btr_free_root. @param[in,out] block root page @param[in] log_mode mtr logging mode */ -static void btr_free_but_not_root( buf_block_t* block, diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index 9efa2d8f8bd..48b2cf58c09 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -3242,6 +3242,41 @@ btr_cur_ins_lock_and_undo( || (flags & BTR_CREATE_FLAG)); ut_ad(mtr->is_named_space(index->table->space)); + if (thr){ + trx_t* trx = thr_get_trx(thr); + + if (index->table->bulk_trx_id + && index->table->bulk_trx_id == trx->id) { + /* For first insert */ + if (trx->mod_tables.empty() + || trx->mod_tables.find(index->table) + == trx->mod_tables.end()) { + + dfield_t *t = dtuple_get_nth_field( + entry, index->db_trx_id()); + + ut_ad(t->len == DATA_TRX_ID_LEN); + memset(t->data, 0, DATA_TRX_ID_LEN); + + err = trx_undo_report_row_operation( + thr, index, entry, NULL, 0, + NULL, NULL, &roll_ptr); + + if (err == DB_SUCCESS) { + roll_ptr = roll_ptr_t(1) + << ROLL_PTR_INSERT_FLAG_POS; + goto upd_sys; + } + + return err; + } + + if (!index->table->allow_insert_undo) { + flags |= BTR_NO_UNDO_LOG_FLAG; + } + } + } + /* Check if there is predicate or GAP lock preventing the insertion */ if (!(flags & BTR_NO_LOCKING_FLAG)) { if (dict_index_is_spatial(index)) { @@ -3526,7 +3561,9 @@ fail_err: ut_ad(thr->graph->trx->id == trx_read_trx_id( static_cast<const byte*>( - trx_id->data))); + trx_id->data)) + || thr->graph->trx->id + == index->table->bulk_trx_id); } } #endif @@ -3582,7 +3619,7 @@ fail_err: } else if (entry->info_bits & REC_INFO_MIN_REC_FLAG) { ut_ad(entry->is_metadata()); ut_ad(index->is_instant()); - ut_ad(flags == BTR_NO_LOCKING_FLAG); + ut_ad(flags & BTR_NO_LOCKING_FLAG); } else { rw_lock_t* ahi_latch = btr_search_sys.get_latch(*index); if (!reorg && cursor->flag == BTR_CUR_HASH) { @@ -5522,6 +5559,7 @@ btr_cur_optimistic_delete_func( if (index->is_instant()) { /* MDEV-17383: free metadata BLOBs! */ index->clear_instant_alter(); + index->table->remove_bulk_trx(); } page_cur_set_after_last(block, btr_cur_get_page_cur(cursor)); @@ -5739,6 +5777,7 @@ btr_cur_pessimistic_delete( if (index->is_instant()) { /* MDEV-17383: free metadata BLOBs! */ index->clear_instant_alter(); + index->table->remove_bulk_trx(); } page_cur_set_after_last( block, diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index 96f2d7b6e3b..f46e634a46d 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -39,6 +39,7 @@ Created 1/8/1996 Heikki Tuuri #include "row0row.h" #include "sql_string.h" #include <iostream> +#include "row0log.h" #define DICT_HEAP_SIZE 100 /*!< initial memory heap size when creating a table or index object */ @@ -1393,3 +1394,39 @@ dict_index_t::vers_history_row( } return(error); } + +void dict_table_t::empty_table(que_thr_t *thr) +{ + mtr_t mtr; + bool rebuild= false; + for (dict_index_t* index= UT_LIST_GET_FIRST(indexes); + index != NULL; index= UT_LIST_GET_NEXT(indexes, index)) + { + if (index->online_status == ONLINE_INDEX_ABORTED + || index->online_status == ONLINE_INDEX_ABORTED_DROPPED) + continue; + + if (index->type & DICT_FTS) + continue; + + if (index->online_status == ONLINE_INDEX_CREATION) + { + if (dict_index_is_clust(index)) + { + row_log_table_empty(index); + rebuild= true; + } + else if (!rebuild) + { + mtr.start(); + mtr_s_lock_index(index, &mtr); + row_log_online_op(index, nullptr, 0); + mtr.commit(); + } + } + + index->empty(thr); + } + + remove_bulk_trx(); +} diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 9a47fb6b30f..cbc06313899 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -15382,6 +15382,12 @@ ha_innobase::extra( trx_register_for_2pc(m_prebuilt->trx); m_prebuilt->sql_stat_start = true; break; + case HA_EXTRA_IGNORE_INSERT: + if (m_prebuilt->table->bulk_trx_id == m_prebuilt->trx->id + || UT_LIST_GET_LEN(m_prebuilt->trx->trx_savepoints)) { + m_prebuilt->table->allow_insert_undo= true; + } + break; default:/* Do nothing */ ; } diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 363cf50aa90..de60f77c92f 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -11466,3 +11466,89 @@ ib_sequence_t::operator++(int) UNIV_NOTHROW return(current); } + +/** Get the metadata tuple from the index. It can be used later +to insert after freeing the leaf segment. +@param index metadata record to be read from +@param mtr mini-transaction to read the metadata record +@param heap heap where tuple is created +@return tuple for metadata record */ +static +dtuple_t* get_instant_metadata_tuple(dict_index_t* index, mem_heap_t* heap) +{ + mtr_t mtr; + btr_pcur_t pcur; + + mtr.start(); + index->set_modified(mtr); + btr_pcur_open_at_index_side(true, index, BTR_MODIFY_TREE, &pcur, true, + 0, &mtr); + ut_ad(btr_pcur_is_before_first_on_page(&pcur)); + btr_pcur_move_to_next_on_page(&pcur); + + buf_block_t* block = btr_pcur_get_block(&pcur); + ut_ad(page_is_leaf(block->frame)); + ut_ad(!page_has_prev(block->frame)); + ut_ad(!buf_block_get_page_zip(block)); + const rec_t* rec = btr_pcur_get_rec(&pcur); + ut_ad(rec_is_metadata(rec, *index)); + ut_ad(page_rec_is_user_rec(rec)); + + mem_heap_t* offsets_heap = NULL; + rec_offs* offsets= rec_get_offsets(rec, index, NULL, true, + ULINT_UNDEFINED, &offsets_heap); + dtuple_t* entry= row_metadata_to_tuple(rec, index, offsets, heap, + REC_INFO_METADATA_ALTER, false); + dfield_t* dfield = dtuple_get_nth_field(entry, index->first_user_field()); + index->table->serialise_columns(heap, dfield); + const dfield_t* trx_id= dtuple_get_nth_field( + entry, dict_col_get_clust_pos( + dict_table_get_sys_col(index->table, DATA_TRX_ID), index)); + memset(trx_id->data, 0, DATA_TRX_ID_LEN); + mtr.commit(); + return entry; +} + +void dict_index_t::empty(que_thr_t *thr) +{ + mtr_t mtr; + dtuple_t* metadata_tuple; + mem_heap_t* heap= nullptr; + bool meta_rec_exist= is_instant(); + + if (meta_rec_exist) + { + heap= mem_heap_create(1024); + metadata_tuple= get_instant_metadata_tuple(this, heap); + } + + mtr.start(); + mtr.set_named_space_id(table->space->id); + /* Free the indexes */ + buf_block_t* root_block= buf_page_get( + page_id_t(table->space->id, page), + table->space->zip_size(), RW_X_LATCH, &mtr); + if (root_block) + btr_free_but_not_root(root_block, mtr.get_log_mode()); + + mtr.memset(root_block, PAGE_HEADER + PAGE_BTR_SEG_LEAF, + FSEG_HEADER_SIZE, 0); + if (!fseg_create(table->space, PAGE_HEADER + PAGE_BTR_SEG_LEAF, + &mtr, false, root_block)) + { + ut_ad(0); + } + + btr_root_page_init(root_block, id, this, &mtr); + if (meta_rec_exist) + btr_set_instant(root_block, *this, &mtr); + mtr.commit(); + if (meta_rec_exist) + { + dberr_t err = row_ins_clust_index_entry_low( + BTR_NO_LOCKING_FLAG | BTR_NO_UNDO_LOG_FLAG, BTR_MODIFY_TREE, + this, n_uniq, metadata_tuple, 0, nullptr); + ut_ad(err == DB_SUCCESS); + mem_heap_free(heap); + } +} diff --git a/storage/innobase/include/btr0btr.h b/storage/innobase/include/btr0btr.h index 7fae1ad163b..856bcff82c2 100644 --- a/storage/innobase/include/btr0btr.h +++ b/storage/innobase/include/btr0btr.h @@ -330,6 +330,14 @@ btr_node_ptr_get_child_page_no( const rec_offs* offsets)/*!< in: array returned by rec_get_offsets() */ MY_ATTRIBUTE((warn_unused_result)); +/** Initialize the root page of the b-tree +@param[in,out] block root block +@param[in] index_id index id +@param[in] index index of root page +@param[in,out] mtr mini-transaction */ +void btr_root_page_init(buf_block_t *block, index_id_t index_id, + dict_index_t *index, mtr_t *mtr); + /** Create the root node for a new index tree. @param[in] type type of the index @param[in,out] space tablespace where created @@ -757,4 +765,11 @@ Global variable controlling if scrubbing should be performed */ extern my_bool srv_immediate_scrub_data_uncompressed; extern Atomic_counter<uint32_t> btr_validate_index_running; +/** Free a B-tree except the root page. The root page MUST be freed after +this by calling btr_free_root. +@param[in,out] block root page +@param[in] log_mode mtr logging mode */ +void +btr_free_but_not_root(buf_block_t *block, mtr_log_t log_mode); + #endif diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 1ad2517c8fb..64e5008ac0e 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -1368,6 +1368,9 @@ public: everything in overflow) size of the longest possible row and index of a field which made index records too big to fit on a page.*/ inline record_size_info_t record_size_info() const; + + /** Empty the index content and reinitialize the root page */ + void empty(que_thr_t *thr); }; /** Detach a virtual column from an index. @@ -1950,6 +1953,15 @@ struct dict_table_t { char (&tbl_name)[NAME_LEN + 1], size_t *db_name_len, size_t *tbl_name_len) const; + /** Empty the table */ + void empty_table(que_thr_t *thr); + + void remove_bulk_trx() + { + bulk_trx_id= 0; + allow_insert_undo= false; + } + private: /** Initialize instant->field_map. @param[in] table table definition to copy from */ @@ -2316,6 +2328,13 @@ public: /** mysql_row_templ_t for base columns used for compute the virtual columns */ dict_vcol_templ_t* vc_templ; + + /** Trx id of bulk operation. This is under the protection of + exclusive lock of table object */ + trx_id_t bulk_trx_id; + + /** Allow insert undo for bulk insert operation */ + bool allow_insert_undo; }; inline void dict_index_t::set_modified(mtr_t& mtr) const diff --git a/storage/innobase/include/row0log.h b/storage/innobase/include/row0log.h index 5ec4b9c1103..b046a19ac7e 100644 --- a/storage/innobase/include/row0log.h +++ b/storage/innobase/include/row0log.h @@ -102,9 +102,8 @@ row_log_online_op( /*==============*/ dict_index_t* index, /*!< in/out: index, S or X latched */ const dtuple_t* tuple, /*!< in: index tuple */ - trx_id_t trx_id) /*!< in: transaction ID for insert, + trx_id_t trx_id);/*!< in: transaction ID for insert, or 0 for delete */ - ATTRIBUTE_COLD __attribute__((nonnull)); /******************************************************//** Gets the error status of the online index rebuild log. @@ -258,6 +257,11 @@ row_log_estimate_work( const dict_index_t* index); #endif /* HAVE_PSI_STAGE_INTERFACE */ +/** Logs an empty operation of the table which means it should empty +the table. +@param index clustered index */ +void row_log_table_empty(dict_index_t *index); + #include "row0log.ic" #endif /* row0log.h */ diff --git a/storage/innobase/include/trx0rec.h b/storage/innobase/include/trx0rec.h index 9aeff6312f6..d802deca75f 100644 --- a/storage/innobase/include/trx0rec.h +++ b/storage/innobase/include/trx0rec.h @@ -295,7 +295,9 @@ record */ a not delete marked record; also the fields of the record can change */ #define TRX_UNDO_DEL_MARK_REC 14 /* delete marking of a record; fields - do not change */ + do not change */ +#define TRX_UNDO_EMPTY 15 /* Empty the table */ + #define TRX_UNDO_CMPL_INFO_MULT 16U /* compilation info is multiplied by this and ORed to the type above */ #define TRX_UNDO_UPD_EXTERN 128U /* This bit can be ORed to type_cmpl diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 5e41d037b6c..87e64b3951e 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -2555,15 +2555,20 @@ row_ins_clust_index_entry_low( rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs* offsets = offsets_; rec_offs_init(offsets_); + trx_t* trx = thr_get_trx(thr); + buf_block_t* block; DBUG_ENTER("row_ins_clust_index_entry_low"); + DEBUG_SYNC_C("row_ins_clust_index_entry_low_enter"); + ut_ad(dict_index_is_clust(index)); ut_ad(!dict_index_is_unique(index) || n_uniq == dict_index_get_n_unique(index)); ut_ad(!n_uniq || n_uniq == dict_index_get_n_unique(index)); - ut_ad(!thr_get_trx(thr)->in_rollback); + ut_ad(!trx->in_rollback); +start_read: mtr_start(&mtr); if (index->table->is_temporary()) { @@ -2634,6 +2639,44 @@ row_ins_clust_index_entry_low( } #endif /* UNIV_DEBUG */ + block = btr_cur_get_block(cursor); + + if (block->page.id().page_no() == index->page + && !(flags & BTR_NO_UNDO_LOG_FLAG) + && !index->table->is_temporary() + && !entry->is_metadata() && !trx->duplicates + && !trx->ddl && !trx->internal + && page_is_empty(block->frame)) { + + DEBUG_SYNC_C("empty_root_page_insert"); + + err = lock_table(0, index->table, LOCK_X, thr); + + if (err == DB_LOCK_WAIT) { + mtr_commit(&mtr); + + trx->error_state = err; + + que_thr_stop_for_mysql(thr); + + thr->lock_state = QUE_THR_LOCK_ROW; + + lock_wait_suspend_thread(thr); + + thr->lock_state = QUE_THR_LOCK_NOLOCK; + + err = trx->error_state; + + if (err != DB_SUCCESS) { + goto func_exit; + } + + goto start_read; + } + + index->table->bulk_trx_id = trx->id; + } + if (UNIV_UNLIKELY(entry->info_bits != 0)) { ut_ad(entry->is_metadata()); ut_ad(flags == BTR_NO_LOCKING_FLAG); @@ -2644,7 +2687,7 @@ row_ins_clust_index_entry_low( if (rec_get_info_bits(rec, page_rec_is_comp(rec)) & REC_INFO_MIN_REC_FLAG) { - thr_get_trx(thr)->error_info = index; + trx->error_info = index; err = DB_DUPLICATE_KEY; goto err_exit; } @@ -2677,7 +2720,7 @@ row_ins_clust_index_entry_low( /* fall through */ case DB_SUCCESS_LOCKED_REC: case DB_DUPLICATE_KEY: - thr_get_trx(thr)->error_info = cursor->index; + trx->error_info = cursor->index; } } else { /* Note that the following may return also @@ -2761,7 +2804,7 @@ do_insert: log_write_up_to(mtr.commit_lsn(), true);); err = row_ins_index_entry_big_rec( entry, big_rec, offsets, &offsets_heap, index, - thr_get_trx(thr)->mysql_thd); + trx->mysql_thd); dtuple_convert_back_big_rec(index, entry, big_rec); } else { if (err == DB_SUCCESS diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index 4a98ac24185..0010cf86d86 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -54,7 +54,9 @@ enum row_tab_op { /** Update a record in place */ ROW_T_UPDATE, /** Delete (purge) a record */ - ROW_T_DELETE + ROW_T_DELETE, + /** Empty the table */ + ROW_T_EMPTY }; /** Index record modification operations during online index creation */ @@ -62,7 +64,9 @@ enum row_op { /** Insert a record */ ROW_OP_INSERT = 0x61, /** Delete a record */ - ROW_OP_DELETE + ROW_OP_DELETE, + /** Empy the index */ + ROW_OP_EMPTY }; /** Size of the modification log entry header, in bytes */ @@ -339,8 +343,8 @@ row_log_online_op( ulint avail_size; row_log_t* log; - ut_ad(dtuple_validate(tuple)); - ut_ad(dtuple_get_n_fields(tuple) == dict_index_get_n_fields(index)); + ut_ad(!tuple || dtuple_validate(tuple)); + ut_ad(!tuple || dtuple_get_n_fields(tuple) == dict_index_get_n_fields(index)); ut_ad(rw_lock_own_flagged(&index->lock, RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)); @@ -354,14 +358,19 @@ row_log_online_op( row_merge_buf_encode(), because here we do not encode extra_size+1 (and reserve 0 as the end-of-chunk marker). */ - size = rec_get_converted_size_temp( - index, tuple->fields, tuple->n_fields, &extra_size); - ut_ad(size >= extra_size); - ut_ad(size <= sizeof log->tail.buf); + if (!tuple) { + mrec_size = 4; + extra_size = 0; + } else { + size = rec_get_converted_size_temp( + index, tuple->fields, tuple->n_fields, &extra_size); + ut_ad(size >= extra_size); + ut_ad(size <= sizeof log->tail.buf); - mrec_size = ROW_LOG_HEADER_SIZE - + (extra_size >= 0x80) + size - + (trx_id ? DATA_TRX_ID_LEN : 0); + mrec_size = ROW_LOG_HEADER_SIZE + + (extra_size >= 0x80) + size + + (trx_id ? DATA_TRX_ID_LEN : 0); + } log = index->online_log; mutex_enter(&log->mutex); @@ -390,6 +399,8 @@ row_log_online_op( *b++ = ROW_OP_INSERT; trx_write_trx_id(b, trx_id); b += DATA_TRX_ID_LEN; + } else if (tuple == nullptr) { + *b++ = ROW_OP_EMPTY; } else { *b++ = ROW_OP_DELETE; } @@ -402,9 +413,15 @@ row_log_online_op( *b++ = (byte) extra_size; } - rec_convert_dtuple_to_temp( - b + extra_size, index, tuple->fields, tuple->n_fields); - b += size; + if (tuple) { + rec_convert_dtuple_to_temp( + b + extra_size, index, tuple->fields, + tuple->n_fields); + b += size; + } else { + *b++ = 0; + *b++ = 0; + } if (mrec_size >= avail_size) { const os_offset_t byte_offset @@ -2399,6 +2416,18 @@ func_exit_committed: goto func_exit; } +/** Applies the empty table to a table that was rebuilt. +@param index clustered index +@retrun success if index gets emptied */ +static +dberr_t +row_log_table_apply_empty(dict_index_t* index, que_thr_t *thr) +{ + dict_table_t* new_table= index->online_log->table; + new_table->empty_table(thr); + return DB_SUCCESS; +} + /******************************************************//** Applies an operation to a table that was rebuilt. @return NULL on failure (mrec corruption) or when out of data; @@ -2669,6 +2698,11 @@ row_log_table_apply_op( thr, new_trx_id_col, mrec, offsets, offsets_heap, heap, dup, old_pk); break; + case ROW_T_EMPTY: + *error = row_log_table_apply_empty(dup->index, thr); + log->head.total += 4; + next_mrec = mrec + 3; + break; } ut_ad(log->head.total <= log->tail.total); @@ -3454,6 +3488,9 @@ row_log_apply_op_low( } goto duplicate; + case ROW_OP_EMPTY: + ut_ad(0); + break; } } else { switch (op) { @@ -3525,6 +3562,9 @@ insert_the_rec: 0, NULL, &mtr); ut_ad(!big_rec); break; + case ROW_OP_EMPTY: + ut_ad(0); + break; } mem_heap_empty(offsets_heap); } @@ -3600,6 +3640,17 @@ row_log_apply_op( op = static_cast<enum row_op>(*mrec++); trx_id = 0; break; + case ROW_OP_EMPTY: + { + mem_heap_t* heap = mem_heap_create(512); + que_fork_t* fork = que_fork_create( + NULL, NULL, QUE_FORK_MYSQL_INTERFACE, heap); + que_thr_t* thr = que_thr_create(fork, heap, nullptr); + index->empty(thr); + *error = DB_SUCCESS; + mem_heap_free(heap); + return mrec + 4; + } default: corrupted: ut_ad(0); @@ -4042,3 +4093,17 @@ row_log_apply( DBUG_RETURN(error); } + +void row_log_table_empty(dict_index_t *index) +{ + row_log_t* log= index->online_log; + ulint avail_size; + if (byte* b = row_log_table_open(log, 4, &avail_size)) + { + *b++ = ROW_T_EMPTY; + *b++= 0; + *b++= 0; + *b++= 0; + row_log_table_close(index, b, 4, avail_size); + } +} diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index e306cb3429b..604425d82dd 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -1754,6 +1754,15 @@ row_merge_read_clustered_index( /* There is no previous tuple yet. */ prev_mtuple.fields = NULL; + if (trx_id_t bulk_trx_id = old_table->bulk_trx_id) { + if (trx->read_view.is_open() + && !trx->read_view.changes_visible( + bulk_trx_id, old_table->name)) { + trx->op_info=""; + DBUG_RETURN(DB_SUCCESS); + } + } + for (ulint i = 0; i < n_index; i++) { if (index[i]->type & DICT_FTS) { diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc index 5579e53b6c1..b856da86fc1 100644 --- a/storage/innobase/row/row0purge.cc +++ b/storage/innobase/row/row0purge.cc @@ -896,6 +896,7 @@ row_purge_parse_undo_rec( switch (type) { case TRX_UNDO_RENAME_TABLE: return false; + case TRX_UNDO_EMPTY: case TRX_UNDO_INSERT_METADATA: case TRX_UNDO_INSERT_REC: /* These records do not store any transaction identifier. @@ -986,6 +987,9 @@ err_exit: if (type == TRX_UNDO_INSERT_METADATA) { node->ref = &trx_undo_metadata; return(true); + } else if (type == TRX_UNDO_EMPTY) { + node->ref = nullptr; + return true; } ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref), @@ -1043,6 +1047,8 @@ row_purge_record_func( ut_ad(!trx_undo_roll_ptr_is_insert(node->roll_ptr)); switch (node->rec_type) { + case TRX_UNDO_EMPTY: + break; case TRX_UNDO_DEL_MARK_REC: purged = row_purge_del_mark(node); if (purged) { diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 7a859ab8b09..16b5e43e9c5 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -4397,6 +4397,16 @@ early_not_found: DBUG_RETURN(DB_END_OF_INDEX); } + if (trx_id_t bulk_trx_id = index->table->bulk_trx_id) { + if (trx->isolation_level != TRX_ISO_READ_UNCOMMITTED + && trx->read_view.is_open() + && !trx->read_view.changes_visible( + bulk_trx_id, index->table->name)) { + trx->op_info = ""; + DBUG_RETURN(DB_END_OF_INDEX); + } + } + /* if the query is a plain locking SELECT, and the isolation level is <= TRX_ISO_READ_COMMITTED, then this is set to FALSE */ bool did_semi_consistent_read = false; diff --git a/storage/innobase/row/row0uins.cc b/storage/innobase/row/row0uins.cc index 0ce136c5906..f4c37d26d76 100644 --- a/storage/innobase/row/row0uins.cc +++ b/storage/innobase/row/row0uins.cc @@ -384,6 +384,7 @@ static bool row_undo_ins_parse_undo_rec(undo_node_t* node, bool dict_locked) goto close_table; case TRX_UNDO_INSERT_METADATA: case TRX_UNDO_INSERT_REC: + case TRX_UNDO_EMPTY: break; case TRX_UNDO_RENAME_TABLE: dict_table_t* table = node->table; @@ -424,6 +425,9 @@ close_table: ptr = trx_undo_rec_get_row_ref( ptr, clust_index, &node->ref, node->heap); + } else if (node->rec_type == TRX_UNDO_EMPTY) { + node->ref = nullptr; + return true; } else { node->ref = &trx_undo_metadata; if (!row_undo_search_clust_to_pcur(node)) { @@ -596,6 +600,11 @@ row_undo_ins( log_free_check(); ut_ad(!node->table->is_temporary()); err = row_undo_ins_remove_clust_rec(node); + break; + case TRX_UNDO_EMPTY: + node->table->empty_table(thr); + err = DB_SUCCESS; + break; } dict_table_close(node->table, dict_locked, FALSE); diff --git a/storage/innobase/row/row0undo.cc b/storage/innobase/row/row0undo.cc index 375de331255..0ef870714c1 100644 --- a/storage/innobase/row/row0undo.cc +++ b/storage/innobase/row/row0undo.cc @@ -363,6 +363,7 @@ static bool row_undo_rec_get(undo_node_t* node) switch (trx_undo_rec_get_type(node->undo_rec)) { case TRX_UNDO_INSERT_METADATA: + case TRX_UNDO_EMPTY: /* This record type was introduced in MDEV-11369 instant ADD COLUMN, which was implemented after MDEV-12288 removed the insert_undo log. There is no diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc index 1df61ac5b56..05da87a3531 100644 --- a/storage/innobase/trx/trx0rec.cc +++ b/storage/innobase/trx/trx0rec.cc @@ -411,6 +411,17 @@ trx_undo_page_report_insert( *ptr++ = TRX_UNDO_INSERT_REC; ptr += mach_u64_write_much_compressed(ptr, trx->undo_no); ptr += mach_u64_write_much_compressed(ptr, index->table->id); + + /* Table is in bulk operation */ + if (index->table->bulk_trx_id == trx->id + && !index->table->allow_insert_undo) { + ut_ad(trx->mod_tables.empty() + || trx->mod_tables.find(index->table) + == trx->mod_tables.end()); + undo_block->frame[first_free + 2] = TRX_UNDO_EMPTY; + goto done; + } + /*----------------------------------------*/ /* Store then the fields required to uniquely determine the record to be inserted in the clustered index */ @@ -488,7 +499,7 @@ trx_undo_rec_get_pars( type_cmpl &= ~TRX_UNDO_UPD_EXTERN; *type = type_cmpl & (TRX_UNDO_CMPL_INFO_MULT - 1); ut_ad(*type >= TRX_UNDO_RENAME_TABLE); - ut_ad(*type <= TRX_UNDO_DEL_MARK_REC); + ut_ad(*type <= TRX_UNDO_EMPTY); *cmpl_info = type_cmpl / TRX_UNDO_CMPL_INFO_MULT; *undo_no = mach_read_next_much_compressed(&ptr); diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index bd54af76d9d..58058e59c9f 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -1746,6 +1746,14 @@ trx_mark_sql_stat_end( fts_savepoint_laststmt_refresh(trx); } + for (auto iter = trx->mod_tables.begin(); + iter != trx->mod_tables.end(); + ++iter) { + if (iter->first->bulk_trx_id == trx->id + || UT_LIST_GET_LEN(trx->trx_savepoints)) + iter->first->allow_insert_undo= true; + } + return; } |