List:Internals« Previous MessageNext Message »
From:dlenev Date:July 28 2005 9:07am
Subject:bk commit into 5.0 tree (dlenev:1.1931) BUG#10055
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of dlenev. When dlenev does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.1931 05/07/28 13:06:52 dlenev@stripped +15 -0
  First version of fix for bug #10055 "Using stored function with
  information_schema causes empty result set".
  
  To enable full access to contents of I_S tables from stored functions
  or statements that use them, we manipulate with thread's open tables
  state and ensure that we won't cause deadlock when we open tables by
  ignoring flushes and global read locks.

  sql/sql_update.cc
    1.163 05/07/28 13:06:45 dlenev@stripped +3 -2
    open_tables()/open_normal_and_derived_tables():
      Added 'flags' argument to be able open tables even if some has done
      a flush or hold namelock on them.

  sql/sql_show.cc
    1.254 05/07/28 13:06:45 dlenev@stripped +61 -24
    get_all_tables():
      Now we use THD::reset_n_/restore_backup_open_tables_state() for 
      saving/restoring open tables state instead of working with it directly
      (This also allows us to have proper content of I_S system tables in
      statements with stored functions and in stored functions). We also
      ignore possible flushes and global read lock when opening tables
      (we create deadlock otherwise). Added back locking of tables to which
      we access when we build contents of I_S.TABLES.
      We also do all needed manipulations with LEX in this function and not
      in get_schema_tables_result() now.
    fill_schema_proc():
      Now we use open_proc_table_for_read/close_proc_table() for access to
      mysql.proc table (so we won't cause deadlock if we already have some
      tables open and locked, this also allows us to have proper content in
      ROUTINES system table in statements using stored functions/in stored
      functions).
    get_schema_tables_result():
      Moved all manipulations with Open_tables_state and LEX needed for
      safe opening of tables to ST_SCHEMA_TABLE::fill_table functions
      (i.e. get_all_tables() and fill_schema_proc()).

  sql/sql_prepare.cc
    1.137 05/07/28 13:06:44 dlenev@stripped +2 -2
    open_tables()/open_normal_and_derived_tables():
      Added 'flags' argument to be able open tables even if some has done
      a flush or hold namelock on them.

  sql/sql_handler.cc
    1.73 05/07/28 13:06:44 dlenev@stripped +1 -1
    open_tables()/open_normal_and_derived_tables():
      Added 'flags' argument to be able open tables even if some has done
      a flush or hold namelock on them.

  sql/sql_class.h
    1.248 05/07/28 13:06:44 dlenev@stripped +9 -4
    Open_tables_state, THD:
      Replaced push_open_tables_state/pop_open_tables_state() methods which
      were saving/restoring current open tables state in/from THD::open_state_list
      with reset_n_backup_open_tables_state/restore_backup_open_tables_state()
      methods which assume that backup storage for this state is allocated on
      stack (or elsewhere) by their caller.

  sql/sql_class.cc
    1.194 05/07/28 13:06:44 dlenev@stripped +19 -24
    Open_tables_state, THD:
      Replaced push_open_tables_state/pop_open_tables_state() methods which
      were saving/restoring current open tables state in/from THD::open_state_list
      with reset_n_backup_open_tables_state/restore_backup_open_tables_state()
      methods which assume that backup storage for this state is allocated on
      stack (or elsewhere) by their caller.

  sql/sql_base.cc
    1.268 05/07/28 13:06:44 dlenev@stripped +15 -9
    close_thread_tables():
      Get rid of 'stopper' argument which is no longer used. Now when we need
      to open and then close some table without touching tables which are already
      opened we use THD::reset_n/restore_backup_open_tables_state() methods.
    open_tables()/open_normal_and_derived_tables():
      Added 'flags' argument to be able open tables even if some has done
      a flush or hold namelock on them.

  sql/sp.h
    1.25 05/07/28 13:06:44 dlenev@stripped +7 -0
    Added declarations of open_proc_table_for_read()/close_proc_table() to be
    able to use them in sql_show.cc.

  sql/sp.cc
    1.83 05/07/28 13:06:44 dlenev@stripped +21 -17
    close_proc_table/open_proc_table_for_read/db_find_routine():
      Replaced push_open_tables_state/pop_open_tables_state() methods which
      were saving/restoring current open tables state in/from THD::open_state_list
      with reset_n_backup_open_tables_state/restore_backup_open_tables_state()
      methods which assume that backup storage for this state is allocated on
      stack (or elsewhere) by their caller.
    open_proc_table_for_read():
      Since now we can have several open tables states stacked up we can't rely
      rely on checking whether we have some tables open in previous state.
      Instead we always assume that some tables are open and we need to ignore
      global read lock and flush while locking mysql.proc.

  sql/mysql_priv.h
    1.327 05/07/28 13:06:44 dlenev@stripped +3 -4
    close_thread_tables():
      Get rid of 'stopper' argument which is no longer used. Now when we need
      to open and then close some table without touching tables which are already
      opened we use THD::reset_n/restore_backup_open_tables_state() methods.
    open_tables()/open_normal_and_derived_tables():
      Added 'flags' argument to be able open tables even if some has done
      a flush or hold namelock on them.

  mysql-test/t/sp.test
    1.130 05/07/28 13:06:44 dlenev@stripped +15 -0
    Added test for bug #10055 "Using stored function with information_schema causes
    empty result set".

  mysql-test/t/information_schema.test
    1.43 05/07/28 13:06:44 dlenev@stripped +9 -3
    Disabled part of test which described functionality which could easily cause
    deadlocks. Added coverage for new behavior in this case.

  mysql-test/r/sp.result
    1.135 05/07/28 13:06:44 dlenev@stripped +9 -0
    Added test for bug #10055 "Using stored function with information_schema causes
    empty result set".

  mysql-test/r/information_schema.result
    1.61 05/07/28 13:06:44 dlenev@stripped +2 -6
    Disabled part of test which described functionality which could easily cause
    deadlocks. Added coverage for new behavior in this case.

  mysql-test/r/alter_table.result
    1.50 05/07/28 13:06:44 dlenev@stripped +2 -2
    Updated test results. This change was caused by the fact that now when
    we build contents of I_S tables (and thus output of SHOW INDEX) we
    don't use instances of tables which may be already opened and locked
    by thread (we always use new instance).

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	dlenev
# Host:	brandersnatch.localdomain
# Root:	/home/dlenev/src/mysql-5.0-is

--- 1.326/sql/mysql_priv.h	2005-07-13 17:50:27 +04:00
+++ 1.327/sql/mysql_priv.h	2005-07-28 13:06:44 +04:00
@@ -482,8 +482,7 @@
 void free_items(Item *item);
 void cleanup_items(Item *item);
 class THD;
-void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0,
-                         TABLE *stopper= 0);
+void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0);
 bool check_one_table_access(THD *thd, ulong privilege,
 			   TABLE_LIST *tables);
 bool check_routine_access(THD *thd,ulong want_access,char *db,char *name,
@@ -924,10 +923,10 @@
 int setup_ftfuncs(SELECT_LEX* select);
 int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
 void wait_for_refresh(THD *thd);
-int open_tables(THD *thd, TABLE_LIST **tables, uint *counter);
+int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
 int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
 bool open_and_lock_tables(THD *thd,TABLE_LIST *tables);
-bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables);
+bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
 int lock_tables(THD *thd, TABLE_LIST *tables, uint counter);
 TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
 			    const char *table_name, bool link_in_list);

--- 1.267/sql/sql_base.cc	2005-07-13 13:57:01 +04:00
+++ 1.268/sql/sql_base.cc	2005-07-28 13:06:44 +04:00
@@ -401,8 +401,7 @@
     upper level) and will leave prelocked mode if needed.
 */
 
-void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived, 
-                         TABLE *stopper)
+void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
 {
   bool found_old_table;
   prelocked_mode_type prelocked_mode= thd->prelocked_mode;
@@ -508,7 +507,7 @@
   DBUG_PRINT("info", ("thd->open_tables: %p", thd->open_tables));
 
  found_old_table= 0;
-  while (thd->open_tables != stopper)
+  while (thd->open_tables)
     found_old_table|=close_thread_table(thd, &thd->open_tables);
   thd->some_tables_deleted=0;
 
@@ -1771,6 +1770,9 @@
     thd - thread handler
     start - list of tables in/out
     counter - number of opened tables will be return using this parameter
+    flags   - bitmap of flags to modify how the tables will be open:
+              MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has
+              done a flush or namelock on it.
 
   NOTE
     Unless we are already in prelocked mode, this function will also precache
@@ -1788,7 +1790,7 @@
     -1 - error
 */
 
-int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
+int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
 {
   TABLE_LIST *tables;
   bool refresh;
@@ -1863,7 +1865,7 @@
     }
     (*counter)++;
     if (!tables->table &&
-	!(tables->table= open_table(thd, tables, &new_frm_mem, &refresh, 0)))
+	!(tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags)))
     {
       free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
       if (tables->view)
@@ -2089,7 +2091,8 @@
 {
   DBUG_ENTER("simple_open_n_lock_tables");
   uint counter;
-  if (open_tables(thd, &tables, &counter) || lock_tables(thd, tables, counter))
+  if (open_tables(thd, &tables, &counter, 0) ||
+      lock_tables(thd, tables, counter))
     DBUG_RETURN(-1);				/* purecov: inspected */
   DBUG_RETURN(0);
 }
@@ -2116,7 +2119,7 @@
 {
   uint counter;
   DBUG_ENTER("open_and_lock_tables");
-  if (open_tables(thd, &tables, &counter) ||
+  if (open_tables(thd, &tables, &counter, 0) ||
       lock_tables(thd, tables, counter) ||
       mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
       (thd->fill_derived_tables() &&
@@ -2133,6 +2136,9 @@
     open_normal_and_derived_tables
     thd		- thread handler
     tables	- list of tables for open
+    flags       - bitmap of flags to modify how the tables will be open:
+                  MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has
+                  done a flush or namelock on it.
 
   RETURN
     FALSE - ok
@@ -2143,12 +2149,12 @@
     data from the tables.
 */
 
-bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables)
+bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags)
 {
   uint counter;
   DBUG_ENTER("open_normal_and_derived_tables");
   DBUG_ASSERT(!thd->fill_derived_tables());
-  if (open_tables(thd, &tables, &counter) ||
+  if (open_tables(thd, &tables, &counter, flags) ||
       mysql_handle_derived(thd->lex, &mysql_derived_prepare))
     DBUG_RETURN(TRUE); /* purecov: inspected */
   DBUG_RETURN(0);

--- 1.193/sql/sql_class.cc	2005-07-13 13:48:03 +04:00
+++ 1.194/sql/sql_class.cc	2005-07-28 13:06:44 +04:00
@@ -157,8 +157,8 @@
 ** Thread specific functions
 ****************************************************************************/
 
-Open_tables_state::Open_tables_state()
-  :version(refresh_version)
+Open_tables_state::Open_tables_state(ulong version_arg)
+  :version(version_arg)
 {
   reset_open_tables_state();
 }
@@ -172,7 +172,7 @@
 
 THD::THD()
   :Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0),
-   Open_tables_state(),
+   Open_tables_state(refresh_version),
    user_time(0), global_read_lock(0), is_fatal_error(0),
    rand_used(0), time_zone_used(0),
    last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0),
@@ -1789,31 +1789,26 @@
   access to mysql.proc table to find definitions of stored routines.
 ****************************************************************************/
 
-bool THD::push_open_tables_state()
+void THD::reset_n_backup_open_tables_state(Open_tables_state *backup)
 {
-  Open_tables_state *state;
-  DBUG_ENTER("push_open_table_state");
-  /* Currently we only push things one level */
-  DBUG_ASSERT(open_state_list.elements == 0);
-
-  if (!(state= (Open_tables_state*) alloc(sizeof(*state))))
-    DBUG_RETURN(1);                             // Fatal error is set
-  /* Store state for currently open tables */
-  state->set_open_tables_state(this);
-  if (open_state_list.push_back(state, mem_root))
-    DBUG_RETURN(1);                             // Fatal error is set
+  DBUG_ENTER("reset_n_backup_open_tables_state");
+  backup->set_open_tables_state(this);
   reset_open_tables_state();
-  DBUG_RETURN(0);
+  DBUG_VOID_RETURN;
 }
 
-void THD::pop_open_tables_state()
-{
-  Open_tables_state *state;
-  DBUG_ENTER("pop_open_table_state");
-  /* Currently we only push things one level */
-  DBUG_ASSERT(open_state_list.elements == 1);
 
-  state= open_state_list.pop();
-  set_open_tables_state(state);
+void THD::restore_backup_open_tables_state(Open_tables_state *backup)
+{
+  DBUG_ENTER("restore_backup_open_tables_state");
+  /*
+    Before we will throw away current open tables state we want
+    to be sure that it was properly cleaned up.
+  */
+  DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 &&
+              handler_tables == 0 && derived_tables == 0 &&
+              lock == 0 && locked_tables == 0 &&
+              prelocked_mode == NON_PRELOCKED);
+  set_open_tables_state(backup);
   DBUG_VOID_RETURN;
 }

--- 1.247/sql/sql_class.h	2005-07-13 13:48:03 +04:00
+++ 1.248/sql/sql_class.h	2005-07-28 13:06:44 +04:00
@@ -996,7 +996,13 @@
   ulong	version;
   uint current_tablenr;
 
-  Open_tables_state();
+  /*
+    This constructor serves for creation of Open_tables_state instances
+    which are used as backup storage.
+  */
+  Open_tables_state() {};
+
+  Open_tables_state(ulong version_arg);
 
   void set_open_tables_state(Open_tables_state *state)
   {
@@ -1203,7 +1209,6 @@
   List	     <MYSQL_ERROR> warn_list;
   uint	     warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
   uint	     total_warn_count;
-  List	     <Open_tables_state> open_state_list;
   /*
     Id of current query. Statement can be reused to execute several queries
     query_id is global in context of the whole MySQL server.
@@ -1457,8 +1462,8 @@
              (variables.sql_mode & MODE_STRICT_ALL_TABLES)));
   }
   void set_status_var_init();
-  bool push_open_tables_state();
-  void pop_open_tables_state();
+  void reset_n_backup_open_tables_state(Open_tables_state *backup);
+  void restore_backup_open_tables_state(Open_tables_state *backup);
 };
 
 

--- 1.253/sql/sql_show.cc	2005-07-07 17:48:40 +04:00
+++ 1.254/sql/sql_show.cc	2005-07-28 13:06:45 +04:00
@@ -20,6 +20,7 @@
 #include "mysql_priv.h"
 #include "sql_select.h"                         // For select_describe
 #include "repl_failsafe.h"
+#include "sp.h"
 #include "sp_head.h"
 #include <my_dir.h>
 
@@ -351,7 +352,7 @@
   thd->lex->view_prepare_mode= TRUE;
 
   /* Only one table for now, but VIEW can involve several tables */
-  if (open_normal_and_derived_tables(thd, table_list))
+  if (open_normal_and_derived_tables(thd, table_list, 0))
   {
     DBUG_RETURN(TRUE);
   }
@@ -550,7 +551,7 @@
   DBUG_ENTER("mysqld_list_fields");
   DBUG_PRINT("enter",("table: %s",table_list->table_name));
 
-  if (open_normal_and_derived_tables(thd, table_list))
+  if (open_normal_and_derived_tables(thd, table_list, 0))
     DBUG_VOID_RETURN;
   table= table_list->table;
 
@@ -1934,6 +1935,8 @@
   TABLE *table= tables->table;
   SELECT_LEX *select_lex= &lex->select_lex;
   SELECT_LEX *old_all_select_lex= lex->all_selects_list;
+  TABLE_LIST **old_query_tables_last= lex->query_tables_last;
+  enum_sql_command old_sql_command= lex->sql_command;
   SELECT_LEX *lsel= tables->schema_select_lex;
   ST_SCHEMA_TABLE *schema_table= tables->schema_table;
   SELECT_LEX sel;
@@ -1948,34 +1951,68 @@
   COND *partial_cond; 
   uint derived_tables= lex->derived_tables; 
   int error= 1;
+  Open_tables_state open_tables_state_backup;
+  bool some_tables_locked= thd->lock && thd->lock->lock_count ||
+                           thd->locked_tables && thd->locked_tables->lock_count;
   DBUG_ENTER("get_all_tables");
 
   LINT_INIT(end);
   LINT_INIT(len);
 
+  /*
+    Let us set fake sql_command so views won't try to merge
+    themselves into main statement.
+
+    QQ: May be it is better to create proxy LEX than do all
+        that juggling ?
+  */
+  lex->sql_command= SQLCOM_SHOW_FIELDS;
+  thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
+
+  schema_table_idx= get_schema_table_idx(schema_table);
+
   if (lsel)
   {
-    TABLE *old_open_tables= thd->open_tables;
     TABLE_LIST *show_table_list= (TABLE_LIST*) lsel->table_list.first;
     bool res;
+    /*
+      This branch works only if we have SHOW COLUMNS or SHOW KEYS/INDEX
+      commands with explicitly specified table, so we don't need to
+      handle locking of tables here (like we do it for SCH_TABLES).
+    */
+    DBUG_ASSERT(schema_table_idx == SCH_COLUMNS ||
+                schema_table_idx == SCH_STATISTICS);
 
     lex->all_selects_list= lsel;
-    res= open_normal_and_derived_tables(thd, show_table_list);
+    res= open_normal_and_derived_tables(thd, show_table_list,
+                                        MYSQL_LOCK_IGNORE_FLUSH);
     if (schema_table->process_table(thd, show_table_list,
                                     table, res, show_table_list->db,
                                     show_table_list->alias))
       goto err;
-    close_thread_tables(thd, 0, 0, old_open_tables);
+    close_thread_tables(thd);
     show_table_list->table= 0;
     error= 0;
     goto err;
   }
 
-  schema_table_idx= get_schema_table_idx(schema_table);
   lock_type= TL_UNLOCK;
 
   if (schema_table_idx == SCH_TABLES)
+  {
+    if (some_tables_locked)
+    {
+      /*
+        We can't open and _lock_ tables if we already have some
+        tables locked since it can cause deadlock.
+        QQ: May be we should produce warning here ? Or even better try to
+            extract as much information as possible without locking ?
+      */
+      error= 0;
+      goto err;
+    }
     lock_type= TL_READ;
+  }
 
   if (make_db_list(thd, &bases, &idx_field_vals,
                    &with_i_schema, 0))
@@ -2058,19 +2095,25 @@
           else
           {
             int res;
-            TABLE *old_open_tables= thd->open_tables;
             if (make_table_list(thd, &sel, base_name, file_name))
               goto err;
             TABLE_LIST *show_table_list= (TABLE_LIST*) sel.table_list.first;
             show_table_list->lock_type= lock_type;
             lex->all_selects_list= &sel;
             lex->derived_tables= 0;
-            res= open_normal_and_derived_tables(thd, show_table_list);
+            if (lock_type == TL_READ)
+            {
+              DBUG_ASSERT(!some_tables_locked);
+              res= open_and_lock_tables(thd, show_table_list);
+            }
+            else
+              res= open_normal_and_derived_tables(thd, show_table_list,
+                                                  MYSQL_LOCK_IGNORE_FLUSH);
             if (schema_table->process_table(thd, show_table_list, table,
                                             res, base_name,
                                             show_table_list->alias))
               goto err;
-            close_thread_tables(thd, 0, 0, old_open_tables);
+            close_thread_tables(thd);
           }
         }
       }
@@ -2084,8 +2127,12 @@
 
   error= 0;
 err:
+  thd->restore_backup_open_tables_state(&open_tables_state_backup);
   lex->derived_tables= derived_tables;
   lex->all_selects_list= old_all_select_lex;
+  lex->query_tables_last= old_query_tables_last;
+  *old_query_tables_last= 0;
+  lex->sql_command= old_sql_command;
   DBUG_RETURN(error);
 }
 
@@ -2711,12 +2758,14 @@
   TABLE_LIST proc_tables;
   const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
   int res= 0;
-  TABLE *table= tables->table, *old_open_tables= thd->open_tables;
+  TABLE *table= tables->table;
   bool full_access;
   char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
+  Open_tables_state open_tables_state_backup;
   DBUG_ENTER("fill_schema_proc");
 
   strxmov(definer, thd->priv_user, "@", thd->priv_host, NullS);
+  /* We use this TABLE_LIST instance only for checking of privileges. */
   bzero((char*) &proc_tables,sizeof(proc_tables));
   proc_tables.db= (char*) "mysql";
   proc_tables.db_length= 5;
@@ -2724,7 +2773,7 @@
   proc_tables.table_name_length= 4;
   proc_tables.lock_type= TL_READ;
   full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1);
-  if (!(proc_table= open_ltable(thd, &proc_tables, TL_READ)))
+  if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup)))
   {
     DBUG_RETURN(1);
   }
@@ -2750,7 +2799,7 @@
 
 err:
   proc_table->file->ha_index_end();
-  close_thread_tables(thd, 0, 0, old_open_tables);
+  close_proc_table(thd, &open_tables_state_backup);
   DBUG_RETURN(res);
 }
 
@@ -3567,11 +3616,6 @@
     TABLE_LIST *table_list= tab->table->pos_in_table_list;
     if (table_list->schema_table && thd->fill_derived_tables())
     {
-      TABLE_LIST **query_tables_last= lex->query_tables_last;
-      TABLE *old_derived_tables= thd->derived_tables;
-      MYSQL_LOCK *sql_lock= thd->lock;
-      lex->sql_command= SQLCOM_SHOW_FIELDS;
-      DBUG_ASSERT(!*query_tables_last);
       if (&lex->unit != lex->current_select->master_unit()) // is subselect
       {
         table_list->table->file->extra(HA_EXTRA_RESET_STATE);
@@ -3582,16 +3626,9 @@
       else
         table_list->table->file->records= 0;
 
-      thd->derived_tables= 0;
-      thd->lock=0;
       if (table_list->schema_table->fill_table(thd, table_list,
                                                tab->select_cond))
         result= 1;
-      thd->lock= sql_lock;
-      lex->sql_command= SQLCOM_SELECT;
-      thd->derived_tables= old_derived_tables;
-      lex->query_tables_last= query_tables_last;
-      *query_tables_last= 0;
     }
   }
   thd->no_warnings_for_error= 0;

--- 1.162/sql/sql_update.cc	2005-07-13 17:12:18 +04:00
+++ 1.163/sql/sql_update.cc	2005-07-28 13:06:45 +04:00
@@ -138,7 +138,7 @@
 
   LINT_INIT(timestamp_query_id);
 
-  if (open_tables(thd, &table_list, &table_count))
+  if (open_tables(thd, &table_list, &table_count, 0))
     DBUG_RETURN(1);
 
   if (table_list->multitable_view)
@@ -632,7 +632,8 @@
   thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
 
   /* open tables and create derived ones, but do not lock and fill them */
-  if ((original_multiupdate && open_tables(thd, &table_list, & table_count)) ||
+  if ((original_multiupdate &&
+       open_tables(thd, &table_list, &table_count, 0)) ||
       mysql_handle_derived(lex, &mysql_derived_prepare))
     DBUG_RETURN(TRUE);
   /*

--- 1.60/mysql-test/r/information_schema.result	2005-07-13 12:29:37 +04:00
+++ 1.61/mysql-test/r/information_schema.result	2005-07-28 13:06:44 +04:00
@@ -465,12 +465,8 @@
 drop table t1;
 create table t1 (s1 int);
 insert into t1 values (0),(9),(0);
-select s1 from t1 where s1 in (select version from
-information_schema.tables) union select version from
-information_schema.tables;
-s1
-0
-10
+select * from t1, information_schema.tables;
+s1	TABLE_CATALOG	TABLE_SCHEMA	TABLE_NAME	TABLE_TYPE	ENGINE	VERSION	ROW_FORMAT	TABLE_ROWS	AVG_ROW_LENGTH	DATA_LENGTH	MAX_DATA_LENGTH	INDEX_LENGTH	DATA_FREE	AUTO_INCREMENT	CREATE_TIME	UPDATE_TIME	CHECK_TIME	TABLE_COLLATION	CHECKSUM	CREATE_OPTIONS	TABLE_COMMENT
 drop table t1;
 SHOW CREATE TABLE INFORMATION_SCHEMA.character_sets;
 Table	Create Table

--- 1.42/mysql-test/t/information_schema.test	2005-07-06 02:24:32 +04:00
+++ 1.43/mysql-test/t/information_schema.test	2005-07-28 13:06:44 +04:00
@@ -274,9 +274,15 @@
 
 create table t1 (s1 int);
 insert into t1 values (0),(9),(0);
-select s1 from t1 where s1 in (select version from
-information_schema.tables) union select version from
-information_schema.tables;
+# We can't allow full access to INFORMATION_SCHEMA.TABLES system view
+# from queries which also use ordinary tables or under LOCK TABLES
+# since this can easily cause deadlock. Disabled this part of test until
+# this limitation will be removed.
+# select s1 from t1 where s1 in (select version from
+# information_schema.tables) union select version from
+# information_schema.tables;
+# Added coverage for new behavior in this case.
+select * from t1, information_schema.tables;
 drop table t1;
 
 SHOW CREATE TABLE INFORMATION_SCHEMA.character_sets;

--- 1.49/mysql-test/r/alter_table.result	2005-05-16 16:21:31 +04:00
+++ 1.50/mysql-test/r/alter_table.result	2005-07-28 13:06:44 +04:00
@@ -314,7 +314,7 @@
 SHOW INDEX FROM t1;
 Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment
 t1	0	PRIMARY	1	Host	A	NULL	NULL	NULL		BTREE	
-t1	0	PRIMARY	2	User	A	3	NULL	NULL		BTREE	
+t1	0	PRIMARY	2	User	A	0	NULL	NULL		BTREE	
 ALTER TABLE t1 ENABLE KEYS;
 UNLOCK TABLES;
 CHECK TABLES t1;
@@ -338,7 +338,7 @@
 SHOW INDEX FROM t1;
 Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment
 t1	0	PRIMARY	1	Host	A	NULL	NULL	NULL		BTREE	
-t1	0	PRIMARY	2	User	A	2	NULL	NULL		BTREE	
+t1	0	PRIMARY	2	User	A	0	NULL	NULL		BTREE	
 t1	1	Host	1	Host	A	NULL	NULL	NULL		BTREE	disabled
 ALTER TABLE t1 ENABLE KEYS;
 SHOW INDEX FROM t1;

--- 1.134/mysql-test/r/sp.result	2005-07-11 14:46:33 +04:00
+++ 1.135/mysql-test/r/sp.result	2005-07-28 13:06:44 +04:00
@@ -3062,4 +3062,13 @@
 drop procedure bug6063|
 drop procedure bug7088_1|
 drop procedure bug7088_2|
+drop function if exists bug10055|
+create function bug10055(v char(255)) returns char(255) return lower(v)|
+select t.column_name, bug10055(t.column_name)
+from information_schema.columns as t
+where t.table_schema = 'test' and t.table_name = 't1'|
+column_name	bug10055(t.column_name)
+id	id
+data	data
+drop function bug10055|
 drop table t1,t2;

--- 1.129/mysql-test/t/sp.test	2005-07-11 14:46:33 +04:00
+++ 1.130/mysql-test/t/sp.test	2005-07-28 13:06:44 +04:00
@@ -3834,6 +3834,21 @@
 
 
 #
+# Bug#10055 "Using stored function with information_schema causes empty
+#            result set"
+#
+--disable_warnings
+drop function if exists bug10055|
+--enable_warnings
+create function bug10055(v char(255)) returns char(255) return lower(v)|
+# This select should not crash server and should return all fields in t1
+select t.column_name, bug10055(t.column_name)
+from information_schema.columns as t
+where t.table_schema = 'test' and t.table_name = 't1'|
+drop function bug10055|
+
+
+#
 # BUG#NNNN: New bug synopsis
 #
 #--disable_warnings

--- 1.82/sql/sp.cc	2005-07-13 14:20:38 +04:00
+++ 1.83/sql/sp.cc	2005-07-28 13:06:44 +04:00
@@ -68,13 +68,16 @@
 
   SYNOPSIS
     close_proc_table()
-      thd  Thread context
+      thd     Thread context
+      backup  Pointer to Open_tables_state instance which holds
+              information about tables which were open before we
+              decided to access mysql.proc.
 */
 
-static void close_proc_table(THD *thd)
+void close_proc_table(THD *thd, Open_tables_state *backup)
 {
   close_thread_tables(thd);
-  thd->pop_open_tables_state();
+  thd->restore_backup_open_tables_state(backup);
 }
 
 
@@ -83,7 +86,10 @@
 
   SYNOPSIS
     open_proc_table_for_read()
-      thd  Thread context
+      thd     Thread context
+      backup  Pointer to Open_tables_state instance where information about
+              currently open tables will be saved, and from which will be
+              restored when we will end work with mysql.proc.
 
   NOTES
     Thanks to restrictions which we put on opening and locking of
@@ -97,11 +103,10 @@
     #	Pointer to TABLE object of mysql.proc
 */
 
-static TABLE *open_proc_table_for_read(THD *thd)
+TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
 {
   TABLE_LIST tables;
   TABLE *table;
-  bool old_open_tables= thd->open_tables != 0;
   bool refresh;
   DBUG_ENTER("open_proc_table");
 
@@ -112,8 +117,7 @@
   if (!mysql_proc_table_exists)
     DBUG_RETURN(0);
 
-  if (thd->push_open_tables_state())
-    DBUG_RETURN(0);
+  thd->reset_n_backup_open_tables_state(backup);
 
   bzero((char*) &tables, sizeof(tables));
   tables.db= (char*) "mysql";
@@ -121,7 +125,7 @@
   if (!(table= open_table(thd, &tables, thd->mem_root, &refresh,
                           MYSQL_LOCK_IGNORE_FLUSH)))
   {
-    thd->pop_open_tables_state();
+    thd->restore_backup_open_tables_state(backup);
     mysql_proc_table_exists= 0;
     DBUG_RETURN(0);
   }
@@ -130,15 +134,14 @@
 
   table->reginfo.lock_type= TL_READ;
   /*
-    If we have other tables opened, we have to ensure we are not blocked
-    by a flush tables or global read lock, as this could lead to a deadlock
+    We have to ensure we are not blocked by a flush tables or global read
+    lock, as this could lead to a deadlock if we have other tables opened.
   */
   if (!(thd->lock= mysql_lock_tables(thd, &table, 1,
-                                     old_open_tables ?
                                      (MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK |
-                                      MYSQL_LOCK_IGNORE_FLUSH) : 0)))
+                                      MYSQL_LOCK_IGNORE_FLUSH))))
   {
-    close_proc_table(thd);
+    close_proc_table(thd, backup);
     DBUG_RETURN(0);
   }
   DBUG_RETURN(table);
@@ -271,12 +274,13 @@
   char buff[65];
   String str(buff, sizeof(buff), &my_charset_bin);
   ulong sql_mode;
+  Open_tables_state open_tables_state_backup;
   DBUG_ENTER("db_find_routine");
   DBUG_PRINT("enter", ("type: %d name: %*s",
 		       type, name->m_name.length, name->m_name.str));
 
   *sphp= 0;                                     // In case of errors
-  if (!(table= open_proc_table_for_read(thd)))
+  if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup)))
     DBUG_RETURN(SP_OPEN_TABLE_FAILED);
 
   if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK)
@@ -371,7 +375,7 @@
   chistics.comment.str= ptr;
   chistics.comment.length= length;
 
-  close_proc_table(thd);
+  close_proc_table(thd, &open_tables_state_backup);
   table= 0;
 
   {
@@ -449,7 +453,7 @@
 
  done:
   if (table)
-    close_proc_table(thd);
+    close_proc_table(thd, &open_tables_state_backup);
   DBUG_RETURN(ret);
 }
 

--- 1.24/sql/sp.h	2005-07-13 13:57:01 +04:00
+++ 1.25/sql/sp.h	2005-07-28 13:06:44 +04:00
@@ -90,6 +90,13 @@
 
 extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first);
 
+/*
+  Routines which allow open/lock and close mysql.proc table even when
+  we already have some tables open and locked.
+*/
+TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup);
+void close_proc_table(THD *thd, Open_tables_state *backup);
+
 //
 // Utilities...
 //

--- 1.72/sql/sql_handler.cc	2005-07-01 08:05:36 +04:00
+++ 1.73/sql/sql_handler.cc	2005-07-28 13:06:44 +04:00
@@ -187,7 +187,7 @@
 
   /* for now HANDLER can be used only for real TABLES */
   tables->required_type= FRMTYPE_TABLE;
-  error= open_tables(thd, &tables, &counter);
+  error= open_tables(thd, &tables, &counter, 0);
 
   HANDLER_TABLES_HACK(thd);
   if (error)

--- 1.136/sql/sql_prepare.cc	2005-07-14 15:27:14 +04:00
+++ 1.137/sql/sql_prepare.cc	2005-07-28 13:06:44 +04:00
@@ -925,7 +925,7 @@
     If we would use locks, then we have to ensure we are not using
     TL_WRITE_DELAYED as having two such locks can cause table corruption.
   */
-  if (open_normal_and_derived_tables(thd, table_list))
+  if (open_normal_and_derived_tables(thd, table_list, 0))
     goto error;
 
   if ((values= its++))
@@ -1005,7 +1005,7 @@
   if (update_precheck(thd, table_list))
     goto error;
 
-  if (open_tables(thd, &table_list, &table_count))
+  if (open_tables(thd, &table_list, &table_count, 0))
     goto error;
 
   if (table_list->multitable_view)
Thread
bk commit into 5.0 tree (dlenev:1.1931) BUG#10055dlenev28 Jul