Commit 5c9cedaf authored by Marko Mäkelä's avatar Marko Mäkelä

Skip the 'default row' pseudo-record where needed

The 'default row' record (which stores the default values of
instantly added columns when they were added) must be ignored
during normal operation.

Because btr_pcur_store_position() can theoretically be invoked
on the pseudo-record, we must check every caller of
btr_pcur_restore_position(). Similarly, when positioning the
cursor to the start of an index with btr_pcur_open_at_index_side(),
or when moving to the previous record, we must be careful to
skip the 'default row' record.

rec_is_default_row(): Check whether a leaf page record is the
'default row' pseudo-record.

row_import_set_sys_max_row_id(): Check for a table that is empty but
contains the 'default row' record.

IndexPurge::open(), row_merge_read_clustered_index(),
dict_stats_analyze_index_level(), row_sel_try_search_shortcut(),
row_sel(), sel_restore_position_for_mysql(), row_search_mvcc():
Skip the 'default row' pseudo-record.

fts_get_max_doc_id(): Assert that the 'default row' cannot exist
in the secondary index FTS_DOC_ID_INDEX(FTS_DOC_ID).

page_find_rec_max_not_deleted(): Assert that we are operating
on a leaf page. Also skip the 'default row' pseudo-record, not
only delete-marked records.

row_search_get_max_rec(): Assert that page_find_rec_max_not_deleted()
did not return any delete-marked or 'default row' record.
parent b52423d7
......@@ -1109,11 +1109,19 @@ dict_stats_analyze_index_level(
/* there should not be any pages on the left */
ut_a(btr_page_get_prev(page, mtr) == FIL_NULL);
/* check whether the first record on the leftmost page is marked
as such, if we are on a non-leaf level */
ut_a((level == 0)
== !(REC_INFO_MIN_REC_FLAG & rec_get_info_bits(
btr_pcur_get_rec(&pcur), page_is_comp(page))));
if (REC_INFO_MIN_REC_FLAG & rec_get_info_bits(
btr_pcur_get_rec(&pcur), page_is_comp(page))) {
ut_ad(btr_pcur_is_on_user_rec(&pcur));
if (level == 0) {
/* Skip the 'default row' pseudo-record */
ut_ad(index->is_instant());
btr_pcur_move_to_next_user_rec(&pcur, mtr);
}
} else {
/* The first record on the leftmost page must be
marked as such on each level except the leaf level. */
ut_a(level == 0);
}
prev_rec = NULL;
prev_rec_is_copied = false;
......
......@@ -3599,6 +3599,8 @@ fts_get_max_doc_id(
return(0);
}
ut_ad(!index->is_instant());
dfield = dict_index_get_nth_field(index, 0);
#if 0 /* This can fail when renaming a column to FTS_DOC_ID_COL_NAME. */
......@@ -3633,6 +3635,7 @@ fts_get_max_doc_id(
goto func_exit;
}
ut_ad(!rec_is_default_row(rec, index));
offsets = rec_get_offsets(
rec, index, offsets, true, ULINT_UNDEFINED, &heap);
......
......@@ -33,6 +33,7 @@ Created 5/30/1994 Heikki Tuuri
#include "rem0types.h"
#include "mtr0types.h"
#include "page0types.h"
#include "dict0dict.h"
#include "trx0types.h"
#endif /*! UNIV_INNOCHECKSUM */
#include <ostream>
......@@ -768,6 +769,21 @@ rec_offs_comp(const ulint* offsets)
return(*rec_offs_base(offsets) & REC_OFFS_COMPACT);
}
/** Determine if the record is the 'default row' pseudo-record
in the clustered index.
@param[in] rec leaf page record
@param[in] index index of the record
@return whether the record is the 'default row' pseudo-record */
inline
bool
rec_is_default_row(const rec_t* rec, const dict_index_t* index)
{
bool is = rec_get_info_bits(rec, dict_table_is_comp(index->table))
& REC_INFO_MIN_REC_FLAG;
ut_ad(!is || index->is_instant());
return is;
}
/******************************************************//**
Gets the physical size of a field.
@return length of field */
......
......@@ -26,7 +26,6 @@ Created 5/30/1994 Heikki Tuuri
#include "mach0data.h"
#include "ut0byte.h"
#include "dict0dict.h"
#include "dict0boot.h"
#include "btr0types.h"
......
......@@ -2807,19 +2807,26 @@ page_find_rec_max_not_deleted(
const rec_t* rec = page_get_infimum_rec(page);
const rec_t* prev_rec = NULL; // remove warning
/* Because the page infimum is never delete-marked,
/* Because the page infimum is never delete-marked
and never the 'default row' pseudo-record (MIN_REC_FLAG)),
prev_rec will always be assigned to it first. */
ut_ad(!rec_get_deleted_flag(rec, page_rec_is_comp(rec)));
ut_ad(!rec_get_info_bits(rec, page_rec_is_comp(rec)));
ut_ad(page_is_leaf(page));
if (page_is_comp(page)) {
do {
if (!rec_get_deleted_flag(rec, true)) {
if (!(rec[-REC_NEW_INFO_BITS]
& (REC_INFO_DELETED_FLAG
| REC_INFO_MIN_REC_FLAG))) {
prev_rec = rec;
}
rec = page_rec_get_next_low(rec, true);
} while (rec != page + PAGE_NEW_SUPREMUM);
} else {
do {
if (!rec_get_deleted_flag(rec, false)) {
if (!(rec[-REC_OLD_INFO_BITS]
& (REC_INFO_DELETED_FLAG
| REC_INFO_MIN_REC_FLAG))) {
prev_rec = rec;
}
rec = page_rec_get_next_low(rec, false);
......
......@@ -1439,6 +1439,11 @@ IndexPurge::open() UNIV_NOTHROW
btr_pcur_open_at_index_side(
true, m_index, BTR_MODIFY_LEAF, &m_pcur, true, 0, &m_mtr);
if (rec_is_default_row(btr_pcur_get_rec(&m_pcur), m_index)) {
ut_ad(btr_pcur_is_on_user_rec(&m_pcur));
/* Skip the 'default row' pseudo-record. */
btr_pcur_move_to_next_user_rec(&m_pcur, &m_mtr);
}
}
/**
......@@ -2319,7 +2324,14 @@ row_import_set_sys_max_row_id(
rec = btr_pcur_get_rec(&pcur);
/* Check for empty table. */
if (!page_rec_is_infimum(rec)) {
if (page_rec_is_infimum(rec)) {
/* The table is empty. */
err = DB_SUCCESS;
} else if (rec_is_default_row(rec, index)) {
/* The clustered index contains the 'default row',
that is, the table is empty. */
err = DB_SUCCESS;
} else {
ulint len;
const byte* field;
mem_heap_t* heap = NULL;
......@@ -2346,9 +2358,6 @@ row_import_set_sys_max_row_id(
if (heap != NULL) {
mem_heap_free(heap);
}
} else {
/* The table is empty. */
err = DB_SUCCESS;
}
btr_pcur_close(&pcur);
......
......@@ -1856,6 +1856,13 @@ row_merge_read_clustered_index(
btr_pcur_open_at_index_side(
true, clust_index, BTR_SEARCH_LEAF, &pcur, true, 0, &mtr);
if (rec_is_default_row(btr_pcur_get_rec(&pcur), clust_index)) {
ut_ad(btr_pcur_is_on_user_rec(&pcur));
/* Skip the 'default row' pseudo-record. */
btr_pcur_move_to_next_user_rec(&pcur, &mtr);
} else {
ut_ad(!clust_index->is_instant());
}
if (old_table != new_table) {
/* The table is being rebuilt. Identify the columns
......
......@@ -1492,6 +1492,15 @@ row_sel_try_search_shortcut(
return(SEL_RETRY);
}
if (rec_is_default_row(rec, index)) {
/* Skip the 'default row' pseudo-record. */
if (!btr_pcur_move_to_next_user_rec(&plan->pcur, mtr)) {
return(SEL_RETRY);
}
rec = btr_pcur_get_rec(&plan->pcur);
}
ut_ad(plan->mode == PAGE_CUR_GE);
/* As the cursor is now placed on a user record after a search with
......@@ -1819,6 +1828,12 @@ row_sel(
goto next_rec;
}
if (rec_is_default_row(rec, index)) {
/* Skip the 'default row' pseudo-record. */
cost_counter++;
goto next_rec;
}
if (!consistent_read) {
/* Try to place a lock on the index record */
ulint lock_type;
......@@ -3597,7 +3612,13 @@ sel_restore_position_for_mysql(
case BTR_PCUR_ON:
if (!success && moves_up) {
next:
btr_pcur_move_to_next(pcur, mtr);
if (btr_pcur_move_to_next(pcur, mtr)
&& rec_is_default_row(
btr_pcur_get_rec(pcur),
pcur->btr_cur.page_cur.index)) {
btr_pcur_move_to_next(pcur, mtr);
}
return(TRUE);
}
return(!success);
......@@ -3608,7 +3629,9 @@ sel_restore_position_for_mysql(
/* positioned to record after pcur->old_rec. */
pcur->pos_state = BTR_PCUR_IS_POSITIONED;
prev:
if (btr_pcur_is_on_user_rec(pcur) && !moves_up) {
if (btr_pcur_is_on_user_rec(pcur) && !moves_up
&& !rec_is_default_row(btr_pcur_get_rec(pcur),
pcur->btr_cur.page_cur.index)) {
btr_pcur_move_to_prev(pcur, mtr);
}
return(TRUE);
......@@ -4716,12 +4739,24 @@ row_search_mvcc(
corruption */
if (comp) {
if (rec_get_info_bits(rec, true) & REC_INFO_MIN_REC_FLAG) {
/* Skip the 'default row' pseudo-record. */
ut_ad(index->is_instant());
goto next_rec;
}
next_offs = rec_get_next_offs(rec, TRUE);
if (UNIV_UNLIKELY(next_offs < PAGE_NEW_SUPREMUM)) {
goto wrong_offs;
}
} else {
if (rec_get_info_bits(rec, false) & REC_INFO_MIN_REC_FLAG) {
/* Skip the 'default row' pseudo-record. */
ut_ad(index->is_instant());
goto next_rec;
}
next_offs = rec_get_next_offs(rec, FALSE);
if (UNIV_UNLIKELY(next_offs < PAGE_OLD_SUPREMUM)) {
......@@ -5993,6 +6028,9 @@ row_search_get_max_rec(
btr_pcur_close(&pcur);
ut_ad(!rec
|| !(rec_get_info_bits(rec, dict_table_is_comp(index->table))
& (REC_INFO_MIN_REC_FLAG | REC_INFO_DELETED_FLAG)));
return(rec);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment