Commit 97db6c15 authored by Nikita Malyavin's avatar Nikita Malyavin

MDEV-20618 Assertion failed in row_upd_sec_index_entry

Add a proper error handling of innobase_get_computed_value results in
row_upd_store_row/row_upd_store_v_row.

Also add an assertion in row_vers_build_clust_v_col to fail during row
purge.
Add one more assertion in row_sel_sec_rec_is_for_clust_rec for possible
future catches.
parent a3d66090
......@@ -747,4 +747,66 @@ ANALYZE TABLE t1, t2;
--eval $query
DROP TABLE t1, t2;
if($support_virtual_index)
{
--echo #
--echo # MDEV-20618 Assertion `btr_validate_index(index, 0, false)' failed
--echo # in row_upd_sec_index_entry
--echo #
CREATE TABLE t1 (A BIT(15), VA BIT(10) GENERATED ALWAYS AS (A),PK INT,
PRIMARY KEY (PK), UNIQUE KEY (VA));
INSERT IGNORE INTO t1 VALUES ( '\r1','a',1);
--error ER_DATA_TOO_LONG
REPLACE INTO t1 (PK) VALUES (1);
DROP TABLE t1;
--echo #
--echo # MDEV-17890 Record in index was not found on update, server crash in
--echo # row_upd_build_difference_binary or
--echo # Assertion `0' failed in row_upd_sec_index_entry
--echo #
CREATE TABLE t1 (
pk BIGINT AUTO_INCREMENT,
b BIT(15),
v BIT(10) AS (b) VIRTUAL,
PRIMARY KEY(pk),
UNIQUE(v)
);
INSERT IGNORE INTO t1 (b) VALUES (b'101110001110100'),(b'011101');
SELECT pk, b INTO OUTFILE 'load.data' FROM t1;
--error ER_DATA_TOO_LONG
LOAD DATA INFILE 'load.data' REPLACE INTO TABLE t1 (pk, b);
--let $datadir= `SELECT @@datadir`
--remove_file $datadir/test/load.data
DROP TABLE t1;
--echo #
--echo # MDEV-17834 Server crashes in row_upd_build_difference_binary
--echo # on LOAD DATA into table with indexed virtual column
--echo #
CREATE TABLE t1 (
pk INT,
i TINYINT,
ts TIMESTAMP NULL,
vi TINYINT AS (i+1) PERSISTENT,
vts TIMESTAMP(5) AS (ts) VIRTUAL,
PRIMARY KEY(pk),
UNIQUE(vts)
);
INSERT IGNORE INTO t1 (pk,i) VALUES (1,127);
--write_file $MYSQLTEST_VARDIR/tmp/load.data
1 4 2019-01-01 00:00:00
EOF
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
--error ER_WARN_DATA_OUT_OF_RANGE
eval LOAD DATA INFILE '$MYSQLTEST_VARDIR/tmp/load.data' REPLACE INTO TABLE t1 (pk,i,ts);
--remove_file $MYSQLTEST_VARDIR/tmp/load.data
DROP TABLE t1;
}
......@@ -825,6 +825,56 @@ a1 a2 b
0 NULL 1
DROP TABLE t1, t2;
#
# MDEV-20618 Assertion `btr_validate_index(index, 0, false)' failed
# in row_upd_sec_index_entry
#
CREATE TABLE t1 (A BIT(15), VA BIT(10) GENERATED ALWAYS AS (A),PK INT,
PRIMARY KEY (PK), UNIQUE KEY (VA));
INSERT IGNORE INTO t1 VALUES ( '\r1','a',1);
Warnings:
Warning 1906 The value specified for generated column 'VA' in table 't1' has been ignored
Warning 1264 Out of range value for column 'VA' at row 1
REPLACE INTO t1 (PK) VALUES (1);
ERROR 22001: Data too long for column 'VA' at row 1
DROP TABLE t1;
#
# MDEV-17890 Record in index was not found on update, server crash in
# row_upd_build_difference_binary or
# Assertion `0' failed in row_upd_sec_index_entry
#
CREATE TABLE t1 (
pk BIGINT AUTO_INCREMENT,
b BIT(15),
v BIT(10) AS (b) VIRTUAL,
PRIMARY KEY(pk),
UNIQUE(v)
);
INSERT IGNORE INTO t1 (b) VALUES (b'101110001110100'),(b'011101');
Warnings:
Warning 1264 Out of range value for column 'v' at row 1
SELECT pk, b INTO OUTFILE 'load.data' FROM t1;
LOAD DATA INFILE 'load.data' REPLACE INTO TABLE t1 (pk, b);
ERROR 22001: Data too long for column 'v' at row 1
DROP TABLE t1;
#
# MDEV-17834 Server crashes in row_upd_build_difference_binary
# on LOAD DATA into table with indexed virtual column
#
CREATE TABLE t1 (
pk INT,
i TINYINT,
ts TIMESTAMP NULL,
vi TINYINT AS (i+1) PERSISTENT,
vts TIMESTAMP(5) AS (ts) VIRTUAL,
PRIMARY KEY(pk),
UNIQUE(vts)
);
INSERT IGNORE INTO t1 (pk,i) VALUES (1,127);
Warnings:
Warning 1264 Out of range value for column 'vi' at row 1
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/load.data' REPLACE INTO TABLE t1 (pk,i,ts);
ERROR 22003: Out of range value for column 'vi' at row 1
DROP TABLE t1;
#
# BUG#21365158 WL8149:ASSERTION `!TABLE || (!TABLE->WRITE_SET
#
......
......@@ -827,6 +827,56 @@ a1 a2 b
0 NULL 1
DROP TABLE t1, t2;
#
# MDEV-20618 Assertion `btr_validate_index(index, 0, false)' failed
# in row_upd_sec_index_entry
#
CREATE TABLE t1 (A BIT(15), VA BIT(10) GENERATED ALWAYS AS (A),PK INT,
PRIMARY KEY (PK), UNIQUE KEY (VA));
INSERT IGNORE INTO t1 VALUES ( '\r1','a',1);
Warnings:
Warning 1906 The value specified for generated column 'VA' in table 't1' has been ignored
Warning 1264 Out of range value for column 'VA' at row 1
REPLACE INTO t1 (PK) VALUES (1);
ERROR 22001: Data too long for column 'VA' at row 1
DROP TABLE t1;
#
# MDEV-17890 Record in index was not found on update, server crash in
# row_upd_build_difference_binary or
# Assertion `0' failed in row_upd_sec_index_entry
#
CREATE TABLE t1 (
pk BIGINT AUTO_INCREMENT,
b BIT(15),
v BIT(10) AS (b) VIRTUAL,
PRIMARY KEY(pk),
UNIQUE(v)
);
INSERT IGNORE INTO t1 (b) VALUES (b'101110001110100'),(b'011101');
Warnings:
Warning 1264 Out of range value for column 'v' at row 1
SELECT pk, b INTO OUTFILE 'load.data' FROM t1;
LOAD DATA INFILE 'load.data' REPLACE INTO TABLE t1 (pk, b);
ERROR 22001: Data too long for column 'v' at row 1
DROP TABLE t1;
#
# MDEV-17834 Server crashes in row_upd_build_difference_binary
# on LOAD DATA into table with indexed virtual column
#
CREATE TABLE t1 (
pk INT,
i TINYINT,
ts TIMESTAMP NULL,
vi TINYINT AS (i+1) PERSISTENT,
vts TIMESTAMP(5) AS (ts) VIRTUAL,
PRIMARY KEY(pk),
UNIQUE(vts)
);
INSERT IGNORE INTO t1 (pk,i) VALUES (1,127);
Warnings:
Warning 1264 Out of range value for column 'vi' at row 1
LOAD DATA INFILE 'MYSQLTEST_VARDIR/tmp/load.data' REPLACE INTO TABLE t1 (pk,i,ts);
ERROR 22003: Out of range value for column 'vi' at row 1
DROP TABLE t1;
DROP VIEW IF EXISTS v1,v2;
DROP TABLE IF EXISTS t1,t2,t3;
DROP PROCEDURE IF EXISTS p1;
......
......@@ -456,8 +456,7 @@ select pk, col_bit+0, vcol_bit+0 from t1;
pk col_bit+0 vcol_bit+0
99 10000 1023
REPLACE LOW_PRIORITY INTO `t1` (`pk`) VALUES (99);
Warnings:
Warning 1264 Out of range value for column 'vcol_bit' at row 1
ERROR 22001: Data too long for column 'vcol_bit' at row 1
drop table t1;
#
# MDEV-17837 REPLACE on table with virtual_field can cause crash in set_ok_status()
......@@ -476,8 +475,7 @@ INSERT IGNORE INTO t1 (pk,i) VALUES (1,127);
Warnings:
Warning 1264 Out of range value for column 'vi' at row 1
REPLACE INTO t1 (pk,i) VALUES (1,2);
Warnings:
Warning 1264 Out of range value for column 'vi' at row 1
ERROR 22003: Out of range value for column 'vi' at row 1
DROP TABLE t1;
SET @sql_mode=@old_sql_mode;
#
......
......@@ -431,6 +431,7 @@ replace INTO `t1` (`pk`,col_bit) VALUES (99,1000);
select pk, col_bit+0, vcol_bit+0 from t1;
replace INTO `t1` (`pk`,col_bit) VALUES (99,10000);
select pk, col_bit+0, vcol_bit+0 from t1;
--error ER_DATA_TOO_LONG
REPLACE LOW_PRIORITY INTO `t1` (`pk`) VALUES (99);
drop table t1;
......@@ -451,6 +452,7 @@ CREATE TABLE t1 (
INSERT INTO t1 (pk,i) VALUES (1,1);
TRUNCATE TABLE t1;
INSERT IGNORE INTO t1 (pk,i) VALUES (1,127);
--error ER_WARN_DATA_OUT_OF_RANGE
REPLACE INTO t1 (pk,i) VALUES (1,2);
DROP TABLE t1;
SET @sql_mode=@old_sql_mode;
......
......@@ -1748,15 +1748,13 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
}
if (table->vfield)
{
my_bool abort_on_warning= thd->abort_on_warning;
/*
We have not yet called update_virtual_fields(VOL_UPDATE_FOR_READ)
in handler methods for the just read row in record[1].
*/
table->move_fields(table->field, table->record[1], table->record[0]);
thd->abort_on_warning= 0;
table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_REPLACE);
thd->abort_on_warning= abort_on_warning;
if (table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_REPLACE))
goto err;
table->move_fields(table->field, table->record[0], table->record[1]);
}
if (info->handle_duplicates == DUP_UPDATE)
......
......@@ -103,6 +103,7 @@ this program; if not, write to the Free Software Foundation, Inc.,
#include "srv0mon.h"
#include "srv0srv.h"
#include "srv0start.h"
#include "rem0rec.h"
#ifdef UNIV_DEBUG
#include "trx0purge.h"
#endif /* UNIV_DEBUG */
......@@ -21881,6 +21882,13 @@ void innobase_free_row_for_vcol(VCOL_STORAGE *storage)
}
void innobase_report_computed_value_failed(dtuple_t *row)
{
ib::error() << "Compute virtual column values failed for "
<< rec_printer(row).str();
}
/** Get the computed value by supplying the base column values.
@param[in,out] row the data row
@param[in] col virtual column
......@@ -22008,13 +22016,6 @@ innobase_get_computed_value(
dbug_tmp_restore_column_map(mysql_table->write_set, old_write_set);
if (ret != 0) {
// FIXME: Why this error message is macro-hidden?
#ifdef INNODB_VIRTUAL_DEBUG
ib::warn() << "Compute virtual column values failed ";
fputs("InnoDB: Cannot compute value for following record ",
stderr);
dtuple_print(stderr, row);
#endif /* INNODB_VIRTUAL_DEBUG */
DBUG_RETURN(NULL);
}
......
......@@ -874,6 +874,12 @@ class ib_vcol_row
}
};
/** Report virtual value computation failure in ib::error
@param[in] row the data row
*/
ATTRIBUTE_COLD
void innobase_report_computed_value_failed(dtuple_t *row);
/** Get the computed value by supplying the base column values.
@param[in,out] row the data row
@param[in] col virtual column
......
......@@ -154,11 +154,15 @@ fields are compared with collation!
must be protected by a page s-latch
@param[in] clust_index clustered index
@param[in] thr query thread
@return TRUE if the secondary record is equal to the corresponding
fields in the clustered record, when compared with collation;
FALSE if not equal or if the clustered record has been marked for deletion */
@retval DB_COMPUTE_VALUE_FAILED in case of virtual column value computation
failure.
@retval DB_SUCCESS_LOCKED_REC if the secondary record is equal to the
corresponding fields in the clustered record, when compared with
collation;
@retval DB_SUCCESS if not equal or if the clustered record has been marked
for deletion */
static
ibool
dberr_t
row_sel_sec_rec_is_for_clust_rec(
const rec_t* sec_rec,
dict_index_t* sec_index,
......@@ -190,7 +194,7 @@ row_sel_sec_rec_is_for_clust_rec(
it is not visible in the read view. Besides,
if there are any externally stored columns,
some of them may have already been purged. */
return(FALSE);
return DB_SUCCESS;
}
heap = mem_heap_create(256);
......@@ -242,6 +246,10 @@ row_sel_sec_rec_is_for_clust_rec(
thr->prebuilt->m_mysql_table,
record, NULL, NULL, NULL);
if (vfield == NULL) {
innobase_report_computed_value_failed(row);
return DB_COMPUTE_VALUE_FAILED;
}
clust_len = vfield->len;
clust_field = static_cast<byte*>(vfield->data);
} else {
......@@ -275,7 +283,7 @@ row_sel_sec_rec_is_for_clust_rec(
sec_field, sec_len,
ifield->prefix_len,
clust_index->table)) {
return FALSE;
return DB_SUCCESS;
}
continue;
......@@ -311,19 +319,19 @@ row_sel_sec_rec_is_for_clust_rec(
rtr_read_mbr(sec_field, &sec_mbr);
if (!MBR_EQUAL_CMP(&sec_mbr, &tmp_mbr)) {
return FALSE;
return DB_SUCCESS;
}
} else {
if (0 != cmp_data_data(col->mtype, col->prtype,
clust_field, len,
sec_field, sec_len)) {
return FALSE;
return DB_SUCCESS;
}
}
}
return TRUE;
return DB_SUCCESS_LOCKED_REC;
}
/*********************************************************************//**
......@@ -890,7 +898,7 @@ row_sel_get_clust_rec(
dict_index_t* index;
rec_t* clust_rec;
rec_t* old_vers;
dberr_t err;
dberr_t err = DB_SUCCESS;
mem_heap_t* heap = NULL;
rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
rec_offs* offsets = offsets_;
......@@ -932,7 +940,7 @@ row_sel_get_clust_rec(
clustered index record did not exist in the read view of
trx. */
goto func_exit;
goto err_exit;
}
offsets = rec_get_offsets(clust_rec, index, offsets, true,
......@@ -997,7 +1005,7 @@ row_sel_get_clust_rec(
clust_rec = old_vers;
if (clust_rec == NULL) {
goto func_exit;
goto err_exit;
}
}
......@@ -1014,13 +1022,14 @@ row_sel_get_clust_rec(
visit through secondary index records that would not really
exist in our snapshot. */
if ((old_vers
|| rec_get_deleted_flag(rec, dict_table_is_comp(
plan->table)))
&& !row_sel_sec_rec_is_for_clust_rec(rec, plan->index,
clust_rec, index,
thr)) {
goto func_exit;
if (old_vers || rec_get_deleted_flag(rec, dict_table_is_comp(
plan->table))) {
err = row_sel_sec_rec_is_for_clust_rec(rec,
plan->index, clust_rec,
index, thr);
if (err != DB_SUCCESS_LOCKED_REC) {
goto err_exit;
}
}
}
......@@ -1033,7 +1042,6 @@ row_sel_get_clust_rec(
row_sel_fetch_columns(index, clust_rec, offsets,
UT_LIST_GET_FIRST(plan->columns));
*out_rec = clust_rec;
func_exit:
err = DB_SUCCESS;
err_exit:
if (UNIV_LIKELY_NULL(heap)) {
......@@ -3565,10 +3573,18 @@ Row_sel_get_clust_rec_for_mysql::operator()(
|| trx->isolation_level <= TRX_ISO_READ_UNCOMMITTED
|| dict_index_is_spatial(sec_index)
|| rec_get_deleted_flag(rec, dict_table_is_comp(
sec_index->table)))
&& !row_sel_sec_rec_is_for_clust_rec(
rec, sec_index, clust_rec, clust_index, thr)) {
clust_rec = NULL;
sec_index->table)))) {
err = row_sel_sec_rec_is_for_clust_rec(rec, sec_index,
clust_rec, clust_index, thr);
switch (err) {
case DB_SUCCESS:
clust_rec = NULL;
break;
case DB_SUCCESS_LOCKED_REC:
break;
default:
goto err_exit;
}
}
err = DB_SUCCESS;
......
......@@ -2101,9 +2101,11 @@ row_upd_eval_new_vals(
@param[in,out] node row update node
@param[in] update an update vector if it is update
@param[in] thd mysql thread handle
@param[in,out] mysql_table mysql table object */
@param[in,out] mysql_table mysql table object
@return true if success
false if virtual column value computation fails. */
static
void
bool
row_upd_store_v_row(
upd_node_t* node,
const upd_t* update,
......@@ -2165,24 +2167,33 @@ row_upd_store_v_row(
&mysql_table);
/* Need to compute, this happens when
deleting row */
innobase_get_computed_value(
node->row, col, index,
&vc.heap, node->heap,
NULL, thd, mysql_table,
record, NULL, NULL, NULL);
dfield_t* vfield =
innobase_get_computed_value(
node->row, col, index,
&vc.heap, node->heap,
NULL, thd, mysql_table,
record, NULL, NULL,
NULL);
if (vfield == NULL) {
return false;
}
}
}
}
}
return true;
}
/** Stores to the heap the row on which the node->pcur is positioned.
@param[in] node row update node
@param[in] thd mysql thread handle
@param[in,out] mysql_table NULL, or mysql table object when
user thread invokes dml */
user thread invokes dml
@return false if virtual column value computation fails
true otherwise. */
static
void
bool
row_upd_store_row(
upd_node_t* node,
THD* thd,
......@@ -2226,8 +2237,12 @@ row_upd_store_row(
NULL, NULL, NULL, ext, node->heap);
if (node->table->n_v_cols) {
row_upd_store_v_row(node, node->is_delete ? NULL : node->update,
bool ok = row_upd_store_v_row(node,
node->is_delete ? NULL : node->update,
thd, mysql_table);
if (!ok) {
return false;
}
}
if (node->is_delete) {
......@@ -2242,6 +2257,7 @@ row_upd_store_row(
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
return true;
}
/***********************************************************//**
......@@ -2957,9 +2973,12 @@ row_upd_del_mark_clust_rec(
/* Store row because we have to build also the secondary index
entries */
row_upd_store_row(node, trx->mysql_thd,
if (!row_upd_store_row(node, trx->mysql_thd,
thr->prebuilt && thr->prebuilt->table == node->table
? thr->prebuilt->m_mysql_table : NULL);
? thr->prebuilt->m_mysql_table : NULL)) {
err = DB_COMPUTE_VALUE_FAILED;
return err;
}
/* Mark the clustered index record deleted; we do not have to check
locks, because we assume that we have an x-lock on the record */
......@@ -3168,8 +3187,11 @@ row_upd_clust_step(
goto exit_func;
}
row_upd_store_row(node, trx->mysql_thd,
thr->prebuilt ? thr->prebuilt->m_mysql_table : NULL);
if(!row_upd_store_row(node, trx->mysql_thd,
thr->prebuilt ? thr->prebuilt->m_mysql_table : NULL)) {
err = DB_COMPUTE_VALUE_FAILED;
goto exit_func;
}
if (row_upd_changes_ord_field_binary(index, node->update, thr,
node->row, node->ext)) {
......
......@@ -445,9 +445,11 @@ row_vers_must_preserve_del_marked(
@param[in] clust_index clustered index
@param[in] index the secondary index
@param[in] heap heap used to build virtual dtuple
@param[in,out] vcol_info virtual column information. */
@param[in,out] vcol_info virtual column information.
@return true in case of success
false if virtual column computation fails */
static
void
bool
row_vers_build_clust_v_col(
dtuple_t* row,
dict_index_t* clust_index,
......@@ -471,7 +473,8 @@ row_vers_build_clust_v_col(
if (vcol_info && !vcol_info->table()) {
vcol_info->set_table(maria_table);
return;
// wait for second fetch
return true;
}
for (ulint i = 0; i < dict_index_get_n_fields(index); i++) {
......@@ -484,12 +487,18 @@ row_vers_build_clust_v_col(
col = reinterpret_cast<const dict_v_col_t*>(
ind_field->col);
innobase_get_computed_value(
dfield_t *vfield = innobase_get_computed_value(
row, col, clust_index, &vc.heap,
heap, NULL, thd, maria_table, record, NULL,
NULL, NULL);
if (vfield == NULL) {
innobase_report_computed_value_failed(row);
ut_ad(0);
return false;
}
}
}
return true;
}
/** Build latest virtual column data from undo log
......@@ -826,8 +835,11 @@ row_vers_build_cur_vrow(
mtr->commit();
}
row_vers_build_clust_v_col(
bool res = row_vers_build_clust_v_col(
row, clust_index, index, heap, vcol_info);
if (!res) {
return NULL;
}
if (vcol_info != NULL && vcol_info->is_first_fetch()) {
return NULL;
......@@ -948,10 +960,14 @@ row_vers_old_has_index_entry(
mtr->commit();
}
row_vers_build_clust_v_col(
bool res = row_vers_build_clust_v_col(
row, clust_index, index, heap,
vcol_info);
if (!res) {
goto unsafe_to_purge;
}
if (vcol_info && vcol_info->is_first_fetch()) {
goto unsafe_to_purge;
}
......
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