mirror of
https://github.com/MariaDB/server.git
synced 2025-01-20 14:02:32 +01:00
fd6db50c0b
Fixed bugs: #16814: SHOW INNODB STATUS format error in LATEST FOREIGN KEY ERROR section dict_foreign_key_error_report(): Always print a newline after invoking dict_print_info_on_foreign_key_in_create_format(). #16827: Better InnoDB error message if ibdata files omitted from my.cnf. #17126: CHECK TABLE on InnoDB causes a short hang during check of adaptive hash. CHECK TABLE blocking other queries, by releasing the btr_search_latch periodically during the adaptive hash table validation. #17405: Valgrind: conditional jump or move depends on uninitialised valuesw. buf_block_init(): Reset magic_n, buf_fix_count, and io_fix to avoid testing uninitialised variables. #18077: InnoDB uses full explicit table locks in stored FUNCTION. #18238: When locks exhaust the buffer pool, InnoDB does not roll back the trx. Check in pessimistic insert and update if the buffer pool is exhausted by locks. #18252: Disk space leaks in updates of InnoDB BLOB rows. btr_cur_pessimistic_update(): Invoke rec_get_offset() after rec_set_field_extern_bits(). btr_store_big_rec_extern_fields(): Note that offsets will no longer be valid after calling this function. #18283: When InnoDB returns error 'lock table full', MySQL can write to binlog too much. #18384: InnoDB memory leak on duplicate key errors if row has many columns. row_ins_duplicate_error_in_clust(): Call mem_heap_free(heap) at func_exit if needed. #18350: Use consistent read in CREATE ... SELECT .. if innodb_locks_unsafe_for_binlog is used. innobase/btr/btr0cur.c: Applied innodb-5.0-ss368 snapshot innobase/btr/btr0sea.c: Applied innodb-5.0-ss368 snapshot innobase/buf/buf0buf.c: Applied innodb-5.0-ss368 snapshot innobase/buf/buf0lru.c: Applied innodb-5.0-ss368 snapshot innobase/data/data0type.c: Applied innodb-5.0-ss368 snapshot dtype_print(): Fix printing of prtype. innobase/dict/dict0dict.c: Applied innodb-5.0-ss368 snapshot innobase/fil/fil0fil.c: Applied innodb-5.0-ss368 snapshot fil_extend_space_to_desired_size(): in UNIV_HOTBACKUP builds, do not touch srv_data_file_sizes[] or srv_n_data_files. innobase/ha/ha0ha.c: Applied innodb-5.0-ss368 snapshot innobase/include/btr0cur.h: Applied innodb-5.0-ss368 snapshot innobase/include/buf0lru.h: Applied innodb-5.0-ss368 snapshot innobase/include/ha0ha.h: Applied innodb-5.0-ss368 snapshot innobase/include/page0page.ic: Applied innodb-5.0-ss368 snapshot Remove UNIV_RELEASE_NOT_YET_STABLE and related checks. innobase/include/univ.i: Applied innodb-5.0-ss368 snapshot Remove UNIV_RELEASES_NOT_YET_STABLE and related checks. innobase/row/row0ins.c: Applied innodb-5.0-ss368 snapshot innobase/row/row0sel.c: Applied innodb-5.0-ss368 snapshot Remove UNIV_RELEASE_NOT_YET_STABLE and related checks. page_rec_is_comp(): Remove the bounds check. row_sel_field_store_in_mysql_format(): Turn the assertions on mbminlen, mbmaxlen, and templ->type into debug assertions. innobase/row/row0upd.c: Applied innodb-5.0-ss368 snapshot mysql-test/t/innodb.test: Applied innodb-5.0-ss368 snapshot sql/ha_innodb.cc: Applied innodb-5.0-ss368 snapshot Fix memory allocation bug (by changing MY_WME to MY_FAE) in get_share. Also partially fix coding style of the function.
828 lines
21 KiB
Text
828 lines
21 KiB
Text
/******************************************************
|
|
Index page routines
|
|
|
|
(c) 1994-1996 Innobase Oy
|
|
|
|
Created 2/2/1994 Heikki Tuuri
|
|
*******************************************************/
|
|
|
|
#include "mach0data.h"
|
|
#include "rem0cmp.h"
|
|
#include "mtr0log.h"
|
|
|
|
#ifdef UNIV_MATERIALIZE
|
|
#undef UNIV_INLINE
|
|
#define UNIV_INLINE
|
|
#endif
|
|
|
|
/*****************************************************************
|
|
Returns the max trx id field value. */
|
|
UNIV_INLINE
|
|
dulint
|
|
page_get_max_trx_id(
|
|
/*================*/
|
|
page_t* page) /* in: page */
|
|
{
|
|
ut_ad(page);
|
|
|
|
return(mach_read_from_8(page + PAGE_HEADER + PAGE_MAX_TRX_ID));
|
|
}
|
|
|
|
/*****************************************************************
|
|
Sets the max trx id field value if trx_id is bigger than the previous
|
|
value. */
|
|
UNIV_INLINE
|
|
void
|
|
page_update_max_trx_id(
|
|
/*===================*/
|
|
page_t* page, /* in: page */
|
|
dulint trx_id) /* in: transaction id */
|
|
{
|
|
ut_ad(page);
|
|
|
|
if (ut_dulint_cmp(page_get_max_trx_id(page), trx_id) < 0) {
|
|
|
|
page_set_max_trx_id(page, trx_id);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************
|
|
Reads the given header field. */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_header_get_field(
|
|
/*==================*/
|
|
page_t* page, /* in: page */
|
|
ulint field) /* in: PAGE_LEVEL, ... */
|
|
{
|
|
ut_ad(page);
|
|
ut_ad(field <= PAGE_INDEX_ID);
|
|
|
|
return(mach_read_from_2(page + PAGE_HEADER + field));
|
|
}
|
|
|
|
/*****************************************************************
|
|
Sets the given header field. */
|
|
UNIV_INLINE
|
|
void
|
|
page_header_set_field(
|
|
/*==================*/
|
|
page_t* page, /* in: page */
|
|
ulint field, /* in: PAGE_LEVEL, ... */
|
|
ulint val) /* in: value */
|
|
{
|
|
ut_ad(page);
|
|
ut_ad(field <= PAGE_N_RECS);
|
|
ut_ad(field == PAGE_N_HEAP || val < UNIV_PAGE_SIZE);
|
|
ut_ad(field != PAGE_N_HEAP || (val & 0x7fff) < UNIV_PAGE_SIZE);
|
|
|
|
mach_write_to_2(page + PAGE_HEADER + field, val);
|
|
}
|
|
|
|
/*****************************************************************
|
|
Returns the pointer stored in the given header field. */
|
|
UNIV_INLINE
|
|
byte*
|
|
page_header_get_ptr(
|
|
/*================*/
|
|
/* out: pointer or NULL */
|
|
page_t* page, /* in: page */
|
|
ulint field) /* in: PAGE_FREE, ... */
|
|
{
|
|
ulint offs;
|
|
|
|
ut_ad(page);
|
|
ut_ad((field == PAGE_FREE)
|
|
|| (field == PAGE_LAST_INSERT)
|
|
|| (field == PAGE_HEAP_TOP));
|
|
|
|
offs = page_header_get_field(page, field);
|
|
|
|
ut_ad((field != PAGE_HEAP_TOP) || offs);
|
|
|
|
if (offs == 0) {
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
return(page + offs);
|
|
}
|
|
|
|
/*****************************************************************
|
|
Sets the pointer stored in the given header field. */
|
|
UNIV_INLINE
|
|
void
|
|
page_header_set_ptr(
|
|
/*================*/
|
|
page_t* page, /* in: page */
|
|
ulint field, /* in: PAGE_FREE, ... */
|
|
byte* ptr) /* in: pointer or NULL*/
|
|
{
|
|
ulint offs;
|
|
|
|
ut_ad(page);
|
|
ut_ad((field == PAGE_FREE)
|
|
|| (field == PAGE_LAST_INSERT)
|
|
|| (field == PAGE_HEAP_TOP));
|
|
|
|
if (ptr == NULL) {
|
|
offs = 0;
|
|
} else {
|
|
offs = ptr - page;
|
|
}
|
|
|
|
ut_ad((field != PAGE_HEAP_TOP) || offs);
|
|
|
|
page_header_set_field(page, field, offs);
|
|
}
|
|
|
|
/*****************************************************************
|
|
Resets the last insert info field in the page header. Writes to mlog
|
|
about this operation. */
|
|
UNIV_INLINE
|
|
void
|
|
page_header_reset_last_insert(
|
|
/*==========================*/
|
|
page_t* page, /* in: page */
|
|
mtr_t* mtr) /* in: mtr */
|
|
{
|
|
ut_ad(page && mtr);
|
|
|
|
mlog_write_ulint(page + PAGE_HEADER + PAGE_LAST_INSERT, 0,
|
|
MLOG_2BYTES, mtr);
|
|
}
|
|
|
|
/****************************************************************
|
|
Determine whether the page is in new-style compact format. */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_is_comp(
|
|
/*=========*/
|
|
/* out: nonzero if the page is in compact
|
|
format, zero if it is in old-style format */
|
|
page_t* page) /* in: index page */
|
|
{
|
|
return(UNIV_EXPECT(page_header_get_field(page, PAGE_N_HEAP) & 0x8000,
|
|
0x8000));
|
|
}
|
|
|
|
/****************************************************************
|
|
TRUE if the record is on a page in compact format. */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_rec_is_comp(
|
|
/*=============*/
|
|
/* out: nonzero if in compact format */
|
|
const rec_t* rec) /* in: record */
|
|
{
|
|
return(page_is_comp(ut_align_down((rec_t*) rec, UNIV_PAGE_SIZE)));
|
|
}
|
|
|
|
/****************************************************************
|
|
Gets the first record on the page. */
|
|
UNIV_INLINE
|
|
rec_t*
|
|
page_get_infimum_rec(
|
|
/*=================*/
|
|
/* out: the first record in record list */
|
|
page_t* page) /* in: page which must have record(s) */
|
|
{
|
|
ut_ad(page);
|
|
|
|
if (page_is_comp(page)) {
|
|
return(page + PAGE_NEW_INFIMUM);
|
|
} else {
|
|
return(page + PAGE_OLD_INFIMUM);
|
|
}
|
|
}
|
|
|
|
/****************************************************************
|
|
Gets the last record on the page. */
|
|
UNIV_INLINE
|
|
rec_t*
|
|
page_get_supremum_rec(
|
|
/*==================*/
|
|
/* out: the last record in record list */
|
|
page_t* page) /* in: page which must have record(s) */
|
|
{
|
|
ut_ad(page);
|
|
|
|
if (page_is_comp(page)) {
|
|
return(page + PAGE_NEW_SUPREMUM);
|
|
} else {
|
|
return(page + PAGE_OLD_SUPREMUM);
|
|
}
|
|
}
|
|
|
|
/****************************************************************
|
|
TRUE if the record is a user record on the page. */
|
|
UNIV_INLINE
|
|
ibool
|
|
page_rec_is_user_rec_low(
|
|
/*=====================*/
|
|
/* out: TRUE if a user record */
|
|
ulint offset) /* in: record offset on page */
|
|
{
|
|
ut_ad(offset >= PAGE_NEW_INFIMUM);
|
|
#if PAGE_OLD_INFIMUM < PAGE_NEW_INFIMUM
|
|
# error "PAGE_OLD_INFIMUM < PAGE_NEW_INFIMUM"
|
|
#endif
|
|
#if PAGE_OLD_SUPREMUM < PAGE_NEW_SUPREMUM
|
|
# error "PAGE_OLD_SUPREMUM < PAGE_NEW_SUPREMUM"
|
|
#endif
|
|
#if PAGE_NEW_INFIMUM > PAGE_OLD_SUPREMUM
|
|
# error "PAGE_NEW_INFIMUM > PAGE_OLD_SUPREMUM"
|
|
#endif
|
|
#if PAGE_OLD_INFIMUM > PAGE_NEW_SUPREMUM
|
|
# error "PAGE_OLD_INFIMUM > PAGE_NEW_SUPREMUM"
|
|
#endif
|
|
#if PAGE_NEW_SUPREMUM > PAGE_OLD_SUPREMUM_END
|
|
# error "PAGE_NEW_SUPREMUM > PAGE_OLD_SUPREMUM_END"
|
|
#endif
|
|
#if PAGE_OLD_SUPREMUM > PAGE_NEW_SUPREMUM_END
|
|
# error "PAGE_OLD_SUPREMUM > PAGE_NEW_SUPREMUM_END"
|
|
#endif
|
|
ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);
|
|
|
|
return(UNIV_LIKELY(offset != PAGE_NEW_SUPREMUM)
|
|
&& UNIV_LIKELY(offset != PAGE_NEW_INFIMUM)
|
|
&& UNIV_LIKELY(offset != PAGE_OLD_INFIMUM)
|
|
&& UNIV_LIKELY(offset != PAGE_OLD_SUPREMUM));
|
|
}
|
|
|
|
/****************************************************************
|
|
TRUE if the record is the supremum record on a page. */
|
|
UNIV_INLINE
|
|
ibool
|
|
page_rec_is_supremum_low(
|
|
/*=====================*/
|
|
/* out: TRUE if the supremum record */
|
|
ulint offset) /* in: record offset on page */
|
|
{
|
|
ut_ad(offset >= PAGE_NEW_INFIMUM);
|
|
ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);
|
|
|
|
return(UNIV_UNLIKELY(offset == PAGE_NEW_SUPREMUM)
|
|
|| UNIV_UNLIKELY(offset == PAGE_OLD_SUPREMUM));
|
|
}
|
|
|
|
/****************************************************************
|
|
TRUE if the record is the infimum record on a page. */
|
|
UNIV_INLINE
|
|
ibool
|
|
page_rec_is_infimum_low(
|
|
/*=====================*/
|
|
/* out: TRUE if the infimum record */
|
|
ulint offset) /* in: record offset on page */
|
|
{
|
|
ut_ad(offset >= PAGE_NEW_INFIMUM);
|
|
ut_ad(offset <= UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START);
|
|
|
|
return(UNIV_UNLIKELY(offset == PAGE_NEW_INFIMUM)
|
|
|| UNIV_UNLIKELY(offset == PAGE_OLD_INFIMUM));
|
|
}
|
|
|
|
/****************************************************************
|
|
TRUE if the record is a user record on the page. */
|
|
UNIV_INLINE
|
|
ibool
|
|
page_rec_is_user_rec(
|
|
/*=================*/
|
|
/* out: TRUE if a user record */
|
|
const rec_t* rec) /* in: record */
|
|
{
|
|
return(page_rec_is_user_rec_low(
|
|
ut_align_offset(rec, UNIV_PAGE_SIZE)));
|
|
}
|
|
|
|
/****************************************************************
|
|
TRUE if the record is the supremum record on a page. */
|
|
UNIV_INLINE
|
|
ibool
|
|
page_rec_is_supremum(
|
|
/*=================*/
|
|
/* out: TRUE if the supremum record */
|
|
const rec_t* rec) /* in: record */
|
|
{
|
|
return(page_rec_is_supremum_low(
|
|
ut_align_offset(rec, UNIV_PAGE_SIZE)));
|
|
}
|
|
|
|
/****************************************************************
|
|
TRUE if the record is the infimum record on a page. */
|
|
UNIV_INLINE
|
|
ibool
|
|
page_rec_is_infimum(
|
|
/*================*/
|
|
/* out: TRUE if the infimum record */
|
|
const rec_t* rec) /* in: record */
|
|
{
|
|
return(page_rec_is_infimum_low(
|
|
ut_align_offset(rec, UNIV_PAGE_SIZE)));
|
|
}
|
|
|
|
/*****************************************************************
|
|
Compares a data tuple to a physical record. Differs from the function
|
|
cmp_dtuple_rec_with_match in the way that the record must reside on an
|
|
index page, and also page infimum and supremum records can be given in
|
|
the parameter rec. These are considered as the negative infinity and
|
|
the positive infinity in the alphabetical order. */
|
|
UNIV_INLINE
|
|
int
|
|
page_cmp_dtuple_rec_with_match(
|
|
/*===========================*/
|
|
/* out: 1, 0, -1, if dtuple is greater, equal,
|
|
less than rec, respectively, when only the
|
|
common first fields are compared */
|
|
dtuple_t* dtuple, /* in: data tuple */
|
|
rec_t* rec, /* in: physical record on a page; may also
|
|
be page infimum or supremum, in which case
|
|
matched-parameter values below are not
|
|
affected */
|
|
const ulint* offsets,/* in: array returned by rec_get_offsets() */
|
|
ulint* matched_fields, /* in/out: number of already completely
|
|
matched fields; when function returns
|
|
contains the value for current comparison */
|
|
ulint* matched_bytes) /* in/out: number of already matched
|
|
bytes within the first field not completely
|
|
matched; when function returns contains the
|
|
value for current comparison */
|
|
{
|
|
ulint rec_offset;
|
|
|
|
ut_ad(dtuple_check_typed(dtuple));
|
|
ut_ad(rec_offs_validate(rec, NULL, offsets));
|
|
ut_ad(!rec_offs_comp(offsets) == !page_rec_is_comp(rec));
|
|
|
|
rec_offset = ut_align_offset(rec, UNIV_PAGE_SIZE);
|
|
|
|
if (UNIV_UNLIKELY(rec_offset == PAGE_NEW_INFIMUM)
|
|
|| UNIV_UNLIKELY(rec_offset == PAGE_OLD_INFIMUM)) {
|
|
return(1);
|
|
}
|
|
if (UNIV_UNLIKELY(rec_offset == PAGE_NEW_SUPREMUM)
|
|
|| UNIV_UNLIKELY(rec_offset == PAGE_OLD_SUPREMUM)) {
|
|
return(-1);
|
|
}
|
|
|
|
return(cmp_dtuple_rec_with_match(dtuple, rec, offsets,
|
|
matched_fields,
|
|
matched_bytes));
|
|
}
|
|
|
|
/*****************************************************************
|
|
Gets the number of user records on page (infimum and supremum records
|
|
are not user records). */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_get_n_recs(
|
|
/*============*/
|
|
/* out: number of user records */
|
|
page_t* page) /* in: index page */
|
|
{
|
|
return(page_header_get_field(page, PAGE_N_RECS));
|
|
}
|
|
|
|
/*****************************************************************
|
|
Gets the number of dir slots in directory. */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_dir_get_n_slots(
|
|
/*=================*/
|
|
/* out: number of slots */
|
|
page_t* page) /* in: index page */
|
|
{
|
|
return(page_header_get_field(page, PAGE_N_DIR_SLOTS));
|
|
}
|
|
/*****************************************************************
|
|
Sets the number of dir slots in directory. */
|
|
UNIV_INLINE
|
|
void
|
|
page_dir_set_n_slots(
|
|
/*=================*/
|
|
/* out: number of slots */
|
|
page_t* page, /* in: index page */
|
|
ulint n_slots)/* in: number of slots */
|
|
{
|
|
page_header_set_field(page, PAGE_N_DIR_SLOTS, n_slots);
|
|
}
|
|
|
|
/*****************************************************************
|
|
Gets the number of records in the heap. */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_dir_get_n_heap(
|
|
/*================*/
|
|
/* out: number of user records */
|
|
page_t* page) /* in: index page */
|
|
{
|
|
return(page_header_get_field(page, PAGE_N_HEAP) & 0x7fff);
|
|
}
|
|
|
|
/*****************************************************************
|
|
Sets the number of records in the heap. */
|
|
UNIV_INLINE
|
|
void
|
|
page_dir_set_n_heap(
|
|
/*================*/
|
|
page_t* page, /* in: index page */
|
|
ulint n_heap) /* in: number of records */
|
|
{
|
|
ut_ad(n_heap < 0x8000);
|
|
|
|
page_header_set_field(page, PAGE_N_HEAP, n_heap | (0x8000 &
|
|
page_header_get_field(page, PAGE_N_HEAP)));
|
|
}
|
|
|
|
/*****************************************************************
|
|
Gets pointer to nth directory slot. */
|
|
UNIV_INLINE
|
|
page_dir_slot_t*
|
|
page_dir_get_nth_slot(
|
|
/*==================*/
|
|
/* out: pointer to dir slot */
|
|
page_t* page, /* in: index page */
|
|
ulint n) /* in: position */
|
|
{
|
|
ut_ad(page_dir_get_n_slots(page) > n);
|
|
|
|
return(page + UNIV_PAGE_SIZE - PAGE_DIR
|
|
- (n + 1) * PAGE_DIR_SLOT_SIZE);
|
|
}
|
|
|
|
/******************************************************************
|
|
Used to check the consistency of a record on a page. */
|
|
UNIV_INLINE
|
|
ibool
|
|
page_rec_check(
|
|
/*===========*/
|
|
/* out: TRUE if succeed */
|
|
rec_t* rec) /* in: record */
|
|
{
|
|
page_t* page;
|
|
|
|
ut_a(rec);
|
|
|
|
page = buf_frame_align(rec);
|
|
|
|
ut_a(rec <= page_header_get_ptr(page, PAGE_HEAP_TOP));
|
|
ut_a(rec >= page + PAGE_DATA);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/*******************************************************************
|
|
Gets the record pointed to by a directory slot. */
|
|
UNIV_INLINE
|
|
rec_t*
|
|
page_dir_slot_get_rec(
|
|
/*==================*/
|
|
/* out: pointer to record */
|
|
page_dir_slot_t* slot) /* in: directory slot */
|
|
{
|
|
return(buf_frame_align(slot) + mach_read_from_2(slot));
|
|
}
|
|
|
|
/*******************************************************************
|
|
This is used to set the record offset in a directory slot. */
|
|
UNIV_INLINE
|
|
void
|
|
page_dir_slot_set_rec(
|
|
/*==================*/
|
|
page_dir_slot_t* slot, /* in: directory slot */
|
|
rec_t* rec) /* in: record on the page */
|
|
{
|
|
ut_ad(page_rec_check(rec));
|
|
|
|
mach_write_to_2(slot, ut_align_offset(rec, UNIV_PAGE_SIZE));
|
|
}
|
|
|
|
/*******************************************************************
|
|
Gets the number of records owned by a directory slot. */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_dir_slot_get_n_owned(
|
|
/*======================*/
|
|
/* out: number of records */
|
|
page_dir_slot_t* slot) /* in: page directory slot */
|
|
{
|
|
rec_t* rec = page_dir_slot_get_rec(slot);
|
|
return(rec_get_n_owned(rec, page_rec_is_comp(rec)));
|
|
}
|
|
|
|
/*******************************************************************
|
|
This is used to set the owned records field of a directory slot. */
|
|
UNIV_INLINE
|
|
void
|
|
page_dir_slot_set_n_owned(
|
|
/*======================*/
|
|
page_dir_slot_t* slot, /* in: directory slot */
|
|
ulint n) /* in: number of records owned
|
|
by the slot */
|
|
{
|
|
rec_t* rec = page_dir_slot_get_rec(slot);
|
|
rec_set_n_owned(rec, page_rec_is_comp(rec), n);
|
|
}
|
|
|
|
/****************************************************************
|
|
Calculates the space reserved for directory slots of a given number of
|
|
records. The exact value is a fraction number n * PAGE_DIR_SLOT_SIZE /
|
|
PAGE_DIR_SLOT_MIN_N_OWNED, and it is rounded upwards to an integer. */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_dir_calc_reserved_space(
|
|
/*=========================*/
|
|
ulint n_recs) /* in: number of records */
|
|
{
|
|
return((PAGE_DIR_SLOT_SIZE * n_recs + PAGE_DIR_SLOT_MIN_N_OWNED - 1)
|
|
/ PAGE_DIR_SLOT_MIN_N_OWNED);
|
|
}
|
|
|
|
/****************************************************************
|
|
Gets the pointer to the next record on the page. */
|
|
UNIV_INLINE
|
|
rec_t*
|
|
page_rec_get_next(
|
|
/*==============*/
|
|
/* out: pointer to next record */
|
|
rec_t* rec) /* in: pointer to record */
|
|
{
|
|
ulint offs;
|
|
page_t* page;
|
|
|
|
ut_ad(page_rec_check(rec));
|
|
|
|
page = ut_align_down(rec, UNIV_PAGE_SIZE);
|
|
|
|
offs = rec_get_next_offs(rec, page_is_comp(page));
|
|
|
|
if (UNIV_UNLIKELY(offs >= UNIV_PAGE_SIZE)) {
|
|
fprintf(stderr,
|
|
"InnoDB: Next record offset is nonsensical %lu in record at offset %lu\n"
|
|
"InnoDB: rec address %p, first buffer frame %p\n"
|
|
"InnoDB: buffer pool high end %p, buf fix count %lu\n",
|
|
(ulong)offs, (ulong)(rec - page),
|
|
rec, buf_pool->frame_zero,
|
|
buf_pool->high_end,
|
|
(ulong)buf_block_align(rec)->buf_fix_count);
|
|
buf_page_print(page);
|
|
|
|
ut_error;
|
|
}
|
|
|
|
if (UNIV_UNLIKELY(offs == 0)) {
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
return(page + offs);
|
|
}
|
|
|
|
/****************************************************************
|
|
Sets the pointer to the next record on the page. */
|
|
UNIV_INLINE
|
|
void
|
|
page_rec_set_next(
|
|
/*==============*/
|
|
rec_t* rec, /* in: pointer to record, must not be page supremum */
|
|
rec_t* next) /* in: pointer to next record, must not be page
|
|
infimum */
|
|
{
|
|
page_t* page;
|
|
ulint offs;
|
|
|
|
ut_ad(page_rec_check(rec));
|
|
ut_ad(!page_rec_is_supremum(rec));
|
|
page = ut_align_down(rec, UNIV_PAGE_SIZE);
|
|
|
|
if (next) {
|
|
ut_ad(!page_rec_is_infimum(next));
|
|
ut_ad(page == ut_align_down(next, UNIV_PAGE_SIZE));
|
|
offs = (ulint) (next - page);
|
|
} else {
|
|
offs = 0;
|
|
}
|
|
|
|
rec_set_next_offs(rec, page_is_comp(page), offs);
|
|
}
|
|
|
|
/****************************************************************
|
|
Gets the pointer to the previous record. */
|
|
UNIV_INLINE
|
|
rec_t*
|
|
page_rec_get_prev(
|
|
/*==============*/
|
|
/* out: pointer to previous record */
|
|
rec_t* rec) /* in: pointer to record, must not be page
|
|
infimum */
|
|
{
|
|
page_dir_slot_t* slot;
|
|
ulint slot_no;
|
|
rec_t* rec2;
|
|
rec_t* prev_rec = NULL;
|
|
page_t* page;
|
|
|
|
ut_ad(page_rec_check(rec));
|
|
|
|
page = ut_align_down(rec, UNIV_PAGE_SIZE);
|
|
|
|
ut_ad(!page_rec_is_infimum(rec));
|
|
|
|
slot_no = page_dir_find_owner_slot(rec);
|
|
|
|
ut_a(slot_no != 0);
|
|
|
|
slot = page_dir_get_nth_slot(page, slot_no - 1);
|
|
|
|
rec2 = page_dir_slot_get_rec(slot);
|
|
|
|
while (rec != rec2) {
|
|
prev_rec = rec2;
|
|
rec2 = page_rec_get_next(rec2);
|
|
}
|
|
|
|
ut_a(prev_rec);
|
|
|
|
return(prev_rec);
|
|
}
|
|
|
|
/*******************************************************************
|
|
Looks for the record which owns the given record. */
|
|
UNIV_INLINE
|
|
rec_t*
|
|
page_rec_find_owner_rec(
|
|
/*====================*/
|
|
/* out: the owner record */
|
|
rec_t* rec) /* in: the physical record */
|
|
{
|
|
ut_ad(page_rec_check(rec));
|
|
|
|
if (page_rec_is_comp(rec)) {
|
|
while (rec_get_n_owned(rec, TRUE) == 0) {
|
|
rec = page_rec_get_next(rec);
|
|
}
|
|
} else {
|
|
while (rec_get_n_owned(rec, FALSE) == 0) {
|
|
rec = page_rec_get_next(rec);
|
|
}
|
|
}
|
|
|
|
return(rec);
|
|
}
|
|
|
|
/****************************************************************
|
|
Returns the sum of the sizes of the records in the record list, excluding
|
|
the infimum and supremum records. */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_get_data_size(
|
|
/*===============*/
|
|
/* out: data in bytes */
|
|
page_t* page) /* in: index page */
|
|
{
|
|
ulint ret;
|
|
|
|
ret = (ulint)(page_header_get_field(page, PAGE_HEAP_TOP)
|
|
- (page_is_comp(page)
|
|
? PAGE_NEW_SUPREMUM_END
|
|
: PAGE_OLD_SUPREMUM_END)
|
|
- page_header_get_field(page, PAGE_GARBAGE));
|
|
|
|
ut_ad(ret < UNIV_PAGE_SIZE);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/*****************************************************************
|
|
Calculates free space if a page is emptied. */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_get_free_space_of_empty(
|
|
/*=========================*/
|
|
/* out: free space */
|
|
ulint comp) /* in: nonzero=compact page layout */
|
|
{
|
|
if (UNIV_LIKELY(comp)) {
|
|
return((ulint)(UNIV_PAGE_SIZE
|
|
- PAGE_NEW_SUPREMUM_END
|
|
- PAGE_DIR
|
|
- 2 * PAGE_DIR_SLOT_SIZE));
|
|
}
|
|
|
|
return((ulint)(UNIV_PAGE_SIZE
|
|
- PAGE_OLD_SUPREMUM_END
|
|
- PAGE_DIR
|
|
- 2 * PAGE_DIR_SLOT_SIZE));
|
|
}
|
|
|
|
/****************************************************************
|
|
Each user record on a page, and also the deleted user records in the heap
|
|
takes its size plus the fraction of the dir cell size /
|
|
PAGE_DIR_SLOT_MIN_N_OWNED bytes for it. If the sum of these exceeds the
|
|
value of page_get_free_space_of_empty, the insert is impossible, otherwise
|
|
it is allowed. This function returns the maximum combined size of records
|
|
which can be inserted on top of the record heap. */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_get_max_insert_size(
|
|
/*=====================*/
|
|
/* out: maximum combined size for inserted records */
|
|
page_t* page, /* in: index page */
|
|
ulint n_recs) /* in: number of records */
|
|
{
|
|
ulint occupied;
|
|
ulint free_space;
|
|
|
|
if (page_is_comp(page)) {
|
|
occupied = page_header_get_field(page, PAGE_HEAP_TOP)
|
|
- PAGE_NEW_SUPREMUM_END + page_dir_calc_reserved_space(
|
|
n_recs + page_dir_get_n_heap(page) - 2);
|
|
|
|
free_space = page_get_free_space_of_empty(TRUE);
|
|
} else {
|
|
occupied = page_header_get_field(page, PAGE_HEAP_TOP)
|
|
- PAGE_OLD_SUPREMUM_END + page_dir_calc_reserved_space(
|
|
n_recs + page_dir_get_n_heap(page) - 2);
|
|
|
|
free_space = page_get_free_space_of_empty(FALSE);
|
|
}
|
|
|
|
/* Above the 'n_recs +' part reserves directory space for the new
|
|
inserted records; the '- 2' excludes page infimum and supremum
|
|
records */
|
|
|
|
if (occupied > free_space) {
|
|
|
|
return(0);
|
|
}
|
|
|
|
return(free_space - occupied);
|
|
}
|
|
|
|
/****************************************************************
|
|
Returns the maximum combined size of records which can be inserted on top
|
|
of the record heap if a page is first reorganized. */
|
|
UNIV_INLINE
|
|
ulint
|
|
page_get_max_insert_size_after_reorganize(
|
|
/*======================================*/
|
|
/* out: maximum combined size for inserted records */
|
|
page_t* page, /* in: index page */
|
|
ulint n_recs) /* in: number of records */
|
|
{
|
|
ulint occupied;
|
|
ulint free_space;
|
|
|
|
occupied = page_get_data_size(page)
|
|
+ page_dir_calc_reserved_space(n_recs + page_get_n_recs(page));
|
|
|
|
free_space = page_get_free_space_of_empty(page_is_comp(page));
|
|
|
|
if (occupied > free_space) {
|
|
|
|
return(0);
|
|
}
|
|
|
|
return(free_space - occupied);
|
|
}
|
|
|
|
/****************************************************************
|
|
Puts a record to free list. */
|
|
UNIV_INLINE
|
|
void
|
|
page_mem_free(
|
|
/*==========*/
|
|
page_t* page, /* in: index page */
|
|
rec_t* rec, /* in: pointer to the (origin of) record */
|
|
const ulint* offsets)/* in: array returned by rec_get_offsets() */
|
|
{
|
|
rec_t* free;
|
|
ulint garbage;
|
|
|
|
ut_ad(rec_offs_validate(rec, NULL, offsets));
|
|
ut_ad(!rec_offs_comp(offsets) == !page_rec_is_comp(rec));
|
|
free = page_header_get_ptr(page, PAGE_FREE);
|
|
|
|
page_rec_set_next(rec, free);
|
|
page_header_set_ptr(page, PAGE_FREE, rec);
|
|
|
|
#if 0 /* It's better not to destroy the user's data. */
|
|
|
|
/* Clear the data bytes of the deleted record in order to improve
|
|
the compression ratio of the page and to make it easier to read
|
|
page dumps in corruption reports. The extra bytes of the record
|
|
cannot be cleared, because page_mem_alloc() needs them in order
|
|
to determine the size of the deleted record. */
|
|
memset(rec, 0, rec_offs_data_size(offsets));
|
|
#endif
|
|
|
|
garbage = page_header_get_field(page, PAGE_GARBAGE);
|
|
|
|
page_header_set_field(page, PAGE_GARBAGE,
|
|
garbage + rec_offs_size(offsets));
|
|
}
|
|
|
|
#ifdef UNIV_MATERIALIZE
|
|
#undef UNIV_INLINE
|
|
#define UNIV_INLINE UNIV_INLINE_ORIGINAL
|
|
#endif
|