mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-21138 Assertion col->ord_part' or
f.col->ord_part' failed in row_build_index_entry_low
First part (row0mysql.cc) fixes ins_node_set_new_row() usage workflow as it is designed to operate on empty row (see row_get_prebuilt_insert_row() for example). Second part (row0ins.cc) fixes duplicate key error in FTS_DOC_ID_INDEX since history rows must not generate entries in that index. We detect FTS_DOC_ID_INDEX by a number of attributes and skip it if the row is historical. Misc fixes: row_build_index_entry_low() does not accept non-NULL tuple for FTS index (subject assertion fails), assertion (index->type != DICT_FTS) adds code understanding. Now as historical_row is copied in row_update_vers_insert() there is no need to copy the row twice: ROW_COPY_POINTERS is used to build historical_row initially. dbug_print_rec() debug functions.
This commit is contained in:
@ -130,3 +130,22 @@ ERROR 42S02: Table 'test.xx' doesn't exist
|
|||||||
drop procedure pr;
|
drop procedure pr;
|
||||||
drop trigger tr;
|
drop trigger tr;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
#
|
||||||
|
# MDEV-21138 Assertion `col->ord_part' or `f.col->ord_part' failed in row_build_index_entry_low
|
||||||
|
#
|
||||||
|
create table t1 (
|
||||||
|
f1 int, f2 text, f3 int, fulltext (f2), key(f1), key(f3),
|
||||||
|
foreign key r (f3) references t1 (f1) on delete set null)
|
||||||
|
with system versioning engine innodb;
|
||||||
|
insert into t1 values (1, repeat('a', 8193), 1), (1, repeat('b', 8193), 1);
|
||||||
|
select f1, f3, check_row(row_start, row_end) from t1;
|
||||||
|
f1 f3 check_row(row_start, row_end)
|
||||||
|
1 1 CURRENT ROW
|
||||||
|
1 1 CURRENT ROW
|
||||||
|
delete from t1;
|
||||||
|
select f1, f3, check_row(row_start, row_end) from t1 for system_time all;
|
||||||
|
f1 f3 check_row(row_start, row_end)
|
||||||
|
1 1 HISTORICAL ROW
|
||||||
|
1 NULL ERROR: row_end == row_start
|
||||||
|
1 1 HISTORICAL ROW
|
||||||
|
drop table t1;
|
||||||
|
@ -241,6 +241,26 @@ B2 salary
|
|||||||
1 2500
|
1 2500
|
||||||
drop table t1;
|
drop table t1;
|
||||||
drop table t2;
|
drop table t2;
|
||||||
|
# Ensure FTS retains correct history
|
||||||
|
create table t1 (
|
||||||
|
x int, y text, fulltext (y),
|
||||||
|
row_start SYS_DATATYPE as row start invisible,
|
||||||
|
row_end SYS_DATATYPE as row end invisible,
|
||||||
|
period for system_time (row_start, row_end))
|
||||||
|
with system versioning engine innodb;
|
||||||
|
insert into t1 values (1, repeat('LONG', 2048));
|
||||||
|
update t1 set x= x + 1;
|
||||||
|
select x, left(y, 4), length(y), check_row(row_start, row_end) from t1 for system_time all order by x, y;
|
||||||
|
x left(y, 4) length(y) check_row(row_start, row_end)
|
||||||
|
1 LONG 8192 HISTORICAL ROW
|
||||||
|
2 LONG 8192 CURRENT ROW
|
||||||
|
update t1 set y= 'SHORT';
|
||||||
|
select x, left(y, 4), length(y), check_row(row_start, row_end) from t1 for system_time all order by x, y;
|
||||||
|
x left(y, 4) length(y) check_row(row_start, row_end)
|
||||||
|
1 LONG 8192 HISTORICAL ROW
|
||||||
|
2 LONG 8192 HISTORICAL ROW
|
||||||
|
2 SHOR 5 CURRENT ROW
|
||||||
|
drop tables t1;
|
||||||
### Issue tempesta-tech/mariadb#365, bug 7 (duplicate of historical row)
|
### Issue tempesta-tech/mariadb#365, bug 7 (duplicate of historical row)
|
||||||
create or replace table t1 (a int primary key, b int)
|
create or replace table t1 (a int primary key, b int)
|
||||||
with system versioning engine myisam;
|
with system versioning engine myisam;
|
||||||
|
@ -94,4 +94,19 @@ drop procedure pr;
|
|||||||
drop trigger tr;
|
drop trigger tr;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-21138 Assertion `col->ord_part' or `f.col->ord_part' failed in row_build_index_entry_low
|
||||||
|
--echo #
|
||||||
|
create table t1 (
|
||||||
|
f1 int, f2 text, f3 int, fulltext (f2), key(f1), key(f3),
|
||||||
|
foreign key r (f3) references t1 (f1) on delete set null)
|
||||||
|
with system versioning engine innodb;
|
||||||
|
insert into t1 values (1, repeat('a', 8193), 1), (1, repeat('b', 8193), 1);
|
||||||
|
select f1, f3, check_row(row_start, row_end) from t1;
|
||||||
|
delete from t1;
|
||||||
|
select f1, f3, check_row(row_start, row_end) from t1 for system_time all;
|
||||||
|
|
||||||
|
# cleanup
|
||||||
|
drop table t1;
|
||||||
|
|
||||||
--source suite/versioning/common_finish.inc
|
--source suite/versioning/common_finish.inc
|
||||||
|
@ -147,6 +147,21 @@ select @tmp2 = sys_trx_start as B2, salary from t2;
|
|||||||
drop table t1;
|
drop table t1;
|
||||||
drop table t2;
|
drop table t2;
|
||||||
|
|
||||||
|
--echo # Ensure FTS retains correct history
|
||||||
|
replace_result $sys_datatype_expl SYS_DATATYPE;
|
||||||
|
eval create table t1 (
|
||||||
|
x int, y text, fulltext (y),
|
||||||
|
row_start $sys_datatype_expl as row start invisible,
|
||||||
|
row_end $sys_datatype_expl as row end invisible,
|
||||||
|
period for system_time (row_start, row_end))
|
||||||
|
with system versioning engine innodb;
|
||||||
|
insert into t1 values (1, repeat('LONG', 2048));
|
||||||
|
update t1 set x= x + 1;
|
||||||
|
select x, left(y, 4), length(y), check_row(row_start, row_end) from t1 for system_time all order by x, y;
|
||||||
|
update t1 set y= 'SHORT';
|
||||||
|
select x, left(y, 4), length(y), check_row(row_start, row_end) from t1 for system_time all order by x, y;
|
||||||
|
drop tables t1;
|
||||||
|
|
||||||
--echo ### Issue tempesta-tech/mariadb#365, bug 7 (duplicate of historical row)
|
--echo ### Issue tempesta-tech/mariadb#365, bug 7 (duplicate of historical row)
|
||||||
create or replace table t1 (a int primary key, b int)
|
create or replace table t1 (a int primary key, b int)
|
||||||
with system versioning engine myisam;
|
with system versioning engine myisam;
|
||||||
|
@ -206,6 +206,7 @@ struct ins_node_t
|
|||||||
if this is NULL, entry list should be created
|
if this is NULL, entry list should be created
|
||||||
and buffers for sys fields in row allocated */
|
and buffers for sys fields in row allocated */
|
||||||
void vers_update_end(row_prebuilt_t *prebuilt, bool history_row);
|
void vers_update_end(row_prebuilt_t *prebuilt, bool history_row);
|
||||||
|
bool vers_history_row() const; /* true if 'row' is historical */
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Create an insert object.
|
/** Create an insert object.
|
||||||
|
@ -3566,6 +3566,16 @@ row_ins_get_row_from_select(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool ins_node_t::vers_history_row() const
|
||||||
|
{
|
||||||
|
if (!table->versioned())
|
||||||
|
return false;
|
||||||
|
dfield_t* row_end = dtuple_get_nth_field(row, table->vers_end);
|
||||||
|
return row_end->vers_history_row();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************//**
|
/***********************************************************//**
|
||||||
Inserts a row to a table.
|
Inserts a row to a table.
|
||||||
@return DB_SUCCESS if operation successfully completed, else error
|
@return DB_SUCCESS if operation successfully completed, else error
|
||||||
@ -3604,12 +3614,31 @@ row_ins(
|
|||||||
ut_ad(node->state == INS_NODE_INSERT_ENTRIES);
|
ut_ad(node->state == INS_NODE_INSERT_ENTRIES);
|
||||||
|
|
||||||
while (node->index != NULL) {
|
while (node->index != NULL) {
|
||||||
if (node->index->type != DICT_FTS) {
|
dict_index_t *index = node->index;
|
||||||
|
/*
|
||||||
|
We do not insert history rows into FTS_DOC_ID_INDEX because
|
||||||
|
it is unique by FTS_DOC_ID only and we do not want to add
|
||||||
|
row_end to unique key. Fulltext field works the way new
|
||||||
|
FTS_DOC_ID is created on every fulltext UPDATE, so holding only
|
||||||
|
FTS_DOC_ID for history is enough.
|
||||||
|
*/
|
||||||
|
const unsigned type = index->type;
|
||||||
|
if (index->type & DICT_FTS) {
|
||||||
|
} else if (!(type & DICT_UNIQUE) || index->n_uniq > 1
|
||||||
|
|| !node->vers_history_row()) {
|
||||||
|
|
||||||
dberr_t err = row_ins_index_entry_step(node, thr);
|
dberr_t err = row_ins_index_entry_step(node, thr);
|
||||||
|
|
||||||
if (err != DB_SUCCESS) {
|
if (err != DB_SUCCESS) {
|
||||||
DBUG_RETURN(err);
|
DBUG_RETURN(err);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
/* Unique indexes with system versioning must contain
|
||||||
|
the version end column. The only exception is a hidden
|
||||||
|
FTS_DOC_ID_INDEX that InnoDB may create on a hidden or
|
||||||
|
user-created FTS_DOC_ID column. */
|
||||||
|
ut_ad(!strcmp(index->name, FTS_DOC_ID_INDEX_NAME));
|
||||||
|
ut_ad(!strcmp(index->fields[0].name, FTS_DOC_ID_COL_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
node->index = dict_table_get_next_index(node->index);
|
node->index = dict_table_get_next_index(node->index);
|
||||||
|
@ -2104,10 +2104,18 @@ row_mysql_unfreeze_data_dictionary(
|
|||||||
@param buf Buffer to hold start time data */
|
@param buf Buffer to hold start time data */
|
||||||
void thd_get_query_start_data(THD *thd, char *buf);
|
void thd_get_query_start_data(THD *thd, char *buf);
|
||||||
|
|
||||||
/** Function restores btr_pcur_t, creates dtuple_t from rec_t,
|
/** Insert history row when evaluating foreign key referential action.
|
||||||
sets row_end = CURRENT_TIMESTAMP/trx->id, inserts it to a table and updates
|
|
||||||
table statistics.
|
1. Create new dtuple_t 'row' from node->historical_row;
|
||||||
This is used in UPDATE CASCADE/SET NULL of a system versioning table.
|
2. Update its row_end to current timestamp;
|
||||||
|
3. Insert it to a table;
|
||||||
|
4. Update table statistics.
|
||||||
|
|
||||||
|
This is used in UPDATE CASCADE/SET NULL of a system versioned referenced table.
|
||||||
|
|
||||||
|
node->historical_row: dtuple_t containing pointers of row changed by refertial
|
||||||
|
action.
|
||||||
|
|
||||||
@param[in] thr current query thread
|
@param[in] thr current query thread
|
||||||
@param[in] node a node which just updated a row in a foreign table
|
@param[in] node a node which just updated a row in a foreign table
|
||||||
@return DB_SUCCESS or some error */
|
@return DB_SUCCESS or some error */
|
||||||
@ -2119,9 +2127,16 @@ static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node)
|
|||||||
dict_table_t* table = node->table;
|
dict_table_t* table = node->table;
|
||||||
ut_ad(table->versioned());
|
ut_ad(table->versioned());
|
||||||
|
|
||||||
dtuple_t* row = node->historical_row;
|
dtuple_t* row;
|
||||||
ut_ad(row);
|
const ulint n_cols = dict_table_get_n_cols(table);
|
||||||
node->historical_row = NULL;
|
const ulint n_v_cols = dict_table_get_n_v_cols(table);
|
||||||
|
|
||||||
|
ut_ad(n_cols == dtuple_get_n_fields(node->historical_row));
|
||||||
|
ut_ad(n_v_cols == dtuple_get_n_v_fields(node->historical_row));
|
||||||
|
|
||||||
|
row = dtuple_create_with_vcol(node->historical_heap, n_cols, n_v_cols);
|
||||||
|
|
||||||
|
dict_table_copy_types(row, table);
|
||||||
|
|
||||||
ins_node_t* insert_node =
|
ins_node_t* insert_node =
|
||||||
ins_node_create(INS_DIRECT, table, node->historical_heap);
|
ins_node_create(INS_DIRECT, table, node->historical_heap);
|
||||||
@ -2134,6 +2149,22 @@ static dberr_t row_update_vers_insert(que_thr_t* thr, upd_node_t* node)
|
|||||||
insert_node->common.parent = thr;
|
insert_node->common.parent = thr;
|
||||||
ins_node_set_new_row(insert_node, row);
|
ins_node_set_new_row(insert_node, row);
|
||||||
|
|
||||||
|
ut_ad(n_cols > DATA_N_SYS_COLS);
|
||||||
|
// Exclude DB_ROW_ID, DB_TRX_ID, DB_ROLL_PTR
|
||||||
|
for (ulint i = 0; i < n_cols - DATA_N_SYS_COLS; i++) {
|
||||||
|
dfield_t *dst= dtuple_get_nth_field(row, i);
|
||||||
|
dfield_t *src= dtuple_get_nth_field(node->historical_row, i);
|
||||||
|
dfield_copy(dst, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ulint i = 0; i < n_v_cols; i++) {
|
||||||
|
dfield_t *dst= dtuple_get_nth_v_field(row, i);
|
||||||
|
dfield_t *src= dtuple_get_nth_v_field(node->historical_row, i);
|
||||||
|
dfield_copy(dst, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
node->historical_row = NULL;
|
||||||
|
|
||||||
row_end = dtuple_get_nth_field(row, table->vers_end);
|
row_end = dtuple_get_nth_field(row, table->vers_end);
|
||||||
if (dict_table_get_nth_col(table, table->vers_end)->vers_native()) {
|
if (dict_table_get_nth_col(table, table->vers_end)->vers_native()) {
|
||||||
mach_write_to_8(row_end_data, trx->id);
|
mach_write_to_8(row_end_data, trx->id);
|
||||||
|
@ -294,6 +294,8 @@ row_build_index_entry_low(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ut_ad(!(index->type & DICT_FTS));
|
||||||
|
|
||||||
if ((!ind_field || ind_field->prefix_len == 0)
|
if ((!ind_field || ind_field->prefix_len == 0)
|
||||||
&& (!dfield_is_ext(dfield)
|
&& (!dfield_is_ext(dfield)
|
||||||
|| dict_index_is_clust(index))) {
|
|| dict_index_is_clust(index))) {
|
||||||
|
@ -38,6 +38,7 @@ Created 5/11/1994 Heikki Tuuri
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "my_cpu.h"
|
#include "my_cpu.h"
|
||||||
|
#include "rem0rec.h"
|
||||||
|
|
||||||
/**********************************************************//**
|
/**********************************************************//**
|
||||||
Returns the number of milliseconds since some epoch. The
|
Returns the number of milliseconds since some epoch. The
|
||||||
@ -627,4 +628,42 @@ fatal_or_error::~fatal_or_error()
|
|||||||
|
|
||||||
} // namespace ib
|
} // namespace ib
|
||||||
|
|
||||||
|
#ifndef DBUG_OFF
|
||||||
|
const char * dbug_print_rec(const rec_t* rec, const rec_offs* offsets)
|
||||||
|
{
|
||||||
|
rec_printer r(rec, offsets);
|
||||||
|
return r.str().c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * dbug_print_rec(const rec_t* rec, ulint info, const rec_offs* offsets)
|
||||||
|
{
|
||||||
|
rec_printer r(rec, info, offsets);
|
||||||
|
return r.str().c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * dbug_print_rec(const dtuple_t* tuple)
|
||||||
|
{
|
||||||
|
rec_printer r(tuple);
|
||||||
|
return r.str().c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * dbug_print_rec(const dfield_t* field, ulint n)
|
||||||
|
{
|
||||||
|
rec_printer r(field, n);
|
||||||
|
return r.str().c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * dbug_print_rec(const rec_t* rec, dict_index_t* index)
|
||||||
|
{
|
||||||
|
rec_offs offsets_[REC_OFFS_NORMAL_SIZE];
|
||||||
|
rec_offs* offsets = offsets_;
|
||||||
|
rec_offs_init(offsets_);
|
||||||
|
mem_heap_t* tmp_heap = NULL;
|
||||||
|
offsets = rec_get_offsets(rec, index, offsets, true,
|
||||||
|
ULINT_UNDEFINED, &tmp_heap);
|
||||||
|
rec_printer r(rec, offsets);
|
||||||
|
return r.str().c_str();
|
||||||
|
}
|
||||||
|
#endif /* !DBUG_OFF */
|
||||||
|
|
||||||
#endif /* !UNIV_INNOCHECKSUM */
|
#endif /* !UNIV_INNOCHECKSUM */
|
||||||
|
Reference in New Issue
Block a user