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