Skip to content

Commit 6cedb67

Browse files
committed
MDEV-21088 Table cannot be loaded after instant ADD/DROP COLUMN
btr_cur_instant_init_low(): Accurately parse the metadata record header for ROW_FORMAT=DYNAMIC and ROW_FORMAT=COMPACT. CHAR columns used to be unnecessarily written as nonempty strings of bytes.
1 parent 1c92406 commit 6cedb67

File tree

6 files changed

+101
-27
lines changed

6 files changed

+101
-27
lines changed

mysql-test/suite/innodb/r/instant_alter,4k.rdiff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@
318318
FROM information_schema.global_status
319319
WHERE variable_name = 'innodb_instant_alter_column';
320320
instants
321-
-184
322-
+186
321+
-187
322+
+189
323323
SET GLOBAL innodb_purge_rseg_truncate_frequency= @saved_frequency;
324324
#
325325
# MDEV-18266: Changing an index comment unnecessarily rebuilds index

mysql-test/suite/innodb/r/instant_alter.result

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,14 @@ a b c
904904
1 a NULL
905905
2 bah 3
906906
DROP TABLE t1;
907+
CREATE TABLE t1(a CHAR(5) CHARACTER SET utf8 PRIMARY KEY) ENGINE=InnoDB ROW_FORMAT=REDUNDANT;
908+
INSERT INTO t1 VALUES('barf');
909+
ALTER TABLE t1 ADD b INT FIRST, ALGORITHM=INSTANT;
910+
ALTER TABLE t1 ADD vb INT AS (b);
911+
SELECT * FROM t1;
912+
b a vb
913+
NULL barf NULL
914+
DROP TABLE t1;
907915
CREATE TABLE t1
908916
(id INT PRIMARY KEY, c2 INT UNIQUE,
909917
c3 POINT NOT NULL DEFAULT ST_GeomFromText('POINT(3 4)'),
@@ -1753,6 +1761,14 @@ a b c
17531761
1 a NULL
17541762
2 bah 3
17551763
DROP TABLE t1;
1764+
CREATE TABLE t1(a CHAR(5) CHARACTER SET utf8 PRIMARY KEY) ENGINE=InnoDB ROW_FORMAT=COMPACT;
1765+
INSERT INTO t1 VALUES('barf');
1766+
ALTER TABLE t1 ADD b INT FIRST, ALGORITHM=INSTANT;
1767+
ALTER TABLE t1 ADD vb INT AS (b);
1768+
SELECT * FROM t1;
1769+
b a vb
1770+
NULL barf NULL
1771+
DROP TABLE t1;
17561772
CREATE TABLE t1
17571773
(id INT PRIMARY KEY, c2 INT UNIQUE,
17581774
c3 POINT NOT NULL DEFAULT ST_GeomFromText('POINT(3 4)'),
@@ -2602,12 +2618,20 @@ a b c
26022618
1 a NULL
26032619
2 bah 3
26042620
DROP TABLE t1;
2621+
CREATE TABLE t1(a CHAR(5) CHARACTER SET utf8 PRIMARY KEY) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
2622+
INSERT INTO t1 VALUES('barf');
2623+
ALTER TABLE t1 ADD b INT FIRST, ALGORITHM=INSTANT;
2624+
ALTER TABLE t1 ADD vb INT AS (b);
2625+
SELECT * FROM t1;
2626+
b a vb
2627+
NULL barf NULL
2628+
DROP TABLE t1;
26052629
disconnect analyze;
26062630
SELECT variable_value-@old_instant instants
26072631
FROM information_schema.global_status
26082632
WHERE variable_name = 'innodb_instant_alter_column';
26092633
instants
2610-
184
2634+
187
26112635
SET GLOBAL innodb_purge_rseg_truncate_frequency= @saved_frequency;
26122636
#
26132637
# MDEV-18266: Changing an index comment unnecessarily rebuilds index

mysql-test/suite/innodb/t/instant_alter.test

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,15 @@ INSERT INTO t1 VALUES(2,'bah',3);
790790
SELECT * FROM t1;
791791
DROP TABLE t1;
792792

793+
# MDEV-21088 Table cannot be loaded after instant ADD/DROP COLUMN
794+
eval CREATE TABLE t1(a CHAR(5) CHARACTER SET utf8 PRIMARY KEY) $engine;
795+
INSERT INTO t1 VALUES('barf');
796+
ALTER TABLE t1 ADD b INT FIRST, ALGORITHM=INSTANT;
797+
# this evicts and reloads the table definition until MDEV-17468 is fixed
798+
ALTER TABLE t1 ADD vb INT AS (b);
799+
SELECT * FROM t1;
800+
DROP TABLE t1;
801+
793802
dec $format;
794803
let $redundant_4k= 0;
795804
}

storage/innobase/btr/btr0cur.cc

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -484,14 +484,55 @@ static dberr_t btr_cur_instant_init_low(dict_index_t* index, mtr_t* mtr)
484484
/* This metadata record includes a BLOB that identifies
485485
any dropped or reordered columns. */
486486
ulint trx_id_offset = index->trx_id_offset;
487-
if (!trx_id_offset) {
488-
/* The PRIMARY KEY contains variable-length columns.
489-
For the metadata record, variable-length columns are
490-
always written with zero length. The DB_TRX_ID will
491-
start right after any fixed-length columns. */
487+
/* If !index->trx_id_offset, the PRIMARY KEY contains
488+
variable-length columns. For the metadata record,
489+
variable-length columns should be written with zero
490+
length. However, before MDEV-21088 was fixed, for
491+
variable-length encoded PRIMARY KEY column of type
492+
CHAR, we wrote more than zero bytes. That is why we
493+
must determine the actual length of each PRIMARY KEY
494+
column. The DB_TRX_ID will start right after any
495+
PRIMARY KEY columns. */
496+
ut_ad(index->n_uniq);
497+
498+
/* We cannot invoke rec_get_offsets() before
499+
index->table->deserialise_columns(). Therefore,
500+
we must duplicate some logic here. */
501+
if (trx_id_offset) {
502+
} else if (index->table->not_redundant()) {
503+
/* PRIMARY KEY columns can never be NULL.
504+
We can skip the null flag bitmap. */
505+
const byte* lens = rec - (REC_N_NEW_EXTRA_BYTES + 1)
506+
- index->n_core_null_bytes;
507+
unsigned n_add = rec_get_n_add_field(lens);
508+
ut_ad(index->n_core_fields + n_add >= index->n_fields);
509+
lens -= n_add;
510+
492511
for (uint i = index->n_uniq; i--; ) {
493-
trx_id_offset += index->fields[i].fixed_len;
512+
const dict_field_t& f = index->fields[i];
513+
unsigned len = f.fixed_len;
514+
if (!len) {
515+
len = *lens--;
516+
if ((len & 0x80)
517+
&& DATA_BIG_COL(f.col)) {
518+
/* 1exxxxxxx xxxxxxxx */
519+
len &= 0x3f;
520+
len <<= 8;
521+
len |= *lens--;
522+
}
523+
}
524+
trx_id_offset += len;
494525
}
526+
} else if (rec_get_1byte_offs_flag(rec)) {
527+
trx_id_offset = rec_1_get_field_end_info(
528+
rec, index->n_uniq - 1);
529+
ut_ad(!(trx_id_offset & REC_1BYTE_SQL_NULL_MASK));
530+
trx_id_offset &= ~REC_1BYTE_SQL_NULL_MASK;
531+
} else {
532+
trx_id_offset = rec_2_get_field_end_info(
533+
rec, index->n_uniq - 1);
534+
ut_ad(!(trx_id_offset & REC_2BYTE_SQL_NULL_MASK));
535+
trx_id_offset &= ~REC_2BYTE_SQL_NULL_MASK;
495536
}
496537

497538
const byte* ptr = rec + trx_id_offset

storage/innobase/include/rem0rec.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,24 @@ inline unsigned rec_get_n_add_field_len(ulint n_add_field)
291291
return n_add_field < 0x80 ? 1 : 2;
292292
}
293293

294+
/** Get the added field count in a REC_STATUS_INSTANT record.
295+
@param[in,out] header variable header of a REC_STATUS_INSTANT record
296+
@return number of added fields */
297+
inline unsigned rec_get_n_add_field(const byte*& header)
298+
{
299+
unsigned n_fields_add = *--header;
300+
if (n_fields_add < 0x80) {
301+
ut_ad(rec_get_n_add_field_len(n_fields_add) == 1);
302+
return n_fields_add;
303+
}
304+
305+
n_fields_add &= 0x7f;
306+
n_fields_add |= unsigned(*--header) << 7;
307+
ut_ad(n_fields_add < REC_MAX_N_FIELDS);
308+
ut_ad(rec_get_n_add_field_len(n_fields_add) == 2);
309+
return n_fields_add;
310+
}
311+
294312
/** Set the added field count in a REC_STATUS_INSTANT record.
295313
@param[in,out] header variable header of a REC_STATUS_INSTANT record
296314
@param[in] n_add number of added fields, minus 1

storage/innobase/rem/rem0rec.cc

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -231,24 +231,6 @@ rec_get_n_extern_new(
231231
return(n_extern);
232232
}
233233

234-
/** Get the added field count in a REC_STATUS_INSTANT record.
235-
@param[in,out] header variable header of a REC_STATUS_INSTANT record
236-
@return number of added fields */
237-
static inline unsigned rec_get_n_add_field(const byte*& header)
238-
{
239-
unsigned n_fields_add = *--header;
240-
if (n_fields_add < 0x80) {
241-
ut_ad(rec_get_n_add_field_len(n_fields_add) == 1);
242-
return n_fields_add;
243-
}
244-
245-
n_fields_add &= 0x7f;
246-
n_fields_add |= unsigned(*--header) << 7;
247-
ut_ad(n_fields_add < REC_MAX_N_FIELDS);
248-
ut_ad(rec_get_n_add_field_len(n_fields_add) == 2);
249-
return n_fields_add;
250-
}
251-
252234
/** Format of a leaf-page ROW_FORMAT!=REDUNDANT record */
253235
enum rec_leaf_format {
254236
/** Temporary file record */

0 commit comments

Comments
 (0)