Skip to content

Commit db25305

Browse files
committed
MDEV-14407 Assertion failure during rollback
Rollback attempted to dereference DB_ROLL_PTR=0, which cannot possibly be a valid undo log pointer. A safe canonical value would be roll_ptr_t(1) << ROLL_PTR_INSERT_FLAG_POS which is what was chosen in MDEV-12288. This bug was reproduced in 10.3 only. Potentially, the problem could have been introduced by MDEV-11415, which suppresses undo logging for ALGORITHM=COPY operations. In those operations, we should actually have written the safe value of DB_ROLL_PTR instead of writing 0. However, the test in commit 5421e3a demonstrates that access to the rebuilt table by earlier-started transactions should actually have been refused with ER_TABLE_DEF_CHANGED. btr_cur_ins_lock_and_undo(): When undo logging is disabled, use the safe value of DB_ROLL_PTR. btr_cur_optimistic_insert(): Validate the DB_TRX_ID,DB_ROLL_PTR before inserting into a clustered index leaf page. ins_node_t::sys_buf[]: Replaces row_id_buf and trx_id_buf and some heap usage. row_ins_alloc_sys_fields(): Initialize ins_node_t::sys_buf[]. trx_undo_page_report_modify(): Assert that the DB_ROLL_PTR is not 0. trx_undo_get_undo_rec_low(): Assert that the roll_ptr is valid before trying to dereference it. dict_index_t::is_primary(): Check if the index is the primary key.
1 parent be6307c commit db25305

File tree

6 files changed

+55
-27
lines changed

6 files changed

+55
-27
lines changed

storage/innobase/btr/btr0cur.cc

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2811,7 +2811,7 @@ btr_cur_ins_lock_and_undo(
28112811
}
28122812

28132813
if (flags & BTR_NO_UNDO_LOG_FLAG) {
2814-
roll_ptr = 0;
2814+
roll_ptr = roll_ptr_t(1) << ROLL_PTR_INSERT_FLAG_POS;
28152815
} else {
28162816
err = trx_undo_report_row_operation(thr, index, entry,
28172817
NULL, 0, NULL, NULL,
@@ -3016,7 +3016,7 @@ btr_cur_optimistic_insert(
30163016

30173017
DBUG_LOG("ib_cur",
30183018
"insert " << index->name << " (" << index->id << ") by "
3019-
<< ib::hex(thr ? trx_get_id_for_print(thr_get_trx(thr)) : 0)
3019+
<< ib::hex(thr ? thr->graph->trx->id : 0)
30203020
<< ' ' << rec_printer(entry).str());
30213021
DBUG_EXECUTE_IF("do_page_reorganize",
30223022
btr_page_reorganize(page_cursor, index, mtr););
@@ -3033,6 +3033,29 @@ btr_cur_optimistic_insert(
30333033
goto fail_err;
30343034
}
30353035

3036+
#ifdef UNIV_DEBUG
3037+
if (!(flags & BTR_CREATE_FLAG)
3038+
&& index->is_primary() && page_is_leaf(page)) {
3039+
const dfield_t* trx_id = dtuple_get_nth_field(
3040+
entry, dict_col_get_clust_pos(
3041+
dict_table_get_sys_col(index->table,
3042+
DATA_TRX_ID),
3043+
index));
3044+
3045+
ut_ad(trx_id->len == DATA_TRX_ID_LEN);
3046+
ut_ad(trx_id[1].len == DATA_ROLL_PTR_LEN);
3047+
ut_ad(*static_cast<const byte*>
3048+
(trx_id[1].data) & 0x80);
3049+
if (!(flags & BTR_NO_UNDO_LOG_FLAG)) {
3050+
ut_ad(thr->graph->trx->id);
3051+
ut_ad(thr->graph->trx->id
3052+
== trx_read_trx_id(
3053+
static_cast<const byte*>(
3054+
trx_id->data)));
3055+
}
3056+
}
3057+
#endif
3058+
30363059
*rec = page_cur_tuple_insert(
30373060
page_cursor, entry, index, offsets, heap,
30383061
n_ext, mtr);

storage/innobase/include/dict0mem.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,13 @@ struct dict_index_t{
973973
and the .ibd file is missing, or a
974974
page cannot be read or decrypted */
975975
inline bool is_readable() const;
976+
977+
/** @return whether the index is the primary key index
978+
(not the clustered index of the change buffer) */
979+
bool is_primary() const
980+
{
981+
return DICT_CLUSTERED == (type & (DICT_CLUSTERED | DICT_IBUF));
982+
}
976983
};
977984

978985
/** The status of online index creation */

storage/innobase/include/row0ins.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*****************************************************************************
22
33
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
4-
Copyright (c) 2017, MariaDB Corporation.
4+
Copyright (c) 2017, 2018, MariaDB Corporation.
55
66
This program is free software; you can redistribute it and/or modify it under
77
the terms of the GNU General Public License as published by the Free Software
@@ -198,10 +198,11 @@ struct ins_node_t{
198198
this should be reset to NULL */
199199
UT_LIST_BASE_NODE_T(dtuple_t)
200200
entry_list;/* list of entries, one for each index */
201-
byte* row_id_buf;/* buffer for the row id sys field in row */
201+
/** buffer for the system columns */
202+
byte sys_buf[DATA_ROW_ID_LEN
203+
+ DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN];
202204
trx_id_t trx_id; /*!< trx id or the last trx which executed the
203205
node */
204-
byte* trx_id_buf;/* buffer for the trx id sys field in row */
205206
mem_heap_t* entry_sys_heap;
206207
/* memory heap used as auxiliary storage;
207208
entry_list and sys fields are stored here;

storage/innobase/row/row0ins.cc

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -139,49 +139,44 @@ row_ins_alloc_sys_fields(
139139
{
140140
dtuple_t* row;
141141
dict_table_t* table;
142-
mem_heap_t* heap;
143142
const dict_col_t* col;
144143
dfield_t* dfield;
145-
byte* ptr;
146144

147145
row = node->row;
148146
table = node->table;
149-
heap = node->entry_sys_heap;
150147

151-
ut_ad(row && table && heap);
152148
ut_ad(dtuple_get_n_fields(row) == dict_table_get_n_cols(table));
153149

154150
/* allocate buffer to hold the needed system created hidden columns. */
155-
const uint len = DATA_ROW_ID_LEN + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN;
156-
ptr = static_cast<byte*>(mem_heap_zalloc(heap, len));
151+
compile_time_assert(DATA_ROW_ID_LEN
152+
+ DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN
153+
== sizeof node->sys_buf);
154+
memset(node->sys_buf, 0, sizeof node->sys_buf);
155+
/* Assign DB_ROLL_PTR to 1 << ROLL_PTR_INSERT_FLAG_POS */
156+
node->sys_buf[DATA_ROW_ID_LEN + DATA_TRX_ID_LEN] = 0x80;
157157

158158
/* 1. Populate row-id */
159159
col = dict_table_get_sys_col(table, DATA_ROW_ID);
160160

161161
dfield = dtuple_get_nth_field(row, dict_col_get_no(col));
162162

163-
dfield_set_data(dfield, ptr, DATA_ROW_ID_LEN);
164-
165-
node->row_id_buf = ptr;
166-
167-
ptr += DATA_ROW_ID_LEN;
163+
dfield_set_data(dfield, node->sys_buf, DATA_ROW_ID_LEN);
168164

169165
/* 2. Populate trx id */
170166
col = dict_table_get_sys_col(table, DATA_TRX_ID);
171167

172168
dfield = dtuple_get_nth_field(row, dict_col_get_no(col));
173169

174-
dfield_set_data(dfield, ptr, DATA_TRX_ID_LEN);
175-
176-
node->trx_id_buf = ptr;
177-
178-
ptr += DATA_TRX_ID_LEN;
170+
dfield_set_data(dfield, &node->sys_buf[DATA_ROW_ID_LEN],
171+
DATA_TRX_ID_LEN);
179172

180173
col = dict_table_get_sys_col(table, DATA_ROLL_PTR);
181174

182175
dfield = dtuple_get_nth_field(row, dict_col_get_no(col));
183176

184-
dfield_set_data(dfield, ptr, DATA_ROLL_PTR_LEN);
177+
dfield_set_data(dfield, &node->sys_buf[DATA_ROW_ID_LEN
178+
+ DATA_TRX_ID_LEN],
179+
DATA_ROLL_PTR_LEN);
185180
}
186181

187182
/*********************************************************************//**
@@ -3474,7 +3469,7 @@ row_ins_alloc_row_id_step(
34743469

34753470
row_id = dict_sys_get_new_row_id();
34763471

3477-
dict_sys_write_row_id(node->row_id_buf, row_id);
3472+
dict_sys_write_row_id(node->sys_buf, row_id);
34783473
}
34793474

34803475
/***********************************************************//**
@@ -3767,10 +3762,9 @@ row_ins_step(
37673762
This happens, for example, when a row update moves it to another
37683763
partition. In that case, we have already set the IX lock on the
37693764
table during the search operation, and there is no need to set
3770-
it again here. But we must write trx->id to node->trx_id_buf. */
3765+
it again here. But we must write trx->id to node->sys_buf. */
37713766

3772-
memset(node->trx_id_buf, 0, DATA_TRX_ID_LEN);
3773-
trx_write_trx_id(node->trx_id_buf, trx->id);
3767+
trx_write_trx_id(&node->sys_buf[DATA_ROW_ID_LEN], trx->id);
37743768

37753769
if (node->state == INS_NODE_SET_IX_LOCK) {
37763770

storage/innobase/row/row0mysql.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1520,7 +1520,7 @@ row_insert_for_mysql(
15201520

15211521
if (prebuilt->clust_index_was_generated) {
15221522
/* set row id to prebuilt */
1523-
ut_memcpy(prebuilt->row_id, node->row_id_buf, DATA_ROW_ID_LEN);
1523+
memcpy(prebuilt->row_id, node->sys_buf, DATA_ROW_ID_LEN);
15241524
}
15251525

15261526
dict_stats_update_if_needed(table);

storage/innobase/trx/trx0rec.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,7 @@ trx_undo_page_report_modify(
954954
dict_index_get_sys_col_pos(
955955
index, DATA_ROLL_PTR), &flen);
956956
ut_ad(flen == DATA_ROLL_PTR_LEN);
957+
ut_ad(memcmp(field, field_ref_zero, DATA_ROLL_PTR_LEN));
957958

958959
ptr += mach_u64_write_compressed(ptr, trx_read_roll_ptr(field));
959960

@@ -2106,6 +2107,8 @@ trx_undo_get_undo_rec_low(
21062107

21072108
trx_undo_decode_roll_ptr(roll_ptr, &is_insert, &rseg_id, &page_no,
21082109
&offset);
2110+
ut_ad(page_no > FSP_FIRST_INODE_PAGE_NO);
2111+
ut_ad(offset >= TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE);
21092112
rseg = is_temp
21102113
? trx_sys->temp_rsegs[rseg_id]
21112114
: trx_sys->rseg_array[rseg_id];

0 commit comments

Comments
 (0)