List:Commits« Previous MessageNext Message »
From:Dmitry Lenev Date:April 5 2011 12:38pm
Subject:bzr push into mysql-trunk branch (Dmitry.Lenev:3535 to 3536) Bug#11746602
View as plain text  
 3536 Dmitry Lenev	2011-04-05
      Another follow-up for the patch for Bug#11746602
      27480: Extend CREATE TEMPORARY TABLES privilege to
      allow temp table operations).
      
      Got rid of extra call to open_temporary_tables() during
      HANDLER OPEN introduced by one of previous patches.
      This call to open_temporary_tables() was necessary for 
      privilege checking. It became possible to remove it by 
      moving privilege check inside of mysql_ha_open() call
      
      Also all HANDLER commands were converted to using Sql_cmd
      infrastructure.
     @ mysql-test/include/handler.inc
        Adjusted test case to the fact that now HANDLER OPEN pre-opens
        temporary tables only after it checks if it is called under
        LOCK TABLES.
     @ mysql-test/r/handler_innodb.result
        Adjusted test case to the fact that now HANDLER OPEN pre-opens
        temporary tables only after it checks if it is called under
        LOCK TABLES.
     @ mysql-test/r/handler_myisam.result
        Adjusted test case to the fact that now HANDLER OPEN pre-opens
        temporary tables only after it checks if it is called under
        LOCK TABLES.
     @ sql/sql_handler.cc
        Got rid of extra call to open_temporary_tables() during
        HANDLER OPEN. This call to open_temporary_tables() was
        necessary for privilege checking. It became possible to
        remove it by moving privilege check inside of mysql_ha_open()
        call. To support this change mysql_ha_open() was split in
        two parts one specific for HANDLER OPEN and one common for
        HANDLER OPEN and re-open of table for HANDLER READ.
        Also all HANDLER commands were converted to using Sql_cmd
        infrastructure.
     @ sql/sql_handler.h
        Now all HANDLER commands use Sql_cmd infrastructure.
     @ sql/sql_lex.h
        Since HANDLER READ command is now represented by
        class inherited from Sql_cmd there is no need in
        LEX::ha_read_mode/ha_rkey_mode members.
        Since we still need to keep value for ha_rkey_mode
        during statement parsing, until the moment when
        Sql_cmd_handler_read instance is constructed, a
        corresponding member was added to Yacc_state.
     @ sql/sql_parse.cc
        All HANDLER commands were moved to Sql_cmd infrastructure.
        Also got rid of extra call to open_temporary_tables()
        for HANDLER OPEN statement. Now instead of pre-opening
        temporary tables separately, for privilege checking, we do 
        privilege checking after main call to open_temporary_tables(),
        inside of Sql_cmd_handler_open::execute() method.
     @ sql/sql_yacc.yy
        Changed part of grammar for HANDLER statements to use
        Sql_cmd infrastructure.

    modified:
      mysql-test/include/handler.inc
      mysql-test/r/handler_innodb.result
      mysql-test/r/handler_myisam.result
      sql/sql_handler.cc
      sql/sql_handler.h
      sql/sql_lex.h
      sql/sql_parse.cc
      sql/sql_yacc.yy
 3535 Dmitry Lenev	2011-04-02
      Yet another follow-up for the patch for Bug#11746602
      27480: Extend CREATE TEMPORARY TABLES privilege to
      allow temp table operations).
      
      This patch tries to eliminate performance overhead which
      was introduced by one of previous patches for this bug.
      
      One of those patches added pre-opening of temporary tables as
      a separate stage of statement execution, handled by a separate
      call to open_temporary_tables(). As result we started to
      construct table definition key twice per statement almost for
      each table to be open by statement.
      
      This patch addresses the issue by changing code to avoid
      construction of keys and borrowing pre-constructed key from
      TABLE_LIST::mdl_request::key instead, where it is possible.
     @ sql/sql_admin.cc
        Adjusted prepare_for_repair() code to use get_table_def_key()
        instead of create_table_def_key() to get table definition
        cache key (the former piggy-backs on pre-constructed
        TABLE_LIST::mdl_request::key instead of constructing new
        instance of key like the latter).
     @ sql/sql_base.cc
        - Introduced get_table_def_key() function which allows to
          get table definition cache key for the table list element
          cheaply, by piggy-backing on pre-constructed
          TABLE_LIST::mdl_request::key.
          Adjusted code to use this function instead of
          create_table_def_key(), which constructs new instance of
          key, where possible.
        - Made create_table_def_key() private to sql_base.cc as it
          is no longer used outside of this file. Also changed it to
          accept database and table name instead of table list element
          since now it is only used in cases when table list element
          is not easily available.
          Changed some code which specifically constructed temporary
          TABLE_LIST objects to call create_table_def_key() to not
          to do this.
        - Changed find_temporary_table(TABLE_LIST) to use key borrowed
          from TABLE_LIST::mdl_request::key instead of especially
          constructed key. This change allows us to save on key
          construction but adds a small overhead during key comparison.
          Such a trade-off should be acceptable as overhead during key
          comparison is only a few instructions.
        - Signatures of get_table_share/get_table_share_with_discover()
          and tdc_open_view() were changed to accept constant table
          definition key to allow them to be used in cases when keys
          were borrowed from MDL subsystem.
     @ sql/sql_base.h
        - Introduced get_table_def_key() function which allows to
          get table definition cache key for the table list element
          cheaply, by piggy-backing on pre-constructed
          TABLE_LIST::mdl_request::key.
          key, where possible.
        - Made create_table_def_key() private to sql_base.cc as it
          is no longer used outside of this file.
        - Signatures of get_table_share/get_table_share_with_discover()
          and tdc_open_view() were changed to accept constant table
          definition key to allow them to be used in cases when keys
          were borrowed from MDL subsystem.
     @ sql/sql_show.cc
        Adjusted fill_schema_table_from_frm() code to use
        get_table_def_key() instead of create_table_def_key() to get
        table definition cache key (the former piggy-backs on
        pre-constructed TABLE_LIST::mdl_request::key instead of
        constructing new instance of key like the latter).
     @ sql/sql_table.cc
        Changed two places in mysql_alter_table() code where we tried
        to open tables using table list element objects which were not
        fully/correctly initialized and which, therefore, were impossible
        to use to get table definition cache key from them.
     @ sql/sql_view.cc
        Adjusted fill_defined_view_parts() code to use
        get_table_def_key() instead of create_table_def_key() to
        get table definition cache key (the former piggy-backs on
        pre-constructed TABLE_LIST::mdl_request::key instead of
        constructing new instance of key like the latter).
     @ sql/table.cc
        Changed signature of alloc_table_share() to accept constant
        table definition key to allow it to be used in cases when
        key is borrowed from MDL subsystem.
     @ sql/table.h
        Changed signature of alloc_table_share() to accept constant
        table definition key to allow it to be used in cases when
        key is borrowed from MDL subsystem.

    modified:
      sql/sql_admin.cc
      sql/sql_base.cc
      sql/sql_base.h
      sql/sql_show.cc
      sql/sql_table.cc
      sql/sql_view.cc
      sql/table.cc
      sql/table.h
=== modified file 'mysql-test/include/handler.inc'
--- a/mysql-test/include/handler.inc	2011-03-26 10:56:27 +0000
+++ b/mysql-test/include/handler.inc	2011-04-05 12:37:41 +0000
@@ -819,7 +819,7 @@ handler t1 open;
 handler t1 read next;
 --error ER_LOCK_OR_ACTIVE_TRANSACTION
 handler t2 close;
---error ER_CANT_REOPEN_TABLE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 handler t3 open;
 --error ER_LOCK_OR_ACTIVE_TRANSACTION
 handler t4 open;

=== modified file 'mysql-test/r/handler_innodb.result'
--- a/mysql-test/r/handler_innodb.result	2011-03-26 10:56:27 +0000
+++ b/mysql-test/r/handler_innodb.result	2011-04-05 12:37:41 +0000
@@ -800,7 +800,7 @@ ERROR HY000: Can't execute the given com
 handler t2 close;
 ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 handler t3 open;
-ERROR HY000: Can't reopen table: 't3'
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 handler t4 open;
 ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 # After UNLOCK TABLES handlers should be around and

=== modified file 'mysql-test/r/handler_myisam.result'
--- a/mysql-test/r/handler_myisam.result	2011-03-26 10:56:27 +0000
+++ b/mysql-test/r/handler_myisam.result	2011-04-05 12:37:41 +0000
@@ -798,7 +798,7 @@ ERROR HY000: Can't execute the given com
 handler t2 close;
 ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 handler t3 open;
-ERROR HY000: Can't reopen table: 't3'
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 handler t4 open;
 ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 # After UNLOCK TABLES handlers should be around and

=== modified file 'sql/sql_handler.cc'
--- a/sql/sql_handler.cc	2011-03-26 10:56:27 +0000
+++ b/sql/sql_handler.cc	2011-04-05 12:37:41 +0000
@@ -60,12 +60,15 @@
 #include "sql_base.h"                           // insert_fields
 #include "sql_select.h"
 #include "transaction.h"
+#include "sql_parse.h"                          // check_table_access
 
 #define HANDLER_TABLES_HASH_SIZE 120
 
 static enum enum_ha_read_modes rkey_to_rnext[]=
 { RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
 
+static bool mysql_ha_open_table(THD *thd, TABLE_LIST *table);
+
 /*
   Get hash key and hash key length.
 
@@ -150,40 +153,25 @@ static void mysql_ha_close_table(THD *th
   tables->mdl_request.ticket= NULL;
 }
 
-/*
-  Open a HANDLER table.
 
-  SYNOPSIS
-    mysql_ha_open()
-    thd                         Thread identifier.
-    tables                      A list of tables with the first entry to open.
-    reopen                      Re-open a previously opened handler table.
+/**
+  Execute a HANDLER OPEN statement.
 
-  DESCRIPTION
-    Though this function takes a list of tables, only the first list entry
-    will be opened.
-    'reopen' is set when a handler table is to be re-opened. In this case,
-    'tables' is the pointer to the hashed TABLE_LIST object which has been
-    saved on the original open.
-    'reopen' is also used to suppress the sending of an 'ok' message.
+  @param  thd   The current thread.
 
-  RETURN
-    FALSE OK
-    TRUE  Error
+  @retval FALSE on success.
+  @retval TRUE on failure.
 */
 
-bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
+bool Sql_cmd_handler_open::execute(THD *thd)
 {
   TABLE_LIST    *hash_tables = NULL;
   char          *db, *name, *alias;
-  uint          dblen, namelen, aliaslen, counter;
-  bool          error;
-  TABLE         *backup_open_tables;
-  MDL_savepoint mdl_savepoint;
-  DBUG_ENTER("mysql_ha_open");
-  DBUG_PRINT("enter",("'%s'.'%s' as '%s'  reopen: %d",
-                      tables->db, tables->table_name, tables->alias,
-                      (int) reopen));
+  uint          dblen, namelen, aliaslen;
+  TABLE_LIST    *tables= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
+  DBUG_ENTER("Sql_cmd_handler_open::execute");
+  DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
+                      tables->db, tables->table_name, tables->alias));
 
   if (thd->locked_tables_mode)
   {
@@ -212,8 +200,15 @@ bool mysql_ha_open(THD *thd, TABLE_LIST 
       DBUG_RETURN(TRUE);
     }
   }
-  else if (! reopen) /* Otherwise we have 'tables' already. */
+  else
   {
+    /*
+      Otherwise we might have handler with the same name already.
+
+      Note that it is safe to disclose this information before doing privilege
+      check. Current user can always find out that handler is open by using
+      HANDLER ... READ command, which doesn't requires any privileges.
+    */
     if (my_hash_search(&thd->handler_tables_hash, (uchar*) tables->alias,
                        strlen(tables->alias) + 1))
     {
@@ -224,50 +219,84 @@ bool mysql_ha_open(THD *thd, TABLE_LIST 
     }
   }
 
-  if (! reopen)
+  /* copy the TABLE_LIST struct */
+  dblen= strlen(tables->db) + 1;
+  namelen= strlen(tables->table_name) + 1;
+  aliaslen= strlen(tables->alias) + 1;
+  if (!(my_multi_malloc(MYF(MY_WME),
+                        &hash_tables, (uint) sizeof(*hash_tables),
+                        &db, (uint) dblen,
+                        &name, (uint) namelen,
+                        &alias, (uint) aliaslen,
+                        NullS)))
   {
-    /* copy the TABLE_LIST struct */
-    dblen= strlen(tables->db) + 1;
-    namelen= strlen(tables->table_name) + 1;
-    aliaslen= strlen(tables->alias) + 1;
-    if (!(my_multi_malloc(MYF(MY_WME),
-                          &hash_tables, (uint) sizeof(*hash_tables),
-                          &db, (uint) dblen,
-                          &name, (uint) namelen,
-                          &alias, (uint) aliaslen,
-                          NullS)))
-    {
-      DBUG_PRINT("exit",("ERROR"));
-      DBUG_RETURN(TRUE);
-    }
-    /* structure copy */
-    *hash_tables= *tables;
-    hash_tables->db= db;
-    hash_tables->table_name= name;
-    hash_tables->alias= alias;
-    memcpy(hash_tables->db, tables->db, dblen);
-    memcpy(hash_tables->table_name, tables->table_name, namelen);
-    memcpy(hash_tables->alias, tables->alias, aliaslen);
-    /*
-      We can't request lock with explicit duration for this table
-      right from the start as open_tables() can't handle properly
-      back-off for such locks.
-    */
-    hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED,
-                                  MDL_TRANSACTION);
-    /* for now HANDLER can be used only for real TABLES */
-    hash_tables->required_type= FRMTYPE_TABLE;
+    DBUG_PRINT("exit",("ERROR"));
+    DBUG_RETURN(TRUE);
+  }
+  /* structure copy */
+  *hash_tables= *tables;
+  hash_tables->db= db;
+  hash_tables->table_name= name;
+  hash_tables->alias= alias;
+  memcpy(hash_tables->db, tables->db, dblen);
+  memcpy(hash_tables->table_name, tables->table_name, namelen);
+  memcpy(hash_tables->alias, tables->alias, aliaslen);
+  /*
+    We can't request lock with explicit duration for this table
+    right from the start as open_tables() can't handle properly
+    back-off for such locks.
+  */
+  hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED,
+                                MDL_TRANSACTION);
+  /* for now HANDLER can be used only for real TABLES */
+  hash_tables->required_type= FRMTYPE_TABLE;
 
-    /* add to hash */
-    if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
-    {
-      my_free(hash_tables);
-      DBUG_PRINT("exit",("ERROR"));
-      DBUG_RETURN(TRUE);
-    }
+  /* add to hash */
+  if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables))
+  {
+    my_free(hash_tables);
+    DBUG_PRINT("exit",("ERROR"));
+    DBUG_RETURN(TRUE);
   }
-  else
-    hash_tables= tables;
+
+  if (open_temporary_tables(thd, hash_tables) ||
+      check_table_access(thd, SELECT_ACL, hash_tables, FALSE, UINT_MAX,
+                         FALSE) ||
+      mysql_ha_open_table(thd, hash_tables))
+
+  {
+    my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
+    DBUG_PRINT("exit",("ERROR"));
+    DBUG_RETURN(TRUE);
+  }
+
+  my_ok(thd);
+
+  DBUG_PRINT("exit",("OK"));
+  DBUG_RETURN(FALSE);
+}
+
+
+/**
+  Auxiliary function which opens or re-opens table for HANDLER statements.
+
+  @param thd          Thread context..
+  @param hash_tables  Table list element for table to open.
+
+  @retval FALSE - Success.
+  @retval TRUE  - Failure.
+*/
+
+static bool mysql_ha_open_table(THD *thd, TABLE_LIST *hash_tables)
+{
+  TABLE         *backup_open_tables;
+  MDL_savepoint mdl_savepoint;
+  uint          counter;
+  bool          error;
+
+  DBUG_ENTER("mysql_ha_open_table");
+
+  DBUG_ASSERT(!thd->locked_tables_mode);
 
   /*
     Save and reset the open_tables list so that open_tables() won't
@@ -282,20 +311,17 @@ bool mysql_ha_open(THD *thd, TABLE_LIST 
   mdl_savepoint= thd->mdl_context.mdl_savepoint();
 
   /*
-    open_tables() will set 'hash_tables->table' if successful.
-    It must be NULL for a real open when calling open_tables().
+    'hash_tables->table' must be NULL, unless there is pre-opened
+    temporary table. open_tables() will set it if successful.
   */
-  DBUG_ASSERT(! hash_tables->table);
-
-  error= open_temporary_tables(thd, hash_tables);
+  DBUG_ASSERT(! hash_tables->table || is_temporary_table(hash_tables));
 
-  if (!error)
-    error= open_tables(thd, &hash_tables, &counter, 0);
+  error= open_tables(thd, &hash_tables, &counter, 0);
 
   if (! error &&
       ! (hash_tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
   {
-    my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
+    my_error(ER_ILLEGAL_HA, MYF(0), hash_tables->alias);
     error= TRUE;
   }
   if (!error &&
@@ -311,21 +337,16 @@ bool mysql_ha_open(THD *thd, TABLE_LIST 
   {
     /*
       No need to rollback statement transaction, it's not started.
-      If called with reopen flag, no need to rollback either,
+      If called for re-open, no need to rollback either,
       it will be done at statement end.
     */
     DBUG_ASSERT(thd->transaction.stmt.is_empty());
     close_thread_tables(thd);
     thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
     thd->set_open_tables(backup_open_tables);
-    if (!reopen)
-      my_hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
-    else
-    {
-      hash_tables->table= NULL;
-      /* Safety, cleanup the pointer to satisfy MDL assertions. */
-      hash_tables->mdl_request.ticket= NULL;
-    }
+    hash_tables->table= NULL;
+    /* Safety, cleanup the pointer to satisfy MDL assertions. */
+    hash_tables->mdl_request.ticket= NULL;
     DBUG_PRINT("exit",("ERROR"));
     DBUG_RETURN(TRUE);
   }
@@ -352,34 +373,28 @@ bool mysql_ha_open(THD *thd, TABLE_LIST 
   */
   hash_tables->table->open_by_handler= 1;
 
-  if (! reopen)
-    my_ok(thd);
   DBUG_PRINT("exit",("OK"));
   DBUG_RETURN(FALSE);
 }
 
 
-/*
-  Close a HANDLER table by alias or table name
+/**
+  Execute a HANDLER CLOSE statement.
 
-  SYNOPSIS
-    mysql_ha_close()
-    thd                         Thread identifier.
-    tables                      A list of tables with the first entry to close.
+  @param  thd   The current thread.
 
-  DESCRIPTION
-    Closes the table that is associated (on the handler tables hash) with the
-    name (table->alias) of the specified table.
+  @note  Closes the table that is associated (on the handler tables hash)
+         with the name (TABLE_LIST::alias) of the specified table.
 
-  RETURN
-    FALSE ok
-    TRUE  error
+  @retval FALSE on success.
+  @retval TRUE on failure.
 */
 
-bool mysql_ha_close(THD *thd, TABLE_LIST *tables)
+bool Sql_cmd_handler_close::execute(THD *thd)
 {
+  TABLE_LIST    *tables= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
   TABLE_LIST    *hash_tables;
-  DBUG_ENTER("mysql_ha_close");
+  DBUG_ENTER("Sql_cmd_handler_close::execute");
   DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
                       tables->db, tables->table_name, tables->alias));
 
@@ -465,31 +480,19 @@ handle_condition(THD *thd,
 }
 
 
-/*
-  Read from a HANDLER table.
+/**
+  Execute a HANDLER READ statement.
 
-  SYNOPSIS
-    mysql_ha_read()
-    thd                         Thread identifier.
-    tables                      A list of tables with the first entry to read.
-    mode
-    keyname
-    key_expr
-    ha_rkey_mode
-    cond
-    select_limit_cnt
-    offset_limit_cnt
+  @param  thd   The current thread.
 
-  RETURN
-    FALSE ok
-    TRUE  error
+  @note  Closes the table that is associated (on the handler tables hash)
+         with the name (TABLE_LIST::alias) of the specified table.
+
+  @retval FALSE on success.
+  @retval TRUE on failure.
 */
- 
-bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
-                   enum enum_ha_read_modes mode, char *keyname,
-                   List<Item> *key_expr,
-                   enum ha_rkey_function ha_rkey_mode, Item *cond,
-                   ha_rows select_limit_cnt, ha_rows offset_limit_cnt)
+
+bool Sql_cmd_handler_read::execute(THD *thd)
 {
   TABLE_LIST    *hash_tables;
   TABLE         *table, *backup_open_tables;
@@ -503,7 +506,14 @@ bool mysql_ha_read(THD *thd, TABLE_LIST 
   uchar		*UNINIT_VAR(key);
   uint		UNINIT_VAR(key_len);
   Sql_handler_lock_error_handler sql_handler_lock_error;
-  DBUG_ENTER("mysql_ha_read");
+  LEX           *lex= thd->lex;
+  SELECT_LEX    *select_lex= &lex->select_lex;
+  SELECT_LEX_UNIT *unit= &lex->unit;
+  TABLE_LIST    *tables= select_lex->table_list.first;
+  enum enum_ha_read_modes mode= m_read_mode;
+  Item          *cond= select_lex->where;
+  ha_rows select_limit_cnt, offset_limit_cnt;
+  DBUG_ENTER("Sql_cmd_handler_read::execute");
   DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
                       tables->db, tables->table_name, tables->alias));
 
@@ -513,8 +523,19 @@ bool mysql_ha_read(THD *thd, TABLE_LIST 
     DBUG_RETURN(TRUE);
   }
 
-  thd->lex->select_lex.context.resolve_in_table_list_only(tables);
-  list.push_front(new Item_field(&thd->lex->select_lex.context,
+  /*
+    There is no need to check for table permissions here, because
+    if a user has no permissions to read a table, he won't be
+    able to open it (with SQLCOM_HA_OPEN) in the first place.
+  */
+
+  /* Get limit counters from SELECT_LEX. */
+  unit->set_limit(select_lex);
+  select_limit_cnt= unit->select_limit_cnt;
+  offset_limit_cnt= unit->offset_limit_cnt;
+
+  select_lex->context.resolve_in_table_list_only(tables);
+  list.push_front(new Item_field(&select_lex->context,
                                  NULL, NULL, "*"));
   List_iterator<Item> it(list);
   it++;
@@ -533,7 +554,7 @@ retry:
       /*
         The handler table has been closed. Re-open it.
       */
-      if (mysql_ha_open(thd, hash_tables, 1))
+      if (mysql_ha_open_table(thd, hash_tables))
       {
         DBUG_PRINT("exit",("reopen failed"));
         goto err0;
@@ -609,11 +630,11 @@ retry:
       goto err;
   }
 
-  if (keyname)
+  if (m_key_name)
   {
-    if ((keyno=find_type(keyname, &table->s->keynames, 1+2)-1)<0)
+    if ((keyno=find_type((char*)m_key_name, &table->s->keynames, 1+2)-1)<0)
     {
-      my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), keyname, tables->alias);
+      my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), m_key_name, tables->alias);
       goto err;
     }
     /* Check if the same index involved. */
@@ -626,7 +647,7 @@ retry:
     }
   }
 
-  if (insert_fields(thd, &thd->lex->select_lex.context,
+  if (insert_fields(thd, &select_lex->context,
                     tables->db, tables->alias, &it, 0))
     goto err;
 
@@ -646,7 +667,7 @@ retry:
     case RNEXT:
       if (table->file->inited != handler::NONE)
       {
-        if (keyname)
+        if (m_key_name)
         {
           /* Check if we read from the same index. */
           DBUG_ASSERT((uint) keyno == table->file->get_index());
@@ -660,7 +681,7 @@ retry:
       }
       /* else fall through */
     case RFIRST:
-      if (keyname)
+      if (m_key_name)
       {
         table->file->ha_index_or_rnd_end();
         table->file->ha_index_init(keyno, 1);
@@ -675,7 +696,7 @@ retry:
       mode=RNEXT;
       break;
     case RPREV:
-      DBUG_ASSERT(keyname != 0);
+      DBUG_ASSERT(m_key_name != 0);
       /* Check if we read from the same index. */
       DBUG_ASSERT((uint) keyno == table->file->get_index());
       if (table->file->inited != handler::NONE)
@@ -685,7 +706,7 @@ retry:
       }
       /* else fall through */
     case RLAST:
-      DBUG_ASSERT(keyname != 0);
+      DBUG_ASSERT(m_key_name != 0);
       table->file->ha_index_or_rnd_end();
       table->file->ha_index_init(keyno, 1);
       error= table->file->ha_index_last(table->record[0]);
@@ -693,20 +714,20 @@ retry:
       break;
     case RNEXT_SAME:
       /* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...)  */
-      DBUG_ASSERT(keyname != 0);
+      DBUG_ASSERT(m_key_name != 0);
       error= table->file->ha_index_next_same(table->record[0], key, key_len);
       break;
     case RKEY:
     {
-      DBUG_ASSERT(keyname != 0);
+      DBUG_ASSERT(m_key_name != 0);
       KEY *keyinfo=table->key_info+keyno;
       KEY_PART_INFO *key_part=keyinfo->key_part;
-      if (key_expr->elements > keyinfo->key_parts)
+      if (m_key_expr->elements > keyinfo->key_parts)
       {
 	my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->key_parts);
 	goto err;
       }
-      List_iterator<Item> it_ke(*key_expr);
+      List_iterator<Item> it_ke(*m_key_expr);
       Item *item;
       key_part_map keypart_map;
       for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
@@ -735,8 +756,8 @@ retry:
       table->file->ha_index_init(keyno, 1);
       key_copy(key, table->record[0], table->key_info + keyno, key_len);
       error= table->file->ha_index_read_map(table->record[0],
-                                            key, keypart_map, ha_rkey_mode);
-      mode=rkey_to_rnext[(int)ha_rkey_mode];
+                                            key, keypart_map, m_rkey_mode);
+      mode=rkey_to_rnext[(int)m_rkey_mode];
       break;
     }
     default:

=== modified file 'sql/sql_handler.h'
--- a/sql/sql_handler.h	2010-11-18 16:34:56 +0000
+++ b/sql/sql_handler.h	2011-04-05 12:37:41 +0000
@@ -23,10 +23,103 @@
 class THD;
 struct TABLE_LIST;
 
-bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen);
-bool mysql_ha_close(THD *thd, TABLE_LIST *tables);
-bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *,
-                   List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows);
+/**
+  Sql_cmd_handler_open represents HANDLER OPEN statement.
+
+  @note Some information about this statement, for example, table to be
+        opened is still kept in LEX class.
+*/
+
+class Sql_cmd_handler_open : public Sql_cmd
+{
+public:
+  Sql_cmd_handler_open()
+  {}
+
+  virtual ~Sql_cmd_handler_open()
+  {}
+
+  virtual enum_sql_command sql_command_code() const
+  {
+    return SQLCOM_HA_OPEN;
+  }
+
+  virtual bool execute(THD *thd);
+};
+
+
+/**
+  Sql_cmd_handler_read represents HANDLER READ statement.
+
+  @note Some information about this statement, for example, table
+        list element which identifies HANDLER to be read from,
+        WHERE and LIMIT clauses is still kept in LEX class.
+*/
+
+class Sql_cmd_handler_read : public Sql_cmd
+{
+public:
+  Sql_cmd_handler_read(enum_ha_read_modes read_mode,
+                       const char *key_name,
+                       List<Item> *key_expr,
+                       ha_rkey_function rkey_mode)
+    : m_read_mode(read_mode), m_key_name(key_name), m_key_expr(key_expr),
+      m_rkey_mode(rkey_mode)
+  {}
+
+  virtual ~Sql_cmd_handler_read()
+  {}
+
+  virtual enum_sql_command sql_command_code() const
+  {
+    return SQLCOM_HA_READ;
+  }
+
+  virtual bool execute(THD *thd);
+
+private:
+  /** Read mode for HANDLER READ: FIRST, NEXT, LAST, ... */
+  enum enum_ha_read_modes m_read_mode;
+
+  /**
+    Name of key to be used for reading,
+    NULL in cases when natural row-order is to be used.
+  */
+  const char *m_key_name;
+
+  /** Key values to be satisfied. */
+  List<Item> *m_key_expr;
+
+  /** Type of condition for key values to be satisfied. */
+  enum ha_rkey_function m_rkey_mode;
+};
+
+
+/**
+  Sql_cmd_handler_close represents HANDLER CLOSE statement.
+
+  @note Table list element which identifies HANDLER to be closed
+        still resides in LEX class.
+*/
+
+class Sql_cmd_handler_close : public Sql_cmd
+{
+public:
+  Sql_cmd_handler_close()
+  {}
+
+  virtual ~Sql_cmd_handler_close()
+  {}
+
+  virtual enum_sql_command sql_command_code() const
+  {
+    return SQLCOM_HA_CLOSE;
+  }
+
+  virtual bool execute(THD *thd);
+};
+
+
 void mysql_ha_flush(THD *thd);
 void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables);
 void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables);

=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h	2011-03-08 09:21:39 +0000
+++ b/sql/sql_lex.h	2011-04-05 12:37:41 +0000
@@ -2149,11 +2149,7 @@ struct LEX: public Query_tables_list
   enum SSL_type ssl_type;			/* defined in violite.h */
   enum enum_duplicates duplicates;
   enum enum_tx_isolation tx_isolation;
-  enum enum_ha_read_modes ha_read_mode;
-  union {
-    enum ha_rkey_function ha_rkey_mode;
-    enum xa_option_words xa_opt;
-  };
+  enum xa_option_words xa_opt;
   enum enum_var_type option_type;
   enum enum_view_create_mode create_view_mode;
   enum enum_drop_mode drop_mode;
@@ -2459,6 +2455,7 @@ public:
     m_set_signal_info.clear();
     m_lock_type= TL_READ_DEFAULT;
     m_mdl_type= MDL_SHARED_READ;
+    m_ha_rkey_mode= HA_READ_KEY_EXACT;
   }
 
   ~Yacc_state();
@@ -2471,6 +2468,7 @@ public:
   {
     m_lock_type= TL_READ_DEFAULT;
     m_mdl_type= MDL_SHARED_READ;
+    m_ha_rkey_mode= HA_READ_KEY_EXACT; /* Let us be future-proof. */
   }
 
   /**
@@ -2516,6 +2514,9 @@ public:
   */
   enum_mdl_type m_mdl_type;
 
+  /** Type of condition for key in HANDLER READ statement. */
+  enum ha_rkey_function m_ha_rkey_mode;
+
   /*
     TODO: move more attributes from the LEX structure here.
   */

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2011-04-01 18:08:48 +0000
+++ b/sql/sql_parse.cc	2011-04-05 12:37:41 +0000
@@ -475,7 +475,6 @@ void init_update_queries(void)
   sql_command_flags[SQLCOM_SELECT]|=          CF_PREOPEN_TMP_TABLES;
   sql_command_flags[SQLCOM_SET_OPTION]|=      CF_PREOPEN_TMP_TABLES;
   sql_command_flags[SQLCOM_DO]|=              CF_PREOPEN_TMP_TABLES;
-  sql_command_flags[SQLCOM_HA_OPEN]|=         CF_PREOPEN_TMP_TABLES;
   sql_command_flags[SQLCOM_CALL]|=            CF_PREOPEN_TMP_TABLES;
   sql_command_flags[SQLCOM_CHECKSUM]|=        CF_PREOPEN_TMP_TABLES;
   sql_command_flags[SQLCOM_ANALYZE]|=         CF_PREOPEN_TMP_TABLES;
@@ -3726,32 +3725,6 @@ end_with_restore_list:
     break;
   }
 #endif
-  case SQLCOM_HA_OPEN:
-    DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE))
-      goto error;
-    /* Close temporary tables which were pre-opened for privilege checking. */
-    close_thread_tables(thd);
-    all_tables->table= NULL;
-    res= mysql_ha_open(thd, first_table, 0);
-    break;
-  case SQLCOM_HA_CLOSE:
-    DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    res= mysql_ha_close(thd, first_table);
-    break;
-  case SQLCOM_HA_READ:
-    DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    /*
-      There is no need to check for table permissions here, because
-      if a user has no permissions to read a table, he won't be
-      able to open it (with SQLCOM_HA_OPEN) in the first place.
-    */
-    unit->set_limit(select_lex);
-    res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str,
-                       lex->insert_list, lex->ha_rkey_mode, select_lex->where,
-                       unit->select_limit_cnt, unit->offset_limit_cnt);
-    break;
-
   case SQLCOM_BEGIN:
     if (trans_begin(thd, lex->start_transaction_opt))
       goto error;
@@ -4438,6 +4411,9 @@ create_sp_error:
   case SQLCOM_REPAIR:
   case SQLCOM_TRUNCATE:
   case SQLCOM_ALTER_TABLE:
+  case SQLCOM_HA_OPEN:
+  case SQLCOM_HA_READ:
+  case SQLCOM_HA_CLOSE:
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
     /* fall through */
   case SQLCOM_SIGNAL:

=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy	2011-03-08 09:21:39 +0000
+++ b/sql/sql_yacc.yy	2011-04-05 12:37:41 +0000
@@ -56,6 +56,7 @@
 #include "sql_truncate.h"                      // Sql_cmd_truncate_table
 #include "sql_admin.h"                         // Sql_cmd_analyze/Check..._table
 #include "sql_partition_admin.h"               // Sql_cmd_alter_table_*_part.
+#include "sql_handler.h"                       // Sql_cmd_handler_*
 #include "sql_signal.h"
 #include "event_parse_data.h"
 #include <myisam.h>
@@ -753,6 +754,7 @@ static bool add_create_index (LEX *lex, 
   handlerton *db_type;
   enum row_type row_type;
   enum ha_rkey_function ha_rkey_mode;
+  enum enum_ha_read_modes ha_read_mode;
   enum enum_tx_isolation tx_isolation;
   enum Cast_target cast_type;
   enum Item_udftype udf_type;
@@ -1531,6 +1533,9 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %type <tx_isolation> isolation_types
 
 %type <ha_rkey_mode> handler_rkey_mode
+  
+%type <ha_read_mode> handler_read_or_scan handler_scan_function
+        handler_rkey_function
 
 %type <cast_type> cast_type
 
@@ -1587,7 +1592,6 @@ bool my_yyoverflow(short **a, YYSTYPE **
         equal optional_braces
         opt_mi_check_type opt_to mi_check_types normal_join
         table_to_table_list table_to_table opt_table_list opt_as
-        handler_rkey_function handler_read_or_scan
         single_multi table_wild_list table_wild_one opt_wild
         union_clause union_list
         precision subselect_start opt_and charset
@@ -13376,6 +13380,7 @@ unlock:
 handler:
           HANDLER_SYM table_ident OPEN_SYM opt_table_alias
           {
+            THD *thd= YYTHD;
             LEX *lex= Lex;
             if (lex->sphead)
             {
@@ -13383,11 +13388,15 @@ handler:
               MYSQL_YYABORT;
             }
             lex->sql_command = SQLCOM_HA_OPEN;
-            if (!lex->current_select->add_table_to_list(lex->thd, $2, $4, 0))
+            if (!lex->current_select->add_table_to_list(thd, $2, $4, 0))
+              MYSQL_YYABORT;
+            lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_handler_open();
+            if (lex->m_sql_cmd == NULL)
               MYSQL_YYABORT;
           }
         | HANDLER_SYM table_ident_nodb CLOSE_SYM
           {
+            THD *thd= YYTHD;
             LEX *lex= Lex;
             if (lex->sphead)
             {
@@ -13395,7 +13404,10 @@ handler:
               MYSQL_YYABORT;
             }
             lex->sql_command = SQLCOM_HA_CLOSE;
-            if (!lex->current_select->add_table_to_list(lex->thd, $2, 0, 0))
+            if (!lex->current_select->add_table_to_list(thd, $2, 0, 0))
+              MYSQL_YYABORT;
+            lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_handler_close();
+            if (lex->m_sql_cmd == NULL)
               MYSQL_YYABORT;
           }
         | HANDLER_SYM table_ident_nodb READ_SYM
@@ -13408,7 +13420,6 @@ handler:
             }
             lex->expr_allows_subselect= FALSE;
             lex->sql_command = SQLCOM_HA_READ;
-            lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */
             Item *one= new (YYTHD->mem_root) Item_int((int32) 1);
             if (one == NULL)
               MYSQL_YYABORT;
@@ -13419,42 +13430,50 @@ handler:
           }
           handler_read_or_scan where_clause opt_limit_clause
           {
+            THD *thd= YYTHD;
+            LEX *lex= Lex;
             Lex->expr_allows_subselect= TRUE;
             /* Stored functions are not supported for HANDLER READ. */
-            if (Lex->uses_stored_routines())
+            if (lex->uses_stored_routines())
             {
               my_error(ER_NOT_SUPPORTED_YET, MYF(0),
                        "stored functions in HANDLER ... READ");
               MYSQL_YYABORT;
             }
+            lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_handler_read($5,
+                                  lex->ident.str, lex->insert_list,
+                                  thd->m_parser_state->m_yacc.m_ha_rkey_mode);
+            if (lex->m_sql_cmd == NULL)
+              MYSQL_YYABORT;
           }
         ;
 
 handler_read_or_scan:
-          handler_scan_function       { Lex->ident= null_lex_str; }
-        | ident handler_rkey_function { Lex->ident= $1; }
+          handler_scan_function       { Lex->ident= null_lex_str; $$=$1; }
+        | ident handler_rkey_function { Lex->ident= $1; $$=$2; }
         ;
 
 handler_scan_function:
-          FIRST_SYM { Lex->ha_read_mode = RFIRST; }
-        | NEXT_SYM  { Lex->ha_read_mode = RNEXT;  }
+          FIRST_SYM { $$= RFIRST; }
+        | NEXT_SYM  { $$= RNEXT;  }
         ;
 
 handler_rkey_function:
-          FIRST_SYM { Lex->ha_read_mode = RFIRST; }
-        | NEXT_SYM  { Lex->ha_read_mode = RNEXT;  }
-        | PREV_SYM  { Lex->ha_read_mode = RPREV;  }
-        | LAST_SYM  { Lex->ha_read_mode = RLAST;  }
+          FIRST_SYM { $$= RFIRST; }
+        | NEXT_SYM  { $$= RNEXT;  }
+        | PREV_SYM  { $$= RPREV;  }
+        | LAST_SYM  { $$= RLAST;  }
         | handler_rkey_mode
           {
-            LEX *lex=Lex;
-            lex->ha_read_mode = RKEY;
-            lex->ha_rkey_mode=$1;
-            if (!(lex->insert_list = new List_item))
+            YYTHD->m_parser_state->m_yacc.m_ha_rkey_mode= $1;
+            Lex->insert_list= new List_item;
+            if (! Lex->insert_list)
               MYSQL_YYABORT;
           }
           '(' values ')'
-          {}
+          {
+            $$= RKEY;
+          }
         ;
 
 handler_rkey_mode:

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk branch (Dmitry.Lenev:3535 to 3536) Bug#11746602Dmitry Lenev5 Apr