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

MDEV-14837 Duplicate primary keys are allowed after ADD COLUMN / UPDATE

This bug affected tables where the PRIMARY KEY contains variable-length
columns, and ROW_FORMAT is COMPACT or DYNAMIC.

rec_init_offsets_comp_ordinary(): Do not short-cut the parsing
of the record header for records that contain explicit values
for instantly added columns.

rec_copy_prefix_to_buf(): Copy more header for records that
contain explicit values for instantly added columns.
parent 5a1283a4
......@@ -430,6 +430,16 @@ clust_index_size
connection default;
InnoDB 0 transactions not purged
DROP TABLE t1,t2,t3,t4,big;
CREATE TABLE t1 (a VARCHAR(1) PRIMARY KEY) ENGINE=InnoDB ROW_FORMAT=REDUNDANT;
INSERT INTO t1 SET a='a';
ALTER TABLE t1 ADD COLUMN b INT NOT NULL DEFAULT 0;
UPDATE t1 SET b = 1;
INSERT INTO t1 SET a='a';
ERROR 23000: Duplicate entry 'a' for key 'PRIMARY'
SELECT * FROM t1;
a b
a 1
DROP TABLE t1;
CREATE TABLE t1
(id INT PRIMARY KEY, c2 INT UNIQUE,
c3 POINT NOT NULL DEFAULT ST_GeomFromText('POINT(3 4)'),
......@@ -806,6 +816,16 @@ clust_index_size
connection default;
InnoDB 0 transactions not purged
DROP TABLE t1,t2,t3,t4,big;
CREATE TABLE t1 (a VARCHAR(1) PRIMARY KEY) ENGINE=InnoDB ROW_FORMAT=COMPACT;
INSERT INTO t1 SET a='a';
ALTER TABLE t1 ADD COLUMN b INT NOT NULL DEFAULT 0;
UPDATE t1 SET b = 1;
INSERT INTO t1 SET a='a';
ERROR 23000: Duplicate entry 'a' for key 'PRIMARY'
SELECT * FROM t1;
a b
a 1
DROP TABLE t1;
CREATE TABLE t1
(id INT PRIMARY KEY, c2 INT UNIQUE,
c3 POINT NOT NULL DEFAULT ST_GeomFromText('POINT(3 4)'),
......@@ -1182,10 +1202,20 @@ clust_index_size
connection default;
InnoDB 0 transactions not purged
DROP TABLE t1,t2,t3,t4,big;
CREATE TABLE t1 (a VARCHAR(1) PRIMARY KEY) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
INSERT INTO t1 SET a='a';
ALTER TABLE t1 ADD COLUMN b INT NOT NULL DEFAULT 0;
UPDATE t1 SET b = 1;
INSERT INTO t1 SET a='a';
ERROR 23000: Duplicate entry 'a' for key 'PRIMARY'
SELECT * FROM t1;
a b
a 1
DROP TABLE t1;
disconnect analyze;
SELECT variable_value-@old_instant instants
FROM information_schema.global_status
WHERE variable_name = 'innodb_instant_alter_column';
instants
33
36
SET GLOBAL innodb_purge_rseg_truncate_frequency= @saved_frequency;
......@@ -301,6 +301,16 @@ connection default;
--source include/wait_all_purged.inc
DROP TABLE t1,t2,t3,t4,big;
# MDEV-14837 Duplicate primary keys are allowed after ADD COLUMN / UPDATE
eval CREATE TABLE t1 (a VARCHAR(1) PRIMARY KEY) $engine;
INSERT INTO t1 SET a='a';
ALTER TABLE t1 ADD COLUMN b INT NOT NULL DEFAULT 0;
UPDATE t1 SET b = 1;
--error ER_DUP_ENTRY
INSERT INTO t1 SET a='a';
SELECT * FROM t1;
DROP TABLE t1;
dec $format;
}
disconnect analyze;
......
......@@ -344,9 +344,6 @@ rec_init_offsets_comp_ordinary(
/* We would have !index->is_instant() when rolling back
an instant ADD COLUMN operation. */
nulls -= REC_N_NEW_EXTRA_BYTES;
if (rec_offs_n_fields(offsets) <= n_fields) {
goto ordinary;
}
/* fall through */
case REC_LEAF_TEMP_COLUMNS_ADDED:
ut_ad(index->is_instant());
......@@ -1851,6 +1848,7 @@ rec_copy_prefix_to_buf(
ulint null_mask;
bool is_rtr_node_ptr = false;
ut_ad(n_fields <= index->n_fields);
ut_ad(index->n_core_null_bytes <= UT_BITS_IN_BYTES(index->n_nullable));
UNIV_PREFETCH_RW(*buf);
......@@ -1863,21 +1861,11 @@ rec_copy_prefix_to_buf(
}
switch (rec_get_status(rec)) {
case REC_STATUS_COLUMNS_ADDED:
/* We would have !index->is_instant() when rolling back
an instant ADD COLUMN operation. */
ut_ad(index->is_instant() || page_rec_is_default_row(rec));
if (n_fields >= index->n_core_fields) {
ut_ad(index->is_instant());
ut_ad(n_fields <= index->n_fields);
nulls = &rec[-REC_N_NEW_EXTRA_BYTES];
const ulint n_rec = n_fields + 1
+ rec_get_n_add_field(nulls);
const uint n_nullable = index->get_n_nullable(n_rec);
lens = --nulls - UT_BITS_IN_BYTES(n_nullable);
break;
}
/* fall through */
case REC_STATUS_INFIMUM:
case REC_STATUS_SUPREMUM:
/* infimum or supremum record: no sense to copy anything */
ut_error;
return(NULL);
case REC_STATUS_ORDINARY:
ut_ad(n_fields <= index->n_core_fields);
nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1);
......@@ -1897,11 +1885,15 @@ rec_copy_prefix_to_buf(
nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1);
lens = nulls - index->n_core_null_bytes;
break;
case REC_STATUS_INFIMUM:
case REC_STATUS_SUPREMUM:
/* infimum or supremum record: no sense to copy anything */
ut_error;
return(NULL);
case REC_STATUS_COLUMNS_ADDED:
/* We would have !index->is_instant() when rolling back
an instant ADD COLUMN operation. */
ut_ad(index->is_instant() || page_rec_is_default_row(rec));
nulls = &rec[-REC_N_NEW_EXTRA_BYTES];
const ulint n_rec = index->n_core_fields + 1
+ rec_get_n_add_field(nulls);
const uint n_nullable = index->get_n_nullable(n_rec);
lens = --nulls - UT_BITS_IN_BYTES(n_nullable);
}
UNIV_PREFETCH_R(lens);
......
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