From 22d4f87e6984bd086217147ec8afb1ab6a8059ae Mon Sep 17 00:00:00 2001 From: "Sinisa@sinisa.nasamreza.org" <> Date: Tue, 26 Mar 2002 15:06:05 +0200 Subject: [PATCH] Derived tables ! --- .bzrignore | 2 + mysql-test/r/derived.result | 10 +++ mysql-test/r/union.result | 10 +++ mysql-test/t/derived.test | 7 +++ mysql-test/t/union.test | 4 ++ sql/mysql_priv.h | 1 + sql/sql_class.h | 6 +- sql/sql_derived.cc | 121 ++++++++++++++++++++++++++++++++++++ sql/sql_lex.h | 8 +-- sql/sql_parse.cc | 18 ++++-- sql/sql_union.cc | 8 ++- sql/sql_yacc.yy | 24 +++++++ sql/table.h | 1 + 13 files changed, 208 insertions(+), 12 deletions(-) create mode 100644 mysql-test/r/derived.result create mode 100644 mysql-test/t/derived.test create mode 100644 sql/sql_derived.cc diff --git a/.bzrignore b/.bzrignore index f2da28f44f..cfd2ca463c 100644 --- a/.bzrignore +++ b/.bzrignore @@ -466,3 +466,5 @@ vio/viotest-ssl libmysqld/gstream.cc libmysqld/spatial.cc sql/sql_yacc.yy.orig +Docs/manual.texi.orig +Docs/manual.texi.rej diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result new file mode 100644 index 0000000000..5a41a51463 --- /dev/null +++ b/mysql-test/r/derived.result @@ -0,0 +1,10 @@ +drop table if exists t1,t2; +CREATE TABLE t1 (a int not null, b char (10) not null); +insert into t1 values(1,'a'),(2,'b'),(3,'c'),(3,'c'); +CREATE TABLE t2 (a int not null, b char (10) not null); +insert into t2 values (3,'c'),(4,'d'),(5,'f'),(6,'e'); +select t1.a,t3.y from t1,(select a as y from t2 where b='c') as t3 where t1.a = t3.y; +a y +3 3 +3 3 +drop table if exists t1.t2; diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index 113f668016..ef82b41442 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -177,4 +177,14 @@ a 11 12 13 +(select * from t1 limit 2) union (select * from t2 limit 20,3); +a +1 +2 +set SQL_SELECT_LIMIT=2; +(select * from t1 limit 2) union (select * from t2 limit 3); +a +1 +2 +set SQL_SELECT_LIMIT=DEFAULT; drop table t1,t2; diff --git a/mysql-test/t/derived.test b/mysql-test/t/derived.test new file mode 100644 index 0000000000..4a3e8e86d4 --- /dev/null +++ b/mysql-test/t/derived.test @@ -0,0 +1,7 @@ +drop table if exists t1,t2; +CREATE TABLE t1 (a int not null, b char (10) not null); +insert into t1 values(1,'a'),(2,'b'),(3,'c'),(3,'c'); +CREATE TABLE t2 (a int not null, b char (10) not null); +insert into t2 values (3,'c'),(4,'d'),(5,'f'),(6,'e'); +select t1.a,t3.y from t1,(select a as y from t2 where b='c') as t3 where t1.a = t3.y; +drop table if exists t1.t2; \ No newline at end of file diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index f648ebfd48..086351e9da 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -84,4 +84,8 @@ insert into t1 values (1),(2),(3),(4),(5); insert into t2 values (11),(12),(13),(14),(15); (select * from t1 limit 2) union (select * from t2 limit 3) limit 4; (select * from t1 limit 2) union (select * from t2 limit 3); +(select * from t1 limit 2) union (select * from t2 limit 20,3); +set SQL_SELECT_LIMIT=2; +(select * from t1 limit 2) union (select * from t2 limit 3); +set SQL_SELECT_LIMIT=DEFAULT; drop table t1,t2; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 728d6d7a35..9f9ec22923 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -335,6 +335,7 @@ int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &list,COND *conds, ORDER *order, ORDER *group,Item *having,ORDER *proc_param, ulong select_type,select_result *result); int mysql_union(THD *thd,LEX *lex,select_result *result); +int mysql_derived(THD *thd,LEX *lex,SELECT_LEX *s, TABLE_LIST *t); Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item_result_field ***copy_func, Field **from_field, bool group,bool modify_item); diff --git a/sql/sql_class.h b/sql/sql_class.h index 355987c259..2c945ce733 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -659,15 +659,17 @@ class Table_ident :public Sql_alloc { public: LEX_STRING db; LEX_STRING table; + SELECT_LEX *sel; inline Table_ident(LEX_STRING db_arg,LEX_STRING table_arg,bool force) - :table(table_arg) + :table(table_arg), sel((SELECT_LEX *)0) { if (!force && (current_thd->client_capabilities & CLIENT_NO_SCHEMA)) db.str=0; else db= db_arg; } - inline Table_ident(LEX_STRING table_arg) :table(table_arg) {db.str=0;} + inline Table_ident(LEX_STRING table_arg) :table(table_arg), sel((SELECT_LEX *)0) {db.str=0;} + inline Table_ident(SELECT_LEX *s) : sel(s) {db.str=0; table.str=(char *)""; table.length=0;} inline void change_db(char *db_name) { db.str= db_name; db.length=(uint) strlen(db_name); } }; diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc new file mode 100644 index 0000000000..f1b60dd9b8 --- /dev/null +++ b/sql/sql_derived.cc @@ -0,0 +1,121 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* + Derived tables + These were introduced by Monty and Sinisa <sinisa@mysql.com> +*/ + + +#include "mysql_priv.h" +#include "sql_select.h" +#include "sql_acl.h" + +static const char *any_db="*any*"; // Special symbol for check_access + + +int mysql_derived(THD *thd, LEX *lex,SELECT_LEX *s, TABLE_LIST *t) +{ + SELECT_LEX *sl=s; + List<Item> item_list; + TABLE *table; + int res; + select_union *derived_result; + TABLE_LIST *tables=(TABLE_LIST *)sl->table_list.first; + TMP_TABLE_PARAM tmp_table_param; + DBUG_ENTER("mysql_derived"); + + if (tables) + res=check_table_access(thd,SELECT_ACL, tables); + else + res=check_access(thd, SELECT_ACL, any_db); + if (res) + DBUG_RETURN(-1); + + for (TABLE_LIST *cursor= (TABLE_LIST *)tables; + cursor; + cursor=cursor->next) + { + if (cursor->derived) + { + res=mysql_derived(thd,lex,(SELECT_LEX *)cursor->derived,cursor); + if (res) DBUG_RETURN(res); + } + } + Item *item; + List_iterator<Item> it(sl->item_list); + + while ((item= it++)) + item_list.push_back(item); + + if (!(res=open_and_lock_tables(thd,tables))) + { + if (tables && setup_fields(thd,tables,item_list,0,0,1)) + { + res=-1; + goto exit; + } + bzero((char*) &tmp_table_param,sizeof(tmp_table_param)); + tmp_table_param.field_count=item_list.elements; + if (!(table=create_tmp_table(thd, &tmp_table_param, sl->item_list, + (ORDER*) 0, 0, 1, 0, + (sl->options | thd->options | TMP_TABLE_ALL_COLUMNS)))) + { + res=-1; + goto exit; + } + + if ((derived_result=new select_union(table))) + { + thd->offset_limit=sl->offset_limit; + thd->select_limit=sl->select_limit+sl->offset_limit; + if (thd->select_limit < sl->select_limit) + thd->select_limit= HA_POS_ERROR; + if (thd->select_limit == HA_POS_ERROR) + sl->options&= ~OPTION_FOUND_ROWS; + + res=mysql_select(thd, tables, sl->item_list, + sl->where, (ORDER *) sl->order_list.first, + (ORDER*) sl->group_list.first, + sl->having, (ORDER*) NULL, + sl->options | thd->options | SELECT_NO_UNLOCK, + derived_result); + if (!res) + { +// Here we entirely fix both TABLE_LIST and list of SELECT's as there were no derived tables + if (derived_result->flush()) + res=1; + else + { + t->real_name=table->real_name; + t->table=table; + sl->prev->next=sl->next; + t->derived=(SELECT_LEX *)0; // just in case ... + if (!sl->next) lex->last_select = sl; + } + } + delete derived_result; + } + if (res) + free_tmp_table(thd,table); +exit: + close_thread_tables(thd); + if (res > 0) + send_error(&thd->net, ER_UNKNOWN_COM_ERROR); // temporary only ... + } + DBUG_RETURN(res); +} diff --git a/sql/sql_lex.h b/sql/sql_lex.h index c0ede015eb..5a4cabbfb9 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -101,7 +101,7 @@ typedef struct st_lex_master_info } LEX_MASTER_INFO; -enum sub_select_type {UNSPECIFIED_TYPE,UNION_TYPE, INTERSECT_TYPE, EXCEPT_TYPE, NOT_A_SELECT}; +enum sub_select_type {UNSPECIFIED_TYPE,UNION_TYPE, INTERSECT_TYPE, EXCEPT_TYPE, NOT_A_SELECT, DERIVED_TABLE_TYPE}; /* The state of the lex parsing for selects */ @@ -120,7 +120,7 @@ typedef struct st_select_lex { List<Item_func_match> ftfunc_list; uint in_sum_expr, sort_default; bool create_refs, braces; - st_select_lex *next; + st_select_lex *next, *prev; } SELECT_LEX; @@ -141,7 +141,7 @@ public: typedef struct st_lex { uint yylineno,yytoklen; /* Simulate lex */ LEX_YYSTYPE yylval; - SELECT_LEX select_lex, *select; + SELECT_LEX select_lex, *select, *last_select; uchar *ptr,*tok_start,*tok_end,*end_of_query; char *length,*dec,*change,*name; char *backup_dir; /* For RESTORE/BACKUP */ @@ -185,7 +185,7 @@ typedef struct st_lex { uint grant,grant_tot_col,which_columns, union_option, mqh; thr_lock_type lock_option; bool drop_primary,drop_if_exists,local_file; - bool in_comment,ignore_space,verbose,simple_alter, option_type; + bool in_comment,ignore_space,verbose,simple_alter, option_type, derived_tables; } LEX; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7fd3db1fe2..b916122ee7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1226,6 +1226,14 @@ mysql_execute_command(void) Skip if we are in the slave thread, some table rules have been given and the table list says the query should not be replicated */ + if (lex->derived_tables) + { + for (TABLE_LIST *cursor= tables; + cursor; + cursor=cursor->next) + if (cursor->derived && mysql_derived(thd,lex,(SELECT_LEX *)cursor->derived,cursor)) + DBUG_VOID_RETURN; + } if ((lex->select_lex.next && create_total_list(thd,lex,&tables)) || (table_rules_on && tables && thd->slave_thread && !tables_ok(thd,tables))) @@ -2648,7 +2656,7 @@ mysql_init_query(THD *thd) thd->lex.value_list.empty(); thd->lex.select_lex.table_list.elements=0; thd->free_list=0; thd->lex.union_option=0; - thd->lex.select = &thd->lex.select_lex; + thd->lex.select = thd->lex.last_select = &thd->lex.select_lex; thd->lex.select_lex.table_list.first=0; thd->lex.select_lex.table_list.next= (byte**) &thd->lex.select_lex.table_list.first; thd->lex.select_lex.next=0; @@ -2675,7 +2683,7 @@ mysql_init_select(LEX *lex) select_lex->order_list.next= (byte**) &select_lex->order_list.first; select_lex->group_list.first=0; select_lex->group_list.next= (byte**) &select_lex->group_list.first; - select_lex->next = (SELECT_LEX *)NULL; + select_lex->next = select_lex->prev = (SELECT_LEX *)NULL; } bool @@ -2684,8 +2692,9 @@ mysql_new_select(LEX *lex) SELECT_LEX *select_lex = (SELECT_LEX *) lex->thd->calloc(sizeof(SELECT_LEX)); if (!select_lex) return 1; + lex->select=lex->last_select; lex->select->next=select_lex; - lex->select=select_lex; + lex->select=lex->last_select=select_lex; select_lex->table_list.next= (byte**) &select_lex->table_list.first; select_lex->item_list.empty(); select_lex->when_list.empty(); @@ -3099,7 +3108,7 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, DBUG_RETURN(0); // End of memory alias_str= alias ? alias->str : table->table.str; if (table->table.length > NAME_LEN || - check_table_name(table->table.str,table->table.length) || + (table->table.length && check_table_name(table->table.str,table->table.length)) || table->db.str && check_db_name(table->db.str)) { net_printf(&thd->net,ER_WRONG_TABLE_NAME,table->table.str); @@ -3122,6 +3131,7 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, ptr->real_name=table->table.str; ptr->lock_type=flags; ptr->updating=updating; + ptr->derived=(SELECT_LEX *)table->sel; if (use_index) ptr->use_index=(List<String> *) thd->memdup((gptr) use_index, sizeof(*use_index)); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 7532f43a66..1658fa701c 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -126,8 +126,9 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) } union_result->save_time_stamp=!describe; - for (sl=lex->select=&lex->select_lex;sl;sl=lex->select=sl->next) + for (sl= &lex->select_lex; sl; sl=sl->next) { + lex->select=sl; thd->offset_limit=sl->offset_limit; thd->select_limit=sl->select_limit+sl->offset_limit; if (thd->select_limit < sl->select_limit) @@ -185,7 +186,10 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) thd->options&= ~OPTION_FOUND_ROWS; } else - thd->select_limit= HA_POS_ERROR; // no limit + { + thd->offset_limit= 0; + thd->select_limit= thd->default_select_limit; + } if (describe) thd->select_limit= HA_POS_ERROR; // no limit res=mysql_select(thd,&result_table_list, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 3c9c005fb4..ee31e726eb 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2099,6 +2099,30 @@ join_table: } | '{' ident join_table LEFT OUTER JOIN_SYM join_table ON expr '}' { add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; } + | '(' SELECT_SYM select_part3 ')' opt_table_alias + { + LEX *lex=Lex; + lex->select=lex->select->prev; + if (!($$=add_table_to_list(new Table_ident(Lex->last_select),$5,0,TL_UNLOCK))) + YYABORT; + } + +select_part3: + { + LEX *lex=Lex; + lex->derived_tables=true; + SELECT_LEX *tmp=lex->select; + if (lex->select->linkage == NOT_A_SELECT || mysql_new_select(lex)) + YYABORT; + mysql_init_select(lex); + lex->select->linkage=DERIVED_TABLE_TYPE; + lex->select->prev=tmp; + } + select_options select_item_list select_intoto + +select_intoto: + limit_clause {} + | select_from opt_outer: /* empty */ {} diff --git a/sql/table.h b/sql/table.h index 259c34030b..211a8ea816 100644 --- a/sql/table.h +++ b/sql/table.h @@ -147,6 +147,7 @@ typedef struct st_table_list { bool straight; /* optimize with prev table */ bool updating; /* for replicate-do/ignore table */ bool shared; /* Used twice in union */ + void *derived; } TABLE_LIST; typedef struct st_open_table_list -- 2.30.9