From 836f5a5a1ed7475cf791c72892f0ea2527defad2 Mon Sep 17 00:00:00 2001
From: unknown <bell@sanja.is.com.ua>
Date: Tue, 5 Jul 2005 13:37:02 +0300
Subject: [PATCH] fixed environment creation and cleaning up for processing
 view one by one during checking (BUG#11337)

mysql-test/r/view.result:
  checking views after some view with error (BUG#11337)
mysql-test/t/view.test:
  checking views after some view with error (BUG#11337)
sql/sql_lex.cc:
  environment cleaning up for processing view one by one
sql/sql_lex.h:
  methods for lex cleunup during view processing one by one
sql/sql_table.cc:
  fixed environment creation and cleaning up for processing view one by one (BUG#11337)
---
 mysql-test/r/view.result | 53 ++++++++++++++++++++++++++++++++++++++++
 mysql-test/t/view.test   | 38 ++++++++++++++++++++++++++++
 sql/sql_lex.cc           | 39 +++++++++++++++++++++++++++++
 sql/sql_lex.h            | 11 ++++++---
 sql/sql_table.cc         | 36 +++++++++++++++++++--------
 5 files changed, 164 insertions(+), 13 deletions(-)

diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result
index 68cc0c4cb57..1dc4148c334 100644
--- a/mysql-test/r/view.result
+++ b/mysql-test/r/view.result
@@ -1831,3 +1831,56 @@ select * from v1;
 t
 01:00
 drop view v1;
+CREATE TABLE t1 (col1 time);
+CREATE TABLE t2 (col1 time);
+CREATE VIEW v1 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t1;
+CREATE VIEW v2 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t2;
+CREATE VIEW v3 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t1;
+CREATE VIEW v4 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t2;
+CREATE VIEW v5 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t1;
+CREATE VIEW v6 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t2;
+DROP TABLE t1;
+CHECK TABLE v1, v2, v3, v4, v5, v6;
+Table	Op	Msg_type	Msg_text
+test.v1	check	error	View 'test.v1' references invalid table(s) or column(s) or function(s)
+test.v2	check	status	OK
+test.v3	check	error	View 'test.v3' references invalid table(s) or column(s) or function(s)
+test.v4	check	status	OK
+test.v5	check	error	View 'test.v5' references invalid table(s) or column(s) or function(s)
+test.v6	check	status	OK
+drop view v1, v2, v3, v4, v5, v6;
+drop table t2;
+CREATE TABLE t1 (col1 time);
+CREATE TABLE t2 (col1 time);
+CREATE TABLE t3 (col1 time);
+create function f1 () returns int return (select max(col1) from t1);
+create function f2 () returns int return (select max(col1) from t2);
+CREATE VIEW v1 AS SELECT f1() FROM t3;
+CREATE VIEW v2 AS SELECT f2() FROM t3;
+CREATE VIEW v3 AS SELECT f1() FROM t3;
+CREATE VIEW v4 AS SELECT f2() FROM t3;
+CREATE VIEW v5 AS SELECT f1() FROM t3;
+CREATE VIEW v6 AS SELECT f2() FROM t3;
+drop function f1;
+CHECK TABLE v1, v2, v3, v4, v5, v6;
+Table	Op	Msg_type	Msg_text
+test.v1	check	error	View 'test.v1' references invalid table(s) or column(s) or function(s)
+test.v2	check	status	OK
+test.v3	check	error	View 'test.v3' references invalid table(s) or column(s) or function(s)
+test.v4	check	status	OK
+test.v5	check	error	View 'test.v5' references invalid table(s) or column(s) or function(s)
+test.v6	check	status	OK
+create function f1 () returns int return (select max(col1) from t1);
+DROP TABLE t1;
+CHECK TABLE v1, v2, v3, v4, v5, v6;
+Table	Op	Msg_type	Msg_text
+test.v1	check	error	Table 'test.t1' doesn't exist
+test.v2	check	status	OK
+test.v3	check	error	Table 'test.t1' doesn't exist
+test.v4	check	status	OK
+test.v5	check	error	Table 'test.t1' doesn't exist
+test.v6	check	status	OK
+drop function f1;
+drop function f2;
+drop view v1, v2, v3, v4, v5, v6;
+drop table t2,t3;
diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test
index 13a5f8cef1f..532e40ec28c 100644
--- a/mysql-test/t/view.test
+++ b/mysql-test/t/view.test
@@ -1673,3 +1673,41 @@ create view v1(k, K) as select 1,2;
 create view v1 as SELECT TIME_FORMAT(SEC_TO_TIME(3600),'%H:%i') as t;
 select * from v1;
 drop view v1;
+
+#
+# checking views after some view with error (BUG#11337)
+#
+CREATE TABLE t1 (col1 time);
+CREATE TABLE t2 (col1 time);
+CREATE VIEW v1 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t1;
+CREATE VIEW v2 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t2;
+CREATE VIEW v3 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t1;
+CREATE VIEW v4 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t2;
+CREATE VIEW v5 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t1;
+CREATE VIEW v6 AS SELECT CONVERT_TZ(col1,'GMT','MET') FROM t2;
+DROP TABLE t1;
+CHECK TABLE v1, v2, v3, v4, v5, v6;
+drop view v1, v2, v3, v4, v5, v6;
+drop table t2;
+
+CREATE TABLE t1 (col1 time);
+CREATE TABLE t2 (col1 time);
+CREATE TABLE t3 (col1 time);
+create function f1 () returns int return (select max(col1) from t1);
+create function f2 () returns int return (select max(col1) from t2);
+CREATE VIEW v1 AS SELECT f1() FROM t3;
+CREATE VIEW v2 AS SELECT f2() FROM t3;
+CREATE VIEW v3 AS SELECT f1() FROM t3;
+CREATE VIEW v4 AS SELECT f2() FROM t3;
+CREATE VIEW v5 AS SELECT f1() FROM t3;
+CREATE VIEW v6 AS SELECT f2() FROM t3;
+drop function f1;
+CHECK TABLE v1, v2, v3, v4, v5, v6;
+create function f1 () returns int return (select max(col1) from t1);
+DROP TABLE t1;
+# following will show underlying table until BUG#11555 fix
+CHECK TABLE v1, v2, v3, v4, v5, v6;
+drop function f1;
+drop function f2;
+drop view v1, v2, v3, v4, v5, v6;
+drop table t2,t3;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 08f0c3badf7..1067ce0f6e2 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1916,6 +1916,45 @@ void st_lex::link_first_table_back(TABLE_LIST *first,
 }
 
 
+
+/*
+  cleanup lex for case when we open table by table for processing
+
+  SYNOPSIS
+    st_lex::cleanup_after_one_table_open()
+*/
+
+void st_lex::cleanup_after_one_table_open()
+{
+  /*
+    thd->lex->derived_tables & additional units may be set if we open
+    a view. It is necessary to clear thd->lex->derived_tables flag
+    to prevent processing of derived tables during next open_and_lock_tables
+    if next table is a real table and cleanup & remove underlying units
+    NOTE: all units will be connected to thd->lex->select_lex, because we
+    have not UNION on most upper level.
+    */
+  if (all_selects_list != &select_lex)
+  {
+    derived_tables= 0;
+    /* cleunup underlying units (units of VIEW) */
+    for (SELECT_LEX_UNIT *un= select_lex.first_inner_unit();
+         un;
+         un= un->next_unit())
+      un->cleanup();
+    /* reduce all selects list to default state */
+    all_selects_list= &select_lex;
+    /* remove underlying units (units of VIEW) subtree */
+    select_lex.cut_subtree();
+  }
+  time_zone_tables_used= 0;
+  if (spfuns.records)
+    my_hash_reset(&spfuns);
+  if (spprocs.records)
+    my_hash_reset(&spprocs);
+}
+
+
 /*
   fix some structures at the end of preparation
 
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index a9bfb6da926..ffe3a5ba833 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -371,7 +371,6 @@ typedef class st_select_lex_node SELECT_LEX_NODE;
    SELECT_LEX_UNIT - unit of selects (UNION, INTERSECT, ...) group 
    SELECT_LEXs
 */
-struct st_lex;
 class THD;
 class select_result;
 class JOIN;
@@ -627,7 +626,13 @@ class st_select_lex: public st_select_lex_node
     order_list.first= 0;
     order_list.next= (byte**) &order_list.first;
   }
-  
+  /*
+    This method created for reiniting LEX in mysql_admin_table() and can be
+    used only if you are going remove all SELECT_LEX & units except belonger
+    to LEX (LEX::unit & LEX::select, for other purposes there are
+    SELECT_LEX_UNIT::exclude_level & SELECT_LEX_UNIT::exclude_tree
+  */
+  void cut_subtree() { slave= 0; }
   bool test_limit();
 
   friend void lex_start(THD *thd, uchar *buf, uint length);
@@ -912,7 +917,7 @@ typedef struct st_lex
   {
     return ( query_tables_own_last ? *query_tables_own_last : 0);
   }
-
+  void cleanup_after_one_table_open();
 } LEX;
 
 struct st_lex_local: public st_lex
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 121a89555ce..4f8c14a666e 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -2103,6 +2103,7 @@ static int prepare_for_repair(THD* thd, TABLE_LIST *table_list,
 }
 
 
+
 /*
   RETURN VALUES
     FALSE Message sent to net (admin operation went ok)
@@ -2122,10 +2123,12 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
                                                             HA_CHECK_OPT *),
                               int (view_operator_func)(THD *, TABLE_LIST*))
 {
-  TABLE_LIST *table, *next_global_table;
+  TABLE_LIST *table, *save_next_global, *save_next_local;
+  SELECT_LEX *select= &thd->lex->select_lex;
   List<Item> field_list;
   Item *item;
   Protocol *protocol= thd->protocol;
+  LEX *lex= thd->lex;
   int result_code;
   DBUG_ENTER("mysql_admin_table");
 
@@ -2152,12 +2155,25 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
     thd->open_options|= extra_open_options;
     table->lock_type= lock_type;
     /* open only one table from local list of command */
-    next_global_table= table->next_global;
+    save_next_global= table->next_global;
     table->next_global= 0;
+    save_next_local= table->next_local;
+    table->next_local= 0;
+    select->table_list.first= (byte*)table;
+    /*
+      Time zone tables and SP tables can be add to lex->query_tables list,
+      so it have to be prepared.
+      TODO: Investigate if we can put extra tables into argument instead of
+      using lex->query_tables
+    */
+    lex->query_tables= table;
+    lex->query_tables_last= &table->next_global;
+    lex->query_tables_own_last= 0;;
     thd->no_warnings_for_error= no_warnings_for_error;
     open_and_lock_tables(thd, table);
     thd->no_warnings_for_error= 0;
-    table->next_global= next_global_table;
+    table->next_global= save_next_global;
+    table->next_local= save_next_local;
     /* if view are unsupported */
     if (table->view && view_operator_func == NULL)
     {
@@ -2205,7 +2221,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
         err_msg= (const char *)buf;
       }
       protocol->store(err_msg, system_charset_info);
+      lex->cleanup_after_one_table_open();
       thd->clear_error();
+      /*
+        View opening can be interrupted in the middle of process so some
+        tables can be left opening
+      */
+      close_thread_tables(thd);
       if (protocol->write())
 	goto err;
       continue;
@@ -2274,6 +2296,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
 
 send_result:
 
+    lex->cleanup_after_one_table_open();
     thd->clear_error();  // these errors shouldn't get client
     protocol->prepare_for_resend();
     protocol->store(table_name, system_charset_info);
@@ -2401,13 +2424,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
     }
     close_thread_tables(thd);
     table->table=0;				// For query cache
-    /*
-      thd->lex->derived_tables may be set to non zero value if we open 
-      a view. It is necessary to clear thd->lex->derived_tables flag 
-      to prevent processing of derived tables during next open_and_lock_tables
-      if next table is a real table.
-    */
-    thd->lex->derived_tables= 0;
     if (protocol->write())
       goto err;
   }
-- 
2.30.9