/** Get the pad character code point for a type. @paramtype @returnpadcharactercodepoint
@retval ULINT_UNDEFINED if no padding is specified */ static ulint cmp_get_pad_char(const dtype_t &type) noexcept
{ switch (type.mtype) { default: break; case DATA_FIXBINARY: case DATA_BINARY: if (dtype_get_charset_coll(type.prtype) ==
DATA_MYSQL_BINARY_CHARSET_COLL) /* Starting from 5.0.18, we do not pad VARBINARY or BINARY columns. */ break; /* Fall through */ case DATA_CHAR: case DATA_VARCHAR: case DATA_MYSQL: case DATA_VARMYSQL: /* Space is the padding character for all char and binary
strings, and starting from 5.0.3, also for TEXT strings. */ return0x20; case DATA_BLOB: if (!(type.prtype & DATA_BINARY_TYPE)) return0x20;
}
/* No padding specified */ return ULINT_UNDEFINED;
}
/** Compare a data tuple to a physical record. @paramrecB-treeindexrecord @paramindexindexB-tree @paramtuplesearchkey @parammatchmatchedfields<<16|bytes @paramcompnonzeroifROW_FORMAT=REDUNDANTisnotbeingused @returnthecomparisonresultofdtupleandrec @retval0ifdtupleisequaltorec @retvalnegativeifdtupleislessthanrec
@retval positive if dtuple is greater than rec */ int cmp_dtuple_rec_bytes(const rec_t *rec, const dict_index_t &index, const dtuple_t &tuple, int *match, ulint comp)
noexcept
{
ut_ad(dtuple_check_typed(&tuple));
ut_ad(page_rec_is_leaf(rec));
ut_ad(!(REC_INFO_MIN_REC_FLAG & dtuple_get_info_bits(&tuple)));
ut_ad(!!comp == index.table->not_redundant());
if (i >= cur_field)
{ const dfield_t *const df= dtuple_get_nth_field(&tuple, i);
ut_ad(!dfield_is_ext(df)); if (df->len == UNIV_SQL_NULL)
{
ut_ad(cur_bytes == 0);
less:
ret= field->descending ? 1 : -1; goto non_redundant_order_resolved;
}
switch (df->type.mtype) { case DATA_FIXBINARY: case DATA_BINARY: case DATA_INT: case DATA_SYS_CHILD: case DATA_SYS: break; case DATA_BLOB: if (df->type.prtype & DATA_BINARY_TYPE) break; /* fall through */ default:
cur_bytes= 0;
ret= cmp_data(df->type.mtype, df->type.prtype, field->descending, static_cast<const byte*>(df->data), df->len, f, len); if (ret) goto non_redundant_order_resolved; goto next_field;
}
/* Set the pointers at the current byte */ const byte *rec_b_ptr= f + cur_bytes; const byte *dtuple_b_ptr= static_cast<const byte*>(df->data) + cur_bytes; /* Compare then the fields */ for (const ulint pad= cmp_get_pad_char(df->type);; cur_bytes++)
{ constbool eod= df->len <= cur_bytes;
ulint rec_byte= pad, dtuple_byte= pad;
next_field:
f+= len;
} while (i++, ++field < end);
ut_ad(cur_bytes == 0);
non_redundant_order_resolved:
ut_ad(i >= cur_field);
cur_field= i;
} else
{ for (; cur_field < n_cmp; cur_field++)
{ const dfield_t *df= dtuple_get_nth_field(&tuple, cur_field);
ut_ad(!dfield_is_ext(df));
size_t len; const byte *rec_b_ptr= rec_get_nth_field_old(rec, cur_field, &len); /* If we have matched yet 0 bytes, it may be that one or boththefieldsareSQLnull,ortherecordordtuplemaybe
the predefined minimum record. */ if (df->len == UNIV_SQL_NULL)
{
ut_ad(cur_bytes == 0); if (len == UNIV_SQL_NULL) continue;
redundant_less:
ret= index.fields[cur_field].descending ? 1 : -1; goto order_resolved;
} elseif (len == UNIV_SQL_NULL)
{
ut_ad(cur_bytes == 0); /* We define the SQL null to be the smallest possible value */
redundant_greater:
ret= index.fields[cur_field].descending ? -1 : 1; goto order_resolved;
}
switch (df->type.mtype) { case DATA_FIXBINARY: case DATA_BINARY: case DATA_INT: case DATA_SYS_CHILD: case DATA_SYS: break; case DATA_BLOB: if (df->type.prtype & DATA_BINARY_TYPE) break; /* fall through */ default:
ret= cmp_data(df->type.mtype, df->type.prtype,
index.fields[cur_field].descending, static_cast<const byte*>(df->data), df->len,
rec_b_ptr, len);
cur_bytes= 0; if (!ret) continue; goto order_resolved;
}
/* Set the pointers at the current byte */
rec_b_ptr+= cur_bytes; const byte *dtuple_b_ptr= static_cast<const byte*>(df->data) +
cur_bytes; /* Compare then the fields */ for (const ulint pad= cmp_get_pad_char(df->type);; cur_bytes++)
{ constbool eod= df->len <= cur_bytes;
ulint rec_byte= pad, dtuple_byte= pad;
/* If mode PAGE_CUR_G is specified, we are trying to position the cursortoansweraqueryoftheform"tuple<X",wheretupleisthe inputparameter,andXdenotesanarbitraryphysicalrecordonthe page.WewanttopositionthecursoronthefirstXwhichsatisfies
the condition. */ int up_cmp= int(*iup_fields << 16 | *iup_bytes); int low_cmp= int(*ilow_fields << 16 | *ilow_bytes);
/* Perform binary search. First the search is done through the page directory,afterthatasalinearsearchinthelistofrecords
owned by the upper limit directory slot. */
size_t low= 0, up= ulint{page_dir_get_n_slots(page)} - 1; const rec_t *mid_rec;
/* Perform binary search until the lower and upper limit directory
slots come to the distance 1 of each other */ while (up - low > 1)
{ const size_t mid= (low + up) / 2;
mid_rec= page_dir_slot_get_rec_validate(page,
page_dir_get_nth_slot(page, mid)); if (UNIV_UNLIKELY(!mid_rec)) returntrue; int cur= std::min(low_cmp, up_cmp); int cmp= cmp_dtuple_rec_bytes(mid_rec, index, tuple, &cur, comp); if (cmp > 0)
low_slot_match:
low= mid, low_cmp= cur; elseif (cmp)
up_slot_match:
up= mid, up_cmp= cur; elseif (mode == PAGE_CUR_G || mode == PAGE_CUR_LE) goto low_slot_match; else goto up_slot_match;
}
if (UNIV_LIKELY(!ge))
{ if (cmp < 0) returntrue;
low_match= match;
up_match= 0; if (UNIV_LIKELY(comp != 0))
{
rec= page_rec_next_get<true>(page, rec); if (!rec) returntrue; if (uintptr_t(rec - page) == PAGE_NEW_SUPREMUM)
le_supremum: /* If we matched the full key at the end of a page (but not the index),
the adaptive hash index was successful. */ return page_has_next(page) && match < uniq; switch (rec_get_status(rec)) { case REC_STATUS_INSTANT: case REC_STATUS_ORDINARY: break; default: returntrue;
}
} else
{
rec= page_rec_next_get<false>(page, rec); if (!rec) returntrue; if (uintptr_t(rec - page) == PAGE_OLD_SUPREMUM) goto le_supremum;
} return page_cur_dtuple_cmp(tuple, rec, *index(), &up_match, comp) >= 0;
} else
{ if (cmp > 0) returntrue;
up_match= match; if (match >= uniq) returnfalse;
match= 0; if (!(rec= page_rec_get_prev_const(rec))) returntrue; if (uintptr_t(rec - page) == (comp ? PAGE_NEW_INFIMUM : PAGE_OLD_INFIMUM)) return page_has_prev(page); if (UNIV_LIKELY(comp != 0)) switch (rec_get_status(rec)) { case REC_STATUS_INSTANT: case REC_STATUS_ORDINARY: break; default: returntrue;
} return page_cur_dtuple_cmp(tuple, rec, *index(), &match, comp) <= 0;
}
} #endif/* BTR_CUR_HASH_ADAPT */
/** Try a search shortcut based on the last insert. @parampageindexpage @paramrecPAGE_LAST_INSERTrecord @paramindexindextree @paramtuplesearchkey @paramiupmatchedfieldsintheupperlimitrecord @paramilowmatchedfieldsinthelowerlimitrecord @paramcompnonzeroifROW_FORMAT=REDUNDANTisnotbeingused @returnrecord
@return nullptr if the tuple was not found */ staticbool page_cur_try_search_shortcut(const page_t *page, const rec_t *rec, const dict_index_t &index, const dtuple_t &tuple,
uint16_t *iup, uint16_t *ilow,
ulint comp) noexcept
{
ut_ad(dtuple_check_typed(&tuple));
ut_ad(page_rec_is_user_rec(rec));
uint16_t low= std::min(*ilow, *iup), up= low;
if (page_cur_dtuple_cmp(tuple, rec, index, &low, comp) < 0) returnfalse;
if (comp)
{
rec= page_rec_next_get<true>(page, rec); if (!rec) returnfalse; if (rec != page + PAGE_NEW_SUPREMUM)
{
compare_next: if (page_cur_dtuple_cmp(tuple, rec, index, &up, comp) >= 0) returnfalse;
*iup= up;
}
} else
{
rec= page_rec_next_get<false>(page, rec); if (!rec) returnfalse; if (rec != page + PAGE_OLD_SUPREMUM) goto compare_next;
}
/* If the mode is for R-tree indexes, use the special MBR
related compare functions */ if (mode == PAGE_CUR_RTREE_INSERT && leaf)
{ /* Leaf level insert uses the traditional compare function */
mode= PAGE_CUR_LE; goto check_last_insert;
} elseif (mode > PAGE_CUR_LE) return rtr_cur_search_with_match(block, const_cast<dict_index_t*>(&index),
tuple, mode, cursor, rtr_info); elseif (mode == PAGE_CUR_LE && leaf)
{
check_last_insert: if (page_get_direction(page) != PAGE_RIGHT ||
(tuple->info_bits & REC_INFO_MIN_REC_FLAG)); elseif (uint16_t last= page_header_get_offs(page, PAGE_LAST_INSERT))
{ const rec_t *rec= page + last; if (page_header_get_field(page, PAGE_N_DIRECTION) > 2 &&
page_cur_try_search_shortcut(page, rec, index, *tuple,
iup_fields, ilow_fields, comp))
{
page_cur_position(rec, block, cursor); returnfalse;
}
}
}
/* If mode PAGE_CUR_G is specified, we are trying to position the cursortoansweraqueryoftheform"tuple<X",wheretupleisthe inputparameter,andXdenotesanarbitraryphysicalrecordonthe page.WewanttopositionthecursoronthefirstXwhichsatisfies
the condition. */
uint16_t up_fields= *iup_fields, low_fields= *ilow_fields;
/* Perform binary search. First the search is done through the page directory,afterthatasalinearsearchinthelistofrecords
owned by the upper limit directory slot. */
size_t low= 0, up= ulint{page_dir_get_n_slots(page)} - 1; const rec_t *mid_rec;
/* Perform binary search until the lower and upper limit directory
slots come to the distance 1 of each other */ while (up - low > 1)
{ const size_t mid= (low + up) / 2;
mid_rec=
page_dir_slot_get_rec_validate(page, page_dir_get_nth_slot(page, mid)); if (UNIV_UNLIKELY(!mid_rec)) returntrue;
uint16_t cur= std::min(low_fields, up_fields); int cmp= page_cur_dtuple_cmp(*tuple, mid_rec, index, &cur, comp, leaf); if (cmp > 0)
low_slot_match:
low= mid, low_fields= cur; elseif (cmp)
up_slot_match:
up= mid, up_fields= cur; elseif (mode == PAGE_CUR_G || mode == PAGE_CUR_LE) goto low_slot_match; else goto up_slot_match;
}
/***********************************************************//**
Positions a page cursor on a randomly chosen user record on a page. If there
are no user records, sets the cursor on the infimum record. */ void page_cur_open_on_rnd_user_rec(page_cur_t *cursor)
{ if (const ulint n_recs= page_get_n_recs(cursor->block->page.frame)) if ((cursor->rec= page_rec_get_nth(cursor->block->page.frame,
ut_rnd_interval(n_recs) + 1))) return;
cursor->rec= page_get_infimum_rec(cursor->block->page.frame);
}
/* We do not try to prevent crash on corruption here. ForROW_FORMAT=COMPRESSEDpages,thenext-recordlinksshould bevalidatedinpage_zip_decompress().Corruptionshouldonly
be possible here if the buffer pool was corrupted later. */ for (ulint i= n_owned / 2; i--; )
rec= page_rec_get_next_const(rec);
/* Transfer one record to the underfilled slot */
page_rec_set_n_owned<true>(block, slot_rec, 0, true, mtr); const rec_t* new_rec = page_rec_next_get<true>(block->page.frame,
slot_rec); /* We do not try to prevent crash on corruption here. ForROW_FORMAT=COMPRESSEDpages,thenext-recordlinksshould bevalidatedinpage_zip_decompress().Corruptionshouldonly
be possible here if the buffer pool was corrupted later. */
page_rec_set_n_owned<true>(block, const_cast<rec_t*>(new_rec),
PAGE_DIR_SLOT_MIN_N_OWNED, true, mtr);
mach_write_to_2(slot, new_rec - block->page.frame);
page_rec_set_n_owned(up_rec, up_n_owned - 1, true);
}
/** Trytobalanceanunderfilleddirectoryslotwithanadjacentone, sothatthereareatleasttheminimumnumberofrecordsownedbytheslot; thismayresultinmergingthetwoslots. @param[in,out]blockindexpage
@param[in] s the slot to be balanced */ staticvoid page_dir_balance_slot(const buf_block_t &block, ulint s)
{ constbool comp= page_is_comp(block.page.frame);
ut_ad(!block.page.zip.data);
ut_ad(s > 0);
const uint16_t h= mach_read_from_2(n_heap); if (UNIV_UNLIKELY((h + 1) & 0x6000))
{ /* At the minimum record size of 5+2 bytes, we can only reach this
condition when using innodb_page_size=64k. */
ut_ad((h & 0x7fff) == 8191);
ut_ad(srv_page_size == 65536); return NULL;
}
/** Report page directory corruption. @paramblockindexpage @paramindexindextree
*/
ATTRIBUTE_COLD staticvoid page_cur_directory_corrupted(const buf_block_t &block, const dict_index_t &index)
{
ib::error() << "Directory of " << block.page.id()
<< " of index " << index.name
<< " in table " << index.table->name
<< " is corrupted";
}
/***********************************************************//**
Inserts a record next to page cursor on an uncompressed page.
@return pointer to record
@retval nullptr ifnot enough space was available */
rec_t*
page_cur_insert_rec_low( /*====================*/ const page_cur_t*cur, /*!< in: page cursor */ const rec_t* rec, /*!< in: record to insert after cur */
rec_offs* offsets,/*!< in/out: rec_get_offsets(rec, index) */
mtr_t* mtr) /*!< in/out: mini-transaction */
{
buf_block_t *block= cur->block;
dict_index_t * const index= cur->index;
/* We should not write log for ROW_FORMAT=COMPRESSED pages here. */
ut_ad(!mtr->is_logged() ||
!(index->table->flags & DICT_TF_MASK_ZIP_SSIZE));
/* 1. Get the size of the physical record in the page */ const ulint rec_size= rec_offs_size(offsets);
#ifdef HAVE_MEM_CHECK
{ constvoid *rec_start __attribute__((unused))=
rec - rec_offs_extra_size(offsets);
ulint extra_size __attribute__((unused))=
rec_offs_extra_size(offsets) -
(page_is_comp(block->page.frame)
? REC_N_NEW_EXTRA_BYTES
: REC_N_OLD_EXTRA_BYTES); /* All data bytes of the record must be valid. */
MEM_CHECK_DEFINED(rec, rec_offs_data_size(offsets)); /* The variable-length header must be valid. */
MEM_CHECK_DEFINED(rec_start, extra_size);
} #endif/* HAVE_MEM_CHECK */
/* 2. Try to find suitable space from page memory management */ bool reuse= false;
ssize_t free_offset= 0;
ulint heap_no;
byte *insert_buf;
if (rec_t* free_rec= page_header_get_ptr(block->page.frame, PAGE_FREE))
{ /* Try to reuse the head of PAGE_FREE. */
rec_offs foffsets_[REC_OFFS_NORMAL_SIZE];
mem_heap_t *heap= nullptr;
/* Update the preceding record header, the 'owner' record and
prepare the record to insert. */
rec_t *insert_rec= insert_buf + extra_size; const ulint data_size= rec_offs_data_size(offsets);
memcpy(insert_buf, rec - extra_size, extra_size + data_size);
size_t hdr_common= 0;
ulint n_owned; const byte info_status= static_cast<byte>
(rec_get_info_and_status_bits(rec, comp));
ut_ad(!(rec_get_info_bits(rec, comp) &
~(REC_INFO_DELETED_FLAG | REC_INFO_MIN_REC_FLAG)));
if (comp)
{ #ifdef UNIV_DEBUG switch (rec_get_status(cur->rec)) { case REC_STATUS_ORDINARY: case REC_STATUS_NODE_PTR: case REC_STATUS_INSTANT: case REC_STATUS_INFIMUM: break; case REC_STATUS_SUPREMUM:
ut_ad("wrong status on cur->rec" == 0);
} switch (rec_get_status(rec)) { case REC_STATUS_NODE_PTR:
ut_ad(!page_is_leaf(block->page.frame)); break; case REC_STATUS_INSTANT:
ut_ad(index->is_instant());
ut_ad(page_is_leaf(block->page.frame)); if (!rec_is_metadata(rec, true)) break;
ut_ad(cur->rec == &block->page.frame[PAGE_NEW_INFIMUM]); break; case REC_STATUS_ORDINARY:
ut_ad(page_is_leaf(block->page.frame));
ut_ad(!(rec_get_info_bits(rec, true) & ~REC_INFO_DELETED_FLAG)); break; case REC_STATUS_INFIMUM: case REC_STATUS_SUPREMUM:
ut_ad("wrong status on rec" == 0);
}
ut_ad(rec_get_status(next_rec) != REC_STATUS_INFIMUM); #endif
copied:
ut_ad(!memcmp(insert_buf, rec - extra_size, extra_size -
(comp ? REC_N_NEW_EXTRA_BYTES : REC_N_OLD_EXTRA_BYTES)));
ut_ad(!memcmp(insert_rec, rec, data_size)); /* We have incremented the n_owned field of the owner record. IfthenumberexceedsPAGE_DIR_SLOT_MAX_N_OWNED,wehavetosplitthe
corresponding directory slot in two. */
if (UNIV_UNLIKELY(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED))
{ const ulint owner= page_dir_find_owner_slot(next_rec); if (UNIV_UNLIKELY(owner == ULINT_UNDEFINED))
{
page_cur_directory_corrupted(*block, *index); return nullptr;
}
if (page_dir_split_slot(*block, page_dir_get_nth_slot(block->page.frame,
owner))) return nullptr;
}
if (!page_is_leaf(page_zip->data))
{
ut_ad(!page_zip->n_blobs);
stored-= n_dense * REC_NODE_PTR_SIZE;
} elseif (index->is_clust())
{ /* Move the BLOB pointer array backwards to make space for the
columns DB_TRX_ID,DB_ROLL_PTR and the dense directory slot. */
/* Move the uncompressed area backwards to make space
for one directory slot. */ if (const ulint len = ulint(dir - stored))
{
byte* dst = stored - PAGE_ZIP_DIR_SLOT_SIZE;
memmove(dst, stored, len);
mtr->memmove(*block, dst - page_zip->data, stored - page_zip->data, len);
}
}
/***********************************************************//**
Inserts a record next to page cursor on a compressed and uncompressed
page.
@return pointer to inserted record
@return nullptr on failure */
rec_t*
page_cur_insert_rec_zip( /*====================*/
page_cur_t* cursor, /*!< in/out: page cursor,
logical position unchanged */ const rec_t* rec, /*!< in: pointer to a physical record */
rec_offs* offsets,/*!< in/out: rec_get_offsets(rec, index) */
mtr_t* mtr) /*!< in/out: mini-transaction */
{
page_zip_des_t * const page_zip= page_cur_get_page_zip(cursor);
page_t * const page= cursor->block->page.frame;
dict_index_t * const index = cursor->index;
/* 1. Get the size of the physical record in the page */ const ulint rec_size= rec_offs_size(offsets);
#ifdef HAVE_MEM_CHECK
{ constvoid *rec_start __attribute__((unused))=
rec - rec_offs_extra_size(offsets);
ulint extra_size __attribute__((unused))=
rec_offs_extra_size(offsets) - REC_N_NEW_EXTRA_BYTES; /* All data bytes of the record must be valid. */
MEM_CHECK_DEFINED(rec, rec_offs_data_size(offsets)); /* The variable-length header must be valid. */
MEM_CHECK_DEFINED(rec_start, extra_size);
} #endif/* HAVE_MEM_CHECK */ constbool reorg_before_insert= page_has_garbage(page) &&
rec_size > page_get_max_insert_size(page, 1) &&
rec_size <= page_get_max_insert_size_after_reorganize(page, 1);
constexpr uint16_t page_free_f= PAGE_FREE + PAGE_HEADER;
byte* const page_free = my_assume_aligned<4>(page_free_f + page);
uint16_t free_rec= 0;
/* 2. Try to find suitable space from page memory management */
ulint heap_no;
byte *insert_buf;
if (reorg_before_insert ||
!page_zip_available(page_zip, index->is_clust(), rec_size, 1))
{ /* SET GLOBAL might be executed concurrently. Sample the value once. */
ulint level= page_zip_level; #ifdef UNIV_DEBUG const rec_t * const cursor_rec= page_cur_get_rec(cursor); #endif/* UNIV_DEBUG */
if (page_is_empty(page))
{
ut_ad(page_cur_is_before_first(cursor));
/* This is an empty page. Recreate to remove the modification log. */
page_create_zip(cursor->block, index,
page_header_get_field(page, PAGE_LEVEL), 0, mtr);
ut_ad(!page_header_get_ptr(page, PAGE_FREE));
if (page_zip_available(page_zip, index->is_clust(), rec_size, 1)) goto use_heap;
/* The cursor should remain on the page infimum. */ return nullptr;
}
if (page_zip->m_nonempty || page_has_garbage(page))
{
ulint pos= page_rec_get_n_recs_before(cursor->rec);
if (UNIV_UNLIKELY(pos == ULINT_UNDEFINED)) return nullptr;
if (insert_rec)
{
ulint pos= page_rec_get_n_recs_before(insert_rec); if (UNIV_UNLIKELY(!pos || pos == ULINT_UNDEFINED)) return nullptr;
/* We are writing entire page images to the log. Reduce the redo
log volume by reorganizing the page at the same time. */ switch (page_zip_reorganize(cursor->block, index, level, mtr)) { case DB_SUCCESS: /* The page was reorganized: Seek to pos. */ if (pos <= 1)
cursor->rec= page + PAGE_NEW_INFIMUM; elseif (!(cursor->rec= page_rec_get_nth(page, pos - 1)))
{
cursor->rec= page + PAGE_NEW_INFIMUM; return nullptr;
}
insert_rec= page + rec_get_next_offs(cursor->rec, 1);
rec_offs_make_valid(insert_rec, index, page_is_leaf(page), offsets); break; case DB_FAIL: /* Theoretically, we could try one last resort of page_zip_reorganize()followedbypage_zip_available(),butthat wouldbeveryunlikelytosucceed.(Ifthefullreorganizedpage failedtocompress,whywoulditsucceedtocompressthepage,
plus log the insert of this record?) */
/* Out of space: restore the page */ if (!page_zip_decompress(page_zip, page, false))
ut_error; /* Memory corrupted? */
ut_ad(page_validate(page, index)); /* fall through */ default:
insert_rec= nullptr;
}
} return insert_rec;
}
free_rec= mach_read_from_2(page_free); if (free_rec)
{ /* Try to allocate from the head of the free list. */
rec_offs foffsets_[REC_OFFS_NORMAL_SIZE];
mem_heap_t *heap= nullptr;
if (rec_offs_size(foffsets) < rec_size)
{
too_small: if (UNIV_LIKELY_NULL(heap))
mem_heap_free(heap);
free_rec= 0; goto use_heap;
}
/* On compressed pages, do not relocate records from
the free list. If extra_size would grow, use the heap. */ const ssize_t extra_size_diff= lint(rec_offs_extra_size(offsets) -
rec_offs_extra_size(foffsets));
if (UNIV_UNLIKELY(extra_size_diff < 0))
{ /* Add an offset to the extra_size. */ if (rec_offs_size(foffsets) < rec_size - ssize_t(extra_size_diff)) goto too_small;
insert_buf-= extra_size_diff;
} elseif (UNIV_UNLIKELY(extra_size_diff)) /* Do not allow extra_size to grow */ goto too_small;
byte *const free_rec_ptr= page + free_rec;
heap_no= rec_get_heap_no_new(free_rec_ptr);
int16_t next_free= mach_read_from_2(free_rec_ptr - REC_NEXT); /* With innodb_page_size=64k, int16_t would be unsafe to use here,
but that cannot be used with ROW_FORMAT=COMPRESSED. */
static_assert(UNIV_ZIP_SIZE_SHIFT_MAX == 14, "compatibility"); if (next_free)
{
next_free= static_cast<int16_t>(next_free + free_rec); if (UNIV_UNLIKELY(int{PAGE_NEW_SUPREMUM_END + REC_N_NEW_EXTRA_BYTES} >
next_free ||
uint16_t(next_free) >= srv_page_size))
{ if (UNIV_LIKELY_NULL(heap))
mem_heap_free(heap); return nullptr;
}
}
if (!page_is_leaf(page))
{ /* Zero out the node pointer of free_rec, in case it will not be
overwritten by insert_rec. */
ut_ad(rec_size > REC_NODE_PTR_SIZE);
if (rec_offs_size(foffsets) > rec_size)
memset(rec_get_end(free_rec_ptr, foffsets) -
REC_NODE_PTR_SIZE, 0, REC_NODE_PTR_SIZE);
} elseif (index->is_clust())
{ /* Zero out DB_TRX_ID,DB_ROLL_PTR in free_rec, in case they will
not be overwritten by insert_rec. */
/* next record after current before the insertion */ const rec_t *next_rec = page_rec_next_get<true>(page, cursor->rec); if (UNIV_UNLIKELY(!next_rec ||
rec_get_status(next_rec) == REC_STATUS_INFIMUM ||
rec_get_status(cursor->rec) > REC_STATUS_INFIMUM)) return nullptr;
/* 3. Create the record */
byte *insert_rec= rec_copy(insert_buf, rec, offsets);
rec_offs_make_valid(insert_rec, index, page_is_leaf(page), offsets);
/* 4. Insert the record in the linked list of records */
ut_ad(cursor->rec != insert_rec);
ut_ad(rec_get_status(insert_rec) < REC_STATUS_INFIMUM);
/* 5. Set the n_owned field in the inserted record to zero,
and set the heap_no field */
rec_set_bit_field_1(insert_rec, 0, REC_NEW_N_OWNED,
REC_N_OWNED_MASK, REC_N_OWNED_SHIFT);
rec_set_bit_field_2(insert_rec, heap_no, REC_NEW_HEAP_NO,
REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT);
/* 8. Now we have incremented the n_owned field of the owner record.IfthenumberexceedsPAGE_DIR_SLOT_MAX_N_OWNED,
we have to split the corresponding directory slot in two. */ if (UNIV_UNLIKELY(n_owned == PAGE_DIR_SLOT_MAX_N_OWNED))
{ const ulint owner= page_dir_find_owner_slot(next_rec); if (UNIV_UNLIKELY(owner == ULINT_UNDEFINED))
{
page_cur_directory_corrupted(*cursor->block, *index); return nullptr;
}
page_zip_dir_split_slot(cursor->block, owner, mtr);
}
/***********************************************************//**
Deletes a record at the page cursor. The cursor is moved to the next
record after the deleted one. */ void
page_cur_delete_rec( /*================*/
page_cur_t* cursor, /*!< in/out: a page cursor */ const rec_offs* offsets,/*!< in: rec_get_offsets(
cursor->rec, index) */
mtr_t* mtr) /*!< in/out: mini-transaction */
{
page_dir_slot_t* cur_dir_slot;
rec_t* current_rec;
rec_t* prev_rec = NULL;
rec_t* next_rec;
ulint cur_n_owned;
rec_t* rec;
/* page_zip_validate() will fail here when btr_cur_pessimistic_delete()invokesbtr_set_min_rec_mark(). Then,both"page_zip"and"block->page.frame"wouldhavethe min-rec-marksetonthesmallestuserrecord,but "block->page.frame"wouldadditionallyhaveitsetonthe smallest-but-onerecord.Becausesloppy page_zip_validate_low()onlyignoresmin-rec-flagdifferences
in the smallest user record, it cannot be used here either. */
/* The record must not be the supremum or infimum record. */
ut_ad(page_rec_is_user_rec(current_rec));
if (page_get_n_recs(block->page.frame) == 1
&& !rec_is_alter_metadata(current_rec, *index)) { /* Empty the page. */
ut_ad(page_is_leaf(block->page.frame)); /* Usually, this should be the root page, andthewholeindextreeshouldbecomeempty. However,thiscouldalsobeacallin btr_cur_pessimistic_update()todeletetheonly
record in the page and to insert another one. */
ut_ad(page_rec_is_supremum(page_rec_get_next(cursor->rec)));
page_cur_set_after_last(block, cursor);
page_create_empty(page_cur_get_block(cursor), const_cast<dict_index_t*>(index), mtr); return;
}
/* Save to local variables some data associated with current_rec */
ulint cur_slot_no = page_dir_find_owner_slot(current_rec);
if (UNIV_UNLIKELY(!cur_slot_no || cur_slot_no == ULINT_UNDEFINED)) { /* Avoid crashing due to a corrupted page. */
page_cur_directory_corrupted(*block, *index); return;
}
/* The page gets invalid for btr_pcur_restore_pos(). Weavoidinvokingbuf_block_modify_clock_inc(block)becauseits consistencycheckswouldfailforthedummyblockthatisbeing
used during IMPORT TABLESPACE. */
block->modify_clock++;
/* Find the next and the previous record. Note that the cursor is
left at the next record. */
/* rec now points to the record of the previous directory slot. Look
for the immediate predecessor of current_rec in a loop. */
while (current_rec != rec) {
prev_rec = rec; if (!(rec = page_rec_get_next(rec))) { /* Avoid crashing due to a corrupted page. */ return;
}
}
if (!(next_rec = page_cur_move_to_next(cursor))) { /* Avoid crashing due to a corrupted page. */ return;
}
/* Remove the record from the linked list of records */ /* If the deleted record is pointed to by a dir slot, update the recordpointerinslot.Inthefollowingif-clauseweassumethat prev_recisownedbythesameslot,i.e.,PAGE_DIR_SLOT_MIN_N_OWNED
>= 2. */ /* Update the number of owned records of the slot */
/* The first slot is always pointing to the infimum record.
Find the directory slot pointing to s. */ const byte * const first_slot= page + srv_page_size - (PAGE_DIR + 2);
alignas(2) byte slot_offs[2];
mach_write_to_2(slot_offs, s - page);
static_assert(PAGE_DIR_SLOT_SIZE == 2, "compatibility");
while (memcmp_aligned<2>(slot, slot_offs, 2)) if ((slot+= 2) == first_slot) goto corrupted;
if (rec == s)
{
s= prev_rec;
mach_write_to_2(slot, s - page);
}
/* The first slot is always pointing to the infimum record.
Find the directory slot pointing to s. */ const byte * const first_slot= page + srv_page_size - (PAGE_DIR + 2);
alignas(2) byte slot_offs[2];
mach_write_to_2(slot_offs, s - page);
static_assert(PAGE_DIR_SLOT_SIZE == 2, "compatibility");
while (memcmp_aligned<2>(slot, slot_offs, 2)) if ((slot+= 2) == first_slot) goto corrupted;
if (rec == s)
{
s= prev_rec;
mach_write_to_2(slot, s - page);
}
/*******************************************************************//**
Print the first n numbers, generated by ut_rnd_gen() to make sure
(visually) that it works properly. */ void
test_ut_rnd_gen( int n) /*!< in: print first n numbers */
{ int i; unsignedlonglong rnd;
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.