summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
Diffstat (limited to 'storage')
-rw-r--r--storage/innobase/btr/btr0btr.cc65
-rw-r--r--storage/innobase/btr/btr0cur.cc43
-rw-r--r--storage/innobase/dict/dict0mem.cc37
-rw-r--r--storage/innobase/handler/ha_innodb.cc6
-rw-r--r--storage/innobase/handler/handler0alter.cc86
-rw-r--r--storage/innobase/include/btr0btr.h15
-rw-r--r--storage/innobase/include/dict0mem.h19
-rw-r--r--storage/innobase/include/row0log.h8
-rw-r--r--storage/innobase/include/trx0rec.h4
-rw-r--r--storage/innobase/row/row0ins.cc51
-rw-r--r--storage/innobase/row/row0log.cc93
-rw-r--r--storage/innobase/row/row0merge.cc9
-rw-r--r--storage/innobase/row/row0purge.cc6
-rw-r--r--storage/innobase/row/row0sel.cc10
-rw-r--r--storage/innobase/row/row0uins.cc9
-rw-r--r--storage/innobase/row/row0undo.cc1
-rw-r--r--storage/innobase/trx/trx0rec.cc13
-rw-r--r--storage/innobase/trx/trx0trx.cc8
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;
}