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

MDEV-13818 CREATE INDEX leaks memory if running out of undo log space

row_merge_create_index_graph(): Relay the internal state
from dict_create_index_step(). Our caller should free the index
only if it was not copied, added to the cache, and freed.

row_merge_create_index(): Free the index template if it was
not added to the cache. This is a safer variant of the logic
that was introduced in 65070bef in 10.2.

prepare_inplace_alter_table_dict(): Add additional fault injection
to exercise a code path where we have already added an index
to the cache.
parent 1a5028f4
......@@ -3,6 +3,9 @@
#
CREATE TABLE t1(c1 INT PRIMARY KEY, c2 CHAR(1), c3 INT UNSIGNED) ENGINE=InnoDB;
SET @saved_debug_dbug = @@SESSION.debug_dbug;
SET DEBUG_DBUG='+d,create_index_metadata_fail';
ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3);
ERROR HY000: The table 't1' is full
SET DEBUG_DBUG='+d,ib_create_table_fail_too_many_trx';
ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3);
ERROR HY000: Too many active concurrent transactions
......@@ -11,21 +14,21 @@ ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3);
SET DEBUG_DBUG='+d,dict_set_index_corrupted';
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check Warning InnoDB: Index c2 is marked as corrupted
test.t1 check Warning InnoDB: Index c3 is marked as corrupted
test.t1 check Warning InnoDB: Index "c2" is marked as corrupted
test.t1 check Warning InnoDB: Index "c3" is marked as corrupted
test.t1 check error Corrupt
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check Warning InnoDB: Index c2 is marked as corrupted
test.t1 check Warning InnoDB: Index c3 is marked as corrupted
test.t1 check Warning InnoDB: Index "c2" is marked as corrupted
test.t1 check Warning InnoDB: Index "c3" is marked as corrupted
test.t1 check error Corrupt
ALTER TABLE t1 DROP INDEX c2;
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check Warning InnoDB: Index c3 is marked as corrupted
test.t1 check Warning InnoDB: Index "c3" is marked as corrupted
test.t1 check error Corrupt
ALTER TABLE t1 ADD INDEX (c2,c3);
ERROR HY000: Index c3 is corrupted
ERROR HY000: Index "c3" is corrupted
ALTER TABLE t1 CHANGE c3 c3 INT NOT NULL;
CHECK TABLE t1;
Table Op Msg_type Msg_text
......
......@@ -7,10 +7,7 @@
--source include/not_crashrep.inc
--disable_query_log
call mtr.add_suppression('InnoDB: cannot find a free slot for an undo log');
call mtr.add_suppression('InnoDB: row_merge_rename_index_to_add failed with error 47');
call mtr.add_suppression('InnoDB: Flagged corruption of `c[23]`');
call mtr.add_suppression('InnoDB: Index `c[23]` .*is corrupted');
call mtr.add_suppression('InnoDB: Flagged corruption of c[23]');
--enable_query_log
--echo #
......@@ -19,6 +16,10 @@ call mtr.add_suppression('InnoDB: Index `c[23]` .*is corrupted');
CREATE TABLE t1(c1 INT PRIMARY KEY, c2 CHAR(1), c3 INT UNSIGNED) ENGINE=InnoDB;
SET @saved_debug_dbug = @@SESSION.debug_dbug;
SET DEBUG_DBUG='+d,create_index_metadata_fail';
--error ER_RECORD_FILE_FULL
ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3);
SET DEBUG_DBUG='+d,ib_create_table_fail_too_many_trx';
--error ER_TOO_MANY_CONCURRENT_TRXS
ALTER TABLE t1 ADD INDEX (c2), ADD INDEX (c3);
......
......@@ -3090,10 +3090,18 @@ prepare_inplace_alter_table_dict(
/* Create the indexes in SYS_INDEXES and load into dictionary. */
for (ulint a = 0; a < ctx->num_to_add_index; a++) {
DBUG_EXECUTE_IF(
"create_index_metadata_fail",
if (a + 1 == ctx->num_to_add_index) {
ctx->trx->error_state = DB_OUT_OF_FILE_SPACE;
ctx->add_index[a] = NULL;
goto index_created;
});
ctx->add_index[a] = row_merge_create_index(
ctx->trx, ctx->new_table, &index_defs[a]);
#ifndef DBUG_OFF
index_created:
#endif
add_key_nums[a] = index_defs[a].key_number;
if (!ctx->add_index[a]) {
......
/*****************************************************************************
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
......@@ -84,7 +85,7 @@ ut_dbg_assertion_failed(
/** Debug assertion. Does nothing unless UNIV_DEBUG is defined. */
#define ut_ad(EXPR) ut_a(EXPR)
/** Debug statement. Does nothing unless UNIV_DEBUG is defined. */
#define ut_d(EXPR) do {EXPR;} while (0)
#define ut_d(EXPR) EXPR
#else
/** Debug assertion. Does nothing unless UNIV_DEBUG is defined. */
#define ut_ad(EXPR)
......
/*****************************************************************************
Copyright (c) 2005, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2014, 2018, MariaDB Corporation.
Copyright (c) 2014, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
......@@ -3702,7 +3702,7 @@ row_merge_create_index_graph(
/*=========================*/
trx_t* trx, /*!< in: trx */
dict_table_t* table, /*!< in: table */
dict_index_t* index) /*!< in: index */
dict_index_t*& index) /*!< in,out: index */
{
ind_node_t* node; /*!< Index creation node */
mem_heap_t* heap; /*!< Memory heap */
......@@ -3726,6 +3726,8 @@ row_merge_create_index_graph(
err = trx->error_state;
index = node->index;
que_graph_free((que_t*) que_node_get_parent(thr));
return(err);
......@@ -3767,20 +3769,21 @@ row_merge_create_index(
ifield->prefix_len);
}
ut_d(const dict_index_t* const index_template = index);
/* Add the index to SYS_INDEXES, using the index prototype. */
err = row_merge_create_index_graph(trx, table, index);
if (err == DB_SUCCESS) {
index = dict_table_get_index_on_name(table, index_def->name);
ut_a(index);
ut_ad(index != index_template);
/* Note the id of the transaction that created this
index, we use it to restrict readers from accessing
this index, to ensure read consistency. */
ut_ad(index->trx_id == trx->id);
} else {
ut_ad(!index || index == index_template);
if (index) {
dict_mem_index_free(index);
}
index = NULL;
}
......
......@@ -3097,10 +3097,18 @@ prepare_inplace_alter_table_dict(
/* Create the indexes in SYS_INDEXES and load into dictionary. */
for (ulint a = 0; a < ctx->num_to_add_index; a++) {
DBUG_EXECUTE_IF(
"create_index_metadata_fail",
if (a + 1 == ctx->num_to_add_index) {
ctx->trx->error_state = DB_OUT_OF_FILE_SPACE;
ctx->add_index[a] = NULL;
goto index_created;
});
ctx->add_index[a] = row_merge_create_index(
ctx->trx, ctx->new_table, &index_defs[a]);
#ifndef DBUG_OFF
index_created:
#endif
add_key_nums[a] = index_defs[a].key_number;
if (!ctx->add_index[a]) {
......
/*****************************************************************************
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
......@@ -84,7 +85,7 @@ ut_dbg_assertion_failed(
/** Debug assertion. Does nothing unless UNIV_DEBUG is defined. */
#define ut_ad(EXPR) ut_a(EXPR)
/** Debug statement. Does nothing unless UNIV_DEBUG is defined. */
#define ut_d(EXPR) do {EXPR;} while (0)
#define ut_d(EXPR) EXPR
#else
/** Debug assertion. Does nothing unless UNIV_DEBUG is defined. */
#define ut_ad(EXPR)
......
/*****************************************************************************
Copyright (c) 2005, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2014, 2018, MariaDB Corporation.
Copyright (c) 2014, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
......@@ -3705,7 +3705,7 @@ row_merge_create_index_graph(
/*=========================*/
trx_t* trx, /*!< in: trx */
dict_table_t* table, /*!< in: table */
dict_index_t* index) /*!< in: index */
dict_index_t*& index) /*!< in,out: index */
{
ind_node_t* node; /*!< Index creation node */
mem_heap_t* heap; /*!< Memory heap */
......@@ -3729,6 +3729,8 @@ row_merge_create_index_graph(
err = trx->error_state;
index = node->index;
que_graph_free((que_t*) que_node_get_parent(thr));
return(err);
......@@ -3770,20 +3772,21 @@ row_merge_create_index(
ifield->prefix_len);
}
ut_d(const dict_index_t* const index_template = index);
/* Add the index to SYS_INDEXES, using the index prototype. */
err = row_merge_create_index_graph(trx, table, index);
if (err == DB_SUCCESS) {
index = dict_table_get_index_on_name(table, index_def->name);
ut_a(index);
ut_ad(index != index_template);
/* Note the id of the transaction that created this
index, we use it to restrict readers from accessing
this index, to ensure read consistency. */
ut_ad(index->trx_id == trx->id);
} else {
ut_ad(!index || index == index_template);
if (index) {
dict_mem_index_free(index);
}
index = NULL;
}
......
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