Commit cb16bc95 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-14906 Assertion index->is_instant() failed on DELETE

The assertion would fail with the following trace:

rec_init_offsets_comp_ordinary(..., format=REC_LEAF_COLUMNS_ADDED)
rec_init_offsets()
rec_get_offsets_func()
rec_copy_prefix_to_dtuple()
dict_index_build_data_tuple()
btr_pcur_restore_position_func()

When btr_cur_store_position() had stored pcur->old_rec, the table
contained instantly added columns. The table was emptied
(dict_index_t::remove_instant() invoked) between the 'store' and 'restore'
operations, causing the assertion to fail. Here is a non-deterministic
test case to repeat the scenario:

	--source include/have_innodb.inc
	--connect (con1,localhost,root,,test)
	CREATE TABLE t1 (pk INT PRIMARY KEY) ENGINE = InnoDB;
	INSERT INTO t1 VALUES (0);
	ALTER TABLE t1 ADD COLUMN a INT;
	ALTER TABLE t1 ADD UNIQUE KEY (a);
	DELETE FROM t1;
	send INSERT INTO t1 VALUES (1,0),(2,0);
	--connection default
	DELETE FROM t1; # the assertion could fail here
	DROP TABLE t1;
	--disconnect con1

The fix is to normalize the pcur->old_rec so that when the
record prefix is stored, it will always be in the plain format.
This can be done, because the record prefix never includes any
instantly added columns. (It can only include key columns, which
can never be instantly added.)

rec_copy_prefix_to_buf(): Convert REC_STATUS_COLUMNS_ADDED to
REC_STATUS_ORDINARY format.
parent 38bc4bcc
...@@ -1838,6 +1838,7 @@ rec_copy_prefix_to_buf( ...@@ -1838,6 +1838,7 @@ rec_copy_prefix_to_buf(
const byte* nulls; const byte* nulls;
const byte* lens; const byte* lens;
ulint prefix_len = 0; ulint prefix_len = 0;
ulint instant_len = 0;
ut_ad(n_fields <= index->n_fields || dict_index_is_ibuf(index)); ut_ad(n_fields <= index->n_fields || dict_index_is_ibuf(index));
ut_ad(index->n_core_null_bytes <= UT_BITS_IN_BYTES(index->n_nullable)); ut_ad(index->n_core_null_bytes <= UT_BITS_IN_BYTES(index->n_nullable));
...@@ -1892,6 +1893,8 @@ rec_copy_prefix_to_buf( ...@@ -1892,6 +1893,8 @@ rec_copy_prefix_to_buf(
nulls = &rec[-REC_N_NEW_EXTRA_BYTES]; nulls = &rec[-REC_N_NEW_EXTRA_BYTES];
const ulint n_rec = ulint(index->n_core_fields) + 1 const ulint n_rec = ulint(index->n_core_fields) + 1
+ rec_get_n_add_field(nulls); + rec_get_n_add_field(nulls);
instant_len = ulint(&rec[-REC_N_NEW_EXTRA_BYTES] - nulls);
ut_ad(instant_len == 1 || instant_len == 2);
const uint n_nullable = index->get_n_nullable(n_rec); const uint n_nullable = index->get_n_nullable(n_rec);
lens = --nulls - UT_BITS_IN_BYTES(n_nullable); lens = --nulls - UT_BITS_IN_BYTES(n_nullable);
} }
...@@ -1947,7 +1950,7 @@ rec_copy_prefix_to_buf( ...@@ -1947,7 +1950,7 @@ rec_copy_prefix_to_buf(
UNIV_PREFETCH_R(rec + prefix_len); UNIV_PREFETCH_R(rec + prefix_len);
prefix_len += ulint(rec - (lens + 1)); prefix_len += ulint(rec - (lens + 1)) - instant_len;
if ((*buf == NULL) || (*buf_size < prefix_len)) { if ((*buf == NULL) || (*buf_size < prefix_len)) {
ut_free(*buf); ut_free(*buf);
...@@ -1955,9 +1958,21 @@ rec_copy_prefix_to_buf( ...@@ -1955,9 +1958,21 @@ rec_copy_prefix_to_buf(
*buf = static_cast<byte*>(ut_malloc_nokey(prefix_len)); *buf = static_cast<byte*>(ut_malloc_nokey(prefix_len));
} }
memcpy(*buf, lens + 1, prefix_len); if (instant_len) {
ulint hdr = ulint(&rec[-REC_N_NEW_EXTRA_BYTES] - (lens + 1))
return(*buf + (rec - (lens + 1))); - instant_len;
memcpy(*buf, lens + 1, hdr);
memcpy(*buf + hdr, &rec[-REC_N_NEW_EXTRA_BYTES],
prefix_len - hdr);
ut_ad(rec_get_status(*buf + hdr + REC_N_NEW_EXTRA_BYTES)
== REC_STATUS_COLUMNS_ADDED);
rec_set_status(*buf + hdr + REC_N_NEW_EXTRA_BYTES,
REC_STATUS_ORDINARY);
return *buf + hdr + REC_N_NEW_EXTRA_BYTES;
} else {
memcpy(*buf, lens + 1, prefix_len);
return *buf + (rec - (lens + 1));
}
} }
/***************************************************************//** /***************************************************************//**
......
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