void reset_thd(MYSQL_THD thd); /************************************************************************* IMPORTANTNOTE:AnyoperationthatgeneratesredoMUSTcheckthatthere isenoughspaceintheredologbeforeforthatoperation.Thisis donebycallinglog_free_check().Thereasonforcheckingthe availabilityoftheredologspacebeforethestartoftheoperationis thatweMUSTnotholdanysynchonizationobjectswhenperformingthe check. Ifyoumakeachangeinthismodulemakesurethatnocodepathis
introduced where a call to log_free_check() is bypassed. */
/***********************************************************//**
Repositions the pcur in the purge node on the clustered index record, if found. If the record is not found, close pcur.
@returnTRUEif the record was found */ static
ibool
row_purge_reposition_pcur( /*======================*/
btr_latch_mode mode, /*!< in: latching mode */
purge_node_t* node, /*!< in: row purge node */
mtr_t* mtr) /*!< in: mtr */
{ if (node->found_clust) {
ut_ad(node->validate_pcur());
if (node->found_clust) {
btr_pcur_store_position(&node->pcur, mtr);
}
}
/* Close the current cursor if we fail to position it correctly. */ if (!node->found_clust) {
btr_pcur_close(&node->pcur);
}
return(node->found_clust);
}
/***********************************************************//**
Removes a delete marked clustered index record if possible.
@retval trueif the row was not found, or it was successfully removed
@retval falseif the row was modified after the delete marking */ static MY_ATTRIBUTE((nonnull, warn_unused_result)) bool
row_purge_remove_clust_if_poss_low( /*===============================*/
purge_node_t* node, /*!< in/out: row purge node */
btr_latch_mode mode) /*!< in: BTR_MODIFY_LEAF or BTR_PURGE_TREE */
{
dict_index_t* index = dict_table_get_first_index(node->pt.table);
table_id_t table_id = 0;
index_id_t index_id = 0;
dict_table_t *table = nullptr;
pfs_os_file_t f = OS_FILE_CLOSED;
if (table_id) {
retry:
dict_sys.lock(SRW_LOCK_CALL);
table = dict_sys.find_table(table_id); if (!table) {
dict_sys.unlock();
} elseif (table->n_rec_locks) { for (dict_index_t* ind = UT_LIST_GET_FIRST(
table->indexes); ind;
ind = UT_LIST_GET_NEXT(indexes, ind)) { if (ind->id == index_id) {
lock_discard_for_index(*ind);
}
}
}
}
mtr_t mtr{node->trx};
mtr.start();
index->set_modified(mtr);
log_free_check(); bool success = true;
if (!row_purge_reposition_pcur(mode, node, &mtr)) { /* The record was already removed. */
removed:
mtr.commit();
close_and_exit: if (table) {
dict_sys.unlock();
} return success;
}
if (node->pt.table->id == DICT_INDEXES_ID) { /* If this is a record of the SYS_INDEXES table, then wehavetofreethefilesegmentsoftheindextree
associated with the index */ if (!table_id) { const rec_t* rec = btr_pcur_get_rec(&node->pcur);
if (node->roll_ptr != row_get_rec_roll_ptr(rec, index, offsets)) { /* Someone else has modified the record later: do not remove */ goto func_exit;
}
ut_ad(rec_get_deleted_flag(rec, rec_offs_comp(offsets))); /* In delete-marked records, DB_TRX_ID must
always refer to an existing undo log record. */
ut_ad(row_get_rec_trx_id(rec, index, offsets));
/* Persistent cursor is closed if reposition fails. */ if (node->found_clust) {
btr_pcur_commit_specify_mtr(&node->pcur, &mtr);
} else {
mtr_commit(&mtr);
}
goto close_and_exit;
}
/***********************************************************//**
Removes a clustered index record if it has not been modified after the delete
marking.
@retval trueif the row was not found, or it was successfully removed
@retval false the purge needs to be suspended because of running out
of file space. */ static MY_ATTRIBUTE((nonnull, warn_unused_result)) bool
row_purge_remove_clust_if_poss( /*===========================*/
purge_node_t* node) /*!< in/out: row purge node */
{ if (row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_LEAF)) { return(true);
}
for (ulint n_tries = 0;
n_tries < BTR_CUR_RETRY_DELETE_N_TIMES;
n_tries++) { if (row_purge_remove_clust_if_poss_low(node, BTR_PURGE_TREE)) { return(true);
}
func_exit: if (n_cmp_v_col == 0) {
*vrow = NULL;
}
mem_heap_free(tuple_heap);
mem_heap_free(heap);
/* FIXME: In the case of n_cmp_v_col is not the same as n_fields-n_non_v_col,callbackisneededtocomparetherest
columns. At the timebeing, we will need to return true */ return (true);
}
/** @return whether two data tuples are equal */ bool dtuple_coll_eq(const dtuple_t &tuple1, const dtuple_t &tuple2)
{
ut_ad(tuple1.magic_n == DATA_TUPLE_MAGIC_N);
ut_ad(tuple2.magic_n == DATA_TUPLE_MAGIC_N);
ut_ad(dtuple_check_typed(&tuple1));
ut_ad(dtuple_check_typed(&tuple2));
ut_ad(tuple1.n_fields == tuple2.n_fields);
for (ulint i= 0; i < tuple1.n_fields; i++) if (cmp_dfield_dfield(&tuple1.fields[i], &tuple2.fields[i])) returnfalse; returntrue;
}
/** Finds out if a version of the record, where the version >= the current purge_sys.view,shouldhaveientryasitssecondaryindexentry.Wecheck ifthereisanynotdeletemarkedversionoftherecordwherethetrx id>=purgeview,andthesecondaryindexentry==ientry;exactlyin thiscasewereturnTRUE. @paramnodepurgenode @paramindexsecondaryindex @paramientrysecondaryindexentry @parammtrmini-transaction
@return whether ientry cannot be purged */ staticbool row_purge_is_unsafe(const purge_node_t &node,
dict_index_t *index, const dtuple_t *ientry, mtr_t *mtr)
{ const rec_t* rec = btr_pcur_get_rec(&node.pcur);
roll_ptr_t roll_ptr = node.roll_ptr;
trx_id_t trx_id = node.trx_id; const rec_t* version;
rec_t* prev_version;
dict_index_t* clust_index = node.pcur.index();
rec_offs* clust_offsets;
mem_heap_t* heap;
dtuple_t* row; const dtuple_t* entry;
dtuple_t* vrow = NULL;
mem_heap_t* v_heap = NULL;
dtuple_t* cur_vrow = NULL;
if (!rec_get_deleted_flag(rec, rec_offs_comp(clust_offsets))) {
row_ext_t* ext;
/* The top of the stack of versions is locked by the mtrholdingalatchonthepagecontainingthe clusteredindexrecord.Thebottomofthestackis lockedbythefactthatthepurge_sys.viewmust 'overtake'anyreadviewofanactivetransaction. Thus,itissafetofetchtheprefixesfor
externally stored columns. */
row = row_build(ROW_COPY_POINTERS, clust_index,
rec, clust_offsets,
NULL, NULL, NULL, &ext, heap);
/* If entry == NULL, the record contains unset BLOB pointers.Thismustbeafreshlyinsertedrecord.If thisiscalledfrom row_purge_remove_sec_if_poss_low(),thethreadwill holdlatchesontheclusteredindexandthesecondary index.Becausetheinsertworksinthreesteps:
thepurgethreadcansafelyignorefreshlyinserted recordsanddeletethesecondaryindexrecord.The threadthatinsertedthenewrecordwillbeinserting
the secondary index records. */
/* NOTE that we cannot do the comparison as binary fieldsbecausetherowismaybebeingmodifiedsothat theclusteredindexrecordhasalreadybeenupdatedto adifferentbinaryvalueinacharfield,butthe
collation identifies the old and new value anyway! */ if (entry && dtuple_coll_eq(*ientry, *entry)) {
unsafe_to_purge:
mem_heap_free(heap);
if (v_heap) {
mem_heap_free(v_heap);
} returntrue;
}
}
} elseif (dict_index_has_virtual(index)) { /* The current cluster index record could be deleted,butthepreviousversionofitmightnot.Wewill needtogetthevirtualcolumndatafromundorecord
associated with current cluster index */
v_heap = mem_heap_create(100);
if (dict_index_has_virtual(index)) { if (vrow) { if (dtuple_vcol_data_missing(*vrow, *index)) { goto nochange_index;
} /* Keep the virtual row info for the next
version, unless it is changed */ if (v_heap) {
mem_heap_empty(v_heap);
} else {
v_heap = mem_heap_create(100);
}
cur_vrow = dtuple_copy(vrow, v_heap);
dtuple_dup_v_fld(cur_vrow, v_heap);
}
if (!cur_vrow) { /* Nothing for this index has changed,
continue */
nochange_index:
version = prev_version; continue;
}
}
if (!rec_get_deleted_flag(prev_version,
rec_offs_comp(clust_offsets))) {
row_ext_t* ext;
/* The stack of versions is locked by mtr. Thus,itissafetofetchtheprefixesfor
externally stored columns. */
row = row_build(ROW_COPY_POINTERS, clust_index,
prev_version, clust_offsets,
NULL, NULL, NULL, &ext, heap);
if (dict_index_has_virtual(index)) {
ut_ad(cur_vrow);
ut_ad(row->n_v_fields == cur_vrow->n_v_fields);
dtuple_copy_v_fields(row, cur_vrow);
}
/* If entry == NULL, the record contains unset BLOBpointers.Thismustbeafreshly insertedrecordthatwecansafelyignore. Forthejustification,seethecommentsafter
the previous row_build_index_entry() call. */
/* NOTE that we cannot do the comparison as binary fieldsbecausemaybethesecondaryindexrecordhas alreadybeenupdatedtoadifferentbinaryvaluein acharfield,butthecollationidentifiestheold
and new value anyway! */
if (entry && dtuple_coll_eq(*ientry, *entry)) { goto unsafe_to_purge;
}
}
version = prev_version;
}
}
/** Determines if it is possible to remove a secondary index entry. Removalispossibleifthesecondaryindexentrydoesnotrefertoany notdeletemarkedversionofaclusteredindexrecordwhereDB_TRX_ID isnewerthanthepurgeview.
NOTE:Thisfunctionshouldonlybecalledbythepurgethread,only whileholdingalatchontheleafpageofthesecondaryindexentry. Itispossiblethatthisfunctionfirstreturnstrueandthenfalse, ifausertransactioninsertsarecordthatthesecondaryindexentry wouldreferto. However,inthatcase,theusertransactionwouldalsore-insertthe secondaryindexentryafterpurgehasremoveditandreleasedtheleaf pagelatch. @paramnoderowpurgenode @paramindexsecondaryindex @paramentrysecondaryindexentry @parammtrmini-transactionforlookingupclusteredindex
@return whether the secondary index record can be purged */ staticbool row_purge_poss_sec(purge_node_t *node, dict_index_t *index, const dtuple_t *entry, mtr_t *mtr)
{
ut_ad(mtr->trx == node->trx);
ut_ad(!index->is_clust()); constauto savepoint= mtr->get_savepoint(); bool can_delete= !row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, mtr);
if (index->is_spatial()) { if (rtr_search(entry, BTR_PURGE_TREE, &pcur, nullptr, &mtr)) { goto func_exit;
}
} elseif (!row_search_index_entry(entry, BTR_PURGE_TREE,
&pcur, &mtr)) { /* Not found. This is a legitimate condition. In a rollback,InnoDBwillremovesecondaryrecsthatwould bepurgedanyway.Thentheactualpurgewillnotfind thesecondaryindexrecord.Also,thepurgeitselfis eager:ifitcomestoconsiderasecondaryindex record,andnoticesitdoesnotneedtoexistinthe index,itwillremoveit.Thenif/whenthepurge comestoconsiderthesecondaryindexrecordasecond
time, it will not exist any more in the index. */ goto func_exit;
}
/* We should remove the index record if no later version of the row, whichcannotbepurgedyet,requiresitsexistence.Ifsomerequires,
we should do nothing. */
if (page_max_trx_id
== page_get_max_trx_id(btr_cur_get_page(&pcur.btr_cur))
|| row_purge_poss_sec(node, index, entry, &mtr)) {
/* Remove the index record, which should have been
marked for deletion. */ if (!rec_get_deleted_flag(btr_pcur_get_rec(&pcur),
index->table->not_redundant())) {
row_purge_del_mark_error(pcur.btr_cur, *entry); goto func_exit;
}
func_exit:
btr_pcur_close(&pcur); // FIXME: need this?
mtr.commit(); return success;
}
/** Compute a nonzero return value of row_purge_remove_sec_if_poss_leaf(). @parampagelatchedsecondaryindexpage @returnPAGE_MAX_TRX_IDforrow_purge_remove_sec_if_poss_tree()
@retval 1 if a further row_purge_poss_sec() check is necessary */
ATTRIBUTE_NOINLINE ATTRIBUTE_COLD static trx_id_t row_purge_check(const page_t *page) noexcept
{
trx_id_t id= page_get_max_trx_id(page);
ut_ad(id); if (trx_sys.find_same_or_older_in_purge(purge_sys.query->trx, id)) /* Because an active transaction may modify the secondary index butnotPAGE_MAX_TRX_ID,row_purge_poss_sec()mustbeinvoked againafterre-latchingthepage.LetusreturnabogusID.Yes, anactualtransactionwithID1wouldcreatetheInnoDBdictionary tablesindict_sys_t::create_or_check_sys_tables(),butitwould exclusivelywriteTRX_UNDO_INSERT_RECrecords.Purgingthose
records never involves row_purge_remove_sec_if_poss_tree(). */
id= 1; return id;
}
__attribute__((nonnull, warn_unused_result)) /** Remove a secondary index entry if possible, without modifying the tree. @paramnodepurgenode @paramindexsecondaryindex @paramentryindexentry @returnPAGE_MAX_TRX_IDforrow_purge_remove_sec_if_poss_tree() @retval1ifafurtherrow_purge_poss_sec()checkisnecessary
@retval 0 if success or if not found */ static trx_id_t row_purge_remove_sec_if_poss_leaf(purge_node_t *node,
dict_index_t *index, const dtuple_t *entry)
{
mtr_t mtr{node->trx};
btr_pcur_t pcur;
trx_id_t page_max_trx_id = 0;
if (index->is_spatial()) { if (!rtr_search(entry, BTR_MODIFY_LEAF, &pcur, nullptr,
&mtr)) { goto found;
}
} elseif (btr_pcur_open(entry, PAGE_CUR_LE, BTR_MODIFY_LEAF, &pcur,
&mtr)
== DB_SUCCESS
&& !btr_pcur_is_before_first_on_page(&pcur)
&& btr_pcur_get_low_match(&pcur)
== dtuple_get_n_fields(entry)) {
found: /* Before attempting to purge a record, check
if it is safe to do so. */ if (row_purge_poss_sec(node, index, entry, &mtr)) { /* Only delete-marked records should be purged. */ if (!rec_get_deleted_flag(btr_pcur_get_rec(&pcur),
index->table
->not_redundant())) {
row_purge_del_mark_error(pcur.btr_cur, *entry);
mtr.commit();
dict_set_corrupted(node->trx, index, "purge"); goto cleanup;
}
if (id.page_no() != index->page
&& page_get_n_recs(block->page.frame) < 2
&& !lock_test_prdt_page_lock(nullptr, id)){ /* this is the last record on page, andithasa"page"lockonit, whichmeansearchisstilldepending
on it, so do not delete */
DBUG_LOG("purge", "skip purging last" " record on page " << id); goto func_exit;
}
}
/***********************************************************//**
Removes a secondary index entry if possible. */
UNIV_INLINE MY_ATTRIBUTE((nonnull(1,2))) void
row_purge_remove_sec_if_poss( /*=========================*/
purge_node_t* node, /*!< in: row purge node */
dict_index_t* index, /*!< in: index */ const dtuple_t* entry) /*!< in: index entry */
{ if (UNIV_UNLIKELY(!entry)) /* The node->row must have lacked some fields of this index. This ispossiblewhentheundologrecordwaswrittenbeforethisindex
was created. */ return;
if (trx_id_t page_max_trx_id=
row_purge_remove_sec_if_poss_leaf(node, index, entry)) for (auto n_tries= BTR_CUR_RETRY_DELETE_N_TIMES;
!row_purge_remove_sec_if_poss_tree(node, index, entry,
page_max_trx_id);
std::this_thread::sleep_for(BTR_CUR_RETRY_SLEEP_TIME)) /* The delete operation may fail if we have little
file space left (if innodb_file_per_table=0?) */
ut_a(--n_tries);
}
/** Purgesadeletemarkingofarecord. @paramnoderowpurgenode @retvaltrueiftherowwasnotfound,oritwassuccessfullyremoved @retvalfalsethepurgeneedstobesuspendedbecauseof
running out of file space */ static MY_ATTRIBUTE((nonnull, warn_unused_result)) bool row_purge_del_mark(purge_node_t *node) noexcept
{ if (node->index)
{
mem_heap_t *heap= mem_heap_create(1024);
do
{ if (node->index->type & (DICT_FTS | DICT_CORRUPT)) continue; if (!node->index->is_committed()) continue;
dtuple_t* entry= row_build_index_entry_low(node->row, nullptr,
node->index, heap,
ROW_BUILD_FOR_PURGE);
row_purge_remove_sec_if_poss(node, node->index, entry);
mem_heap_empty(heap);
} while ((node->index= dict_table_get_next_index(node->index)));
#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF("enable_row_purge_del_mark_exit_sync_point",
debug_sync_set_action
(node->trx->mysql_thd,
STRING_WITH_LEN("now SIGNAL row_purge_del_mark_finished"));
); #endif
return result;
}
/***********************************************************//**
Purges an update of an existing record. Also purges an update of a delete
marked record if that record contained an externally stored field. */ static void
row_purge_upd_exist_or_extern( const que_thr_t*thr, /*!< in: query thread */
purge_node_t* node, /*!< in: row purge node */ const trx_undo_rec_t* undo_rec) /*!< in: record to purge */
{
mem_heap_t* heap;
do { if (node->index->type & (DICT_FTS | DICT_CORRUPT)) { continue;
}
if (!node->index->is_committed()) { continue;
}
if (row_upd_changes_ord_field_binary(node->index, node->update,
thr, NULL, NULL)) { /* Build the older version of the index entry */
dtuple_t* entry = row_build_index_entry_low(
node->row, NULL, node->index,
heap, ROW_BUILD_FOR_PURGE);
row_purge_remove_sec_if_poss(
node, node->index, entry);
ut_ad(node->pt.table);
mem_heap_empty(heap);
}
} while ((node->index = dict_table_get_next_index(node->index)));
mem_heap_free(heap);
skip_secondaries:
mtr_t mtr{node->trx};
dict_index_t* index = dict_table_get_first_index(node->pt.table); /* Free possible externally stored fields */ for (ulint i = 0; i < upd_get_n_fields(node->update); i++) {
/* We use the fact that new_val points to undo_recandgetthustheoffsetof dfielddatainsidetheundorecord.Thenwe cancalculatefromnode->roll_ptrthefile
address of the new_val data */
/* We have to acquire an SX-latch to the clustered
index tree (exclude other tree changes) */
mtr_sx_lock_index(index, &mtr);
index->set_modified(mtr);
/* NOTE: we must also acquire a U latch to the rootpageofthetree.Wewillneeditwhenwe freepagesfromthetree.Ifthetreeisofheight1, thetreeX-latchdoesNOTprotecttherootpage, becauseitisalsoaleafpage.Sincewewillhavea latchonanundologpage,wewouldbreakthe latchingorderifwewouldonlylaterlatchthe
root page of such a tree! */
/** Build a partial row from an update undo log record for purge. Anycolumnswhichoccurasorderinginanyindexofthetablearepresent. Anymissingcolumnsareindicatedbycol->mtype==DATA_MISSING.
@paramptrremainingpartoftheundologrecord @paramindexclusteredindex @paramnodepurgenode
@return pointer to remaining part of undo record */ static byte *row_purge_get_partial(const byte *ptr, const dict_index_t &index,
purge_node_t *node)
{ bool first_v_col= true; bool is_undo_log= true;
/* Mark all columns in the row uninitialized, so that
we can distinguish missing fields from fields that are SQL NULL. */ for (ulint i= 0; i < index.table->n_cols; i++)
node->row->fields[i].type.mtype= DATA_MISSING;
dtuple_init_v_fld(node->row);
for (const upd_field_t *uf= node->update->fields, *const ue=
node->update->fields + node->update->n_fields; uf != ue; uf++)
{ if (!uf->old_v_val)
{ const dict_col_t &c= *dict_index_get_nth_col(&index, uf->field_no); if (!c.is_dropped())
node->row->fields[c.ind]= uf->new_val;
}
}
if (!col->ord_part || spatial_status == SPATIAL_ONLY ||
node->rec_type == TRX_UNDO_UPD_DEL_REC) continue; /* If the prefix of this BLOB column is indexed, ensure that enough
prefix is stored in the undo log record. */
ut_a(dfield_get_len(dfield) >= BTR_EXTERN_FIELD_REF_SIZE);
ut_a(dict_table_has_atomic_blobs(index.table) ||
dfield_get_len(dfield) >=
REC_ANTELOPE_MAX_INDEX_COL_LEN + BTR_EXTERN_FIELD_REF_SIZE);
}
for (ulint i= 0; i < index.n_uniq; i++)
{
dfield_t &field= node->row->fields[index.fields[i].col->ind]; if (field.type.mtype == DATA_MISSING)
field= node->ref->fields[i];
}
returnconst_cast<byte*>(ptr);
}
MY_ATTRIBUTE((nonnull,warn_unused_result)) /** Parses the row reference and other info in a modify undo log record. @param[in]noderowundonode @param[in]undo_recrecordtopurge @param[in]thrquerythread @param[in]thdworkerthread @param[out]updated_externtrueifanexternallystoredfieldwas updated
@return true if purge operation required */ static bool
row_purge_parse_undo_rec(
purge_node_t* node, const trx_undo_rec_t* undo_rec,
que_thr_t* thr, const THD* thd, bool* updated_extern)
{
dict_index_t* clust_index;
undo_no_t undo_no;
table_id_t table_id;
roll_ptr_t roll_ptr;
byte info_bits;
byte type;
switch (type) { case TRX_UNDO_RENAME_TABLE: returnfalse; case TRX_UNDO_EMPTY: case TRX_UNDO_INSERT_METADATA: case TRX_UNDO_INSERT_REC: /* These records do not store any transaction identifier. */
node->trx_id = TRX_ID_MAX; break; default: #ifdef UNIV_DEBUG
ut_ad("unknown undo log record type" == 0); returnfalse; case TRX_UNDO_UPD_DEL_REC: case TRX_UNDO_UPD_EXIST_REC: case TRX_UNDO_DEL_MARK_REC: #endif/* UNIV_DEBUG */
ptr = trx_undo_update_rec_get_sys_cols(ptr, &node->trx_id,
&roll_ptr, &info_bits); break;
}
node->pt = node->tables[table_id]; if (!node->pt.table) { returnfalse;
}
ut_ad(!node->pt.table->is_temporary());
if (TABLE *maria_table = node->pt.get_maria_table()) {
maria_table->in_use = (THD*)thd;
}
if (clust_index->is_corrupted()) { /* The table was corrupt in the data dictionary. dict_set_corrupted()worksonanindex,and
we do not have an index to call it with. */
DBUG_ASSERT(table_id == node->pt.table->id); returnfalse;
}
switch (type) { case TRX_UNDO_INSERT_METADATA:
node->ref = &trx_undo_metadata; returntrue; case TRX_UNDO_EMPTY:
node->ref = nullptr; returntrue;
}
switch (node->rec_type) { case TRX_UNDO_EMPTY: break; case TRX_UNDO_DEL_MARK_REC:
purged = row_purge_del_mark(node); if (purged) { if (node->pt.table->stat_initialized()
&& srv_stats_include_delete_marked) {
dict_stats_update_if_needed(
node->pt.table, *thr->graph->trx);
}
MONITOR_INC(MONITOR_N_DEL_ROW_PURGE);
} break; case TRX_UNDO_INSERT_METADATA: case TRX_UNDO_INSERT_REC:
node->roll_ptr |= 1ULL << ROLL_PTR_INSERT_FLAG_POS; /* fall through */ default: if (!updated_extern) { break;
} /* fall through */ case TRX_UNDO_UPD_EXIST_REC:
row_purge_upd_exist_or_extern(thr, node, undo_rec);
MONITOR_INC(MONITOR_N_UPD_EXIST_EXTERN); break;
}
if (node->found_clust) {
node->found_clust = false;
btr_pcur_close(&node->pcur);
}
return(purged);
}
/***********************************************************//**
Fetches an undo log record and does the purge for the recorded operation. If none left, or the current purge completed, returns the control to the
parent node, which is always a query thread node. */ static MY_ATTRIBUTE((nonnull)) void
row_purge( /*======*/
purge_node_t* node, /*!< in: row purge node */ const trx_undo_rec_t* undo_rec, /*!< in: record to purge */
que_thr_t* thr, /*!< in: query thread */ const THD* thd) /*!< in: Worker thread */
{ if (undo_rec != reinterpret_cast<trx_undo_rec_t*>(-1)) { bool updated_extern;
while (row_purge_parse_undo_rec(node, undo_rec, thr,
thd, &updated_extern)) {
#ifdef UNIV_DEBUG /***********************************************************//**
Validate the persistent cursor. The purge node has two references
to the clustered index record - one via the ref member, and the
other via the persistent cursor. These two references must match
each other if the found_clust flag is set.
@returntrueif the stored copy of persistent cursor is consistent
with the ref member.*/ bool
purge_node_t::validate_pcur()
{ if (!found_clust) { return(true);
}
/* Here we are comparing the purge ref record and the stored initial partinpersistentcursor.Bothcaseswestoren_uniqfieldsofthe clusterindexandsoitisfinetodothecomparison.Wenotethis
dependency here as pcur and ref belong to different modules. */ int st = cmp_dtuple_rec(ref, pcur.old_rec, clust_index, offsets);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.