Skip to content

Commit af83ed9

Browse files
committed
MDEV-20661 Virtual fields are not recalculated on system fields value assignment
Fix stale virtual field value in 4 cases: when virtual field depends on row_start/row_end in timestamp/trx_id versioned table. row_start dep is recalculated in vers_update_fields() (SQL and InnoDB layer). row_end dep is recalculated on history row insert.
1 parent af57c65 commit af83ed9

File tree

9 files changed

+169
-57
lines changed

9 files changed

+169
-57
lines changed

mysql-test/suite/versioning/r/update.result

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,3 +335,18 @@ insert into t1 (a, b) values (1, 2);
335335
replace into t1 (a, b) values (3, 2);
336336
replace into t1 (a, b) values (4, 2);
337337
drop table t1;
338+
#
339+
# MDEV-20661 Virtual fields are not recalculated on system fields value assignment
340+
#
341+
create table t1 (
342+
a int,
343+
row_start SYS_DATATYPE as row start invisible,
344+
row_end SYS_DATATYPE as row end invisible,
345+
period for system_time (row_start, row_end),
346+
v1 bigint unsigned as (a ^ row_start) unique,
347+
v2 bigint unsigned as (a ^ row_end) unique
348+
) engine=innodb with system versioning;
349+
insert into t1 (a) values (1), (2);
350+
update ignore t1 set a= 3;
351+
delete history from t1;
352+
drop table t1;

mysql-test/suite/versioning/t/update.test

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,4 +265,25 @@ replace into t1 (a, b) values (4, 2);
265265
# cleanup
266266
drop table t1;
267267

268+
--echo #
269+
--echo # MDEV-20661 Virtual fields are not recalculated on system fields value assignment
270+
--echo #
271+
272+
replace_result $sys_datatype_expl SYS_DATATYPE;
273+
eval create table t1 (
274+
a int,
275+
row_start $sys_datatype_expl as row start invisible,
276+
row_end $sys_datatype_expl as row end invisible,
277+
period for system_time (row_start, row_end),
278+
v1 bigint unsigned as (a ^ row_start) unique,
279+
v2 bigint unsigned as (a ^ row_end) unique
280+
) engine=innodb with system versioning;
281+
282+
insert into t1 (a) values (1), (2);
283+
update ignore t1 set a= 3;
284+
delete history from t1;
285+
286+
# cleanup
287+
drop table t1;
288+
268289
source suite/versioning/common_finish.inc;

sql/sql_insert.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,10 @@ int vers_insert_history_row(TABLE *table)
16581658
if (row_start->cmp(row_start->ptr, row_end->ptr) >= 0)
16591659
return 0;
16601660

1661+
if (table->vfield &&
1662+
table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_READ))
1663+
return HA_ERR_GENERIC;
1664+
16611665
return table->file->ha_write_row(table->record[0]);
16621666
}
16631667

sql/table.cc

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8078,29 +8078,24 @@ void TABLE::vers_update_fields()
80788078
bitmap_set_bit(write_set, vers_start_field()->field_index);
80798079
bitmap_set_bit(write_set, vers_end_field()->field_index);
80808080

8081-
if (versioned(VERS_TIMESTAMP))
8081+
if (!vers_write)
80828082
{
8083-
if (!vers_write)
8084-
{
8085-
file->column_bitmaps_signal();
8086-
return;
8087-
}
8088-
if (vers_start_field()->store_timestamp(in_use->query_start(),
8089-
in_use->query_start_sec_part()))
8090-
DBUG_ASSERT(0);
8083+
file->column_bitmaps_signal();
8084+
return;
80918085
}
8092-
else
8086+
8087+
if (versioned(VERS_TIMESTAMP) &&
8088+
vers_start_field()->store_timestamp(in_use->query_start(),
8089+
in_use->query_start_sec_part()))
80938090
{
8094-
if (!vers_write)
8095-
{
8096-
file->column_bitmaps_signal();
8097-
return;
8098-
}
8091+
DBUG_ASSERT(0);
80998092
}
81008093

81018094
vers_end_field()->set_max();
81028095
bitmap_set_bit(read_set, vers_end_field()->field_index);
81038096
file->column_bitmaps_signal();
8097+
if (vfield)
8098+
update_virtual_fields(file, VCOL_UPDATE_FOR_READ);
81048099
}
81058100

81068101

storage/innobase/handler/ha_innodb.cc

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21814,3 +21814,70 @@ ib_push_frm_error(
2181421814
break;
2181521815
}
2181621816
}
21817+
21818+
/** Writes 8 bytes to nth tuple field
21819+
@param[in] tuple where to write
21820+
@param[in] nth index in tuple
21821+
@param[in] data what to write
21822+
@param[in] buf field data buffer */
21823+
static void set_tuple_col_8(dtuple_t *tuple, int col, uint64_t data, byte *buf)
21824+
{
21825+
dfield_t *dfield= dtuple_get_nth_field(tuple, col);
21826+
ut_ad(dfield->type.len == 8);
21827+
if (dfield->len == UNIV_SQL_NULL)
21828+
{
21829+
dfield_set_data(dfield, buf, 8);
21830+
}
21831+
ut_ad(dfield->len == dfield->type.len && dfield->data);
21832+
mach_write_to_8(dfield->data, data);
21833+
}
21834+
21835+
void ins_node_t::vers_update_end(row_prebuilt_t *prebuilt, bool history_row)
21836+
{
21837+
ut_ad(prebuilt->ins_node == this);
21838+
trx_t *trx= prebuilt->trx;
21839+
#ifndef DBUG_OFF
21840+
ut_ad(table->vers_start != table->vers_end);
21841+
const mysql_row_templ_t *t= prebuilt->get_template_by_col(table->vers_end);
21842+
ut_ad(t);
21843+
ut_ad(t->mysql_col_len == 8);
21844+
#endif
21845+
21846+
if (history_row)
21847+
{
21848+
set_tuple_col_8(row, table->vers_end, trx->id, vers_end_buf);
21849+
}
21850+
else /* ROW_INS_VERSIONED */
21851+
{
21852+
set_tuple_col_8(row, table->vers_end, TRX_ID_MAX, vers_end_buf);
21853+
#ifndef DBUG_OFF
21854+
t= prebuilt->get_template_by_col(table->vers_start);
21855+
ut_ad(t);
21856+
ut_ad(t->mysql_col_len == 8);
21857+
#endif
21858+
set_tuple_col_8(row, table->vers_start, trx->id, vers_start_buf);
21859+
}
21860+
dict_index_t *clust_index= dict_table_get_first_index(table);
21861+
THD *thd= trx->mysql_thd;
21862+
TABLE *mysql_table= prebuilt->m_mysql_table;
21863+
mem_heap_t *local_heap= NULL;
21864+
for (ulint col_no= 0; col_no < dict_table_get_n_v_cols(table); col_no++)
21865+
{
21866+
21867+
const dict_v_col_t *v_col= dict_table_get_nth_v_col(table, col_no);
21868+
for (ulint i= 0; i < unsigned(v_col->num_base); i++)
21869+
{
21870+
dict_col_t *base_col= v_col->base_col[i];
21871+
if (base_col->ind == table->vers_end)
21872+
{
21873+
innobase_get_computed_value(row, v_col, clust_index, &local_heap,
21874+
table->heap, NULL, thd, mysql_table,
21875+
mysql_table->record[0], NULL, NULL, NULL);
21876+
}
21877+
}
21878+
}
21879+
if (local_heap)
21880+
{
21881+
mem_heap_free(local_heap);
21882+
}
21883+
}

storage/innobase/include/row0ins.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ row_ins_step(
163163
#define INS_NODE_INSERT_ENTRIES 3 /* index entries should be built and
164164
inserted */
165165

166+
struct row_prebuilt_t;
167+
166168
/** Insert node structure */
167169
struct ins_node_t
168170
{
@@ -203,6 +205,7 @@ struct ins_node_t
203205
entry_list and sys fields are stored here;
204206
if this is NULL, entry list should be created
205207
and buffers for sys fields in row allocated */
208+
void vers_update_end(row_prebuilt_t *prebuilt, bool history_row);
206209
};
207210

208211
/** Create an insert object.

storage/innobase/include/row0upd.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,32 @@ struct upd_t{
454454
fields[n_fields++] = field;
455455
}
456456

457-
/** Determine if the given field_no is modified.
457+
void remove_element(ulint i)
458+
{
459+
ut_ad(n_fields > 0);
460+
ut_ad(i < n_fields);
461+
while (i < n_fields - 1)
462+
{
463+
fields[i]= fields[i + 1];
464+
i++;
465+
}
466+
n_fields--;
467+
}
468+
469+
bool remove(const ulint field_no)
470+
{
471+
for (ulint i= 0; i < n_fields; ++i)
472+
{
473+
if (field_no == fields[i].field_no)
474+
{
475+
remove_element(i);
476+
return true;
477+
}
478+
}
479+
return false;
480+
}
481+
482+
/** Determine if the given field_no is modified.
458483
@return true if modified, false otherwise. */
459484
bool is_modified(const ulint field_no) const
460485
{

storage/innobase/row/row0mysql.cc

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,23 +1331,6 @@ row_mysql_get_table_status(
13311331
return(err);
13321332
}
13331333

1334-
/** Writes 8 bytes to nth tuple field
1335-
@param[in] tuple where to write
1336-
@param[in] nth index in tuple
1337-
@param[in] data what to write
1338-
@param[in] buf field data buffer */
1339-
static
1340-
void
1341-
set_tuple_col_8(dtuple_t* tuple, int col, uint64_t data, byte* buf) {
1342-
dfield_t* dfield = dtuple_get_nth_field(tuple, col);
1343-
ut_ad(dfield->type.len == 8);
1344-
if (dfield->len == UNIV_SQL_NULL) {
1345-
dfield_set_data(dfield, buf, 8);
1346-
}
1347-
ut_ad(dfield->len == dfield->type.len && dfield->data);
1348-
mach_write_to_8(dfield->data, data);
1349-
}
1350-
13511334
/** Does an insert for MySQL.
13521335
@param[in] mysql_rec row in the MySQL format
13531336
@param[in,out] prebuilt prebuilt struct in MySQL handle
@@ -1415,29 +1398,8 @@ row_insert_for_mysql(
14151398
&blob_heap);
14161399

14171400
if (ins_mode != ROW_INS_NORMAL) {
1418-
#ifndef DBUG_OFF
1419-
ut_ad(table->vers_start != table->vers_end);
1420-
const mysql_row_templ_t* t
1421-
= prebuilt->get_template_by_col(table->vers_end);
1422-
ut_ad(t);
1423-
ut_ad(t->mysql_col_len == 8);
1424-
#endif
1425-
1426-
if (ins_mode == ROW_INS_HISTORICAL) {
1427-
set_tuple_col_8(node->row, table->vers_end, trx->id,
1428-
node->vers_end_buf);
1429-
} else /* ROW_INS_VERSIONED */ {
1430-
set_tuple_col_8(node->row, table->vers_end, TRX_ID_MAX,
1431-
node->vers_end_buf);
1432-
#ifndef DBUG_OFF
1433-
t = prebuilt->get_template_by_col(table->vers_start);
1434-
ut_ad(t);
1435-
ut_ad(t->mysql_col_len == 8);
1436-
#endif
1437-
set_tuple_col_8(node->row, table->vers_start, trx->id,
1438-
node->vers_start_buf);
1439-
}
1440-
}
1401+
node->vers_update_end(prebuilt, ins_mode == ROW_INS_HISTORICAL);
1402+
}
14411403

14421404
savept = trx_savept_take(trx);
14431405

storage/innobase/row/row0upd.cc

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3526,5 +3526,25 @@ void upd_node_t::vers_update_fields(const trx_t *trx, ulint idx)
35263526
}
35273527

35283528
dfield_set_data(&ufield->new_val, update->vers_sys_value, col->len);
3529-
}
35303529

3530+
for (ulint col_no= 0; col_no < dict_table_get_n_v_cols(table); col_no++)
3531+
{
3532+
3533+
const dict_v_col_t *v_col= dict_table_get_nth_v_col(table, col_no);
3534+
if (!v_col->m_col.ord_part)
3535+
continue;
3536+
for (ulint i= 0; i < unsigned(v_col->num_base); i++)
3537+
{
3538+
dict_col_t *base_col= v_col->base_col[i];
3539+
if (base_col->ind == col->ind)
3540+
{
3541+
/* Virtual column depends on system field value
3542+
which we updated above. Remove it from update
3543+
vector, so it is recalculated in
3544+
row_upd_store_v_row() (see !update branch). */
3545+
update->remove(v_col->v_pos);
3546+
break;
3547+
}
3548+
}
3549+
}
3550+
}

0 commit comments

Comments
 (0)