diff --git a/mysql-test/suite/versioning/r/select.result b/mysql-test/suite/versioning/r/select.result index dda7c08e64c..de92274e17f 100644 --- a/mysql-test/suite/versioning/r/select.result +++ b/mysql-test/suite/versioning/r/select.result @@ -436,6 +436,8 @@ x y 1 1 2 1 3 1 +select * from t1 for system_time all, t2 for system_time all query for system_time all; +ERROR HY000: Wrong versioned query: unused `QUERY FOR SYSTEM_TIME` clause! drop view v1; drop table t1, t2; call innodb_verify_vtq(27); diff --git a/mysql-test/suite/versioning/r/view.result b/mysql-test/suite/versioning/r/view.result index 17ac0bc04e8..3065b244cda 100644 --- a/mysql-test/suite/versioning/r/view.result +++ b/mysql-test/suite/versioning/r/view.result @@ -8,7 +8,7 @@ set @vt1= concat("create view vt1 as select * from t1 for system_time as of time prepare stmt from @vt1; execute stmt; drop prepare stmt; -set @vt2= concat("create view vt2 as select * from t1 for system_time as of timestamp '", @t2, "'"); +set @vt2= concat("create view vt2 as select *, sys_trx_end from t1 for system_time as of timestamp '", @t2, "'"); prepare stmt from @vt2; execute stmt; drop prepare stmt; @@ -76,10 +76,6 @@ x select * from vt1 for system_time all; x 3 -create view error_view as select *, sys_trx_start from t1; -ERROR 42S21: Duplicate column name 'sys_trx_start' -create view error_view as select *, sys_trx_end from t1; -ERROR 42S21: Duplicate column name 'sys_trx_end' create or replace table t1 (x int) with system versioning; insert into t1 values (1), (2); set @t1=now(6); @@ -91,27 +87,14 @@ set @tmp= concat("create or replace view vt1 as select * from t1 for system_time prepare stmt from @tmp; execute stmt; drop prepare stmt; -set @tmp= concat("create or replace view vvt1 as select * from vt1 for system_time as of timestamp '", @t2, "'"); -prepare stmt from @tmp; -execute stmt; -drop prepare stmt; -set @tmp= concat("create or replace view vvvt1 as select * from vvt1 for system_time as of timestamp '", @t3, "'"); -prepare stmt from @tmp; -execute stmt; -drop prepare stmt; select * from vt1 for system_time all; x 1 2 -select * from vvt1 for system_time all; -x -1 -select * from vvvt1 for system_time all; -x create or replace table t1 (x int) with system versioning; create or replace view vt1(c) as select x from t1; -create or replace table t1 (a int) with system versioning; -create or replace table t2 (b int) with system versioning; +create or replace table t1 (a int) with system versioning engine innodb; +create or replace table t2 (b int) with system versioning engine innodb; insert into t1 values (1); insert into t2 values (2); create or replace view vt12 as select * from t1 cross join t2; @@ -121,5 +104,24 @@ a b create or replace view vt12 as select * from t1 for system_time as of timestamp '0-0-0' cross join t2; select * from vt12; a b -drop view vt1, vvt1, vvvt1, vt12; -drop table t1, t2; +create or replace view vt1 as select a, t1.sys_trx_start, t2.sys_trx_end from t1, t2; +ERROR HY000: Creating VIEW `vt1` is prohibited: system fields from multiple tables `t1`, `t2` in query! +create or replace view vt1 as select a, t1.sys_trx_end, t2.sys_trx_end from t1, t2; +ERROR HY000: Creating VIEW `vt1` is prohibited: multiple end system fields `t1.sys_trx_end`, `t2.sys_trx_end` in query! +create or replace table t3 (x int); +create or replace view vt1 as select * from t1, t2, t3; +show create view vt1; +View Create View character_set_client collation_connection +vt1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `vt1` AS select `t1`.`a` AS `a`,`t2`.`b` AS `b`,`t3`.`x` AS `x`,`t1`.`sys_trx_start` AS `sys_trx_start`,`t1`.`sys_trx_end` AS `sys_trx_end` from ((`t1` FOR SYSTEM_TIME ALL join `t2` FOR SYSTEM_TIME ALL) join `t3`) where `t1`.`sys_trx_end` = 18446744073709551615 and `t2`.`sys_trx_end` = 18446744073709551615 latin1 latin1_swedish_ci +create or replace view vt1 as select * from t3, t2, t1; +show create view vt1; +View Create View character_set_client collation_connection +vt1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `vt1` AS select `t3`.`x` AS `x`,`t2`.`b` AS `b`,`t1`.`a` AS `a`,`t2`.`sys_trx_start` AS `sys_trx_start`,`t2`.`sys_trx_end` AS `sys_trx_end` from ((`t3` join `t2` FOR SYSTEM_TIME ALL) join `t1` FOR SYSTEM_TIME ALL) where `t2`.`sys_trx_end` = 18446744073709551615 and `t1`.`sys_trx_end` = 18446744073709551615 latin1 latin1_swedish_ci +create or replace view vt1 as select a, t2.sys_trx_end as endo from t3, t1, t2; +show create view vt1; +View Create View character_set_client collation_connection +vt1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `vt1` AS select `t1`.`a` AS `a`,`t2`.`sys_trx_end` AS `endo`,`t2`.`sys_trx_start` AS `sys_trx_start` from ((`t3` join `t1` FOR SYSTEM_TIME ALL) join `t2` FOR SYSTEM_TIME ALL) where `t1`.`sys_trx_end` = 18446744073709551615 and `t2`.`sys_trx_end` = 18446744073709551615 latin1 latin1_swedish_ci +create or replace view vvt1 as select * from t1, t2, vt1; +ERROR HY000: Creating VIEW `vvt1` is prohibited: versioned VIEW `vt1` in query! +drop view vt1, vt12; +drop table t1, t2, t3; diff --git a/mysql-test/suite/versioning/t/select.test b/mysql-test/suite/versioning/t/select.test index 3f92546d1e4..37b6b8be89d 100644 --- a/mysql-test/suite/versioning/t/select.test +++ b/mysql-test/suite/versioning/t/select.test @@ -197,6 +197,9 @@ delete from t1 where x = 3; insert into t2 values (1); select * from t1, t2 query for system_time all; +--error ER_VERS_WRONG_QUERY +select * from t1 for system_time all, t2 for system_time all query for system_time all; + drop view v1; drop table t1, t2; diff --git a/mysql-test/suite/versioning/t/view.test b/mysql-test/suite/versioning/t/view.test index c284df3ec5e..64be35f70c0 100644 --- a/mysql-test/suite/versioning/t/view.test +++ b/mysql-test/suite/versioning/t/view.test @@ -12,7 +12,7 @@ delete from t1; set @vt1= concat("create view vt1 as select * from t1 for system_time as of timestamp '", @t1, "'"); prepare stmt from @vt1; execute stmt; drop prepare stmt; -set @vt2= concat("create view vt2 as select * from t1 for system_time as of timestamp '", @t2, "'"); +set @vt2= concat("create view vt2 as select *, sys_trx_end from t1 for system_time as of timestamp '", @t2, "'"); prepare stmt from @vt2; execute stmt; drop prepare stmt; select * from vt1 for system_time all; @@ -46,11 +46,6 @@ select * from vt1; select * from t1 for system_time all; select * from vt1 for system_time all; ---error ER_DUP_FIELDNAME -create view error_view as select *, sys_trx_start from t1; ---error ER_DUP_FIELDNAME -create view error_view as select *, sys_trx_end from t1; - create or replace table t1 (x int) with system versioning; insert into t1 values (1), (2); set @t1=now(6); @@ -61,20 +56,14 @@ set @t3=now(6); set @tmp= concat("create or replace view vt1 as select * from t1 for system_time as of timestamp '", @t1, "'"); prepare stmt from @tmp; execute stmt; drop prepare stmt; -set @tmp= concat("create or replace view vvt1 as select * from vt1 for system_time as of timestamp '", @t2, "'"); -prepare stmt from @tmp; execute stmt; drop prepare stmt; -set @tmp= concat("create or replace view vvvt1 as select * from vvt1 for system_time as of timestamp '", @t3, "'"); -prepare stmt from @tmp; execute stmt; drop prepare stmt; select * from vt1 for system_time all; -select * from vvt1 for system_time all; -select * from vvvt1 for system_time all; create or replace table t1 (x int) with system versioning; create or replace view vt1(c) as select x from t1; -create or replace table t1 (a int) with system versioning; -create or replace table t2 (b int) with system versioning; +create or replace table t1 (a int) with system versioning engine innodb; +create or replace table t2 (b int) with system versioning engine innodb; insert into t1 values (1); insert into t2 values (2); create or replace view vt12 as select * from t1 cross join t2; @@ -82,5 +71,21 @@ select * from vt12; create or replace view vt12 as select * from t1 for system_time as of timestamp '0-0-0' cross join t2; select * from vt12; -drop view vt1, vvt1, vvvt1, vt12; -drop table t1, t2; +--error ER_VERS_VIEW_PROHIBITED +create or replace view vt1 as select a, t1.sys_trx_start, t2.sys_trx_end from t1, t2; +--error ER_VERS_VIEW_PROHIBITED +create or replace view vt1 as select a, t1.sys_trx_end, t2.sys_trx_end from t1, t2; + +create or replace table t3 (x int); +create or replace view vt1 as select * from t1, t2, t3; +show create view vt1; +create or replace view vt1 as select * from t3, t2, t1; +show create view vt1; +create or replace view vt1 as select a, t2.sys_trx_end as endo from t3, t1, t2; +show create view vt1; + +--error ER_VERS_VIEW_PROHIBITED +create or replace view vvt1 as select * from t1, t2, vt1; + +drop view vt1, vt12; +drop table t1, t2, t3; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 0aeaa058cf9..81651071c05 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7535,5 +7535,14 @@ ER_VERS_NOT_ALLOWED ER_VERS_WRONG_QUERY_TYPE eng "%`s works only with %`s query type" +ER_VERS_VIEW_PROHIBITED + eng "Creating VIEW %`s is prohibited!" + +ER_VERS_WRONG_QUERY + eng "Wrong versioned query: %s" + +WARN_VERS_ALIAS_TOO_LONG + eng "Auto generated alias for `%s.%s` is too long; using `%s`." + ER_WRONG_TABLESPACE_NAME 42000 eng "Incorrect tablespace name `%-.192s`" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index d0c723af397..8cdced1cb09 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7582,7 +7582,6 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if (!(item= field_iterator.create_item(thd))) DBUG_RETURN(TRUE); - /* This will be deprecated when HIDDEN feature will come to MariaDB. */ if (item->type() == Item::FIELD_ITEM) { Item_field *f= static_cast(item); @@ -7597,8 +7596,8 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, tl->vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED ? slex->vers_conditions.type : tl->vers_conditions.type; - if ((sys_field && vers_hide == VERS_HIDE_FULL && - thd->lex->sql_command != SQLCOM_CREATE_TABLE) || + if ((sys_field && (thd->lex->sql_command == SQLCOM_CREATE_VIEW || + vers_hide == VERS_HIDE_FULL && thd->lex->sql_command != SQLCOM_CREATE_TABLE)) || ((fl & HIDDEN_FLAG) && ( !sys_field || vers_hide == VERS_HIDE_IMPLICIT || diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 5f3c83eed71..7b61efa8fb8 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -7044,3 +7044,25 @@ bool LEX::sp_add_cfetch(THD *thd, const LEX_STRING &name) return true; return false; } + + +bool SELECT_LEX::vers_push_field(THD *thd, TABLE_LIST *table, const char* field_name) +{ + char buf[MAX_FIELD_NAME]; + Item_field *fld= new (thd->mem_root) Item_field(thd, &context, + table->db, table->alias, field_name); + if (!fld) + return true; + + item_list.push_back(fld); + + if (thd->lex->view_list.elements) + { + if (LEX_STRING *l= thd->make_lex_string(field_name, strlen(field_name))) + thd->lex->view_list.push_back(l); + else + return true; + } + + return false; +} diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 06aa248ee02..158130b5ddc 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -991,8 +991,9 @@ public: /* it is for correct printing SELECT options */ thr_lock_type lock_type; - /* System Versioning conditions */ + /* System Versioning */ vers_select_conds_t vers_conditions; + bool vers_push_field(THD *thd, TABLE_LIST *table, const char* field_name); void init_query(); void init_select(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d1d0d08d69a..5901d1d4d6f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -694,6 +694,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, TABLE_LIST *table; int versioned_tables= 0; + int slex_conds_used= 0; Query_arena *arena= 0, backup; if (!thd->stmt_arena->is_conventional() && @@ -707,7 +708,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, { if (table->table && table->table->versioned()) versioned_tables++; - else if (table->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + else if (table->vers_conditions) { my_error(ER_VERSIONING_REQUIRED, MYF(0), "`FOR SYSTEM_TIME` query"); DBUG_RETURN(-1); @@ -716,7 +717,7 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, if (versioned_tables == 0) { - if (slex->vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + if (slex->vers_conditions) { my_error(ER_VERSIONING_REQUIRED, MYF(0), "`FOR SYSTEM_TIME` query"); DBUG_RETURN(-1); @@ -775,17 +776,17 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, { if (table->table && table->table->versioned()) { - vers_select_conds_t &vers_conditions= - table->vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED ? - slex->vers_conditions : table->vers_conditions; + vers_select_conds_t &vers_conditions= !table->vers_conditions? + (++slex_conds_used, slex->vers_conditions) : + table->vers_conditions; - if (vers_conditions.type == FOR_SYSTEM_TIME_UNSPECIFIED) + if (!vers_conditions) { if (vers_conditions.init_from_sysvar(thd)) DBUG_RETURN(-1); } - if (vers_conditions.type != FOR_SYSTEM_TIME_UNSPECIFIED) + if (vers_conditions) { switch (slex->lock_type) { @@ -804,9 +805,9 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, break; } - if (vers_conditions.type == FOR_SYSTEM_TIME_ALL) + if (vers_conditions == FOR_SYSTEM_TIME_ALL) continue; - } + } // if (vers_conditions) COND** dst_cond= where_expr; if (table->on_expr) @@ -979,6 +980,12 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, if (arena) thd->restore_active_arena(arena, &backup); + if (!slex_conds_used && slex->vers_conditions) + { + my_error(ER_VERS_WRONG_QUERY, MYF(0), "unused `QUERY FOR SYSTEM_TIME` clause!"); + DBUG_RETURN(-1); + } + DBUG_RETURN(0); #undef newx } @@ -16985,8 +16992,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, sys_trx_end->flags|= VERS_SYS_END_FLAG | HIDDEN_FLAG; share->versioned= true; share->field= table->field; - share->row_start_field= field_count - 2; - share->row_end_field= field_count - 1; + share->row_start_field= sys_trx_start->field_index; + share->row_end_field= sys_trx_end->field_index; } DBUG_ASSERT(fieldnr == (uint) (reg_field - table->field)); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index d542a5710f6..37703a2bc11 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -453,38 +453,125 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, goto err; } - /* Implicitly add versioning fields if needed */ - { - TABLE_LIST *tl = tables; - while (tl && tl->is_view()) - tl = tl->view->select_lex.table_list.first; - if (tl && tl->table) + { /* System Versioning begin */ + TABLE_LIST *impli_table= NULL, *expli_table= NULL; + const char *impli_start, *impli_end; + Item_field *expli_start= NULL, *expli_end= NULL; + + for (TABLE_LIST *table= tables; table; table= table->next_local) { - TABLE_SHARE *s= tl->table->s; - if (s->versioned) + DBUG_ASSERT(!table->is_view() || table->view); + + // Any versioned table in VIEW will add `FOR SYSTEM_TIME ALL` + WHERE: + // if there are at least one versioned table then VIEW will contain FOR_SYSTEM_TIME_ALL + // (because it is in fact LEX used to parse its SELECT). + if (table->is_view() && table->view->vers_conditions == FOR_SYSTEM_TIME_ALL) { - const char *start= s->vers_start_field()->field_name; - const char *end = s->vers_end_field()->field_name; - - select_lex->item_list.push_back(new (thd->mem_root) Item_field( - thd, &select_lex->context, tables->db, tables->alias, start)); - select_lex->item_list.push_back(new (thd->mem_root) Item_field( - thd, &select_lex->context, tables->db, tables->alias, end)); - - if (lex->view_list.elements) - { - if (LEX_STRING *s= thd->make_lex_string(start, strlen(start))) - lex->view_list.push_back(s); - else - goto err; - if (LEX_STRING *s= thd->make_lex_string(end, strlen(end))) - lex->view_list.push_back(s); - else - goto err; - } + my_printf_error( + ER_VERS_VIEW_PROHIBITED, + "Creating VIEW %`s is prohibited: versioned VIEW %`s in query!", MYF(0), + view->table_name, + table->table_name); + res= true; + goto err; } - } - } + + if (!table->table || !table->table->versioned()) + continue; + + const char *table_start= table->table->vers_start_field()->field_name; + const char *table_end= table->table->vers_end_field()->field_name; + + if (!impli_table) + { + impli_table= table; + impli_start= table_start; + impli_end= table_end; + } + + /* Implicitly add versioning fields if needed */ + Item *item; + List_iterator_fast it(select_lex->item_list); + + DBUG_ASSERT(table->alias); + while ((item= it++)) + { + if (item->real_item()->type() != Item::FIELD_ITEM) + continue; + Item_field *fld= (Item_field*) (item->real_item()); + if (fld->table_name && 0 != my_strcasecmp(table_alias_charset, table->alias, fld->table_name)) + continue; + DBUG_ASSERT(fld->field_name); + if (0 == my_strcasecmp(system_charset_info, table_start, fld->field_name)) + { + if (expli_start) + { + my_printf_error( + ER_VERS_VIEW_PROHIBITED, + "Creating VIEW %`s is prohibited: multiple start system fields `%s.%s`, `%s.%s` in query!", MYF(0), + view->table_name, + expli_table->alias, + expli_start->field_name, + table->alias, + fld->field_name); + res= true; + goto err; + } + if (expli_table) + { + if (expli_table != table) + { +expli_table_err: + my_printf_error( + ER_VERS_VIEW_PROHIBITED, + "Creating VIEW %`s is prohibited: system fields from multiple tables %`s, %`s in query!", MYF(0), + view->table_name, + expli_table->alias, + table->alias); + res= true; + goto err; + } + } + else + expli_table= table; + expli_start= fld; + impli_end= table_end; + } + else if (0 == my_strcasecmp(system_charset_info, table_end, fld->field_name)) + { + if (expli_end) + { + my_printf_error( + ER_VERS_VIEW_PROHIBITED, + "Creating VIEW %`s is prohibited: multiple end system fields `%s.%s`, `%s.%s` in query!", MYF(0), + view->table_name, + expli_table->alias, + expli_end->field_name, + table->alias, + fld->field_name); + res= true; + goto err; + } + if (expli_table) + { + if (expli_table != table) + goto expli_table_err; + } + else + expli_table= table; + expli_end= fld; + impli_start= table_start; + } + } // while ((item= it++)) + } // for (TABLE_LIST *table) + + if (expli_table) + impli_table= expli_table; + if (!expli_start && select_lex->vers_push_field(thd, impli_table, impli_start)) + goto err; + if (!expli_end && select_lex->vers_push_field(thd, impli_table, impli_end)) + goto err; + } /* System Versioning end */ view= lex->unlink_first_table(&link_to_local); @@ -2071,8 +2158,8 @@ bool insert_view_fields(THD *thd, List *list, TABLE_LIST *view) { TABLE_SHARE *s= fld->context->table_list->table->s; if (s->versioned && - (!strcmp(fld->name, s->vers_start_field()->field_name) || - !strcmp(fld->name, s->vers_end_field()->field_name))) + (!strcmp(fld->field_name, s->vers_start_field()->field_name) || + !strcmp(fld->field_name, s->vers_end_field()->field_name))) continue; list->push_back(fld, thd->mem_root); } diff --git a/sql/table.h b/sql/table.h index f6736658674..68f3a2dfe75 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1512,31 +1512,32 @@ public: bool versioned() const { + DBUG_ASSERT(s); return s->versioned; } /* Versioned by SQL layer */ bool versioned_by_sql() const { - DBUG_ASSERT(file); + DBUG_ASSERT(s && file); return s->versioned && !file->versioned(); } bool versioned_by_engine() const { - DBUG_ASSERT(file); + DBUG_ASSERT(s && file); return s->versioned && file->versioned(); } Field *vers_start_field() const { - DBUG_ASSERT(s->versioned); + DBUG_ASSERT(s && s->versioned); return field[s->row_start_field]; } Field *vers_end_field() const { - DBUG_ASSERT(s->versioned); + DBUG_ASSERT(s && s->versioned); return field[s->row_end_field]; } @@ -1874,6 +1875,19 @@ struct vers_select_conds_t } bool init_from_sysvar(THD *thd); + + bool operator== (vers_range_type_t b) + { + return type == b; + } + bool operator!= (vers_range_type_t b) + { + return type != b; + } + operator bool() + { + return type != FOR_SYSTEM_TIME_UNSPECIFIED; + } }; struct LEX;