diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2018-09-14 15:06:58 +0300 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2018-09-14 15:06:58 +0300 |
commit | aba5c72be28cbd3028623070b7bf3d7d1e128be1 (patch) | |
tree | 480989f3f9422c1ab2973b6e11b780be7e5c16dd | |
parent | ed49f9aae2ad4f8d89294e91fba6a9ec704ded49 (diff) | |
download | mariadb-git-aba5c72be28cbd3028623070b7bf3d7d1e128be1.tar.gz |
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.
-rw-r--r-- | mysql-test/suite/innodb/r/instant_alter.result | 20 | ||||
-rw-r--r-- | mysql-test/suite/innodb/t/instant_alter.test | 3 | ||||
-rw-r--r-- | storage/innobase/btr/btr0cur.cc | 1 | ||||
-rw-r--r-- | storage/innobase/handler/handler0alter.cc | 27 |
4 files changed, 40 insertions, 11 deletions
diff --git a/mysql-test/suite/innodb/r/instant_alter.result b/mysql-test/suite/innodb/r/instant_alter.result index bb15b665d49..2f56d0eca4f 100644 --- a/mysql-test/suite/innodb/r/instant_alter.result +++ b/mysql-test/suite/innodb/r/instant_alter.result @@ -464,6 +464,12 @@ ALGORITHM=INSTANT; SET foreign_key_checks=1; ALTER TABLE t2 COMMENT 'domestic keys only', DROP FOREIGN KEY fk; 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; CREATE TABLE t1 (id INT PRIMARY KEY, c2 INT UNIQUE, @@ -875,6 +881,12 @@ ALGORITHM=INSTANT; SET foreign_key_checks=1; ALTER TABLE t2 COMMENT 'domestic keys only', DROP FOREIGN KEY fk; 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; CREATE TABLE t1 (id INT PRIMARY KEY, c2 INT UNIQUE, @@ -1286,11 +1298,17 @@ ALGORITHM=INSTANT; SET foreign_key_checks=1; ALTER TABLE t2 COMMENT 'domestic keys only', DROP FOREIGN KEY fk; 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; disconnect analyze; SELECT variable_value-@old_instant instants FROM information_schema.global_status WHERE variable_name = 'innodb_instant_alter_column'; instants -45 +48 SET GLOBAL innodb_purge_rseg_truncate_frequency= @saved_frequency; diff --git a/mysql-test/suite/innodb/t/instant_alter.test b/mysql-test/suite/innodb/t/instant_alter.test index 18c07bdac73..720e495e3a7 100644 --- a/mysql-test/suite/innodb/t/instant_alter.test +++ b/mysql-test/suite/innodb/t/instant_alter.test @@ -338,6 +338,9 @@ ALGORITHM=INSTANT; SET foreign_key_checks=1; ALTER TABLE t2 COMMENT 'domestic keys only', DROP FOREIGN KEY fk; 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; dec $format; diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index 3d8d08573f7..90d795f2596 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -4764,6 +4764,7 @@ btr_cur_pessimistic_update( know the size of the freed record. */ btr_page_reorganize(page_cursor, index, mtr); rec = page_cursor->rec; + rec_offs_make_valid(rec, index, true, *offsets); } else if (!dict_table_is_locking_disabled(index->table)) { lock_rec_restore_from_page_infimum( btr_cur_get_block(cursor), rec, block); diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index b050a045409..6a3ad4bd2ab 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -4379,6 +4379,7 @@ innobase_add_instant_try( que_thr_t* thr = pars_complete_graph_for_exec( NULL, trx, ctx->heap, NULL); + dberr_t err; if (rec_is_default_row(rec, index)) { ut_ad(page_rec_is_user_rec(rec)); if (!page_has_next(block->frame) @@ -4403,14 +4404,15 @@ innobase_add_instant_try( ulint* offsets = NULL; mem_heap_t* offsets_heap = NULL; big_rec_t* big_rec; - dberr_t error = btr_cur_pessimistic_update( - BTR_NO_LOCKING_FLAG, btr_pcur_get_btr_cur(&pcur), + err = btr_cur_pessimistic_update( + BTR_NO_LOCKING_FLAG | BTR_KEEP_POS_FLAG, + btr_pcur_get_btr_cur(&pcur), &offsets, &offsets_heap, ctx->heap, &big_rec, update, UPD_NODE_NO_ORD_CHANGE, thr, trx->id, &mtr); if (big_rec) { - if (error == DB_SUCCESS) { - error = btr_store_big_rec_extern_fields( + if (err == DB_SUCCESS) { + err = btr_store_big_rec_extern_fields( &pcur, offsets, big_rec, &mtr, BTR_STORE_UPDATE); } @@ -4421,8 +4423,7 @@ innobase_add_instant_try( mem_heap_free(offsets_heap); } btr_pcur_close(&pcur); - mtr.commit(); - return error != DB_SUCCESS; + goto func_exit; } else if (page_rec_is_supremum(rec)) { empty_table: /* The table is empty. */ @@ -4438,7 +4439,6 @@ empty_table: mtr.commit(); mtr.start(); index->set_modified(mtr); - dberr_t err; if (page_t* root = btr_root_get(index, &mtr)) { switch (fil_page_get_type(root)) { case FIL_PAGE_TYPE_INSTANT: @@ -4451,8 +4451,7 @@ empty_table: break; default: DBUG_ASSERT(!"wrong page type"); - mtr.commit(); - return true; + goto func_exit; } mlog_write_ulint(root + FIL_PAGE_TYPE, @@ -4469,8 +4468,16 @@ empty_table: err = DB_CORRUPTION; } +func_exit: 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 |