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

MDEV-17196 Crash during instant ADD COLUMN with long DEFAULT value

A debug assertion would fail if an instant ADD COLUMN operation
involves splitting the leftmost leaf page and storing a default
value off-page. Another debug assertion could fail if the
default value does not fit in an undo log page.

btr_cur_pessimistic_update(): Invoke rec_offs_make_valid()
in order to prevent rec_offs_validate() assertion failure.

innobase_add_instant_try(): Invoke btr_cur_pessimistic_update()
with the BTR_KEEP_POS_FLAG, which is the correct course of action
when BLOBs may need to be written. Whenever returning true,
ensure that my_error() will have been called.
parent ed49f9aa
...@@ -464,6 +464,12 @@ ALGORITHM=INSTANT; ...@@ -464,6 +464,12 @@ ALGORITHM=INSTANT;
SET foreign_key_checks=1; SET foreign_key_checks=1;
ALTER TABLE t2 COMMENT 'domestic keys only', DROP FOREIGN KEY fk; ALTER TABLE t2 COMMENT 'domestic keys only', DROP FOREIGN KEY fk;
ALTER TABLE t1 DROP FOREIGN KEY t1_ibfk_1; ALTER TABLE t1 DROP FOREIGN KEY t1_ibfk_1;
ALTER TABLE t1 ADD COLUMN big BLOB NOT NULL
DEFAULT REPEAT('a', @@GLOBAL.innodb_page_size * .75);
CHECK TABLE t2, t1;
Table Op Msg_type Msg_text
test.t2 check status OK
test.t1 check status OK
DROP TABLE t2, t1; DROP TABLE t2, t1;
CREATE TABLE t1 CREATE TABLE t1
(id INT PRIMARY KEY, c2 INT UNIQUE, (id INT PRIMARY KEY, c2 INT UNIQUE,
...@@ -875,6 +881,12 @@ ALGORITHM=INSTANT; ...@@ -875,6 +881,12 @@ ALGORITHM=INSTANT;
SET foreign_key_checks=1; SET foreign_key_checks=1;
ALTER TABLE t2 COMMENT 'domestic keys only', DROP FOREIGN KEY fk; ALTER TABLE t2 COMMENT 'domestic keys only', DROP FOREIGN KEY fk;
ALTER TABLE t1 DROP FOREIGN KEY t1_ibfk_1; ALTER TABLE t1 DROP FOREIGN KEY t1_ibfk_1;
ALTER TABLE t1 ADD COLUMN big BLOB NOT NULL
DEFAULT REPEAT('a', @@GLOBAL.innodb_page_size * .75);
CHECK TABLE t2, t1;
Table Op Msg_type Msg_text
test.t2 check status OK
test.t1 check status OK
DROP TABLE t2, t1; DROP TABLE t2, t1;
CREATE TABLE t1 CREATE TABLE t1
(id INT PRIMARY KEY, c2 INT UNIQUE, (id INT PRIMARY KEY, c2 INT UNIQUE,
...@@ -1286,11 +1298,17 @@ ALGORITHM=INSTANT; ...@@ -1286,11 +1298,17 @@ ALGORITHM=INSTANT;
SET foreign_key_checks=1; SET foreign_key_checks=1;
ALTER TABLE t2 COMMENT 'domestic keys only', DROP FOREIGN KEY fk; ALTER TABLE t2 COMMENT 'domestic keys only', DROP FOREIGN KEY fk;
ALTER TABLE t1 DROP FOREIGN KEY t1_ibfk_1; ALTER TABLE t1 DROP FOREIGN KEY t1_ibfk_1;
ALTER TABLE t1 ADD COLUMN big BLOB NOT NULL
DEFAULT REPEAT('a', @@GLOBAL.innodb_page_size * .75);
CHECK TABLE t2, t1;
Table Op Msg_type Msg_text
test.t2 check status OK
test.t1 check status OK
DROP TABLE t2, t1; DROP TABLE t2, 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
45 48
SET GLOBAL innodb_purge_rseg_truncate_frequency= @saved_frequency; SET GLOBAL innodb_purge_rseg_truncate_frequency= @saved_frequency;
...@@ -338,6 +338,9 @@ ALGORITHM=INSTANT; ...@@ -338,6 +338,9 @@ ALGORITHM=INSTANT;
SET foreign_key_checks=1; SET foreign_key_checks=1;
ALTER TABLE t2 COMMENT 'domestic keys only', DROP FOREIGN KEY fk; ALTER TABLE t2 COMMENT 'domestic keys only', DROP FOREIGN KEY fk;
ALTER TABLE t1 DROP FOREIGN KEY t1_ibfk_1; ALTER TABLE t1 DROP FOREIGN KEY t1_ibfk_1;
ALTER TABLE t1 ADD COLUMN big BLOB NOT NULL
DEFAULT REPEAT('a', @@GLOBAL.innodb_page_size * .75);
CHECK TABLE t2, t1;
DROP TABLE t2, t1; DROP TABLE t2, t1;
dec $format; dec $format;
......
...@@ -4764,6 +4764,7 @@ btr_cur_pessimistic_update( ...@@ -4764,6 +4764,7 @@ btr_cur_pessimistic_update(
know the size of the freed record. */ know the size of the freed record. */
btr_page_reorganize(page_cursor, index, mtr); btr_page_reorganize(page_cursor, index, mtr);
rec = page_cursor->rec; rec = page_cursor->rec;
rec_offs_make_valid(rec, index, true, *offsets);
} else if (!dict_table_is_locking_disabled(index->table)) { } else if (!dict_table_is_locking_disabled(index->table)) {
lock_rec_restore_from_page_infimum( lock_rec_restore_from_page_infimum(
btr_cur_get_block(cursor), rec, block); btr_cur_get_block(cursor), rec, block);
......
...@@ -4379,6 +4379,7 @@ innobase_add_instant_try( ...@@ -4379,6 +4379,7 @@ innobase_add_instant_try(
que_thr_t* thr = pars_complete_graph_for_exec( que_thr_t* thr = pars_complete_graph_for_exec(
NULL, trx, ctx->heap, NULL); NULL, trx, ctx->heap, NULL);
dberr_t err;
if (rec_is_default_row(rec, index)) { if (rec_is_default_row(rec, index)) {
ut_ad(page_rec_is_user_rec(rec)); ut_ad(page_rec_is_user_rec(rec));
if (!page_has_next(block->frame) if (!page_has_next(block->frame)
...@@ -4403,14 +4404,15 @@ innobase_add_instant_try( ...@@ -4403,14 +4404,15 @@ innobase_add_instant_try(
ulint* offsets = NULL; ulint* offsets = NULL;
mem_heap_t* offsets_heap = NULL; mem_heap_t* offsets_heap = NULL;
big_rec_t* big_rec; big_rec_t* big_rec;
dberr_t error = btr_cur_pessimistic_update( err = btr_cur_pessimistic_update(
BTR_NO_LOCKING_FLAG, btr_pcur_get_btr_cur(&pcur), BTR_NO_LOCKING_FLAG | BTR_KEEP_POS_FLAG,
btr_pcur_get_btr_cur(&pcur),
&offsets, &offsets_heap, ctx->heap, &offsets, &offsets_heap, ctx->heap,
&big_rec, update, UPD_NODE_NO_ORD_CHANGE, &big_rec, update, UPD_NODE_NO_ORD_CHANGE,
thr, trx->id, &mtr); thr, trx->id, &mtr);
if (big_rec) { if (big_rec) {
if (error == DB_SUCCESS) { if (err == DB_SUCCESS) {
error = btr_store_big_rec_extern_fields( err = btr_store_big_rec_extern_fields(
&pcur, offsets, big_rec, &mtr, &pcur, offsets, big_rec, &mtr,
BTR_STORE_UPDATE); BTR_STORE_UPDATE);
} }
...@@ -4421,8 +4423,7 @@ innobase_add_instant_try( ...@@ -4421,8 +4423,7 @@ innobase_add_instant_try(
mem_heap_free(offsets_heap); mem_heap_free(offsets_heap);
} }
btr_pcur_close(&pcur); btr_pcur_close(&pcur);
mtr.commit(); goto func_exit;
return error != DB_SUCCESS;
} else if (page_rec_is_supremum(rec)) { } else if (page_rec_is_supremum(rec)) {
empty_table: empty_table:
/* The table is empty. */ /* The table is empty. */
...@@ -4438,7 +4439,6 @@ innobase_add_instant_try( ...@@ -4438,7 +4439,6 @@ innobase_add_instant_try(
mtr.commit(); mtr.commit();
mtr.start(); mtr.start();
index->set_modified(mtr); index->set_modified(mtr);
dberr_t err;
if (page_t* root = btr_root_get(index, &mtr)) { if (page_t* root = btr_root_get(index, &mtr)) {
switch (fil_page_get_type(root)) { switch (fil_page_get_type(root)) {
case FIL_PAGE_TYPE_INSTANT: case FIL_PAGE_TYPE_INSTANT:
...@@ -4451,8 +4451,7 @@ innobase_add_instant_try( ...@@ -4451,8 +4451,7 @@ innobase_add_instant_try(
break; break;
default: default:
DBUG_ASSERT(!"wrong page type"); DBUG_ASSERT(!"wrong page type");
mtr.commit(); goto func_exit;
return true;
} }
mlog_write_ulint(root + FIL_PAGE_TYPE, mlog_write_ulint(root + FIL_PAGE_TYPE,
...@@ -4469,8 +4468,16 @@ innobase_add_instant_try( ...@@ -4469,8 +4468,16 @@ innobase_add_instant_try(
err = DB_CORRUPTION; err = DB_CORRUPTION;
} }
func_exit:
mtr.commit(); mtr.commit();
return err != DB_SUCCESS;
if (err != DB_SUCCESS) {
my_error_innodb(err, table->s->table_name.str,
user_table->flags);
return true;
}
return false;
} }
/** Update INNODB SYS_COLUMNS on new virtual column's position /** Update INNODB SYS_COLUMNS on new virtual column's position
......
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