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.
This commit is contained in:
parent
5a1283a4fa
commit
fe79ac5b0e
@ -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 @@ 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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user