diff --git a/mysql-test/suite/versioning/common.inc b/mysql-test/suite/versioning/common.inc index 4324dd27cd8..aa3877d1570 100644 --- a/mysql-test/suite/versioning/common.inc +++ b/mysql-test/suite/versioning/common.inc @@ -77,4 +77,26 @@ if ($MTR_COMBINATION_TRX_ID) let $sys_datatype_expl_uc= BIGINT(20) UNSIGNED; let $sys_datatype_max= 18446744073709551615; } + +eval create or replace function current_row(sys_trx_end $sys_datatype_expl) +returns int +deterministic + return sys_trx_end = $sys_datatype_max; + +delimiter ~~; +eval create or replace function check_row(row_start $sys_datatype_expl, row_end $sys_datatype_expl) +returns varchar(255) +deterministic +begin + if row_end < row_start then + return "ERROR: row_end < row_start"; + elseif row_end = row_start then + return "ERROR: row_end == row_start"; + elseif current_row(row_end) then + return "CURRENT ROW"; + end if; + return "HISTORICAL ROW"; +end~~ +delimiter ;~~ + --enable_query_log diff --git a/mysql-test/suite/versioning/common_finish.inc b/mysql-test/suite/versioning/common_finish.inc index 6894267f353..d753fc138fb 100644 --- a/mysql-test/suite/versioning/common_finish.inc +++ b/mysql-test/suite/versioning/common_finish.inc @@ -4,4 +4,6 @@ drop procedure verify_vtq_dummy; drop function sys_commit_ts; drop procedure concat_exec2; drop procedure concat_exec3; +drop function current_row; +drop function check_row; --enable_query_log diff --git a/mysql-test/suite/versioning/r/foreign.result b/mysql-test/suite/versioning/r/foreign.result index bb9042fff9f..38c93d2611b 100644 --- a/mysql-test/suite/versioning/r/foreign.result +++ b/mysql-test/suite/versioning/r/foreign.result @@ -6,8 +6,8 @@ id int unique key ) engine innodb; create table child( parent_id int, -sys_start timestamp(6) as row start invisible, -sys_end timestamp(6) as row end invisible, +sys_start SYS_DATATYPE as row start invisible, +sys_end SYS_DATATYPE as row end invisible, period for system_time(sys_start, sys_end), foreign key(parent_id) references parent(id) on delete restrict @@ -25,7 +25,7 @@ update parent set id=id+1; ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`)) delete from child; update parent set id=id+1; -select * from child for system_time from timestamp 0 to timestamp now(6); +select * from child for system_time all; parent_id 1 1 @@ -39,8 +39,8 @@ id int(10) unsigned unique key ) engine innodb; create table child( parent_id int(10) unsigned primary key, -sys_start timestamp(6) as row start invisible, -sys_end timestamp(6) as row end invisible, +sys_start SYS_DATATYPE as row start invisible, +sys_end SYS_DATATYPE as row end invisible, period for system_time(sys_start, sys_end), foreign key(parent_id) references parent(id) ) engine innodb with system versioning; @@ -58,19 +58,37 @@ id int unique key ) engine innodb; create table child( parent_id int, -sys_start timestamp(6) as row start invisible, -sys_end timestamp(6) as row end invisible, +sys_start SYS_DATATYPE as row start invisible, +sys_end SYS_DATATYPE as row end invisible, period for system_time(sys_start, sys_end), foreign key(parent_id) references parent(id) on delete cascade on update cascade ) engine innodb with system versioning; -ERROR HY000: CASCADE is not supported for TIMESTAMP(6) AS ROW START/END system-versioned tables +insert into parent values(1); +insert into child values(1); +delete from parent where id = 1; +select * from child; +parent_id +select * from child for system_time all; +parent_id +1 +insert into parent values(1); +insert into child values(1); +update parent set id = id + 1; +select * from child; +parent_id +2 +select * from child for system_time all; +parent_id +1 +2 +drop table child; drop table parent; create or replace table parent ( id int primary key, -sys_start timestamp(6) as row start invisible, -sys_end timestamp(6) as row end invisible, +sys_start SYS_DATATYPE as row start invisible, +sys_end SYS_DATATYPE as row end invisible, period for system_time(sys_start, sys_end) ) with system versioning engine innodb; @@ -97,8 +115,8 @@ engine innodb; create or replace table child ( id int primary key, parent_id int not null, -row_start timestamp(6) as row start invisible, -row_end timestamp(6) as row end invisible, +row_start SYS_DATATYPE as row start invisible, +row_end SYS_DATATYPE as row end invisible, period for system_time(row_start, row_end), constraint `parent-fk` foreign key (parent_id) references parent (id) @@ -106,32 +124,64 @@ on delete cascade on update restrict ) with system versioning engine innodb; -ERROR HY000: CASCADE is not supported for TIMESTAMP(6) AS ROW START/END system-versioned tables +insert into parent (id) values (3); +insert into child (id, parent_id) values (3, 3); +delete from parent; +select * from child; +id parent_id +select *, check_row(row_start, row_end) from child for system_time all; +id parent_id check_row(row_start, row_end) +3 3 HISTORICAL ROW +drop table child; drop table parent; ################# # Test SET NULL # ################# -create table parent( +create or replace table parent( id int unique key ) engine innodb; -create table child( +create or replace table child( parent_id int, -sys_start timestamp(6) as row start invisible, -sys_end timestamp(6) as row end invisible, +sys_start SYS_DATATYPE as row start invisible, +sys_end SYS_DATATYPE as row end invisible, period for system_time(sys_start, sys_end), foreign key(parent_id) references parent(id) on delete set null on update set null ) engine innodb with system versioning; -ERROR HY000: SET NULL is not supported for TIMESTAMP(6) AS ROW START/END system-versioned tables +insert into parent values(1); +insert into child values(1); +delete from child; +insert into child values(1); +delete from parent where id = 1; +select * from child; +parent_id +NULL +select *, current_row(sys_end) as current_row from child for system_time all order by sys_end; +parent_id current_row +1 0 +NULL 1 +delete from child; +insert into parent values(1); +insert into child values(1); +update parent set id= id + 1; +select * from child; +parent_id +NULL +select *, current_row(sys_end) as current_row from child for system_time all order by sys_end; +parent_id current_row +1 0 +NULL 0 +NULL 1 +drop table child; drop table parent; ########################### # Parent table is foreign # ########################### create or replace table parent( id int unique key, -sys_start timestamp(6) as row start invisible, -sys_end timestamp(6) as row end invisible, +sys_start SYS_DATATYPE as row start invisible, +sys_end SYS_DATATYPE as row end invisible, period for system_time(sys_start, sys_end) ) engine innodb with system versioning; create or replace table child( @@ -162,16 +212,16 @@ drop table parent; create or replace table a ( cola int(10) primary key, v_cola int(10) as (cola mod 10) virtual, -sys_start timestamp(6) as row start invisible, -sys_end timestamp(6) as row end invisible, +sys_start SYS_DATATYPE as row start invisible, +sys_end SYS_DATATYPE as row end invisible, period for system_time(sys_start, sys_end) ) engine=innodb with system versioning; create index v_cola on a (v_cola); create or replace table b( cola int(10), v_cola int(10), -sys_start timestamp(6) as row start invisible, -sys_end timestamp(6) as row end invisible, +sys_start SYS_DATATYPE as row start invisible, +sys_end SYS_DATATYPE as row end invisible, period for system_time(sys_start, sys_end) ) engine=innodb with system versioning; alter table b add constraint `v_cola_fk` diff --git a/mysql-test/suite/versioning/t/foreign.test b/mysql-test/suite/versioning/t/foreign.test index 040d7564360..bd6cfd30522 100644 --- a/mysql-test/suite/versioning/t/foreign.test +++ b/mysql-test/suite/versioning/t/foreign.test @@ -8,6 +8,7 @@ create table parent( id int unique key ) engine innodb; +--replace_result $sys_datatype_expl SYS_DATATYPE eval create table child( parent_id int, sys_start $sys_datatype_expl as row start invisible, @@ -32,7 +33,7 @@ insert into child values(1); update parent set id=id+1; delete from child; update parent set id=id+1; -select * from child for system_time from timestamp 0 to timestamp now(6); +select * from child for system_time all; drop table child; drop table parent; @@ -45,6 +46,7 @@ create table parent( id int(10) unsigned unique key ) engine innodb; +--replace_result $sys_datatype_expl SYS_DATATYPE eval create table child( parent_id int(10) unsigned primary key, sys_start $sys_datatype_expl as row start invisible, @@ -70,7 +72,7 @@ create table parent( id int unique key ) engine innodb; ---disable_abort_on_error +--replace_result $sys_datatype_expl SYS_DATATYPE eval create table child( parent_id int, sys_start $sys_datatype_expl as row start invisible, @@ -80,14 +82,10 @@ eval create table child( on delete cascade on update cascade ) engine innodb with system versioning; ---enable_abort_on_error -if ($MTR_COMBINATION_TRX_ID) { insert into parent values(1); insert into child values(1); -delete from parent where id = 1; -delete from child where parent_id = 1; delete from parent where id = 1; select * from child; select * from child for system_time all; @@ -99,8 +97,9 @@ select * from child; select * from child for system_time all; drop table child; -} drop table parent; + +--replace_result $sys_datatype_expl SYS_DATATYPE eval create or replace table parent ( id int primary key, sys_start $sys_datatype_expl as row start invisible, @@ -132,7 +131,7 @@ create or replace table parent ( ) engine innodb; ---disable_abort_on_error +--replace_result $sys_datatype_expl SYS_DATATYPE eval create or replace table child ( id int primary key, parent_id int not null, @@ -145,33 +144,26 @@ eval create or replace table child ( on update restrict ) with system versioning engine innodb; ---enable_abort_on_error -if ($MTR_COMBINATION_TRX_ID) { insert into parent (id) values (3); insert into child (id, parent_id) values (3, 3); ---echo ## FIXME: #415 update of foreign constraints is disabled -delete from child; ---echo ## FIXME END delete from parent; select * from child; ---replace_result $sys_datatype_max MAXVAL -eval select *, row_start < row_end, row_end < $sys_datatype_max from child for system_time all; +select *, check_row(row_start, row_end) from child for system_time all; drop table child; -} drop table parent; --echo ################# --echo # Test SET NULL # --echo ################# -create table parent( +create or replace table parent( id int unique key ) engine innodb; ---disable_abort_on_error -eval create table child( +--replace_result $sys_datatype_expl SYS_DATATYPE +eval create or replace table child( parent_id int, sys_start $sys_datatype_expl as row start invisible, sys_end $sys_datatype_expl as row end invisible, @@ -180,41 +172,31 @@ eval create table child( on delete set null on update set null ) engine innodb with system versioning; ---enable_abort_on_error -if ($MTR_COMBINATION_TRX_ID) { insert into parent values(1); insert into child values(1); delete from child; insert into child values(1); ---echo ## FIXME: #415 update of foreign constraints is disabled -delete from child where parent_id = 1; ---echo ## FIXME END delete from parent where id = 1; select * from child; -select * from child for system_time from timestamp 0 to timestamp now(6); +select *, current_row(sys_end) as current_row from child for system_time all order by sys_end; delete from child; insert into parent values(1); insert into child values(1); -## FIXME: #415 update of foreign constraints is disabled -if (0) -{ -update parent set id=id+1; +update parent set id= id + 1; select * from child; -select * from child for system_time from timestamp 0 to timestamp now(6); -} -## FIXME END +select *, current_row(sys_end) as current_row from child for system_time all order by sys_end; drop table child; -} drop table parent; --echo ########################### --echo # Parent table is foreign # --echo ########################### +--replace_result $sys_datatype_expl SYS_DATATYPE eval create or replace table parent( id int unique key, sys_start $sys_datatype_expl as row start invisible, @@ -254,6 +236,7 @@ drop table parent; --echo # crash on DELETE # --echo ################### +--replace_result $sys_datatype_expl SYS_DATATYPE eval create or replace table a ( cola int(10) primary key, v_cola int(10) as (cola mod 10) virtual, @@ -264,6 +247,7 @@ eval create or replace table a ( create index v_cola on a (v_cola); +--replace_result $sys_datatype_expl SYS_DATATYPE eval create or replace table b( cola int(10), v_cola int(10), diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b3b2c8233bf..fb7399feec7 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -5198,6 +5198,20 @@ extern "C" bool thd_is_strict_mode(const MYSQL_THD thd) } +/** + Get query start time as SQL field data. + Needed by InnoDB. + @param thd Thread object + @param buf Buffer to hold start time data +*/ +void thd_get_query_start_data(THD *thd, char *buf) +{ + LEX_CSTRING field_name; + Field_timestampf f((uchar *)buf, NULL, 0, Field::NONE, &field_name, NULL, 6); + f.store_TIME(thd->query_start(), thd->query_start_sec_part()); +} + + /* Interface for MySQL Server, plugins and storage engines to report when they are going to sleep/stall. diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 0201a904058..d1af8d590da 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4469,19 +4469,6 @@ static bool vers_prepare_keys(THD *thd, HA_CREATE_INFO *create_info, Key *key= NULL; while ((key=key_it++)) { - if (key->type == Key::FOREIGN_KEY && - create_info->vers_info.check_unit == VERS_TIMESTAMP) - { - Foreign_key *fk_key= (Foreign_key*) key; - enum enum_fk_option op; - if (fk_modifies_child(op=fk_key->update_opt) || - fk_modifies_child(op=fk_key->delete_opt)) - { - my_error(ER_VERS_NOT_SUPPORTED, MYF(0), fk_option_name(op)->str, - "TIMESTAMP(6) AS ROW START/END"); - return true; - } - } if (key->type != Key::PRIMARY && key->type != Key::UNIQUE) continue; diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index 2e175731d5f..11936616cd2 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -1492,16 +1492,12 @@ dict_index_t::vers_history_row( ut_ad(col.vers_sys_end()); ulint nfield = dict_col_get_clust_pos(&col, this); const byte *data = rec_get_nth_field(rec, offsets, nfield, &len); - if (col.mtype == DATA_FIXBINARY) { - ut_ad(len == sizeof timestamp_max_bytes); - return 0 != memcmp(data, timestamp_max_bytes, len); - } else { - ut_ad(col.mtype == DATA_INT); + if (col.vers_native()) { ut_ad(len == sizeof trx_id_max_bytes); return 0 != memcmp(data, trx_id_max_bytes, len); } - ut_ad(0); - return false; + ut_ad(len == sizeof timestamp_max_bytes); + return 0 != memcmp(data, timestamp_max_bytes, len); } /** Check if record in secondary index is historical row. diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 0bab513d051..d962c69c0c8 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -654,6 +654,13 @@ struct dict_col_t{ /** @return whether this is system field */ bool vers_sys_field() const { return prtype & DATA_VERSIONED; } + /** @return whether table of this system field is TRX_ID-based */ + bool vers_native() const + { + ut_ad(vers_sys_field()); + ut_ad(mtype == DATA_INT || mtype == DATA_FIXBINARY); + return mtype == DATA_INT; + } /** @return whether this is system versioned */ bool is_versioned() const { return !(~prtype & DATA_VERSIONED); } /** @return whether this is the system version start */ diff --git a/storage/innobase/include/row0upd.h b/storage/innobase/include/row0upd.h index 01fc6cda6ae..d010e15eab7 100644 --- a/storage/innobase/include/row0upd.h +++ b/storage/innobase/include/row0upd.h @@ -577,6 +577,9 @@ struct upd_node_t{ /* column assignment list */ ulint magic_n; + /** System Versioning: modify update vector to set row_start + * (or row_end in case of DELETE) to current trx_id. */ + void vers_set_fields(const trx_t* trx); }; #define UPD_NODE_MAGIC_N 1579975 diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 835ca2ec2d1..a47f1a9b239 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -71,6 +71,15 @@ Created 9/17/2000 Heikki Tuuri #include #include +/** + Get query start time as SQL field data. + Needed by InnoDB. + @param thd Thread object + @param buf Buffer to hold start time data +*/ +void thd_get_query_start_data(THD *thd, char *buf); + + /** Provide optional 4.x backwards compatibility for 5.0 and above */ ibool row_rollback_on_timeout = FALSE; @@ -1781,6 +1790,50 @@ init_fts_doc_id_for_ref( } } +/** System Versioning: modify update vector to set row_start + * (or row_end in case of DELETE) to current trx_id. */ +void upd_node_t::vers_set_fields(const trx_t* trx) +{ + dict_index_t* clust_index = dict_table_get_first_index(table); + upd_field_t* ufield; + dict_col_t* col; + unsigned col_idx; + if (is_delete) { + ufield = &update->fields[0]; + update->n_fields = 0; + is_delete = VERSIONED_DELETE; + col_idx = table->vers_end; + } else { + ut_ad(update->n_fields < table->n_cols); + ufield = &update->fields[update->n_fields]; + col_idx = table->vers_start; + } + col = &table->cols[col_idx]; + UNIV_MEM_INVALID(ufield, sizeof *ufield); + { + ulint field_no = dict_col_get_clust_pos(col, clust_index); + ut_ad(field_no != ULINT_UNDEFINED); + ufield->field_no = field_no; + } + ufield->orig_len = 0; + ufield->exp = NULL; + + if (col->vers_native()) + { + mach_write_to_8(update->vers_sys_value, trx->id); + } else { + thd_get_query_start_data(trx->mysql_thd, (char *) + update->vers_sys_value); + } + + dfield_t* dfield = &ufield->new_val; + dfield_set_data(dfield, update->vers_sys_value, col->len); + dict_col_copy_type(col, &dfield->type); + + update->n_fields++; + ut_ad(in_mysql_interface); // otherwise needs to recalculate node->cmpl_info +} + /** Does an update or delete of a row for MySQL. @param[in,out] prebuilt prebuilt struct in MySQL handle @return error code or DB_SUCCESS */ @@ -1877,42 +1930,7 @@ row_update_for_mysql(row_prebuilt_t* prebuilt) for (;;) { if (vers_set_fields) { - /* System Versioning: modify update vector to set - row_start (or row_end in case of DELETE) - to current trx_id. */ - dict_table_t* table = node->table; - dict_index_t* clust_index = dict_table_get_first_index(table); - upd_t* uvect = node->update; - upd_field_t* ufield; - dict_col_t* col; - unsigned col_idx; - if (node->is_delete) { - ufield = &uvect->fields[0]; - uvect->n_fields = 0; - node->is_delete = VERSIONED_DELETE; - col_idx = table->vers_end; - } else { - ut_ad(uvect->n_fields < table->n_cols); - ufield = &uvect->fields[uvect->n_fields]; - col_idx = table->vers_start; - } - col = &table->cols[col_idx]; - UNIV_MEM_INVALID(ufield, sizeof *ufield); - { - ulint field_no = dict_col_get_clust_pos(col, clust_index); - ut_ad(field_no != ULINT_UNDEFINED); - ufield->field_no = field_no; - } - ufield->orig_len = 0; - ufield->exp = NULL; - - mach_write_to_8(node->update->vers_sys_value, trx->id); - dfield_t* dfield = &ufield->new_val; - dfield_set_data(dfield, node->update->vers_sys_value, 8); - dict_col_copy_type(col, &dfield->type); - - uvect->n_fields++; - ut_ad(node->in_mysql_interface); // otherwise needs to recalculate node->cmpl_info + node->vers_set_fields(trx); } thr->run_node = node; @@ -2193,7 +2211,7 @@ row_update_cascade_for_mysql( return(DB_FOREIGN_EXCEED_MAX_CASCADE); } - trx_t* trx = thr_get_trx(thr); + const trx_t* trx = thr_get_trx(thr); bool vers_set_fields = node->table->versioned() && (node->is_delete == PLAIN_DELETE @@ -2201,43 +2219,7 @@ row_update_cascade_for_mysql( for (;;) { if (vers_set_fields) { - // FIXME: code duplication with row_update_for_mysql() - /* System Versioning: modify update vector to set - row_start (or row_end in case of DELETE) - to current trx_id. */ - dict_table_t* table = node->table; - dict_index_t* clust_index = dict_table_get_first_index(table); - upd_t* uvect = node->update; - upd_field_t* ufield; - dict_col_t* col; - unsigned col_idx; - if (node->is_delete) { - ufield = &uvect->fields[0]; - uvect->n_fields = 0; - node->is_delete = VERSIONED_DELETE; - col_idx = table->vers_end; - } else { - ut_ad(uvect->n_fields < table->n_cols); - ufield = &uvect->fields[uvect->n_fields]; - col_idx = table->vers_start; - } - col = &table->cols[col_idx]; - UNIV_MEM_INVALID(ufield, sizeof *ufield); - { - ulint field_no = dict_col_get_clust_pos(col, clust_index); - ut_ad(field_no != ULINT_UNDEFINED); - ufield->field_no = field_no; - } - ufield->orig_len = 0; - ufield->exp = NULL; - - mach_write_to_8(node->update->vers_sys_value, trx->id); - dfield_t* dfield = &ufield->new_val; - dfield_set_data(dfield, node->update->vers_sys_value, 8); - dict_col_copy_type(col, &dfield->type); - - uvect->n_fields++; - ut_ad(node->in_mysql_interface); // otherwise needs to recalculate node->cmpl_info + node->vers_set_fields(trx); } thr->run_node = node;