/** The search tuple corresponding to TRX_UNDO_INSERT_METADATA. */ const dtuple_t trx_undo_metadata = { /* This also works for REC_INFO_METADATA_ALTER, because the
delete-mark (REC_INFO_DELETED_FLAG) is ignored when searching. */
REC_INFO_METADATA_ADD, 0, 0, 0, nullptr, nullptr #ifdef UNIV_DEBUG
, DATA_TUPLE_MAGIC_N #endif/* UNIV_DEBUG */
};
/*=========== UNDO LOG RECORD CREATION AND DECODING ====================*/
/** Calculate the free space left for extending an undo log record. @paramundo_blockundologpage @paramptrcurrentendoftheundopage
@return bytes left */ static ulint trx_undo_left(const buf_block_t *undo_block, const byte *ptr)
{
ut_ad(ptr >=
&undo_block->page.frame[TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE]); /* The 10 is supposed to be an extra safety margin (and needed for
compatibility with older versions) */
lint left= srv_page_size - (ptr - undo_block->page.frame) -
(10 + FIL_PAGE_DATA_END);
ut_ad(left >= 0); return left < 0 ? 0 : static_cast<ulint>(left);
}
/**********************************************************************//**
Set the next and previous pointers in the undo page for the undo record
that was written to ptr. Update the first free value by the number of bytes
written forthis undo record.
@return offset of the inserted entry on the page if succeeded, 0if fail */ static
uint16_t
trx_undo_page_set_next_prev_and_add( /*================================*/
buf_block_t* undo_block, /*!< in/out: undo log page */
byte* ptr, /*!< in: ptr up to where data has been
written on this undo page. */
mtr_t* mtr) /*!< in: mtr */
{
ut_ad(page_align(ptr) == undo_block->page.frame);
if (UNIV_UNLIKELY(trx_undo_left(undo_block, ptr) < 2)) return0;
/* Update the offset to first free undo record */
mach_write_to_2(ptr_to_first_free, end_of_rec); /* Write offset of the next undo log record */
memcpy(undo_block->page.frame + first_free, ptr_to_first_free, 2); const byte *start= undo_block->page.frame + first_free + 2;
/** Virtual column undo log version. To distinguish it from a length value
in 5.7.8 undo log, it starts with 0xF1 */ staticconst ulint VIRTUAL_COL_UNDO_FORMAT_1 = 0xF1;
/** Write virtual column index info (index id and column position in index) totheundolog @param[in,out]undo_blockundologpage @param[in]tablethetable @param[in]posthevirtualcolumnposition @param[in]ptrundologrecordbeingwritten @param[in]first_v_colwhetherthisisthefirstvirtualcolumn whichcouldstartwithaversionmarker
@return new undo log pointer */ static
byte*
trx_undo_log_v_idx(
buf_block_t* undo_block, const dict_table_t* table,
ulint pos,
byte* ptr, bool first_v_col)
{
ut_ad(pos < table->n_v_def);
dict_v_col_t* vcol = dict_table_get_nth_v_col(table, pos);
byte* old_ptr;
/* The mach_write_compressed(ptr, flen) in
trx_undo_page_report_modify() will consume additional 1 to 5 bytes. */ if (avail < size + 5) { return(NULL);
}
if (first_v_col) { /* write the version marker */
mach_write_to_1(ptr, VIRTUAL_COL_UNDO_FORMAT_1);
ptr += 1;
}
old_ptr = ptr;
ptr += 2;
ptr += mach_write_compressed(ptr, n_idx);
for (constauto& v_index : vcol->v_indexes) { /* This is compatible with ptr+=mach_u64_write_much_compressed(ptr,v_index.index-id)
(the added "if" statement is fixing an old regression). */ if (uint32_t hi= uint32_t(v_index.index->id >> 32)) {
*ptr++ = 0xff;
ptr += mach_write_compressed(ptr, hi);
}
ptr += mach_write_compressed(ptr, uint32_t(v_index.index->id));
ptr += mach_write_compressed(ptr, v_index.nth_field);
}
ut_ad(orig_ptr + size == ptr);
mach_write_to_2(old_ptr, ulint(ptr - old_ptr));
return(ptr);
}
/** Read virtual column index from undo log, and verify the column is still indexed,andreturnitsposition @param[in]tablethetable @param[in]ptrundologpointer @param[out]col_posthecolumnnumberorFIL_NULL ifthecolumnisnotindexedanymore
@return remaining part of undo log record after reading these values */ static const byte*
trx_undo_read_v_idx_low( const dict_table_t* table, const byte* ptr,
uint32_t* col_pos)
{
ulint len = mach_read_from_2(ptr); const byte* old_ptr = ptr;
for (ulint i = 0; i < num_idx; i++) {
index_id_t id = 0; /* This is like mach_u64_read_much_compressed(),
but advancing ptr to the next field. */ if (*ptr == 0xff) {
ptr++;
id = mach_read_next_compressed(&ptr);
id <<= 32;
}
id |= mach_read_next_compressed(&ptr);
ulint pos = mach_read_next_compressed(&ptr);
dict_index_t* index = dict_table_get_next_index(clust_index);
while (index != NULL) { /* Return if we find a matching index. TODO:inthefuture,itmightbeworthtoadd
checks on other indexes */ if (index->id == id) { const dict_col_t* col = dict_index_get_nth_col(
index, pos);
ut_ad(col->is_virtual()); const dict_v_col_t* vcol = reinterpret_cast< const dict_v_col_t*>(col);
*col_pos = vcol->v_pos; return(old_ptr + len);
}
index = dict_table_get_next_index(index);
}
}
return(old_ptr + len);
}
/** Read virtual column index from undo log or online log if the log containssuchinfo,andintheundologcase,verifythecolumnis stillindexed,andoutputitsposition @param[in]tablethetable @param[in]ptrundologpointer @param[in]first_v_colifthisisthefirstvirtualcolumn,which hastheversionmarker @param[in,out]is_undo_logthisfunctionisusedtoparsebothundolog, andonlinelogforvirtualcolumns.So checktoseeifthisisundolog.When first_v_colistrue,is_undo_logisoutput, whenfirst_v_colisfalse,is_undo_logisinput @param[out]field_nothecolumnnumber,orFIL_NULLifnotindexed
@return remaining part of undo log record after reading these values */ const byte*
trx_undo_read_v_idx( const dict_table_t* table, const byte* ptr, bool first_v_col, bool* is_undo_log,
uint32_t* field_no)
{ /* Version marker only put on the first virtual column */ if (first_v_col) { /* Undo log has the virtual undo log marker */
*is_undo_log = (mach_read_from_1(ptr)
== VIRTUAL_COL_UNDO_FORMAT_1);
/** Reports in the undo log of an insert of virtual columns. @param[in]undo_blockundologpage @param[in]tablethetable @param[in]rowdtuplecontainsthevirtualcolumns @param[in,out]ptrlogptr
@return true if write goes well, false if out of space */ static bool
trx_undo_report_insert_virtual(
buf_block_t* undo_block,
dict_table_t* table, const dtuple_t* row,
byte** ptr)
{
byte* start = *ptr; bool first_v_col = true;
if (trx_undo_left(undo_block, *ptr) < 2) { return(false);
}
/* Reserve 2 bytes to write the number ofbytesthestoredfieldstakeinthis
undo record */
*ptr += 2;
for (ulint col_no = 0; col_no < dict_table_get_n_v_cols(table);
col_no++) { const dict_v_col_t* col
= dict_table_get_nth_v_col(table, col_no);
if (col->m_col.ord_part) {
/* make sure enought space to write the length */ if (trx_undo_left(undo_block, *ptr) < 5) { return(false);
}
/* Always mark the end of the log with 2 bytes length field */
mach_write_to_2(start, ulint(*ptr - start));
return(true);
}
/** Reports in the undo log of an insert of a clustered index record. @paramundo_blockundologpage @paramtrxtransaction @paramindexclusteredindex @paramclust_entryindexentrywhichwillbeinsertedtothe clusteredindex @parammtrmini-transaction @paramwrite_emptywriteemptytableundologrecord
@return offset of the inserted entry on the page if succeed, 0 if fail */ static
uint16_t
trx_undo_page_report_insert(
buf_block_t* undo_block,
dict_index_t* index, const dtuple_t* clust_entry,
mtr_t* mtr, bool write_empty)
{
ut_ad(index->is_primary()); /* MariaDB 10.3.1+ in trx_undo_page_init() always initializes TRX_UNDO_PAGE_TYPEas0,butpreviousversionswrote TRX_UNDO_INSERT==1intoinsert_undopages,
or TRX_UNDO_UPDATE == 2 into update_undo pages. */
ut_ad(mach_read_from_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE
+ undo_block->page.frame) <= 2);
if (trx_undo_left(undo_block, ptr) < 2 + 1 + 11 + 11) { /* Not enough space for writing the general parameters */ return(0);
}
/* Reserve 2 bytes for the pointer to the next undo log record */
ptr += 2;
/* Store first some general parameters to the undo log */
*ptr++ = TRX_UNDO_INSERT_REC;
ptr += mach_u64_write_much_compressed(ptr, mtr->trx->undo_no);
ptr += mach_u64_write_much_compressed(ptr, index->table->id);
if (write_empty) { /* Table is in bulk operation */
undo_block->page.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 */ if (UNIV_UNLIKELY(clust_entry->info_bits != 0)) {
ut_ad(clust_entry->is_metadata());
ut_ad(index->is_instant());
ut_ad(undo_block->page.frame[first_free + 2]
== TRX_UNDO_INSERT_REC);
undo_block->page.frame[first_free + 2]
= TRX_UNDO_INSERT_METADATA; goto done;
}
for (unsigned i = 0; i < dict_index_get_n_unique(index); i++) {
const dfield_t* field = dtuple_get_nth_field(clust_entry, i);
ulint flen = dfield_get_len(field);
if (trx_undo_left(undo_block, ptr) < 5) {
return(0);
}
ptr += mach_write_compressed(ptr, flen);
switch (flen) { case0: case UNIV_SQL_NULL: break; default: if (trx_undo_left(undo_block, ptr) < flen) {
/**********************************************************************//**
Reads from an undo log record the general parameters.
@return remaining part of undo log record after reading these values */ const byte*
trx_undo_rec_get_pars( /*==================*/ const trx_undo_rec_t* undo_rec, /*!< in: undo log record */
byte* type, /*!< out: undo record type:
TRX_UNDO_INSERT_REC, ... */
byte* cmpl_info, /*!< out: compiler info, relevant only
for update type records */ bool* updated_extern, /*!< out: true if we updated an
externally stored fild */
undo_no_t* undo_no, /*!< out: undo log record number */
table_id_t* table_id) /*!< out: table id */
{
ulint type_cmpl;
/*******************************************************************//**
Builds a row reference from an undo log record.
@return pointer to remaining part of undo record */ const byte*
trx_undo_rec_get_row_ref( /*=====================*/ const byte* ptr, /*!< in: remaining part of a copy of an undo log record,atthestartoftherowreference; NOTEthatthiscopyoftheundologrecordmust bepreservedaslongastherowreferenceis used,aswedoNOTcopythedatainthe
record! */
dict_index_t* index, /*!< in: clustered index */ const dtuple_t**ref, /*!< out, own: row reference */
mem_heap_t* heap) /*!< in: memory heap from which the memory
needed is allocated */
{
ut_ad(index->is_primary());
/** Skip a row reference from an undo log record. @paramptrpartofanupdateundologrecord @paramindexclusteredindex
@return pointer to remaining part of undo record */ staticconst byte *trx_undo_rec_skip_row_ref(const byte *ptr, const dict_index_t *index)
{
ut_ad(index->is_primary());
ulint ref_len = dict_index_get_n_unique(index);
for (ulint i = 0; i < ref_len; i++) { const byte* field;
uint32_t len, orig_len;
/** Fetch a prefix of an externally stored column, for writing to the undo logofanupdateordeletemarkingofaclusteredindexrecord. @param[out]ext_bufbuffertoholdtheprefixdataandBLOBpointer @param[in]prefix_lenprefixsizetostoreintheundolog @param[in]zip_sizeROW_FORMAT=COMPRESSEDpagesize,or0 @param[in]fieldanexternallystoredcolumn @param[in,out]leninput:lengthoffield;output:usedlengthof ext_buf
@return ext_buf */ static
byte*
trx_undo_page_fetch_ext(
byte* ext_buf,
ulint prefix_len,
ulint zip_size, const byte* field,
ulint* len)
{ /* Fetch the BLOB. */
ulint ext_len = btr_copy_externally_stored_field_prefix(
ext_buf, prefix_len, zip_size, field, *len); /* BLOBs should always be nonempty. */
ut_a(ext_len); /* Append the BLOB pointer to the prefix. */
memcpy(ext_buf + ext_len,
field + *len - BTR_EXTERN_FIELD_REF_SIZE,
BTR_EXTERN_FIELD_REF_SIZE);
*len = ext_len + BTR_EXTERN_FIELD_REF_SIZE; return(ext_buf);
}
/** Writes to the undo log a prefix of an externally stored column. @param[out]ptrundologposition,atleast15bytesmustbe available @param[out]ext_bufabufferofDICT_MAX_FIELD_LEN_BY_FORMAT() size,orNULLwhenshouldnotfetchalonger prefix @param[in]prefix_lenprefixsizetostoreintheundolog @param[in]zip_sizeROW_FORMAT=COMPRESSEDpagesize,or0 @param[in,out]fieldthelocallystoredpartoftheexternally storedcolumn @param[in,out]lenlengthoffield,inbytes @param[in]spatial_statuswhetherthecolumnisusedbyspatialindexor regularindex
@return undo log position */ static
byte*
trx_undo_page_report_modify_ext(
byte* ptr,
byte* ext_buf,
ulint prefix_len,
ulint zip_size, const byte** field,
ulint* len,
spatial_status_t spatial_status)
{
ulint spatial_len= 0;
switch (spatial_status) { case SPATIAL_UNKNOWN: case SPATIAL_NONE: break;
case SPATIAL_MIXED: case SPATIAL_ONLY:
spatial_len = DATA_MBR_LEN; break;
}
/* Encode spatial status into length. */
spatial_len |= ulint(spatial_status) << SPATIAL_STATUS_SHIFT;
if (spatial_status == SPATIAL_ONLY) { /* If the column is only used by gis index, log its
MBR is enough.*/
ptr += mach_write_compressed(ptr, UNIV_EXTERN_STORAGE_FIELD
+ spatial_len);
return(ptr);
}
if (ext_buf) {
ut_a(prefix_len > 0);
/* If an ordering column is externally stored, we will havetostorealongerprefixofthefield.Inthis case,writetothelogamarkerfollowedbythe
original length and the real length of the field. */
ptr += mach_write_compressed(ptr, UNIV_EXTERN_STORAGE_FIELD);
if (dlen <= GEO_DATA_HEADER_SIZE) { for (uint i = 0; i < SPDIMS; ++i) {
mbr[i * 2] = DBL_MAX;
mbr[i * 2 + 1] = -DBL_MAX;
}
} else {
rtree_mbr_from_wkb(dptr + GEO_DATA_HEADER_SIZE, static_cast<uint>(dlen
- GEO_DATA_HEADER_SIZE), SPDIMS, mbr);
}
mem_heap_free(heap);
}
/**********************************************************************//**
Reports in the undo log of an update ordelete marking of a clustered index
record.
@return byte offset of the inserted undo log entry on the page if
succeed, 0if fail */ static
uint16_t
trx_undo_page_report_modify( /*========================*/
buf_block_t* undo_block, /*!< in: undo log page */
dict_index_t* index, /*!< in: clustered index where update or
delete marking is done */ const rec_t* rec, /*!< in: clustered index record which
has NOT yet been modified */ const rec_offs* offsets, /*!< in: rec_get_offsets(rec, index) */ const upd_t* update, /*!< in: update vector which tells the columnstobeupdated;inthecaseof
a delete, this should be set to NULL */
ulint cmpl_info, /*!< in: compiler info on secondary
index updates */ const dtuple_t* row, /*!< in: clustered index row contains
virtual column info */
mtr_t* mtr) /*!< in: mtr */
{
ut_ad(index->is_primary());
ut_ad(rec_offs_validate(rec, index, offsets)); /* MariaDB 10.3.1+ in trx_undo_page_init() always initializes TRX_UNDO_PAGE_TYPEas0,butpreviousversionswrote TRX_UNDO_INSERT==1intoinsert_undopages,
or TRX_UNDO_UPDATE == 2 into update_undo pages. */
ut_ad(mach_read_from_2(TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_TYPE
+ undo_block->page.frame) <= 2);
if (trx_undo_left(undo_block, ptr) < 50) { /* NOTE: the value 50 must be big enough so that the general
fields written below fit on the undo log page */ return0;
}
/* Reserve 2 bytes for the pointer to the next undo log record */
ptr += 2;
/* Store first some general parameters to the undo log */
field = rec_get_nth_field(rec, offsets, index->db_trx_id(), &flen);
ut_ad(flen == DATA_TRX_ID_LEN);
trx_id = trx_read_trx_id(field);
if (!update) {
ut_ad(!rec_is_delete_marked(rec, dict_table_is_comp(table)));
type_cmpl = TRX_UNDO_DEL_MARK_REC;
} elseif (rec_is_delete_marked(rec, dict_table_is_comp(table))) { /* In delete-marked records, DB_TRX_ID must
always refer to an existing update_undo log record. */
ut_ad(trx_id);
type_cmpl = TRX_UNDO_UPD_DEL_REC;
/* We are about to update a delete marked record. Wedon'ttypicallyneedaBLOBprefixinthiscaseunless thedeletemarkingisdonebythesametransaction
(which we check below). */
ignore_prefix = trx_id != mtr->trx->id;
} else {
type_cmpl = TRX_UNDO_UPD_EXIST_REC;
}
/*----------------------------------------*/ /* Store then the fields required to uniquely determine the
record which will be modified in the clustered index */
for (i = 0; i < dict_index_get_n_unique(index); i++) {
/* The ordering columns must not be instant added columns. */
ut_ad(!rec_offs_nth_default(offsets, i));
field = rec_get_nth_field(rec, offsets, i, &flen);
/* The ordering columns must not be stored externally. */
ut_ad(!rec_offs_nth_extern(offsets, i));
ut_ad(dict_index_get_nth_col(index, i)->ord_part);
if (trx_undo_left(undo_block, ptr) < 5) { return(0);
}
ptr += mach_write_compressed(ptr, flen);
if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_block, ptr) < flen) { return(0);
}
memcpy(ptr, field, flen);
ptr += flen;
}
}
/*----------------------------------------*/ /* Save to the undo log the old values of the columns to be updated. */
if (update) { if (trx_undo_left(undo_block, ptr) < 5) { return(0);
}
ulint n_updated = upd_get_n_fields(update);
/* If this is an online update while an inplace alter table isinprogressandthetablehasvirtualcolumn,wewill needtodoublecheckifthereareanynon-indexedcolumns beingregisteredinupdatevectorincasetheywillbeindexed
in new table */ if (dict_index_is_online_ddl(index) && table->n_v_cols > 0) { for (i = 0; i < upd_get_n_fields(update); i++) {
upd_field_t* fld = upd_get_nth_field(
update, i);
ulint pos = fld->field_no;
/* These columns must not have an index
on them */ if (upd_fld_is_virtual_col(fld)
&& dict_table_get_nth_v_col(
table, pos)->v_indexes.empty()) {
n_updated--;
}
}
}
i = 0;
if (UNIV_UNLIKELY(update->is_alter_metadata())) {
ut_ad(update->n_fields >= 1);
ut_ad(!upd_fld_is_virtual_col(&update->fields[0]));
ut_ad(update->fields[0].field_no
== index->first_user_field());
ut_ad(!dfield_is_ext(&update->fields[0].new_val));
ut_ad(!dfield_is_null(&update->fields[0].new_val)); /* The instant ADD COLUMN metadata record does not
contain the BLOB. Do not write anything for it. */
i = !rec_is_alter_metadata(rec, *index);
n_updated -= i;
}
ptr += mach_write_compressed(ptr, n_updated);
for (; i < upd_get_n_fields(update); i++) { if (trx_undo_left(undo_block, ptr) < 5) { return0;
}
ulint pos = fld->field_no; const dict_col_t* col = NULL;
if (is_virtual) { /* Skip the non-indexed column, during
an online alter table */ if (dict_index_is_online_ddl(index)
&& dict_table_get_nth_v_col(
table, pos)->v_indexes.empty()) { continue;
}
/* add REC_MAX_N_FIELDS to mark this
is a virtual col */
ptr += mach_write_compressed(
ptr, pos + REC_MAX_N_FIELDS);
if (trx_undo_left(undo_block, ptr) < 15) { return0;
}
if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_block, ptr) < flen) { return(0);
}
memcpy(ptr, field, flen);
ptr += flen;
}
/* Also record the new value for virtual column */ if (is_virtual) {
field = static_cast<byte*>(fld->new_val.data);
flen = fld->new_val.len; if (flen != UNIV_SQL_NULL) {
flen = ut_min(
flen, max_v_log_len);
}
if (trx_undo_left(undo_block, ptr) < 15) { return(0);
}
ptr += mach_write_compressed(ptr, flen);
if (flen != UNIV_SQL_NULL) { if (trx_undo_left(undo_block, ptr)
< flen) { return(0);
}
memcpy(ptr, field, flen);
ptr += flen;
}
}
}
}
/* Reset the first_v_col, so to put the virtual column undo
version marker again, when we log all the indexed columns */
first_v_col = true;
/*----------------------------------------*/ /* In the case of a delete marking, and also in the case of an update whereanyorderingfieldofanyindexchanges,storethevaluesofall columnswhichoccurasorderingfieldsinanyindex.Thisinfoisused inthepurgeofoldversionswhereweuseittobuildandsearchthe deletemarkedindexrecords,tolookifwecanremovethemfromthe indextree.Notethatstartingfrom4.0.14alsoexternallystored fieldscanbeorderinginsomeindex.Startingfrom5.2,wenolonger storeREC_MAX_INDEX_COL_LENfirstbytestotheundologrecord, butwecanconstructthecolumnprefixfieldsintheindexby fetchingthefirstpageoftheBLOBthatispointedtobythe clusteredindex.Thisworksalsoincrashrecovery,becauseallpages
(including BLOBs) are recovered before anything is rolled back. */
if (trx_undo_left(undo_block, ptr) < 5) { return(0);
}
/* Reserve 2 bytes to write the number of bytes the stored
fields take in this undo record */
ptr += 2;
for (col_no = 0; col_no < dict_table_get_n_cols(table);
col_no++) {
const dict_col_t* col
= dict_table_get_nth_col(table, col_no);
if (!col->ord_part) { continue;
}
const ulint pos = dict_index_get_nth_col_pos(
index, col_no, NULL); /* All non-virtual columns must be present in
the clustered index. */
ut_ad(pos != ULINT_UNDEFINED);
switch (spatial_status) { case SPATIAL_UNKNOWN:
ut_ad(0); /* fall through */ case SPATIAL_MIXED: case SPATIAL_ONLY: /* Externally stored spatially indexed columnswillbe(redundantly)logged again,becausewedidnotwritethe MBRyet,thatis,thepreviouscallto trx_undo_page_report_modify_ext()
was with SPATIAL_UNKNOWN. */ break; case SPATIAL_NONE: if (!update) { /* This is a DELETE operation. */ break;
} /* Avoid redundantly logging indexed
columns that were updated. */
for (i = 0; i < update->n_fields; i++) { const upd_field_t* fld =
upd_get_nth_field(update, i); if (upd_fld_is_virtual_col(fld)) continue; const ulint field_no
= fld->field_no; if (field_no >= index->n_fields
|| dict_index_get_nth_field(
index, field_no)->col
== col) { goto already_logged;
}
}
}
if (true) { /* Write field number to undo log */ if (trx_undo_left(undo_block, ptr) < 5 + 15) { return(0);
}
ptr += mach_write_compressed(ptr, pos);
/* Save the old value of field */
field = rec_get_nth_cfield(
rec, index, offsets, pos, &flen);
if (is_ext) { const dict_col_t* col =
dict_index_get_nth_col(
index, pos);
ulint prefix_len =
dict_max_field_len_store_undo(
table, col);
switch (flen) { case0: case UNIV_SQL_NULL: break; default: if (trx_undo_left(undo_block, ptr)
< flen) { return(0);
}
memcpy(ptr, field, flen);
ptr += flen;
}
}
}
mach_write_to_2(old_ptr, ulint(ptr - old_ptr));
if (row_heap) {
mem_heap_free(row_heap);
}
}
/*----------------------------------------*/ /* Write pointers to the previous and the next undo log records */ if (trx_undo_left(undo_block, ptr) < 2) { return(0);
}
/**********************************************************************//**
Reads from an undo log update record the system field values of the old
version.
@return remaining part of undo log record after reading these values */
byte*
trx_undo_update_rec_get_sys_cols( /*=============================*/ const byte* ptr, /*!< in: remaining part of undo logrecordafterreading
general parameters */
trx_id_t* trx_id, /*!< out: trx id */
roll_ptr_t* roll_ptr, /*!< out: roll ptr */
byte* info_bits) /*!< out: info bits state */
{ /* Read the state of the info bits */
*info_bits = *ptr++;
/*******************************************************************//**
Builds an update vector based on a remaining part of an undo log record.
@return remaining part of the record, NULL if an error detected, which
means that the record is corrupted */
byte*
trx_undo_update_rec_get_update( /*===========================*/ const byte* ptr, /*!< in: remaining part in update undo log record,afterreadingtherowreference NOTEthatthiscopyoftheundologrecordmust bepreservedaslongastheupdatevectoris used,aswedoNOTcopythedatainthe
record! */
dict_index_t* index, /*!< in: clustered index */
ulint type, /*!< in: TRX_UNDO_UPD_EXIST_REC, TRX_UNDO_UPD_DEL_REC,or TRX_UNDO_DEL_MARK_REC;inthelastcase, onlytrxidandrollptrfieldsareaddedto
the update vector */
trx_id_t trx_id, /*!< in: transaction id from this undo record */
roll_ptr_t roll_ptr,/*!< in: roll pointer from this undo record */
byte info_bits,/*!< in: info bits from this undo record */
mem_heap_t* heap, /*!< in: memory heap from which the memory
needed is allocated */
upd_t** upd) /*!< out, own: update vector */
{
upd_field_t* upd_field;
upd_t* update;
ulint n_fields;
byte* buf; bool first_v_col = true; bool is_undo_log = true;
ulint n_skip_field = 0;
if (is_virtual) { /* If new version, we need to check index list to figure
out the correct virtual column position */
ptr = trx_undo_read_v_idx(
index->table, ptr, first_v_col, &is_undo_log,
&field_no);
first_v_col = false; /* This column could be dropped or no longer indexed */ if (field_no == FIL_NULL) { /* Mark this is no longer needed */
upd_field->field_no = REC_MAX_N_FIELDS;
/* We may have to skip dropped indexed virtual columns. Also,wemayhavetotrimtheupdatevectorofametadatarecord ifdict_index_t::clear_instant_alter()wasinvokedonthetable
later, and the number of fields no longer matches. */
if (n_skip_field) {
upd_field_t* d = upd_get_nth_field(update, 0); const upd_field_t* const end = d + n_fields + 2;
for (const upd_field_t* s = d; s != end; s++) { if (s->field_no != REC_MAX_N_FIELDS) {
*d++ = *s;
}
}
/* This thread is executing trx. No other thread can modify our table locks (onlyrecordlocksmightbecreated,inanimplicit-to-explicitconversion).
Hence, no mutex is needed here. */ if (n) for (const lock_t *lock : trx.lock.table_locks) if (lock && lock->type_mode == (LOCK_X | LOCK_TABLE)) returntrue;
/***********************************************************************//**
Writes information to an undo log about an insert, update, or a delete marking
of a clustered index record. This information is used in a rollback of the
transaction and in consistent reads that must look to the history of this
transaction.
@return DB_SUCCESS or error code */
dberr_t
trx_undo_report_row_operation( /*==========================*/
que_thr_t* thr, /*!< in: query thread */
dict_index_t* index, /*!< in: clustered index */ const dtuple_t* clust_entry, /*!< in: in the case of an insert, indexentrytoinsertintothe clusteredindex;inupdates, maycontainaclusteredindex recordtuplethatalsocontains virtualcolumnsofthetable;
otherwise, NULL */ const upd_t* update, /*!< in: in the case of an update,
the update vector, otherwise NULL */
ulint cmpl_info, /*!< in: compiler info on secondary
index updates */ const rec_t* rec, /*!< in: case of an update or delete marking,therecordintheclustered
index; NULL if insert */ const rec_offs* offsets, /*!< in: rec_get_offsets(rec) */
roll_ptr_t* roll_ptr) /*!< out: DB_ROLL_PTR to the
undo log record */
{
trx_t* trx; #ifdef UNIV_DEBUG int loop_count = 0; #endif/* UNIV_DEBUG */
trx = thr_get_trx(thr); /* This function must not be invoked during rollback
(of a TRX_STATE_PREPARE transaction or otherwise). */
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
ut_ad(!trx->in_rollback);
/* We must determine if this is the first time when this
transaction modifies this table. */ auto m = trx->mod_tables.emplace(index->table, trx->undo_no);
ut_ad(m.first->second.valid(trx->undo_no));
if (m.second && index->table->is_native_online_ddl()) {
trx->apply_online_log= true;
}
bool bulk = !rec;
if (!bulk) { /* An UPDATE or DELETE must not be covered by an
earlier start_bulk_insert(). */
ut_ad(!m.first->second.is_bulk_insert());
} elseif (m.first->second.is_bulk_insert()) { /* Above, the emplace() tried to insert an object with !is_bulk_insert().Onlyanexplicitstart_bulk_insert()
(below) can set the flag. */
ut_ad(!m.second); /* We already wrote a TRX_UNDO_EMPTY record. */
ut_ad(thr->run_node);
ut_ad(que_node_get_type(thr->run_node) == QUE_NODE_INSERT);
ut_ad(trx->bulk_insert); return DB_SUCCESS;
} elseif (!m.second || !trx->bulk_insert) {
bulk = false;
} elseif (index->table->is_temporary()) {
} elseif (index->table->bulk_trx_id == trx->id
&& trx_has_lock_x(*trx, *index->table)) {
m.first->second.start_bulk_insert(
index->table,
thd_sql_command(trx->mysql_thd) != SQLCOM_LOAD);
if (first_free
== TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE) { /* The record did not fit on an empty undopage.Discardthefreshlyallocated
page and return an error. */
/* When we remove a page from an undo log,thisisanalogoustoa pessimisticinsertinaB-tree,andwe mustreservethecounterpartofthe treelatch,whichistherseg mutex.Wemustcommitthemini-transaction first,becauseitmaybeholdinglower-level
latches, such as SYNC_FSP_PAGE. */
mtr.commit();
mtr.start(); if (is_temp) {
mtr.set_log_mode(MTR_LOG_NO_REDO);
}
if (is_temp) {
mtr.set_log_mode(MTR_LOG_NO_REDO);
}
undo_block = trx_undo_add_page(undo, &mtr, &err);
DBUG_EXECUTE_IF("ib_err_ins_undo_page_add_failure",
undo_block = NULL;);
} while (UNIV_LIKELY(undo_block != NULL));
if (err != DB_OUT_OF_FILE_SPACE) { goto err_exit;
}
ib_errf(trx->mysql_thd, IB_LOG_LEVEL_ERROR,
DB_OUT_OF_FILE_SPACE, //ER_INNODB_UNDO_LOG_FULL, "No more space left over in %s tablespace for allocating UNDO" " log pages. Please add new data file to the tablespace or" " check if filesystem is full or enable auto-extension for" " the tablespace",
undo->rseg->space == fil_system.sys_space
? "system" : is_temp ? "temporary" : "undo");
goto err_exit;
}
/*============== BUILDING PREVIOUS VERSION OF A RECORD ===============*/
inlineconst buf_block_t *
purge_sys_t::view_guard::get(const page_id_t id, trx_t *trx, mtr_t *mtr)
{
buf_block_t *block;
ut_ad(mtr->is_active()); if (!latch)
{
decltype(purge_sys.pages)::const_iterator i= purge_sys.pages.find(id); if (i != purge_sys.pages.end())
{
block= i->second;
ut_ad(block); return block;
}
}
block= buf_pool.page_fix(id, trx); if (block)
{
mtr->memo_push(block, MTR_MEMO_BUF_FIX); if (latch) /* In MVCC operations (outside purge tasks), we will refresh the buf_pool.LRUposition.Inpurge,weexpectthepagetobefreed
soon, at the end of the current batch. */
buf_page_make_young_if_needed(&block->page);
} return block;
}
/** Build a previous version of a clustered index record. The caller mustholdalatchontheindexpageoftheclusteredindexrecord. @paramrecversionofaclusteredindexrecord @paramindexclusteredindex @paramoffsetsrec_get_offsets(rec,index) @paramheapmemoryheapfromwhichthememoryneededisallocated @paramold_verspreviousversion,orNULLifrecisthefirstinserted version,orifhistorydatahasbeendeleted(anerror), orifthepurgecouldhaveremovedtheversionthough ithasnotyetdoneso @parammtrmini-transaction @paramv_statusTRX_UNDO_PREV_IN_PURGE,... @paramv_heapmemoryheapusedtocreatevrowdtupleifitisnotyet created.Thisheapdiffsfrom"heap"aboveinthatitcouldbe prebuilt->old_vers_heapforselection @paramvrowvirtualcolumninfo,ifany @returnerrorcode @retvalDB_SUCCESSifpreviousversionwassuccessfullybuilt, orifitwasaninsertortheundorecordreferstothetablebeforerebuild
@retval DB_MISSING_HISTORY if the history is missing */
dberr_t trx_undo_prev_version_build(const rec_t *rec, dict_index_t *index,
rec_offs *offsets, mem_heap_t *heap,
rec_t **old_vers, mtr_t *mtr,
ulint v_status,
mem_heap_t *v_heap, dtuple_t **vrow)
{
ut_ad(!index->table->is_temporary());
ut_ad(rec_offs_validate(rec, index, offsets));
if (table_id != index->table->id) { /* The table should have been rebuilt, but purge has notyetremovedtheundologrecordsforthe
now-dropped old table (table_id). */ return DB_SUCCESS;
}
/* (a) If a clustered index record version is such that the trxidstampinitisbiggerthanpurge_sys.view,thenthe BLOBsinthatversionareknowntoexist(thepurgehasnot progressedthatfar);
if (row_upd_changes_field_size_or_external(index, offsets, update)) { /* When CHECK TABLE ... EXTENDED checks for orphan recordsinsecondaryindexes,itnormallycoverssome historythatisalreadybeingpurged.Thisissafeas longastheundologrecordshavenotbeenfreedyet.
However,BLOBsareonlysafetoaccessaslongasthe purge_sys.viewdoesnotpermitthemtobefreed.The check.latchwillfreezethepurge_sys.viewbyblocking purge_sys.clone_oldest_view()atthestartof trx_purge()orbyblockingpurge_sys.batch_cleanup()
at the end of trx_purge(). */ if (check.is_extended() && purge_sys.is_purgeable(trx_id)) { return DB_SUCCESS;
}
/* We should confirm the existence of disowned external data, ifthepreviousversionrecordisdeletemarked.Ifthetrx_id ofthepreviousrecordisseenbypurgeview,weshouldtreat itasmissinghistory,becausethedisownedexternaldata mightbepurgedalready.
Theinheritedexternaldata(BLOBs)canbefreed(purged) aftertrx_idwascommitted,providedthatnoviewwasstarted beforetrx_id.Ifthepurgeviewcanseethecommitted delete-markedrecordbytrx_id,notransactionsneedtoaccess
the BLOB. */
if (update->info_bits & REC_INFO_DELETED_FLAG
&& check.view().changes_visible(trx_id)) { return DB_SUCCESS;
}
/* We have to set the appropriate extern storage bits in the oldversionoftherecord:theexternbitsinrecforthose fieldsthatupdatedoesNOTupdate,aswellasthebitsfor thosefieldsthatupdateupdatestobecomeexternallystored
fields. Store the info: */
dtuple_t* entry = row_rec_to_index_entry(rec, index, offsets,
heap); /* The page containing the clustered index record correspondingtoentryislatched.Thusthe
following call is safe. */ if (!row_upd_index_replace_new_col_vals(entry, *index, update,
heap)) { return (v_status & TRX_UNDO_PREV_IN_PURGE)
? DB_MISSING_HISTORY : DB_CORRUPTION;
}
/* Get number of externally stored columns in updated record */ const ulint n_ext = index->is_primary()
? dtuple_get_n_ext(entry) : 0;
/* Set the old value (which is the after image of an update) in the
update vector to dtuple vrow */ if (v_status & TRX_UNDO_GET_OLD_V_VALUE) {
row_upd_replace_vcol((dtuple_t*)*vrow, index->table, update, false, nullptr, nullptr);
}
#ifdefined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
rec_offs offsets_dbg[REC_OFFS_NORMAL_SIZE];
rec_offs_init(offsets_dbg);
ut_a(!rec_offs_any_null_extern(
*old_vers, rec_get_offsets(*old_vers, index, offsets_dbg,
index->n_core_fields,
ULINT_UNDEFINED, &heap))); #endif// defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
/* The virtual column is no longer indexed or does not exist. Thisneedstoputaftertrx_undo_rec_get_col_val()sothe
undo ptr advances */ if (field_no == FIL_NULL) {
ut_ad(is_virtual); continue;
}
if (is_virtual) {
dict_v_col_t* vcol = dict_table_get_nth_v_col(
table, field_no);
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.39Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-08)
¤
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.