From 7153ff85a18a252f4e8454614de643fb32df8c49 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Fri, 28 Apr 2017 12:07:04 +0300 Subject: [PATCH] SQL: derived tables improvements [closes #185] --- .../{derived_tables.result => derived.result} | 50 ++++++- .../t/{derived_tables.test => derived.test} | 48 ++++++- .../suite/versioning/t/derived_tables.opt | 1 - sql/share/errmsg-utf8.txt | 3 + sql/sql_base.cc | 1 + sql/sql_derived.cc | 130 ++++++++++++++++-- sql/sql_lex.cc | 2 +- sql/sql_lex.h | 1 + sql/sql_select.cc | 24 +++- sql/sql_view.cc | 18 ++- sql/table.h | 4 + 11 files changed, 247 insertions(+), 35 deletions(-) rename mysql-test/suite/versioning/r/{derived_tables.result => derived.result} (62%) rename mysql-test/suite/versioning/t/{derived_tables.test => derived.test} (58%) delete mode 100644 mysql-test/suite/versioning/t/derived_tables.opt diff --git a/mysql-test/suite/versioning/r/derived_tables.result b/mysql-test/suite/versioning/r/derived.result similarity index 62% rename from mysql-test/suite/versioning/r/derived_tables.result rename to mysql-test/suite/versioning/r/derived.result index 638f5da665a..6b26b18adc0 100644 --- a/mysql-test/suite/versioning/r/derived_tables.result +++ b/mysql-test/suite/versioning/r/derived.result @@ -19,13 +19,13 @@ execute stmt; emp_id name mgr 4 john 1 drop prepare stmt; -with ancestors as (select * from emp for system_time all) select * from ancestors for system_time all; +with ancestors as (select * from emp for system_time all) select * from ancestors; emp_id name mgr 1 bill 0 2 bill 1 3 kate 1 4 john 1 -set @tmp= "with ancestors as (select * from emp for system_time all) select * from ancestors for system_time all"; +set @tmp= "with ancestors as (select * from emp for system_time all) select * from ancestors"; prepare stmt from @tmp; execute stmt; emp_id name mgr @@ -80,8 +80,7 @@ as from emp as ee, ancestors as a where ee.mgr = a.emp_id ) -select * from ancestors -"; +select * from ancestors"; prepare stmt from @tmp; execute stmt; emp_id name mgr @@ -99,7 +98,7 @@ select ee.emp_id, ee.name, ee.mgr from emp as ee for system_time as of timestamp @ts, ancestors as a for system_time as of timestamp @ts where ee.mgr = a.emp_id ) -select * from ancestors for system_time as of timestamp @ts; +select * from ancestors; emp_id name mgr 1 bill 0 2 bill 1 @@ -117,8 +116,7 @@ as from emp as ee for system_time as of timestamp @ts, ancestors as a for system_time as of timestamp @ts where ee.mgr = a.emp_id ) -select * from ancestors for system_time as of timestamp @ts; -"; +select * from ancestors"; prepare stmt from @tmp; execute stmt; emp_id name mgr @@ -127,3 +125,41 @@ emp_id name mgr 3 kate 1 drop prepare stmt; drop table emp; +create or replace table t1 (x int) with system versioning; +create or replace table t2 (y int) with system versioning; +insert into t1 values (1); +set @t0= now(6); +insert into t1 values (2); +delete from t1 where x = 1; +insert into t2 values (10); +select * from (select *, t1.sys_trx_end, t1.sys_trx_end as endo from t1) as s0; +ERROR HY000: Derived table is prohibited: multiple end system fields `t1.sys_trx_end`, `t1.sys_trx_end` in query! +select * from (select *, t1.sys_trx_end, t2.sys_trx_start from t1, t2) as s0; +ERROR HY000: Derived table is prohibited: system fields from multiple tables `t1`, `t2` in query! +select * from (select * from t1 for system_time as of timestamp @t0, t2) as s0; +x y +1 10 +select * from (select *, t1.sys_trx_end from t2, t1 for system_time as of timestamp @t0) as s0; +y x +10 1 +select * from (select *, t1.sys_trx_start from t2 for system_time as of now, t1) as s0 query for system_time as of timestamp @t0; +y x +10 1 +set @q= concat("create view vt1 as select * from t1 for system_time as of timestamp '", @t0, "'"); +prepare q from @q; +execute q; +drop prepare q; +select * from vt1; +x +1 +select * from (select * from vt1, t2) as s0; +x y +1 10 +select * from (select *, vt1.sys_trx_end from t2, vt1) as s0; +y x +10 1 +select * from (select *, vt1.sys_trx_start from t2 for system_time as of now, vt1) as s0 query for system_time as of timestamp @t0; +y x +10 1 +drop table t1, t2; +drop view vt1; diff --git a/mysql-test/suite/versioning/t/derived_tables.test b/mysql-test/suite/versioning/t/derived.test similarity index 58% rename from mysql-test/suite/versioning/t/derived_tables.test rename to mysql-test/suite/versioning/t/derived.test index e847a126ea4..867973aee7e 100644 --- a/mysql-test/suite/versioning/t/derived_tables.test +++ b/mysql-test/suite/versioning/t/derived.test @@ -16,8 +16,8 @@ with ancestors as (select * from emp) select * from ancestors; set @tmp= "with ancestors as (select * from emp) select * from ancestors"; prepare stmt from @tmp; execute stmt; drop prepare stmt; -with ancestors as (select * from emp for system_time all) select * from ancestors for system_time all; -set @tmp= "with ancestors as (select * from emp for system_time all) select * from ancestors for system_time all"; +with ancestors as (select * from emp for system_time all) select * from ancestors; +set @tmp= "with ancestors as (select * from emp for system_time all) select * from ancestors"; prepare stmt from @tmp; execute stmt; drop prepare stmt; with recursive ancestors as (select * from emp) select * from ancestors; @@ -54,8 +54,7 @@ as from emp as ee, ancestors as a where ee.mgr = a.emp_id ) -select * from ancestors -"; +select * from ancestors"; prepare stmt from @tmp; execute stmt; drop prepare stmt; with recursive @@ -70,7 +69,7 @@ as from emp as ee for system_time as of timestamp @ts, ancestors as a for system_time as of timestamp @ts where ee.mgr = a.emp_id ) -select * from ancestors for system_time as of timestamp @ts; +select * from ancestors; set @tmp= " with recursive ancestors @@ -84,8 +83,43 @@ as from emp as ee for system_time as of timestamp @ts, ancestors as a for system_time as of timestamp @ts where ee.mgr = a.emp_id ) -select * from ancestors for system_time as of timestamp @ts; -"; +select * from ancestors"; prepare stmt from @tmp; execute stmt; drop prepare stmt; drop table emp; + +create or replace table t1 (x int) with system versioning; +create or replace table t2 (y int) with system versioning; +insert into t1 values (1); +set @t0= now(6); +insert into t1 values (2); +delete from t1 where x = 1; +insert into t2 values (10); + +--error ER_VERS_DERIVED_PROHIBITED +select * from (select *, t1.sys_trx_end, t1.sys_trx_end as endo from t1) as s0; +--error ER_VERS_DERIVED_PROHIBITED +select * from (select *, t1.sys_trx_end, t2.sys_trx_start from t1, t2) as s0; + +# system_time propagation from inner to outer +select * from (select * from t1 for system_time as of timestamp @t0, t2) as s0; +# leading table selection +select * from (select *, t1.sys_trx_end from t2, t1 for system_time as of timestamp @t0) as s0; +# system_time propagation from outer to inner +select * from (select *, t1.sys_trx_start from t2 for system_time as of now, t1) as s0 query for system_time as of timestamp @t0; + +# VIEW instead of t1 +set @q= concat("create view vt1 as select * from t1 for system_time as of timestamp '", @t0, "'"); +prepare q from @q; execute q; drop prepare q; + +# system_time propagation from view +select * from vt1; +# system_time propagation from inner to outer +select * from (select * from vt1, t2) as s0; +# leading table selection +select * from (select *, vt1.sys_trx_end from t2, vt1) as s0; +# system_time propagation from outer to inner +select * from (select *, vt1.sys_trx_start from t2 for system_time as of now, vt1) as s0 query for system_time as of timestamp @t0; + +drop table t1, t2; +drop view vt1; diff --git a/mysql-test/suite/versioning/t/derived_tables.opt b/mysql-test/suite/versioning/t/derived_tables.opt deleted file mode 100644 index 61ababdf408..00000000000 --- a/mysql-test/suite/versioning/t/derived_tables.opt +++ /dev/null @@ -1 +0,0 @@ ---vers-hide=implicit diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 81651071c05..13408634738 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7538,6 +7538,9 @@ ER_VERS_WRONG_QUERY_TYPE ER_VERS_VIEW_PROHIBITED eng "Creating VIEW %`s is prohibited!" +ER_VERS_DERIVED_PROHIBITED + eng "Derived table is prohibited!" + ER_VERS_WRONG_QUERY eng "Wrong versioned query: %s" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 8cdced1cb09..bf6f2c3931e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -7597,6 +7597,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, slex->vers_conditions.type : tl->vers_conditions.type; if ((sys_field && (thd->lex->sql_command == SQLCOM_CREATE_VIEW || + slex->nest_level > 0 || vers_hide == VERS_HIDE_FULL && thd->lex->sql_command != SQLCOM_CREATE_TABLE)) || ((fl & HIDDEN_FLAG) && ( !sys_field || diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index eb99544ea0b..f159708b932 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -713,23 +713,127 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) cursor= cursor->next_local) cursor->outer_join|= JOIN_TYPE_OUTER; } - if ((thd->stmt_arena->is_stmt_prepare() || - !thd->stmt_arena->is_stmt_execute()) && - !derived->is_view() && sl->table_list.elements > 0) + + // System Versioning begin +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + if ((thd->stmt_arena->is_stmt_prepare() || !thd->stmt_arena->is_stmt_execute()) + && sl->table_list.elements > 0) { - TABLE_LIST *tl= sl->table_list.first; - if (tl->table && tl->table->versioned()) + // Similar logic as in mysql_create_view() + 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= sl->table_list.first; table; table= table->next_local) + { + 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(sl->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_DERIVED_PROHIBITED, + "Derived table is prohibited: multiple start system fields `%s.%s`, `%s.%s` in query!", MYF(0), + expli_table->alias, + expli_start->field_name, + table->alias, + fld->field_name); + res= true; + goto exit; + } + if (expli_table) + { + if (expli_table != table) + { +expli_table_err: + my_printf_error( + ER_VERS_DERIVED_PROHIBITED, + "Derived table is prohibited: system fields from multiple tables %`s, %`s in query!", MYF(0), + expli_table->alias, + table->alias); + res= true; + goto exit; + } + } + 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_DERIVED_PROHIBITED, + "Derived table is prohibited: multiple end system fields `%s.%s`, `%s.%s` in query!", MYF(0), + expli_table->alias, + expli_end->field_name, + table->alias, + fld->field_name); + res= true; + goto exit; + } + 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 (impli_table) { - TABLE_SHARE *s= tl->table->s; - const char *db= tl->db; - const char *alias= tl->alias; Query_arena_stmt on_stmt_arena(thd); - sl->item_list.push_back(new (thd->mem_root) Item_field( - thd, &sl->context, db, alias, s->vers_start_field()->field_name)); - sl->item_list.push_back(new (thd->mem_root) Item_field( - thd, &sl->context, db, alias, s->vers_end_field()->field_name)); + if (!expli_start && (res= sl->vers_push_field(thd, impli_table, impli_start))) + goto exit; + if (!expli_end && (res= sl->vers_push_field(thd, impli_table, impli_end))) + goto exit; + + if (impli_table->vers_conditions) + sl->vers_derived_conds= impli_table->vers_conditions; + else if (sl->vers_conditions) + sl->vers_derived_conds= sl->vers_conditions; + else + sl->vers_conditions.import_outer= true; } - } + } // if (sl->table_list.elements > 0) +#pragma GCC diagnostic pop + // System Versioning end } unit->derived= derived; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7b61efa8fb8..7c334d7ce67 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2298,6 +2298,7 @@ void st_select_lex::init_select() join= 0; lock_type= TL_READ_DEFAULT; vers_conditions.empty(); + vers_derived_conds.empty(); } /* @@ -7048,7 +7049,6 @@ bool LEX::sp_add_cfetch(THD *thd, const LEX_STRING &name) 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) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 158130b5ddc..66f943e9f17 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -993,6 +993,7 @@ public: /* System Versioning */ vers_select_conds_t vers_conditions; + vers_select_conds_t vers_derived_conds; bool vers_push_field(THD *thd, TABLE_LIST *table, const char* field_name); void init_query(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5ebd3126a23..9ebe9ddf31b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -771,6 +771,28 @@ int vers_setup_select(THD *thd, TABLE_LIST *tables, COND **where_expr, } } + SELECT_LEX *outer_slex= slex->next_select_in_list(); + if (outer_slex) + { + if (slex->vers_derived_conds) + { + // Propagate derived conditions to outer SELECT_LEX: + if (!outer_slex->vers_conditions) + { + (outer_slex->vers_conditions= slex->vers_derived_conds). + from_inner= true; + } + } + else if (slex->vers_conditions.import_outer) + { + // Propagate query conditions from nearest outer SELECT_LEX: + while (outer_slex && (!outer_slex->vers_conditions || outer_slex->vers_conditions.from_inner)) + outer_slex= outer_slex->next_select_in_list(); + if (outer_slex) + slex->vers_conditions= outer_slex->vers_conditions; + } + } + for (table= tables; table; table= table->next_local) { if (table->table && table->table->versioned()) @@ -1059,7 +1081,7 @@ JOIN::prepare(TABLE_LIST *tables_init, remove_redundant_subquery_clauses(select_lex); } - /* Handle FOR SYSTEM_TIME clause. */ + /* System Versioning: handle FOR SYSTEM_TIME clause. */ if (vers_setup_select(thd, tables_list, &conds, select_lex) < 0) DBUG_RETURN(-1); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 37703a2bc11..46dd7570874 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -454,6 +454,9 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, } { /* System Versioning begin */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" TABLE_LIST *impli_table= NULL, *expli_table= NULL; const char *impli_start, *impli_end; Item_field *expli_start= NULL, *expli_end= NULL; @@ -498,7 +501,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, { if (item->real_item()->type() != Item::FIELD_ITEM) continue; - Item_field *fld= (Item_field*) (item->real_item()); + 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); @@ -567,10 +570,15 @@ expli_table_err: 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; + + if (impli_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; + } +#pragma GCC diagnostic pop } /* System Versioning end */ view= lex->unlink_first_table(&link_to_local); diff --git a/sql/table.h b/sql/table.h index 68f3a2dfe75..e03ea37226a 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1853,12 +1853,15 @@ struct vers_select_conds_t { vers_range_type_t type; vers_range_unit_t unit; + bool import_outer:1; + bool from_inner:1; Item *start, *end; void empty() { type= FOR_SYSTEM_TIME_UNSPECIFIED; unit= UNIT_TIMESTAMP; + import_outer= from_inner= false; start= end= NULL; } @@ -1872,6 +1875,7 @@ struct vers_select_conds_t unit= u; start= s; end= e; + import_outer= from_inner= false; } bool init_from_sysvar(THD *thd);