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