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;
|
connection default;
|
||||||
InnoDB 0 transactions not purged
|
InnoDB 0 transactions not purged
|
||||||
DROP TABLE t1,t2,t3,t4,big;
|
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
|
CREATE TABLE t1
|
||||||
(id INT PRIMARY KEY, c2 INT UNIQUE,
|
(id INT PRIMARY KEY, c2 INT UNIQUE,
|
||||||
c3 POINT NOT NULL DEFAULT ST_GeomFromText('POINT(3 4)'),
|
c3 POINT NOT NULL DEFAULT ST_GeomFromText('POINT(3 4)'),
|
||||||
@ -806,6 +816,16 @@ clust_index_size
|
|||||||
connection default;
|
connection default;
|
||||||
InnoDB 0 transactions not purged
|
InnoDB 0 transactions not purged
|
||||||
DROP TABLE t1,t2,t3,t4,big;
|
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
|
CREATE TABLE t1
|
||||||
(id INT PRIMARY KEY, c2 INT UNIQUE,
|
(id INT PRIMARY KEY, c2 INT UNIQUE,
|
||||||
c3 POINT NOT NULL DEFAULT ST_GeomFromText('POINT(3 4)'),
|
c3 POINT NOT NULL DEFAULT ST_GeomFromText('POINT(3 4)'),
|
||||||
@ -1182,10 +1202,20 @@ clust_index_size
|
|||||||
connection default;
|
connection default;
|
||||||
InnoDB 0 transactions not purged
|
InnoDB 0 transactions not purged
|
||||||
DROP TABLE t1,t2,t3,t4,big;
|
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;
|
disconnect analyze;
|
||||||
SELECT variable_value-@old_instant instants
|
SELECT variable_value-@old_instant instants
|
||||||
FROM information_schema.global_status
|
FROM information_schema.global_status
|
||||||
WHERE variable_name = 'innodb_instant_alter_column';
|
WHERE variable_name = 'innodb_instant_alter_column';
|
||||||
instants
|
instants
|
||||||
33
|
36
|
||||||
SET GLOBAL innodb_purge_rseg_truncate_frequency= @saved_frequency;
|
SET GLOBAL innodb_purge_rseg_truncate_frequency= @saved_frequency;
|
||||||
|
@ -301,6 +301,16 @@ connection default;
|
|||||||
--source include/wait_all_purged.inc
|
--source include/wait_all_purged.inc
|
||||||
DROP TABLE t1,t2,t3,t4,big;
|
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;
|
dec $format;
|
||||||
}
|
}
|
||||||
disconnect analyze;
|
disconnect analyze;
|
||||||
|
@ -344,9 +344,6 @@ ordinary:
|
|||||||
/* We would have !index->is_instant() when rolling back
|
/* We would have !index->is_instant() when rolling back
|
||||||
an instant ADD COLUMN operation. */
|
an instant ADD COLUMN operation. */
|
||||||
nulls -= REC_N_NEW_EXTRA_BYTES;
|
nulls -= REC_N_NEW_EXTRA_BYTES;
|
||||||
if (rec_offs_n_fields(offsets) <= n_fields) {
|
|
||||||
goto ordinary;
|
|
||||||
}
|
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case REC_LEAF_TEMP_COLUMNS_ADDED:
|
case REC_LEAF_TEMP_COLUMNS_ADDED:
|
||||||
ut_ad(index->is_instant());
|
ut_ad(index->is_instant());
|
||||||
@ -1851,6 +1848,7 @@ rec_copy_prefix_to_buf(
|
|||||||
ulint null_mask;
|
ulint null_mask;
|
||||||
bool is_rtr_node_ptr = false;
|
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));
|
ut_ad(index->n_core_null_bytes <= UT_BITS_IN_BYTES(index->n_nullable));
|
||||||
UNIV_PREFETCH_RW(*buf);
|
UNIV_PREFETCH_RW(*buf);
|
||||||
|
|
||||||
@ -1863,21 +1861,11 @@ rec_copy_prefix_to_buf(
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (rec_get_status(rec)) {
|
switch (rec_get_status(rec)) {
|
||||||
case REC_STATUS_COLUMNS_ADDED:
|
case REC_STATUS_INFIMUM:
|
||||||
/* We would have !index->is_instant() when rolling back
|
case REC_STATUS_SUPREMUM:
|
||||||
an instant ADD COLUMN operation. */
|
/* infimum or supremum record: no sense to copy anything */
|
||||||
ut_ad(index->is_instant() || page_rec_is_default_row(rec));
|
ut_error;
|
||||||
if (n_fields >= index->n_core_fields) {
|
return(NULL);
|
||||||
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_ORDINARY:
|
case REC_STATUS_ORDINARY:
|
||||||
ut_ad(n_fields <= index->n_core_fields);
|
ut_ad(n_fields <= index->n_core_fields);
|
||||||
nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1);
|
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);
|
nulls = rec - (REC_N_NEW_EXTRA_BYTES + 1);
|
||||||
lens = nulls - index->n_core_null_bytes;
|
lens = nulls - index->n_core_null_bytes;
|
||||||
break;
|
break;
|
||||||
case REC_STATUS_INFIMUM:
|
case REC_STATUS_COLUMNS_ADDED:
|
||||||
case REC_STATUS_SUPREMUM:
|
/* We would have !index->is_instant() when rolling back
|
||||||
/* infimum or supremum record: no sense to copy anything */
|
an instant ADD COLUMN operation. */
|
||||||
ut_error;
|
ut_ad(index->is_instant() || page_rec_is_default_row(rec));
|
||||||
return(NULL);
|
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);
|
UNIV_PREFETCH_R(lens);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user