/** Check whether all non-virtual index fields are equal. @param[in]indexthesecondaryindex @param[in]afirstindexentrytocompare @param[in]bsecondindexentrytocompare
@return whether all non-virtual fields are equal */ static bool
row_vers_non_virtual_fields_equal( const dict_index_t* index, const dfield_t* a, const dfield_t* b)
{ const dict_field_t* end = &index->fields[index->n_fields];
for (const dict_field_t* ifield = index->fields; ifield != end;
ifield++) { if (!ifield->col->is_virtual()
&& cmp_dfield_dfield(a++, b++)) { returnfalse;
}
}
returntrue;
}
/** Determine if an active transaction has inserted or modified a secondary indexrecord. @param[in]clust_recclusteredindexrecord @param[in]clust_indexclusteredindex @param[in]recsecondaryindexrecord @param[in]indexsecondaryindex @param[in]offsetsrec_get_offsets(rec,index) @param[in,out]mtrmini-transaction @returntheactivetransaction;statemustberecheckedafter acquiringtrx->mutex,andtrx->release_reference()mustbeinvoked
@retval NULL if the record was committed */
UNIV_INLINE
trx_t*
row_vers_impl_x_locked_low( const rec_t* clust_rec,
dict_index_t* clust_index, const rec_t* rec,
dict_index_t* index, const rec_offs* offsets,
mtr_t* mtr)
{
trx_id_t trx_id;
rec_t* prev_version = NULL;
rec_offs clust_offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs* clust_offsets;
mem_heap_t* heap;
dtuple_t* ientry = NULL;
mem_heap_t* v_heap = NULL;
dtuple_t* cur_vrow = NULL;
if (dict_index_has_virtual(index)) {
ulint est_size = DTUPLE_EST_ALLOC(index->n_fields);
/* Allocate the dtuple for virtual columns extracted from undo logwithitsownheap,sotoavoiditbeingfreedaswe
iterating in the version loop below. */
v_heap = mem_heap_create(est_size);
ientry = row_rec_to_index_entry(rec, index, offsets, v_heap);
}
/* We look up if some earlier version, which was modified by thetrx_idtransaction,oftheclusteredindexrecordwould requirerectobeinadifferentstate(deletemarkedor unmarked,orhavedifferentfieldvalues,ornotexisting).If thereissuchaversion,thenrecwasmodifiedbythetrx_id transaction,andithasanimplicitx-lockonrec.Notethat ifclust_recitselfwouldrequirerectobeinadifferent state,thenthetrx_idtransactionhasnotyethadtimeto modifyrec,anddoesnotnecessarilyhaveanimplicitx-lock
on rec. */
for (const rec_t* version = clust_rec;; version = prev_version) {
row_ext_t* ext;
dtuple_t* row;
dtuple_t* entry;
ulint vers_del;
trx_id_t prev_trx_id;
mem_heap_t* old_heap = heap;
dtuple_t* vrow = NULL;
/* We keep the semaphore in mtr on the clust_rec page, so thatnoothertransactioncanupdateitandgetan
implicit x-lock on rec until mtr_commit(mtr). */
/* The oldest visible clustered index version must not be delete-marked,becauseweneverstartatransactionby
inserting a delete-marked record. */
ut_ad(committed || prev_version
|| !rec_get_deleted_flag(version, comp));
/* Free version and clust_offsets. */
mem_heap_free(old_heap);
if (committed) { goto not_locked;
}
if (prev_version == NULL) {
/* We reached the oldest visible version without findinganolderversionofclust_recthatwould matchthesecondaryindexrecord.Ifthesecondary indexrecordisnotdeletemarked,thenclust_rec isconsideredthecorrectmatchofthesecondary
index record and hence holds the implicit lock. */
if (rec_del) { /* The secondary index record is del marked. So,theimplicitlockholderofclust_rec didnotmodifythesecondaryindexrecordyet, andisnotholdinganimplicitlockonit.
Thisassumesthatwheneverarowisinserted orupdated,theleafpagerecordalwaysis createdwithacleardelete-markflag.
(We never insert a delete-marked record.) */
not_locked:
trx->release_reference();
trx = 0;
}
if (dict_index_has_virtual(index)) { if (vrow) { /* Keep the virtual row info for the next
version */
cur_vrow = dtuple_copy(vrow, v_heap);
dtuple_dup_v_fld(cur_vrow, v_heap);
}
if (!cur_vrow) { /* Build index entry out of row */
entry = row_build_index_entry(row, ext, index,
heap);
/* entry could only be NULL (the clusteredindexrecordcouldcontain BLOBpointersthatareNULL)ifwe wereaccessingafreshlyinserted recordbeforeitwasfullyinserted. prev_versioncannotpossiblybesuch anincompleterecord,becauseits transactionwouldhavetobecommitted inorderforlaterversionsofthe
record to be able to exist. */
ut_ad(entry);
/* If the indexed virtual columns has changed, theremustbelogrecordtogeneratevrow. Otherwise,itisnotchanged,sononeed
to compare */ if (!row_vers_non_virtual_fields_equal(
index,
ientry->fields, entry->fields)) { if (rec_del != vers_del) { break;
}
} elseif (!rec_del) { break;
}
/* entry could only be NULL (the clustered index recordcouldcontainBLOBpointersthatareNULL)if wewereaccessingafreshlyinsertedrecordbeforeit wasfullyinserted.prev_versioncannotpossiblybe suchanincompleterecord,becauseitstransaction wouldhavetobecommittedinorderforlaterversions
of the record to be able to exist. */
ut_ad(entry);
/* If we get here, we know that the trx_id transaction modifiedprev_version.Letuscheckifprev_version
would require rec to be in a different state. */
/* The previous version of clust_rec must be accessible,becauseclust_recwasnotafreshinsert. Thereisnoguaranteethatthetransactionisstill
active. */
/* We check if entry and rec are identified in the alphabetical
ordering */ if (0 == cmp_dtuple_rec(entry, rec, index, offsets)) { /* The delete marks of rec and prev_version should be equalforrectobeinthestaterequiredby
prev_version */
if (rec_del != vers_del) {
break;
}
/* It is possible that the row was updated so that the secondaryindexrecordremainedthesamein alphabeticalordering,butthefieldvalueschanged
still. For example, 'abc' -> 'ABC'. Check also that. */
} elseif (!rec_del) { /* The delete mark should be set in rec for it to be
in the state required by prev_version */
break;
}
result_check: if (trx->id != prev_trx_id) { /* prev_version was the first version modified by
the trx_id transaction: no implicit x-lock */ goto not_locked;
}
}
if (trx) {
DBUG_PRINT("info", ("Implicit lock is held by trx:" TRX_ID_FMT,
trx_id));
}
if (v_heap != NULL) {
mem_heap_free(v_heap);
}
mem_heap_free(heap);
DBUG_RETURN(trx);
}
/** Determine if an active transaction has inserted or modified a secondary indexrecord. @param[in,out]caller_trxtrxofcurrentthread @param[in]recsecondaryindexrecord @param[in]indexsecondaryindex @param[in]offsetsrec_get_offsets(rec,index) @returntheactivetransaction;statemustberecheckedafter acquiringtrx->mutex,andtrx->release_reference()mustbeinvoked
@retval NULL if the record was committed */
trx_t*
row_vers_impl_x_locked(
trx_t* caller_trx, const rec_t* rec,
dict_index_t* index, const rec_offs* offsets)
{
mtr_t mtr{caller_trx};
trx_t* trx; const rec_t* clust_rec;
dict_index_t* clust_index;
/* The function must not be invoked under lock_sys latch to prevent latchingorderviolation,i.e.pagelatchmustbeacquiredbefore
lock_sys latch */
lock_sys.assert_unlocked(); /* The current function can be called from lock_rec_unlock_unmodified()
under lock_sys.wr_lock() */
mtr_start(&mtr);
/* Search for the clustered index record. The latch on the pageofclust_reclocksthetopofthestackofversions.The bottomoftheversionstackisnotlocked;oldestversionsmay disappearbythefactthattransactionsmaybecommittedand collectedbythepurge.Thisisnotaproblem,becauseweare
only interested in active transactions. */
if (!clust_rec) { /* In a rare case it is possible that no clust rec is found forasecondaryindexrecord:ifinrow0umod.cc row_undo_mod_remove_clust_low()wehavealreadyremovedthe clustrec,whilepurgeisstillcleaningandremoving secondaryindexrecordsassociatedwithearlierversionsof theclusteredindexrecord.Inthatcasetherecannotbe anyimplicitlockonthesecondaryindexrecord,because anactivetransactionwhichhasmodifiedthesecondaryindex recordhasalsomodifiedtheclusteredindexrecord.Andin arollbackwealwaysundothemodificationstosecondaryindex
records before the clustered index record. */
/** build virtual column value from current cluster index record data @param[in,out]rowtheclusterindexrowindtupleform @param[in]clust_indexclusteredindex @param[in]indexthesecondaryindex
@param[in] heap heap used to build virtual dtuple. */ bool
row_vers_build_clust_v_col(
dtuple_t* row,
dict_index_t* clust_index,
dict_index_t* index,
mem_heap_t* heap,
TABLE* maria_table)
{
THD* thd= current_thd;
/* If this is called by purge thread, set TRX_UNDO_PREV_IN_PURGE bittosearchtheundologuntilwehitthecurrentundologwith
roll_ptr */ const ulint status = trx_id
? TRX_UNDO_PREV_IN_PURGE | TRX_UNDO_GET_OLD_V_VALUE
: TRX_UNDO_GET_OLD_V_VALUE;
/* This is a newly inserted record and cannot bedeleted,Sotheexternallystoredfield
cannot be freed yet. */
dtuple_t* row = row_build(ROW_COPY_POINTERS, clust_index,
rec, *clust_offsets,
NULL, NULL, NULL, NULL, heap);
if (!row_vers_build_clust_v_col(row, clust_index, index,
heap, maria_table)) { return nullptr;
}
/** Find out whether data tuple has missing data type forindexedvirtualcolumn. @paramtupledatatuple @paramindexvirtualindex
@return true if tuple has missing column type */ bool dtuple_vcol_data_missing(const dtuple_t &tuple, const dict_index_t &index)
{ for (ulint i= 0; i < index.n_uniq; i++)
{
dict_col_t *col= index.fields[i].col; if (!col->is_virtual()) continue;
dict_v_col_t *vcol= reinterpret_cast<dict_v_col_t*>(col); for (ulint j= 0; j < index.table->n_v_cols; j++) if (vcol == &index.table->v_cols[j] &&
tuple.v_fields[j].type.mtype == DATA_MISSING) returntrue;
} returnfalse;
}
/*****************************************************************//**
Constructs the version of a clustered index record which a consistent
read should see. We assume that the trx id stored in rec is such that
the consistent read should not see rec in its present version.
@return error code
@retval DB_SUCCESS if a previous version was fetched
@retval DB_MISSING_HISTORY if the history is missing (a sign of corruption) */
dberr_t
row_vers_build_for_consistent_read( /*===============================*/ const rec_t* rec, /*!< in: record in a clustered index; the callermusthavealatchonthepage;this latchlocksthetopofthestackofversions
of this records */
mtr_t* mtr, /*!< in: mtr holding the latch on rec */
dict_index_t* index, /*!< in: the clustered index */
rec_offs** offsets,/*!< in/out: offsets returned by
rec_get_offsets(rec, index) */
ReadView* view, /*!< in: the consistent read view */
mem_heap_t** offset_heap,/*!< in/out: memory heap from which
the offsets are allocated */
mem_heap_t* in_heap,/*!< in: memory heap from which the memory for *old_versisallocated;memoryforpossible intermediateversionsisallocatedandfreed
locally within the function */
rec_t** old_vers,/*!< out, own: old version, or NULL ifthehistoryismissingortherecord doesnotexistintheview,thatis,
it was freshly inserted afterwards */
dtuple_t** vrow) /*!< out: virtual row */
{ const rec_t* version;
rec_t* prev_version;
trx_id_t trx_id;
mem_heap_t* heap = NULL;
byte* buf;
dberr_t err;
#ifdefined __aarch64__&&defined __GNUC__&&__GNUC__==4&&!defined __clang__ /* Avoid GCC 4.8.5 internal compiler error "could not split insn". */ # pragma GCC optimize ("O0") #endif /*****************************************************************//**
Constructs the last committed version of a clustered index record,
which should be seen by a semi-consistent read. */ void
row_vers_build_for_semi_consistent_read( /*====================================*/ const rec_t* rec, /*!< in: record in a clustered index; the callermusthavealatchonthepage;this latchlocksthetopofthestackofversions
of this records */
mtr_t* mtr, /*!< in: mtr holding the latch on rec */
dict_index_t* index, /*!< in: the clustered index */
rec_offs** offsets,/*!< in/out: offsets returned by
rec_get_offsets(rec, index) */
mem_heap_t** offset_heap,/*!< in/out: memory heap from which
the offsets are allocated */
mem_heap_t* in_heap,/*!< in: memory heap from which the memory for *old_versisallocated;memoryforpossible intermediateversionsisallocatedandfreed
locally within the function */ const rec_t** old_vers,/*!< out: rec, old version, or NULL if the recorddoesnotexistintheview,thatis,
it was freshly inserted afterwards */
dtuple_t** vrow) /*!< out: virtual row, old version, or NULL
if it is not updated in the view */
{ const rec_t* version;
mem_heap_t* heap = NULL;
byte* buf;
trx_id_t rec_trx_id = 0;
if (!trx_sys.is_registered(mtr->trx, version_trx_id)) {
committed_version_trx: /* We found a version that belongs to a
committed transaction: return it. */
if (rec == version) {
*old_vers = rec; if (vrow) {
*vrow = NULL;
} break;
}
/* We assume that a rolled-back transaction stays in TRX_STATE_ACTIVEstateuntilallthechangeshavebeen rolledbackandthetransactionisremovedfrom
the global list of transactions. */
if (rec_trx_id == version_trx_id) { /* The transaction was committed while wesearchedforearlierversions. Returnthecurrentversionasa
semi-consistent read. */
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.