List:Internals« Previous MessageNext Message »
From:monty Date:August 15 2005 3:35pm
Subject:bk commit into 5.0 tree (monty:1.1985)
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of monty. When monty 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.1985 05/08/15 18:35:48 monty@stripped +15 -0
  Merge bk-internal.mysql.com:/home/bk/mysql-5.0
  into  mysql.com:/home/my/mysql-5.0

  sql/sql_class.cc
    1.201 05/08/15 18:35:44 monty@stripped +9 -0
    Manual merge

  sql/sql_yacc.yy
    1.414 05/08/15 18:31:02 monty@stripped +0 -0
    Auto merged

  sql/share/errmsg.txt
    1.42 05/08/15 18:31:02 monty@stripped +0 -0
    Auto merged

  sql/sql_update.cc
    1.166 05/08/15 18:31:01 monty@stripped +0 -0
    Auto merged

  sql/sql_show.cc
    1.265 05/08/15 18:31:01 monty@stripped +0 -0
    Auto merged

  sql/sql_select.cc
    1.357 05/08/15 18:31:01 monty@stripped +0 -0
    Auto merged

  sql/sql_parse.cc
    1.469 05/08/15 18:31:01 monty@stripped +0 -0
    Auto merged

  sql/sql_lex.h
    1.193 05/08/15 18:31:01 monty@stripped +0 -0
    Auto merged

  sql/sql_derived.cc
    1.76 05/08/15 18:31:01 monty@stripped +0 -0
    Auto merged

  sql/sql_delete.cc
    1.156 05/08/15 18:31:01 monty@stripped +0 -0
    Auto merged

  sql/sql_class.h
    1.259 05/08/15 18:31:00 monty@stripped +0 -0
    Auto merged

  sql/sql_base.cc
    1.281 05/08/15 18:31:00 monty@stripped +0 -0
    Auto merged

  sql/mysqld.cc
    1.481 05/08/15 18:31:00 monty@stripped +0 -0
    Auto merged

  sql/mysql_priv.h
    1.341 05/08/15 18:31:00 monty@stripped +0 -0
    Auto merged

  sql/ha_berkeley.cc
    1.155 05/08/15 18:31:00 monty@stripped +0 -0
    Auto merged

# 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:	monty
# Host:	narttu.mysql.com
# Root:	/home/my/mysql-5.0/RESYNC

--- 1.154/sql/ha_berkeley.cc	2005-08-12 13:54:35 +03:00
+++ 1.155/sql/ha_berkeley.cc	2005-08-15 18:31:00 +03:00
@@ -2142,7 +2142,7 @@
     end_pos=end_range.less+end_range.equal;
   rows=(end_pos-start_pos)*records;
   DBUG_PRINT("exit",("rows: %g",rows));
-  DBUG_RETURN(rows <= 1.0 ? (ha_rows) 1 : (ha_rows) rows);
+  DBUG_RETURN((ha_rows)(rows <= 1.0 ? 1 : rows));
 }
 
 

--- 1.340/sql/mysql_priv.h	2005-08-12 13:54:35 +03:00
+++ 1.341/sql/mysql_priv.h	2005-08-15 18:31:00 +03:00
@@ -765,23 +765,24 @@
 enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
 				  IGNORE_ERRORS, REPORT_EXCEPT_NON_UNIQUE,
                                   IGNORE_EXCEPT_NON_UNIQUE};
-Field *find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
-			    Item **ref,
-                            find_item_error_report_type report_error,
-                            bool check_privileges,
-                            bool register_tree_change);
 Field *
-find_field_in_table(THD *thd, TABLE_LIST *table_list,
-                    const char *name, const char *item_name,
-                    uint length, Item **ref,
-                    bool check_grants_table, bool check_grants_view,
-                    bool allow_rowid,
-                    uint *cached_field_index_ptr,
-                    bool register_tree_change);
+find_field_in_tables(THD *thd, Item_ident *item,
+                     TABLE_LIST *first_table, TABLE_LIST *last_table,
+                     Item **ref, find_item_error_report_type report_error,
+                     bool check_privileges, bool register_tree_change);
 Field *
-find_field_in_real_table(THD *thd, TABLE *table, const char *name,
-                         uint length, bool check_grants, bool allow_rowid,
-                         uint *cached_field_index_ptr);
+find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
+                        const char *name, const char *item_name,
+                        const char *table_name, const char *db_name,
+                        uint length, Item **ref,
+                        bool check_grants_table, bool check_grants_view,
+                        bool allow_rowid,
+                        uint *cached_field_index_ptr,
+                        bool register_tree_change, TABLE_LIST **actual_table);
+Field *
+find_field_in_table(THD *thd, TABLE *table, const char *name,
+                    uint length, bool check_grants, bool allow_rowid,
+                    uint *cached_field_index_ptr);
 #ifdef HAVE_OPENSSL
 #include <openssl/des.h>
 struct st_des_keyblock
@@ -888,8 +889,10 @@
 				uint uint_geom_type);
 void store_position_for_column(const char *name);
 bool add_to_list(THD *thd, SQL_LIST &list,Item *group,bool asc);
+Name_resolution_context *make_join_on_context(THD *thd, TABLE_LIST *left_op,
+                                              TABLE_LIST *right_op);
 void add_join_on(TABLE_LIST *b,Item *expr);
-void add_join_natural(TABLE_LIST *a,TABLE_LIST *b);
+void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields);
 bool add_proc_to_list(THD *thd, Item *item);
 TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find);
 
@@ -906,8 +909,8 @@
 		   const char *db_name, const char *table_name,
                    List_iterator<Item> *it, bool any_privileges);
 bool setup_tables(THD *thd, Name_resolution_context *context,
-                  TABLE_LIST *tables, Item **conds,
-		  TABLE_LIST **leaves, bool select_insert);
+                  List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
+                  Item **conds, TABLE_LIST **leaves, bool select_insert);
 int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
 	       List<Item> *sum_func_list, uint wild_num);
 bool setup_fields(THD *thd, Item** ref_pointer_array,

--- 1.480/sql/mysqld.cc	2005-08-12 13:55:44 +03:00
+++ 1.481/sql/mysqld.cc	2005-08-15 18:31:00 +03:00
@@ -1050,6 +1050,7 @@
   (void) ha_panic(HA_PANIC_CLOSE);	/* close all tables and logs */
   if (tc_log)
     tc_log->close();
+  xid_cache_free();
   delete_elements(&key_caches, (void (*)(const char*, gptr)) free_key_cache);
   multi_keycache_free();
   end_thr_alarm(1);			/* Free allocated memory */
@@ -2922,6 +2923,11 @@
     using_update_log=1;
   }
 
+  if (xid_cache_init())
+  {
+    sql_print_error("Out of memory");
+    unireg_abort(1);
+  }
   if (ha_init())
   {
     sql_print_error("Can't init databases");

--- 1.280/sql/sql_base.cc	2005-08-12 13:55:44 +03:00
+++ 1.281/sql/sql_base.cc	2005-08-15 18:31:00 +03:00
@@ -501,7 +501,7 @@
   */
   bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
   if (!thd->active_transaction())
-    thd->transaction.xid.null();
+    thd->transaction.xid_state.xid.null();
 
   /* VOID(pthread_sigmask(SIG_SETMASK,&thd->block_signals,NULL)); */
   if (!lock_in_use)
@@ -1668,7 +1668,7 @@
       {
         /* Give right error message */
         thd->clear_error();
-        DBUG_PRINT("error", ("Dicovery of %s/%s failed", db, name));
+        DBUG_PRINT("error", ("Discovery of %s/%s failed", db, name));
         my_printf_error(ER_UNKNOWN_ERROR,
                         "Failed to open '%-.64s', error while "
                         "unpacking from engine",
@@ -2471,152 +2471,255 @@
 
 
 /*****************************************************************************
-** find field in list or tables. if field is unqualifed and unique,
-** return unique field
+* The following find_field_in_XXX procedures implement the core of the
+* name resolution functionality. The entry point to resolve a column name in a
+* list of tables is 'find_field_in_tables'. It calls 'find_field_in_table_ref'
+* for each table reference. In turn, depending on the type of table reference,
+* 'find_field_in_table_ref' calls one of the 'find_field_in_XXX' procedures
+* below specific for the type of table reference.
 ******************************************************************************/
 
-/* Special Field pointers for find_field_in_tables returning */
+/* Special Field pointers as return values of find_field_in_XXX functions. */
 Field *not_found_field= (Field*) 0x1;
 Field *view_ref_found= (Field*) 0x2; 
 
 #define WRONG_GRANT (Field*) -1
 
+static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
+{
+  if (thd->set_query_id)
+  {
+    if (field->query_id != thd->query_id)
+    {
+      field->query_id= thd->query_id;
+      table->used_fields++;
+      table->used_keys.intersect(field->part_of_key);
+    }
+    else
+      thd->dupp_field= field;
+  }
+}
+
 
 /*
-  Find field in table or view
+  Find a field by name in a view that uses merge algorithm.
 
   SYNOPSIS
-    find_field_in_table()
+    find_field_in_view()
     thd				thread handler
-    table_list			table where to find
+    table_list			view to search for 'name'
     name			name of field
     item_name                   name of item if it will be created (VIEW)
     length			length of name
-    ref	[in/out]		expression substituted in VIEW should be
-				  passed using this reference (return
-				  view_ref_found)
-                                  (*ref != NULL) only if *ref contains
-                                  the item that we need to replace.
-    check_grants_table		do check columns grants for table?
-    check_grants_view		do check columns grants for view?
-    allow_rowid			do allow finding of "_rowid" field?
-    cached_field_index_ptr	cached position in field list (used to
-				  speedup prepared tables field finding)
+    ref				expression substituted in VIEW should be passed
+                                using this reference (return view_ref_found)
+    check_grants		do check columns grants for view?
     register_tree_change        TRUE if ref is not stack variable and we
-                                  need register changes in item tree
+                                need register changes in item tree
 
   RETURN
     0			field is not found
     view_ref_found	found value in VIEW (real result is in *ref)
-    #			pointer to field
+    #			pointer to field - only for schema table fields
 */
 
-Field *
-find_field_in_table(THD *thd, TABLE_LIST *table_list,
-                    const char *name, const char *item_name,
-                    uint length, Item **ref,
-                    bool check_grants_table, bool check_grants_view,
-                    bool allow_rowid,
-                    uint *cached_field_index_ptr,
-                    bool register_tree_change)
-{
-  Field *fld;
-  DBUG_ENTER("find_field_in_table");
-  DBUG_PRINT("enter", ("table: '%s'  name: '%s'  item name: '%s'  ref 0x%lx",
-		       table_list->alias, name, item_name, (ulong) ref));
-  if (table_list->field_translation)
+static Field *
+find_field_in_view(THD *thd, TABLE_LIST *table_list,
+                   const char *name, const char *item_name,
+                   uint length, Item **ref, bool check_grants,
+                   bool register_tree_change)
+{
+  DBUG_ENTER("find_field_in_view");
+  DBUG_PRINT("enter",
+             ("view: '%s', field name: '%s', item name: '%s', ref 0x%lx",
+              table_list->alias, name, item_name, (ulong) ref));
+  Field_iterator_view field_it;
+  field_it.set(table_list);
+  DBUG_ASSERT(table_list->schema_table_reformed ||
+              (ref != 0 && table_list->view != 0));
+  for (; !field_it.end_of_fields(); field_it.next())
   {
-    Field_iterator_view field_it;
-    field_it.set(table_list);
-    DBUG_ASSERT(table_list->schema_table_reformed ||
-                (ref != 0 && table_list->view != 0));
-    for (; !field_it.end_of_fields(); field_it.next())
+    if (!my_strcasecmp(system_charset_info, field_it.name(), name))
     {
-      if (!my_strcasecmp(system_charset_info, field_it.name(), name))
-      {
-        if (table_list->schema_table_reformed)
-        {
-          /*
-            Translation table items are always Item_fields 
-            and fixed already('mysql_schema_table' function). 
-            So we can return ->field. It is used only for 
-            'show & where' commands.
-          */
-          DBUG_RETURN(((Item_field*) (field_it.item()))->field);
-        }
+      if (table_list->schema_table_reformed)
+        /*
+          Translation table items are always Item_fields and fixed already
+          ('mysql_schema_table' function). So we can return ->field. It is
+          used only for 'show & where' commands.
+        */
+        DBUG_RETURN(((Item_field*) (field_it.item()))->field);
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-	if (check_grants_view &&
-	    check_grant_column(thd, &table_list->grant,
-			       table_list->view_db.str,
-			       table_list->view_name.str,
-			       name, length))
-	  DBUG_RETURN(WRONG_GRANT);
+      if (check_grants &&
+          check_grant_column(thd, &table_list->grant,
+                             table_list->view_db.str,
+                             table_list->view_name.str,
+                             name, length))
+        DBUG_RETURN(WRONG_GRANT);
 #endif
-        Item *item= field_it.create_item(thd);
-        if (!item)
-        {
-          DBUG_RETURN(0);
-        }
-        /*
-          *ref != NULL means that *ref contains the item that we need to
-          replace. If the item was aliased by the user, set the alias to
-          the replacing item.
-         */
-        if (*ref && !(*ref)->is_autogenerated_name)
-          item->set_name((*ref)->name, (*ref)->name_length,
-                         system_charset_info);
-        if (register_tree_change)
-          thd->change_item_tree(ref, item);
-        else
-          *ref= item;
-	DBUG_RETURN((Field*) view_ref_found);
+      Item *item= field_it.create_item(thd);
+      if (!item)
+        DBUG_RETURN(0);
+      /*
+       *ref != NULL means that *ref contains the item that we need to
+       replace. If the item was aliased by the user, set the alias to
+       the replacing item.
+      */
+      if (*ref && !(*ref)->is_autogenerated_name)
+        item->set_name((*ref)->name, (*ref)->name_length,
+                       system_charset_info);
+      if (register_tree_change)
+        thd->change_item_tree(ref, item);
+      else
+        *ref= item;
+      DBUG_RETURN((Field*) view_ref_found);
+    }
+  }
+  DBUG_RETURN(0);
+}
+
+
+/*
+  Find field by name in a NATURAL/USING join table reference.
+
+  SYNOPSIS
+    find_field_in_natural_join()
+    thd			 [in]  thread handler
+    table_ref            [in]  table reference to search
+    name		 [in]  name of field
+    table_name           [in]  optional table name that qualifies the field
+    db_name              [in]  optional database name that qualifies the field
+    length		 [in]  length of name
+    ref                  [in/out] if 'name' is resolved to a view field, ref is
+                               set to point to the found view field
+    check_grants	 [in]  do check columns grants?
+    register_tree_change [in]  TRUE if ref is not stack variable and we
+                               need register changes in item tree
+    actual_table         [out] the original table reference where the field
+                               belongs - differs from 'table_list' only for
+                               NATURAL/USING joins
+
+  RETURN
+    - Pointer to the found Field
+    - NULL if the field was not found
+    - WRONG_GRANT if no access rights to the found field
+*/
+
+static Field *
+find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
+                           const char *table_name, const char *db_name,
+                           uint length, Item **ref, bool check_grants,
+                           bool register_tree_change, TABLE_LIST **actual_table)
+{
+  DBUG_ENTER("find_field_in_natural_join");
+  DBUG_PRINT("enter", ("natural join, field name: '%s', ref 0x%lx",
+		       name, (ulong) ref));
+  DBUG_ASSERT(table_ref->is_natural_join && table_ref->join_columns);
+  List_iterator_fast<Natural_join_column>
+    field_it(*(table_ref->join_columns));
+  Natural_join_column *nj_col= NULL;
+  Field *found_field= NULL;
+
+  *actual_table= NULL;
+
+  while ((nj_col= field_it++))
+  {
+    if (table_name)
+    {
+      /*
+        Coalesced columns cannot be qualified unless this is the execute phase
+        of prepared statements. The reason is that they do not belong to any
+        table, but for PS the prepare phase already resolves and stores
+        items, so during the execution phase we resolve fully qualified items.
+      */
+      if (!thd->current_arena->is_stmt_execute() && nj_col->is_coalesced)
+        continue;
+      if (table_name[0] &&
+          my_strcasecmp(table_alias_charset, nj_col->table_name(), table_name))
+        continue;
+      if (db_name && db_name[0])
+      {
+        const char *cur_db_name= nj_col->db_name();
+        if (cur_db_name && cur_db_name && strcmp(db_name, cur_db_name))
+          continue;
       }
     }
-    DBUG_RETURN(0);
+
+    if (!my_strcasecmp(system_charset_info, nj_col->name(), name))
+      break;
   }
-  fld= find_field_in_real_table(thd, table_list->table, name, length,
-				check_grants_table, allow_rowid,
-				cached_field_index_ptr);
+
+  if (!nj_col)
+    DBUG_RETURN(NULL);
+
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-  /* check for views with temporary table algorithm */
-  if (check_grants_view && table_list->view &&
-      fld && fld != WRONG_GRANT &&
-      check_grant_column(thd, &table_list->grant,
-			 table_list->view_db.str,
-			 table_list->view_name.str,
-			 name, length))
-  {
+  if (check_grants && nj_col->check_grants(thd, name, length))
     DBUG_RETURN(WRONG_GRANT);
-  }
 #endif
-  DBUG_RETURN(fld);
+
+  if (nj_col->view_field)
+  {
+    /*
+      The found field is a view field, we do as in find_field_in_view()
+      and return a pointer to pointer to the Item of that field.
+    */
+    Item *item= nj_col->create_item(thd);
+    if (!item)
+      DBUG_RETURN(NULL);
+    DBUG_ASSERT(nj_col->table_field == NULL);
+    if (nj_col->table_ref->schema_table_reformed)
+      /*
+        Translation table items are always Item_fields and fixed
+        already('mysql_schema_table' function). So we can return
+        ->field. It is used only for 'show & where' commands.
+      */
+      DBUG_RETURN(((Item_field*) (nj_col->view_field->item))->field);
+
+    if (register_tree_change)
+      thd->change_item_tree(ref, item);
+    else
+      *ref= item;
+    found_field= (Field*) view_ref_found;
+  }
+  else
+  {
+    /* This is a base table. */
+    DBUG_ASSERT(nj_col->view_field == NULL);
+    found_field= nj_col->table_field;
+    update_field_dependencies(thd, found_field, nj_col->table_ref->table);
+  }
+
+  *actual_table= nj_col->table_ref;
+  
+  DBUG_RETURN(found_field);
 }
 
 
 /*
-  Find field in table
+  Find field by name in a base table or a view with temp table algorithm.
 
   SYNOPSIS
-    find_field_in_real_table()
+    find_field_in_table()
     thd				thread handler
-    table_list			table where to find
+    table			table where to search for the field
     name			name of field
     length			length of name
     check_grants		do check columns grants?
     allow_rowid			do allow finding of "_rowid" field?
-    cached_field_index_ptr	cached position in field list (used to
-				  speedup prepared tables field finding)
+    cached_field_index_ptr	cached position in field list (used to speedup
+                                lookup for fields in prepared tables)
 
   RETURN
     0			field is not found
     #			pointer to field
 */
 
-Field *find_field_in_real_table(THD *thd, TABLE *table,
-                                const char *name, uint length,
-                                bool check_grants, bool allow_rowid,
-                                uint *cached_field_index_ptr)
+Field *
+find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
+                    bool check_grants, bool allow_rowid,
+                    uint *cached_field_index_ptr)
 {
+  DBUG_ENTER("find_field_in_table");
+  DBUG_PRINT("enter", ("table: '%s', field name: '%s'", table->alias, name));
   Field **field_ptr, *field;
   uint cached_field_index= *cached_field_index_ptr;
 
@@ -2631,7 +2734,7 @@
   else
   {
     if (!(field_ptr= table->field))
-      return (Field *)0;
+      DBUG_RETURN((Field *)0);
     for (; *field_ptr; ++field_ptr)
       if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name))
         break;
@@ -2647,27 +2750,118 @@
     if (!allow_rowid ||
         my_strcasecmp(system_charset_info, name, "_rowid") ||
         !(field=table->rowid_field))
-      return (Field*) 0;
+      DBUG_RETURN((Field*) 0);
   }
 
-  if (thd->set_query_id)
-  {
-    if (field->query_id != thd->query_id)
-    {
-      field->query_id=thd->query_id;
-      table->used_fields++;
-      table->used_keys.intersect(field->part_of_key);
-    }
-    else
-      thd->dupp_field=field;
-  }
+  update_field_dependencies(thd, field, table);
+
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
   if (check_grants && check_grant_column(thd, &table->grant,
 					 table->s->db,
 					 table->s->table_name, name, length))
-    return WRONG_GRANT;
+    DBUG_RETURN(WRONG_GRANT);
 #endif
-  return field;
+  DBUG_RETURN(field);
+}
+
+
+/*
+  Find field in a table reference.
+
+  SYNOPSIS
+    find_field_in_table_ref()
+    thd			   [in]  thread handler
+    table_list		   [in]  table reference to search
+    name		   [in]  name of field
+    item_name              [in]  name of item if it will be created (VIEW)
+    table_name             [in]  optional table name that qualifies the field
+    db_name                [in]  optional database name that qualifies the
+    length		   [in]  field length of name
+    ref		       [in/out] if 'name' is resolved to a view field, ref
+                                 is set to point to the found view field
+    check_grants_table	   [in]  do check columns grants for table?
+    check_grants_view	   [in]  do check columns grants for view?
+    allow_rowid		   [in]  do allow finding of "_rowid" field?
+    cached_field_index_ptr [in]  cached position in field list (used to
+                                 speedup lookup for fields in prepared tables)
+    register_tree_change   [in]  TRUE if ref is not stack variable and we
+                                 need register changes in item tree
+    actual_table           [out] the original table reference where the field
+                                 belongs - differs from 'table_list' only for
+                                 NATURAL_USING joins.
+
+  RETURN
+    0			field is not found
+    view_ref_found	found value in VIEW (real result is in *ref)
+    #			pointer to field
+*/
+
+Field *
+find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
+                        const char *name, const char *item_name,
+                        const char *table_name, const char *db_name,
+                        uint length, Item **ref,
+                        bool check_grants_table, bool check_grants_view,
+                        bool allow_rowid, uint *cached_field_index_ptr,
+                        bool register_tree_change, TABLE_LIST **actual_table)
+{
+  Field *fld;
+  DBUG_ENTER("find_field_in_table_ref");
+  DBUG_PRINT("enter",
+             ("table: '%s'  field name: '%s'  item name: '%s'  ref 0x%lx",
+              table_list->alias, name, item_name, (ulong) ref));
+
+  /*
+    Check that the table and database that qualify the current field name
+    are the same as the table we are going to search for the field.
+    This is done differently for NATURAL/USING joins because there we can't
+    simply compare the qualifying table and database names with the ones of
+    'table_list' because each field in such a join may originate from a
+    different table.
+    TODO: Ensure that db and tables->db always points to something !
+  */
+  if (!table_list->is_natural_join &&
+      (table_name && table_name[0] &&
+       my_strcasecmp(table_alias_charset, table_list->alias, table_name) ||
+       (db_name && db_name[0] && table_list->db && table_list->db[0] &&
+        strcmp(db_name, table_list->db))))
+    DBUG_RETURN(0);
+
+  if (table_list->field_translation)
+  {
+    if ((fld= find_field_in_view(thd, table_list, name, item_name, length,
+                                 ref, check_grants_view, register_tree_change)))
+      *actual_table= table_list;
+    else
+      *actual_table= NULL;
+  }
+  else if (table_list->is_natural_join)
+    fld= find_field_in_natural_join(thd, table_list, name, table_name,
+                                    db_name,  length, ref,
+                                    /* TIMOUR_TODO: check this with Sanja */
+                                    check_grants_table || check_grants_view,
+                                    register_tree_change, actual_table);
+  else
+  {
+    if ((fld= find_field_in_table(thd, table_list->table, name, length,
+                                  check_grants_table, allow_rowid,
+                                  cached_field_index_ptr)))
+      *actual_table= table_list;
+    else
+      *actual_table= NULL;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+    /* check for views with temporary table algorithm */
+    if (check_grants_view && table_list->view &&
+        fld && fld != WRONG_GRANT &&
+        check_grant_column(thd, &table_list->grant,
+                           table_list->view_db.str,
+                           table_list->view_name.str,
+                           name, length))
+    DBUG_RETURN(WRONG_GRANT);
+#endif
+  }
+
+  DBUG_RETURN(fld);
 }
 
 
@@ -2676,21 +2870,23 @@
 
   SYNOPSIS
     find_field_in_tables()
-    thd                   Pointer to current thread structure
-    item                  Field item that should be found
-    tables                Tables to be searched for item
-    ref                   If 'item' is resolved to a view field, ref is set to
+    thd			  pointer to current thread structure
+    item		  field item that should be found
+    first_table           list of tables to be searched for item
+    last_table            end of the list of tables to search for item. If NULL
+                          then search to the end of the list 'first_table'.
+    ref			  if 'item' is resolved to a view field, ref is set to
                           point to the found view field
-    report_error          Degree of error reporting:
+    report_error	  Degree of error reporting:
                           - IGNORE_ERRORS then do not report any error
                           - IGNORE_EXCEPT_NON_UNIQUE report only non-unique
-                           fields, suppress all other errors
+                            fields, suppress all other errors
                           - REPORT_EXCEPT_NON_UNIQUE report all other errors
                             except when non-unique fields were found
                           - REPORT_ALL_ERRORS
     check_privileges      need to check privileges
-    register_tree_change  TRUE if ref is not stack variable and we
-                            need register changes in item tree
+    register_tree_change  TRUE if ref is not a stack variable and we
+                          to need register changes in item tree
 
   RETURN VALUES
     0			If error: the found field is not unique, or there are
@@ -2704,63 +2900,66 @@
 */
 
 Field *
-find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
+find_field_in_tables(THD *thd, Item_ident *item,
+                     TABLE_LIST *first_table, TABLE_LIST *last_table,
 		     Item **ref, find_item_error_report_type report_error,
                      bool check_privileges, bool register_tree_change)
 {
   Field *found=0;
-  const char *db=item->db_name;
-  const char *table_name=item->table_name;
-  const char *name=item->field_name;
+  const char *db= item->db_name;
+  const char *table_name= item->table_name;
+  const char *name= item->field_name;
   uint length=(uint) strlen(name);
   char name_buff[NAME_LEN+1];
   bool allow_rowid;
+  TABLE_LIST *cur_table= first_table;
+  TABLE_LIST *actual_table;
 
   if (item->cached_table)
   {
     /*
-      This shortcut is used by prepared statements. We assuming that
-      TABLE_LIST *tables is not changed during query execution (which
+      This shortcut is used by prepared statements. We assume that
+      TABLE_LIST *first_table is not changed during query execution (which
       is true for all queries except RENAME but luckily RENAME doesn't
       use fields...) so we can rely on reusing pointer to its member.
       With this optimization we also miss case when addition of one more
       field makes some prepared query ambiguous and so erroneous, but we
       accept this trade off.
     */
-    if (item->cached_table->table && !item->cached_table->view)
-    {
-      found= find_field_in_real_table(thd, item->cached_table->table,
-				      name, length,
-				      test(item->cached_table->
-					   table->grant.want_privilege) &&
-				      check_privileges,
-				      1, &(item->cached_field_index));
-
-    }
+    TABLE_LIST *table_ref= item->cached_table;
+    /*
+      The condition (table_ref->view == NULL) ensures that we will call
+      find_field_in_table even in the case of information schema tables
+      when table_ref->field_translation != NULL.
+      */
+    if (table_ref->table && !table_ref->view)
+      found= find_field_in_table(thd, table_ref->table, name, length,
+                                 test(table_ref->table->
+                                      grant.want_privilege) &&
+                                 check_privileges,
+                                 1, &(item->cached_field_index));
     else
-    {
-      TABLE_LIST *table= item->cached_table;
-      found= find_field_in_table(thd, table, name, item->name, length,
-				       ref,
-				       (table->table &&
-					test(table->table->grant.
-					     want_privilege) &&
-					check_privileges),
-				       (test(table->grant.want_privilege) &&
-					check_privileges),
-				       1, &(item->cached_field_index),
-                                       register_tree_change);
-    }
+      found= find_field_in_table_ref(thd, table_ref, name, item->name,
+                                     NULL, NULL, length, ref,
+                                     (table_ref->table &&
+                                      test(table_ref->table->grant.
+                                           want_privilege) &&
+                                      check_privileges),
+                                     (test(table_ref->grant.want_privilege) &&
+                                      check_privileges),
+                                     1, &(item->cached_field_index),
+                                     register_tree_change,
+                                     &actual_table);
     if (found)
     {
       if (found == WRONG_GRANT)
 	return (Field*) 0;
       {
         SELECT_LEX *current_sel= thd->lex->current_select;
-        SELECT_LEX *last_select= item->cached_table->select_lex;
+        SELECT_LEX *last_select= table_ref->select_lex;
         /*
           If the field was an outer referencee, mark all selects using this
-          sub query as dependent of the outer query
+          sub query as dependent on the outer query
         */
         if (current_sel != last_select)
           mark_select_range_as_dependent(thd, last_select, current_sel,
@@ -2782,46 +2981,52 @@
     db= name_buff;
   }
 
+  /* The field we search for is qualified with a table name and optional db. */
   if (table_name && table_name[0])
-  {						/* Qualified field */
-    bool found_table= 0;
-    for (; tables; tables= tables->next_local)
-    {
-      /* TODO; Ensure that db and tables->db always points to something ! */
-      if (!my_strcasecmp(table_alias_charset, tables->alias, table_name) &&
-	  (!db || !db[0] || !tables->db ||  !tables->db[0] ||
-           !strcmp(db,tables->db)))
-      {
-	found_table=1;
-	Field *find= find_field_in_table(thd, tables, name, item->name,
-                                         length, ref,
-					 (tables->table &&
-					  test(tables->table->grant.
-                                               want_privilege) &&
-                                          check_privileges),
-					 (test(tables->grant.want_privilege) &&
-                                          check_privileges),
-					 1, &(item->cached_field_index),
-                                         register_tree_change);
-	if (find)
-	{
-	  item->cached_table= tables;
-	  if (!tables->cacheable_table)
-	    item->cached_table= 0;
-	  if (find == WRONG_GRANT)
-	    return (Field*) 0;
-	  if (db || !thd->where)
-	    return find;
-	  if (found)
-	  {
-            if (report_error == REPORT_ALL_ERRORS ||
-                report_error == IGNORE_EXCEPT_NON_UNIQUE)
-              my_error(ER_NON_UNIQ_ERROR, MYF(0),
-                       item->full_name(),thd->where);
-	    return (Field*) 0;
-	  }
-	  found=find;
-	}
+  {
+    bool found_table=0;
+    for ( ;
+         (cur_table &&
+          (last_table ?
+           (cur_table != last_table->next_name_resolution_table) : TRUE));
+         cur_table= cur_table->next_name_resolution_table)
+    {
+      DBUG_ASSERT(cur_table);
+      found_table= 1;
+      Field *cur_field= find_field_in_table_ref(thd, cur_table, name,
+                                                item->name, table_name,
+                                                db, length, ref,
+                                                (cur_table->table &&
+                                                 test(cur_table->table->grant.
+                                                      want_privilege) &&
+                                                 check_privileges),
+                                                (test(cur_table->grant.
+                                                      want_privilege)
+                                                 && check_privileges),
+                                                1, &(item->cached_field_index),
+                                                register_tree_change,
+                                                &actual_table);
+      if (cur_field)
+      {
+        /*
+          Store the original table of the field, which may be different from
+          cur_table in the case of NATURAL/USING join.
+        */
+        item->cached_table= (!actual_table->cacheable_table) ? 0 : actual_table;
+
+        if (cur_field == WRONG_GRANT)
+          return (Field*) 0;
+        if (db || !thd->where)
+          return cur_field;
+        if (found)
+        {
+          if (report_error == REPORT_ALL_ERRORS ||
+              report_error == IGNORE_EXCEPT_NON_UNIQUE)
+            my_error(ER_NON_UNIQ_ERROR, MYF(0),
+                     item->full_name(),thd->where);
+          return (Field*) 0;
+        }
+        found= cur_field;
       }
     }
     if (found)
@@ -2845,34 +3050,41 @@
 	return (Field*) not_found_field;
     return (Field*) 0;
   }
-  allow_rowid= tables && !tables->next_local;	// Only one table
-  for (; tables ; tables= tables->next_local)
-  {
-    Field *field;
-    if (!tables->table && !tables->ancestor)
-    {
-      if (report_error == REPORT_ALL_ERRORS ||
-          report_error == REPORT_EXCEPT_NON_UNIQUE)
-	my_error(ER_BAD_FIELD_ERROR, MYF(0), item->full_name(),thd->where);
-      return (Field*) not_found_field;
-    }
 
-    field= find_field_in_table(thd, tables, name, item->name,
-                               length, ref,
-                               (tables->table &&
-                                test(tables->table->grant.
-                                     want_privilege) &&
-                                check_privileges),
-                               (test(tables->grant.want_privilege) &&
-                                check_privileges),
-                               allow_rowid,
-                               &(item->cached_field_index),
-                               register_tree_change);
-    if (field)
+  /* The field we search for is not qualified. */
+  allow_rowid= cur_table && !cur_table->next_local;
+  for ( ;
+        (cur_table &&
+         (last_table ?
+          (cur_table != last_table->next_name_resolution_table) : TRUE));
+        cur_table= cur_table->next_name_resolution_table)
+  {
+    DBUG_ASSERT(cur_table);
+    Field *cur_field= find_field_in_table_ref(thd, cur_table, name, item->name,
+                                              NULL, NULL, length, ref,
+                                              (cur_table->table &&
+                                               test(cur_table->table->grant.
+                                                    want_privilege) &&
+                                               check_privileges),
+                                              (test(cur_table->grant.
+                                                    want_privilege)
+                                               && check_privileges),
+                                              allow_rowid,
+                                              &(item->cached_field_index),
+                                              register_tree_change,
+                                              &actual_table);
+    if (cur_field)
     {
-      if (field == WRONG_GRANT)
+      if (cur_field == WRONG_GRANT)
 	return (Field*) 0;
-      item->cached_table= (!tables->cacheable_table || found) ? 0 : tables;
+
+      /*
+        Store the original table of the field, which may be different from
+        cur_table in the case of NATURAL/USING join.
+      */
+      item->cached_table= (!actual_table->cacheable_table || found) ?
+                          0 : actual_table;
+
       if (found)
       {
 	if (!thd->where)			// Returns first found
@@ -2882,7 +3094,7 @@
           my_error(ER_NON_UNIQ_ERROR, MYF(0), name, thd->where);
 	return (Field*) 0;
       }
-      found= field;
+      found= cur_field;
     }
   }
   if (found)
@@ -3096,6 +3308,622 @@
     return (Item **) not_found_item;
 }
 
+
+/*
+  Test if a string is a member of a list of strings.
+
+  SYNOPSIS
+    test_if_string_in_list()
+    find      the string to look for
+    str_list  a list of strings to be searched
+
+  DESCRIPTION
+    Sequentially search a list of strings for a string, and test whether
+    the list contains the same string.
+
+  RETURN
+    TRUE  if find is in str_list
+    FALSE otherwise
+*/
+
+static bool
+test_if_string_in_list(const char *find, List<String> *str_list)
+{
+  List_iterator<String> str_list_it(*str_list);
+  String *curr_str;
+  size_t find_length= strlen(find);
+  while ((curr_str= str_list_it++))
+  {
+    if (find_length != curr_str->length())
+      continue;
+    if (!strncmp(find, curr_str->ptr(), find_length))
+      return TRUE;
+  }
+  return FALSE;
+}
+
+
+/*
+  Create a new name resolution context for an item so that it is
+  being resolved in a specific table reference.
+
+  SYNOPSIS
+    set_new_item_local_context()
+    thd        pointer to current thread
+    item       item for which new context is created and set
+    table_ref  table ref where an item showld be resolved
+
+  DESCRIPTION
+    Create a new name resolution context for an item, so that the item
+    is resolved only the supplied 'table_ref'.
+
+  RETURN
+    FALSE - if all OK
+    TRUE  - otherwise
+*/
+
+static bool
+set_new_item_local_context(THD *thd, Item_ident *item, TABLE_LIST *table_ref)
+{
+  Name_resolution_context *context;
+  if (!(context= (Name_resolution_context*)
+        thd->calloc(sizeof(Name_resolution_context))))
+    return TRUE;
+  context->init();
+  context->first_name_resolution_table= table_ref;
+  context->last_name_resolution_table=  table_ref;
+  item->context= context;
+  return FALSE;
+}
+
+
+/*
+  Find and mark the common columns of two table references.
+
+  SYNOPSIS
+    mark_common_columns()
+    thd                [in] current thread
+    table_ref_1        [in] the first (left) join operand
+    table_ref_2        [in] the second (right) join operand
+    using_fields       [in] if the join is JOIN...USING - the join columns,
+                            if NATURAL join, then NULL
+    found_using_fields [out] number of fields from the USING clause that were
+                             found among the common fields
+
+  DESCRIPTION
+    The procedure finds the common columns of two relations (either
+    tables or intermediate join results), and adds an equi-join condition
+    to the ON clause of 'table_ref_2' for each pair of matching columns.
+    If some of table_ref_XXX represents a base table or view, then we
+    create new 'Natural_join_column' instances for each column
+    reference and store them in the 'join_columns' of the table
+    reference.
+
+  IMPLEMENTATION
+    The procedure assumes that store_natural_using_join_columns() was
+    called for the previous level of NATURAL/USING joins.
+
+  RETURN
+    TRUE  - if error when some common column is non-unique, or out of memory
+    FALSE - if OK
+*/
+
+static bool
+mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
+                    List<String> *using_fields, uint *found_using_fields)
+{
+  Field_iterator_table_ref it_1, it_2;
+  Natural_join_column *nj_col_1, *nj_col_2;
+  const char *field_name_1, *field_name_2;
+  *found_using_fields= 0;
+  bool add_columns= TRUE;
+  Query_arena *arena, backup;
+  bool result= TRUE;
+
+  DBUG_ENTER("mark_common_columns");
+  DBUG_PRINT("info", ("operand_1: %s, operand_2: %s",
+                      table_ref_1->alias, table_ref_2->alias));
+
+  arena= thd->change_arena_if_needed(&backup);
+
+  /*
+    TABLE_LIST::join_columns could be allocated by the previous call to
+    store_natural_using_join_columns() for the lower level of nested tables.
+  */
+  if (!table_ref_1->join_columns)
+  {
+    if (!(table_ref_1->join_columns= new List<Natural_join_column>))
+      goto err;
+    table_ref_1->is_join_columns_complete= FALSE;
+  }
+  if (!table_ref_2->join_columns)
+  {
+    if (!(table_ref_2->join_columns= new List<Natural_join_column>))
+      goto err;
+    table_ref_2->is_join_columns_complete= FALSE;
+  }
+
+  for (it_1.set(table_ref_1); !it_1.end_of_fields(); it_1.next())
+  {
+    bool is_created_1;
+    if (!(nj_col_1= it_1.get_or_create_column_ref(thd, &is_created_1)))
+      goto err;
+    field_name_1= nj_col_1->name();
+    bool found= FALSE;
+
+    /* If nj_col_1 was just created add it to the list of join columns. */
+    if (is_created_1)
+      table_ref_1->join_columns->push_back(nj_col_1);
+
+    /* Find a field with the same name in table_ref_2. */
+    nj_col_2= NULL;
+    field_name_2= NULL;
+    for (it_2.set(table_ref_2); !it_2.end_of_fields(); it_2.next())
+    {
+      bool is_created_2;
+      Natural_join_column *cur_nj_col_2;
+      if (!(cur_nj_col_2= it_2.get_or_create_column_ref(thd, &is_created_2)))
+        goto err;
+      const char *cur_field_name_2= cur_nj_col_2->name();
+
+      /* If nj_col_1 was just created add it to the list of join columns. */
+      if (add_columns && is_created_2)
+        table_ref_2->join_columns->push_back(cur_nj_col_2);
+
+      /* Compare the two columns and check for duplicate common fields. */
+      if (!my_strcasecmp(system_charset_info, field_name_1, cur_field_name_2))
+      {
+        if (found)
+        {
+          my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1, thd->where);
+          goto err;
+        }
+        nj_col_2= cur_nj_col_2;
+        field_name_2= cur_field_name_2;
+        found= TRUE;
+      }
+    }
+    table_ref_2->is_join_columns_complete= TRUE;
+    add_columns= FALSE;
+    if (!found)
+      continue;
+
+    /*
+      field_1 and field_2 have the same names. Check if they are in the USING
+      clause (if present), mark them as common fields, and add a new
+      equi-join condition to the ON clause.
+    */
+    if (nj_col_2 &&
+        (!using_fields ||
+         (using_fields &&
+          test_if_string_in_list(field_name_1, using_fields))))
+    {
+      Item *item_1=   nj_col_1->create_item(thd);
+      Item *item_2=   nj_col_2->create_item(thd);
+      Field *field_1= nj_col_1->field();
+      Field *field_2= nj_col_2->field();
+      Item_ident *item_ident_1, *item_ident_2;
+      Name_resolution_context *context_1, *context_2;
+      DBUG_PRINT("info", ("new equi-join condition:  %s.%s = %s.%s",
+                          table_ref_1->alias, field_1->field_name,
+                          table_ref_2->alias, field_2->field_name));
+
+      /*
+        The first assert guarantees that the two created items are of
+        type Item_ident.
+      */
+      DBUG_ASSERT(!thd->lex->current_select->no_wrap_view_item);
+      /*
+        In the case of no_wrap_view_item == 0, the created items must be
+        of sub-classes of Item_ident.
+      */
+      DBUG_ASSERT(item_1->type() == Item::FIELD_ITEM ||
+                  item_1->type() == Item::REF_ITEM);
+      DBUG_ASSERT(item_2->type() == Item::FIELD_ITEM ||
+                  item_2->type() == Item::REF_ITEM);
+
+      /*
+        We need to cast item_1,2 to Item_ident, because we need to hook name
+        resolution contexts specific to each item.
+      */
+      item_ident_1= (Item_ident*) item_1;
+      item_ident_2= (Item_ident*) item_2;
+      /*
+        Create and hook special name resolution contexts to each item in the
+        new join condition . We need this to both speed-up subsequent name
+        resolution of these items, and to enable proper name resolution of
+        the items during the execute phase of PS.
+      */
+      if (set_new_item_local_context(thd, item_ident_1, table_ref_1))
+        goto err;
+      if (set_new_item_local_context(thd, item_ident_2, table_ref_2))
+        goto err;
+
+      Item_func_eq *eq_cond= new Item_func_eq(item_ident_1, item_ident_2);
+      if (!eq_cond)
+        goto err; /* Out of memory. */
+
+      /*
+        Add the new equi-join condition to the ON clause. Notice that
+        fix_fields() is applied to all ON conditions in setup_conds()
+        so we don't do it here.
+       */
+      if (table_ref_1->outer_join & JOIN_TYPE_RIGHT)
+        add_join_on(table_ref_1, eq_cond);
+      else
+        add_join_on(table_ref_2, eq_cond);
+
+      nj_col_1->is_common= nj_col_2->is_common= TRUE;
+      nj_col_1->is_coalesced= nj_col_2->is_coalesced= TRUE;
+
+      if (field_1)
+      {
+        /* Mark field_1 used for table cache. */
+        field_1->query_id= thd->query_id;
+        nj_col_1->table_ref->table->used_keys.intersect(field_1->part_of_key);
+      }
+      if (field_2)
+      {
+        /* Mark field_2 used for table cache. */
+        field_2->query_id= thd->query_id;
+        nj_col_2->table_ref->table->used_keys.intersect(field_2->part_of_key);
+      }
+
+      if (using_fields != NULL)
+        ++(*found_using_fields);
+    }
+  }
+  table_ref_1->is_join_columns_complete= TRUE;
+
+  /*
+    Everything is OK.
+    Notice that at this point there may be some column names in the USING
+    clause that are not among the common columns. This is an SQL error and
+    we check for this error in store_natural_using_join_columns() when
+    (found_using_fields < length(join_using_fields)).
+  */
+  result= FALSE;
+
+err:
+  if (arena)
+    thd->restore_backup_item_arena(arena, &backup);
+  DBUG_RETURN(result);
+}
+
+
+
+/*
+  Materialize and store the row type of NATURAL/USING join.
+
+  SYNOPSIS
+    store_natural_using_join_columns()
+    thd                current thread
+    natural_using_join the table reference of the NATURAL/USING join
+    table_ref_1        the first (left) operand (of a NATURAL/USING join).
+    table_ref_2        the second (right) operand (of a NATURAL/USING join).
+    using_fields       if the join is JOIN...USING - the join columns,
+                       if NATURAL join, then NULL
+    found_using_fields number of fields from the USING clause that were
+                       found among the common fields
+
+  DESCRIPTION
+    Iterate over the columns of both join operands and sort and store
+    all columns into the 'join_columns' list of natural_using_join
+    where the list is formed by three parts:
+      part1: The coalesced columns of table_ref_1 and table_ref_2,
+             sorted according to the column order of the first table.
+      part2: The other columns of the first table, in the order in
+             which they were defined in CREATE TABLE.
+      part3: The other columns of the second table, in the order in
+             which they were defined in CREATE TABLE.
+    Time complexity - O(N1+N2), where Ni = length(table_ref_i).
+
+  IMPLEMENTATION
+    The procedure assumes that mark_common_columns() has been called
+    for the join that is being processed.
+
+  RETURN
+    TRUE  - if error when some common column is ambiguous
+    FALSE - if OK
+*/
+
+static bool
+store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join,
+                                 TABLE_LIST *table_ref_1,
+                                 TABLE_LIST *table_ref_2,
+                                 List<String> *using_fields,
+                                 uint found_using_fields)
+{
+  Field_iterator_table_ref it_1, it_2;
+  Natural_join_column *nj_col_1, *nj_col_2;
+  bool is_created;
+  Query_arena *arena, backup;
+  bool result= TRUE;
+
+  DBUG_ENTER("store_natural_using_join_columns");
+
+  arena= thd->change_arena_if_needed(&backup);
+
+  List<Natural_join_column> *non_join_columns;
+  if (!(non_join_columns= new List<Natural_join_column>))
+    goto err;
+  DBUG_ASSERT(!natural_using_join->join_columns);
+  if (!(natural_using_join->join_columns= new List<Natural_join_column>))
+    goto err;
+
+  /* Append the columns of the first join operand. */
+  for (it_1.set(table_ref_1); !it_1.end_of_fields(); it_1.next())
+  {
+    if (!(nj_col_1= it_1.get_or_create_column_ref(thd, &is_created)))
+      goto err;
+    DBUG_ASSERT(!is_created);
+    if (nj_col_1->is_common)
+    {
+      natural_using_join->join_columns->push_back(nj_col_1);
+      /* Reset the common columns for the next call to mark_common_columns. */
+      nj_col_1->is_common= FALSE;
+    }
+    else
+      non_join_columns->push_back(nj_col_1);
+  }
+
+  /*
+    Check that all columns in the USING clause are among the common
+    columns. If this is not the case, report the first one that was
+    not found in an error.
+  */
+  if (using_fields && found_using_fields < using_fields->elements)
+  {
+    String *using_field_name;
+    List_iterator_fast<String> using_fields_it(*using_fields);
+    while ((using_field_name= using_fields_it++))
+    {
+      const char *using_field_name_ptr= using_field_name->ptr();
+      List_iterator_fast<Natural_join_column>
+        it(*(natural_using_join->join_columns));
+      Natural_join_column *common_field;
+      bool found= FALSE;
+      while ((common_field= it++))
+      {
+        if (!my_strcasecmp(system_charset_info,
+                           common_field->name(), using_field_name_ptr))
+          found= TRUE;
+      }
+      if (!found)
+      {
+        my_error(ER_BAD_FIELD_ERROR, MYF(0), using_field_name_ptr,
+                 current_thd->where);
+        delete non_join_columns;
+        goto err;
+      }
+    }
+  }
+
+  /* Append the non-equi-join columns of the second join operand. */
+  for (it_2.set(table_ref_2); !it_2.end_of_fields(); it_2.next())
+  {
+    if (!(nj_col_2= it_2.get_or_create_column_ref(thd, &is_created)))
+      goto err;
+    DBUG_ASSERT(!is_created);
+    if (!nj_col_2->is_common)
+      non_join_columns->push_back(nj_col_2);
+    else
+      /* Reset the common columns for the next call to mark_common_columns. */
+      nj_col_2->is_common= FALSE;
+
+  }
+
+  if (non_join_columns->elements > 0)
+    natural_using_join->join_columns->concat(non_join_columns);
+  else
+    delete non_join_columns;
+  natural_using_join->is_join_columns_complete= TRUE;
+
+
+  result= FALSE;
+
+err:
+  if (arena)
+    thd->restore_backup_item_arena(arena, &backup);
+  DBUG_RETURN(result);
+}
+
+/*
+  Precompute and store the row types of the top-most NATURAL/USING joins.
+
+  SYNOPSIS
+    store_top_level_join_columns()
+    thd            current thread
+    table_ref      nested join or table in a FROM clause
+    left_neighbor  neighbor table reference to the left of table_ref at the
+                   same level in the join tree
+    right_neighbor neighbor table reference to the right of table_ref at the
+                   same level in the join tree
+
+  DESCRIPTION
+    The procedure performs a post-order traversal of a nested join tree
+    and materializes the row types of NATURAL/USING joins in a
+    bottom-up manner until it reaches the TABLE_LIST elements that
+    represent the top-most NATURAL/USING joins. The procedure should be
+    applied to each element of SELECT_LEX::top_join_list (i.e. to each
+    top-level element of the FROM clause).
+
+  IMPLEMENTATION
+    Notice that the table references in the list nested_join->join_list
+    are in reverse order, thus when we iterate over it, we are moving
+    from the right to the left in the FROM clause.
+
+  RETURN
+    TRUE  - if error
+    FALSE - if OK
+*/
+
+static bool
+store_top_level_join_columns(THD *thd, TABLE_LIST *table_ref,
+                             TABLE_LIST *left_neighbor,
+                             TABLE_LIST *right_neighbor)
+{
+  DBUG_ENTER("store_top_level_join_columns");
+  /* Call the procedure recursively for each nested table reference. */
+  if (table_ref->nested_join)
+  {
+    List_iterator_fast<TABLE_LIST> nested_it(table_ref->nested_join->join_list);
+    TABLE_LIST *cur_table_ref;
+    TABLE_LIST *cur_left_neighbor= nested_it++;
+    TABLE_LIST *cur_right_neighbor= NULL;
+    while (cur_left_neighbor)
+    {
+      cur_table_ref= cur_left_neighbor;
+      cur_left_neighbor= nested_it++;
+      if (cur_table_ref->nested_join &&
+          store_top_level_join_columns(thd, cur_table_ref,
+                                       cur_left_neighbor, cur_right_neighbor))
+          DBUG_RETURN(TRUE);
+      cur_right_neighbor= cur_table_ref;
+    }
+  }
+
+  /*
+    If this is a NATURAL/USING join, materialize its result columns and
+    convert to a JOIN ... ON.
+  */
+  if (table_ref->is_natural_join)
+  {
+    DBUG_ASSERT(table_ref->nested_join &&
+                table_ref->nested_join->join_list.elements == 2);
+    List_iterator_fast<TABLE_LIST> operand_it(table_ref->nested_join->join_list);
+    /*
+      Notice that the order of join operands depends on whether table_ref
+      represents a LEFT or a RIGHT join. In a RIGHT join, the operands are
+      in inverted order.
+     */
+    TABLE_LIST *table_ref_2= operand_it++; /* Second NATURAL join operand.*/
+    TABLE_LIST *table_ref_1= operand_it++; /* First NATURAL join operand. */
+    TABLE_LIST *last_leaf_on_the_left= NULL;
+    TABLE_LIST *first_leaf_on_the_right= NULL;
+    List<String> *using_fields= table_ref->join_using_fields;
+    uint found_using_fields;
+
+    /*
+      The two join operands were interchanged in the parser, change the order
+      back for 'mark_common_columns'.
+    */
+    if (table_ref_2->outer_join & JOIN_TYPE_RIGHT)
+      swap_variables(TABLE_LIST*, table_ref_1, table_ref_2);
+    if (mark_common_columns(thd, table_ref_1, table_ref_2,
+                            using_fields, &found_using_fields))
+      DBUG_RETURN(TRUE);
+
+    /*
+      Swap the join operands back, so that we pick the columns of the second
+      one as the coalesced columns. In this way the coalesced columns are the
+      same as of an equivalent LEFT JOIN.
+    */
+    if (table_ref_1->outer_join & JOIN_TYPE_RIGHT)
+      swap_variables(TABLE_LIST*, table_ref_1, table_ref_2);
+    if (store_natural_using_join_columns(thd, table_ref, table_ref_1,
+                                         table_ref_2, using_fields,
+                                         found_using_fields))
+      DBUG_RETURN(TRUE);
+
+    /*
+      Change NATURAL JOIN to JOIN ... ON. We do this for both operands
+      because either one of them or the other is the one with the
+      natural join flag because RIGHT joins are transformed into LEFT,
+      and the two tables may be reordered.
+    */
+    table_ref_1->natural_join= table_ref_2->natural_join= NULL;
+
+    /* Change this table reference to become a leaf for name resolution. */
+    if (left_neighbor)
+    {
+      last_leaf_on_the_left= left_neighbor->last_leaf_for_name_resolution();
+      last_leaf_on_the_left->next_name_resolution_table= table_ref;
+    }
+    if (right_neighbor)
+    {
+      first_leaf_on_the_right= right_neighbor->first_leaf_for_name_resolution();
+      table_ref->next_name_resolution_table= first_leaf_on_the_right;
+    }
+    else
+      table_ref->next_name_resolution_table= NULL;
+  }
+  DBUG_RETURN(FALSE);
+}
+
+
+/*
+  Compute and store the row types of the top-most NATURAL/USING joins
+  in a FROM clause.
+
+  SYNOPSIS
+    setup_natural_join_row_types()
+    thd          current thread
+    from_clause  list of top-level table references in a FROM clause
+
+  DESCRIPTION
+    Apply the procedure 'store_top_level_join_columns' to each of the
+    top-level table referencs of the FROM clause. Adjust the list of tables
+    for name resolution - context->first_name_resolution_table to the
+    top-most, lef-most NATURAL/USING join.
+
+  IMPLEMENTATION
+    Notice that the table references in 'from_clause' are in reverse
+    order, thus when we iterate over it, we are moving from the right
+    to the left in the FROM clause.
+
+  RETURN
+    TRUE  - if error
+    FALSE - if OK
+*/
+static bool setup_natural_join_row_types(THD *thd, List<TABLE_LIST> *from_clause,
+                                         Name_resolution_context *context)
+{
+  thd->where= "from clause";
+  if (from_clause->elements == 0)
+    return FALSE; /* We come here in the case of UNIONs. */
+
+  /* For stored procedures do not redo work if already done. */
+  if (!context->select_lex->first_execution)
+    return FALSE;
+
+  List_iterator_fast<TABLE_LIST> table_ref_it(*from_clause);
+  TABLE_LIST *table_ref; /* Current table reference. */
+  /* Table reference to the left of the current. */
+  TABLE_LIST *left_neighbor= table_ref_it++;
+  /* Table reference to the right of the current. */
+  TABLE_LIST *right_neighbor= NULL;
+
+  while (left_neighbor)
+  {
+    table_ref= left_neighbor;
+    left_neighbor= table_ref_it++;
+    if (store_top_level_join_columns(thd, table_ref,
+                                     left_neighbor, right_neighbor))
+      return TRUE;
+    if (left_neighbor)
+    {
+      TABLE_LIST *first_leaf_on_the_right;
+      first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution();
+      left_neighbor->next_name_resolution_table= first_leaf_on_the_right;
+    }
+    right_neighbor= table_ref;
+  }
+
+  /*
+    Store the top-most, left-most NATURAL/USING join, so that we start
+    the search from that one instead of context->table_list. At this point
+    right_neigbor points to the left-most top-level table reference in the
+    FROM clause.
+  */
+  DBUG_ASSERT(right_neighbor);
+  context->first_name_resolution_table=
+    right_neighbor->first_leaf_for_name_resolution();
+
+  return FALSE;
+}
+
+
 /****************************************************************************
 ** Expand all '*' in given fields
 ****************************************************************************/
@@ -3239,9 +4067,7 @@
   for (TABLE_LIST *table= tables; table; table= table->next_local)
   {
     if (table->view && table->effective_algorithm == VIEW_ALGORITHM_MERGE)
-    {
       list= make_leaves_list(list, table->ancestor);
-    }
     else
     {
       *list= table;
@@ -3258,33 +4084,36 @@
     setup_tables()
     thd		  Thread handler
     context       name resolution contest to setup table list there
-    tables	  Table list
+    from_clause   Top-level list of table references in the FROM clause
+    tables	  Table list (select_lex->table_list)
     conds	  Condition of current SELECT (can be changed by VIEW)
-    leaves        List of join table leaves list
+    leaves        List of join table leaves list (select_lex->leaf_tables)
     refresh       It is onle refresh for subquery
     select_insert It is SELECT ... INSERT command
 
   NOTE
     Check also that the 'used keys' and 'ignored keys' exists and set up the
-    table structure accordingly
-    Create leaf tables list
+    table structure accordingly.
+    Create a list of leaf tables. For queries with NATURAL/USING JOINs,
+    compute the row types of the top most natural/using join table references
+    and link these into a list of table references for name resolution.
 
     This has to be called for all tables that are used by items, as otherwise
     table->map is not set and all Item_field will be regarded as const items.
 
   RETURN
-    FALSE ok;  In this case *map will includes the choosed index
+    FALSE ok;  In this case *map will includes the chosen index
     TRUE  error
 */
 
 bool setup_tables(THD *thd, Name_resolution_context *context,
-                  TABLE_LIST *tables, Item **conds,
-                  TABLE_LIST **leaves, bool select_insert)
+                  List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
+                  Item **conds, TABLE_LIST **leaves, bool select_insert)
 {
   uint tablenr= 0;
   DBUG_ENTER("setup_tables");
 
-  context->table_list= tables;
+  context->table_list= context->first_name_resolution_table= tables;
 
   /*
     this is used for INSERT ... SELECT.
@@ -3356,6 +4185,11 @@
         DBUG_RETURN(1);
     }
   }
+
+  /* Precompute and store the row types of NATURAL/USING joins. */
+  if (setup_natural_join_row_types(thd, from_clause, context))
+    DBUG_RETURN(1);
+
   DBUG_RETURN(0);
 }
 
@@ -3424,12 +4258,12 @@
 	      const char *table_name, List_iterator<Item> *it,
               bool any_privileges)
 {
-  /* allocate variables on stack to avoid pool alloaction */
-  Field_iterator_table table_iter;
-  Field_iterator_view view_iter;
-  uint found;
+  Field_iterator_table_ref field_iterator;
+  bool found;
   char name_buff[NAME_LEN+1];
   DBUG_ENTER("insert_fields");
+  DBUG_PRINT("arena", ("insert_fields: current arena: 0x%lx",
+                       (ulong)thd->current_arena));
 
   if (db_name && lower_case_table_names)
   {
@@ -3443,197 +4277,207 @@
     db_name= name_buff;
   }
 
-  found= 0;
-  for (TABLE_LIST *tables= context->table_list;
+  found= FALSE;
+  for (TABLE_LIST *tables= context->first_name_resolution_table;
        tables;
-       tables= tables->next_local)
+       tables= tables->next_name_resolution_table)
   {
-    Field_iterator *iterator;
-    TABLE_LIST *natural_join_table;
     Field *field;
-    TABLE_LIST *embedded;
-    TABLE_LIST *last;
-    TABLE_LIST *embedding;
     TABLE *table= tables->table;
 
-    if (!table_name || (!my_strcasecmp(table_alias_charset, table_name,
-				       tables->alias) &&
-			(!db_name || !strcmp(tables->db,db_name))))
+    DBUG_ASSERT(tables->is_leaf_for_name_resolution());
+
+    /*
+      If optional table and db names do not match the ones used to qualify
+      the field being expanded, skip this table reference. However if this is
+      a NATURAL/USING join, we can't simply skip the whole table reference,
+      because  its columns may come from different tables/views. For NATURAL/
+      USING joins we perform this test for each column in the loop below.
+    */
+    if (!tables->is_natural_join)
     {
-      bool view;
+      if (table_name && my_strcasecmp(table_alias_charset, table_name, tables->alias)
+          ||
+          (db_name && strcmp(tables->db,db_name)))
+        continue;
+    }
+
+    bool view;
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-      /* Ensure that we have access right to all columns */
-      if (!((table && (table->grant.privilege & SELECT_ACL) ||
-	     tables->view && (tables->grant.privilege & SELECT_ACL))) &&
-	  !any_privileges)
-      {
-        if (tables->view)
-        {
-          view_iter.set(tables);
-          if (check_grant_all_columns(thd, SELECT_ACL, &tables->grant,
-                                      tables->view_db.str,
-                                      tables->view_name.str,
-                                      &view_iter))
-            goto err;
-        }
-        else if (!tables->schema_table)
-        {
-	  DBUG_ASSERT(table != 0);
-          table_iter.set(tables);
-          if (check_grant_all_columns(thd, SELECT_ACL, &table->grant,
-                                      table->s->db,
-                                      table->s->table_name,
-                                      &table_iter))
-            goto err;
-        }
-      }
+    /* Ensure that we have access rights to all fields to be inserted. */
+    if (!((table && (table->grant.privilege & SELECT_ACL) ||
+           tables->view && (tables->grant.privilege & SELECT_ACL))) &&
+        !any_privileges)
+    {
+      field_iterator.set(tables);
+      if (check_grant_all_columns(thd, SELECT_ACL, field_iterator.grant(),
+                                  field_iterator.db_name(), field_iterator.table_name(),
+                                  &field_iterator))
+        DBUG_RETURN(TRUE);
+    }
 #endif
-      natural_join_table= 0;
-      last= embedded= tables;
 
-      while ((embedding= embedded->embedding) &&
-              embedding->join_list->elements != 1)
+
+    /*
+      Update the tables used in the query based on the referenced fields. For
+      views and natural joins this update is performed inside the loop below.
+    */
+    if (table)
+      thd->used_tables|= table->map;
+
+    /*
+      Initialize a generic field iterator for the current table reference.
+      Notice that it is guaranteed that this iterator will iterate over the
+      fields of a single table reference, because 'tables' is a leaf (for
+      name resolution purposes).
+    */
+    field_iterator.set(tables);
+
+    for (; !field_iterator.end_of_fields(); field_iterator.next())
+    {
+      Item *not_used_item;
+      uint not_used_field_index= NO_CACHED_FIELD_INDEX;
+      const char *field_name= field_iterator.name();
+      Item *item;
+
+      /* If this is a column of a NATURAL/USING join, and the star was qualified
+         with a table (and database) name, check if the column is not a coalesced
+         one, and if not, that is belongs to the same table.
+      */
+      if (tables->is_natural_join && table_name)
       {
-        TABLE_LIST *next;
-        List_iterator_fast<TABLE_LIST> it(embedding->nested_join->join_list);
-        last= it++;
-        while ((next= it++))
-          last= next;
-        if (last != tables)
-          break;
-        embedded= embedding;
-      }
-
-      if (tables == last &&
-          !embedded->outer_join &&
-          embedded->natural_join &&
-          !embedded->natural_join->outer_join)
-      {
-        embedding= embedded->natural_join;
-        while (embedding->nested_join)
-          embedding= embedding->nested_join->join_list.head();
-        natural_join_table= embedding;
+        if (field_iterator.is_coalesced()
+            ||
+            my_strcasecmp(table_alias_charset, table_name, field_iterator.table_name())
+            ||
+            (db_name && strcmp(db_name, field_iterator.db_name())))
+          continue;
       }
-      if (tables->field_translation)
+
+      if (!(item= field_iterator.create_item(thd)))
+        DBUG_RETURN(TRUE);
+
+      if (!found)
       {
-        iterator= &view_iter;
-	view= 1;
+        it->replace(item); /* Replace '*' with the first found item. */
+        found= TRUE;
       }
       else
+        it->after(item);   /* Add 'item' to the SELECT list. */
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+      /*
+        Set privilege information for the fields of newly created views.
+        We have that (any_priviliges == TRUE) if and only if we are creating
+        a view. In the time of view creation we can't use the MERGE algorithm,
+        therefore if 'tables' is itself a view, it is represented by a temporary
+        table. Thus in this case we can be sure that 'item' is an Item_field.
+      */
+      if (any_privileges)
       {
-        iterator= &table_iter;
-	view= 0;
+        DBUG_ASSERT(tables->field_translation == NULL && table ||
+                    tables->is_natural_join);
+        DBUG_ASSERT(item->type() == Item::FIELD_ITEM);
+        Item_field *fld= (Item_field*) item;
+        const char *table_name= field_iterator.table_name();
+        if (!tables->schema_table && 
+            !(fld->have_privileges=
+              (get_column_grant(thd, field_iterator.grant(),
+                                field_iterator.db_name(),
+                                table_name, fld->field_name) &
+               VIEW_ANY_ACL)))
+        {
+          my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), "ANY",
+                   thd->priv_user, thd->host_or_ip,
+                   fld->field_name, table_name);
+          DBUG_RETURN(TRUE);
+        }
       }
-      iterator->set(tables);
+#endif
+
+      if ((field= field_iterator.field()))
+      {
+        /*
+          Mark if field used before in this select.
+          Used by 'insert' to verify if a field name is used twice.
+        */
+        if (field->query_id == thd->query_id)
+          thd->dupp_field= field;
+        field->query_id= thd->query_id;
 
-      /* for view used tables will be collected in following loop */
-      if (table)
-	thd->used_tables|= table->map;
-
-      for (; !iterator->end_of_fields(); iterator->next())
-      {
-        Item *not_used_item;
-        uint not_used_field_index= NO_CACHED_FIELD_INDEX;
-        const char *field_name= iterator->name();
-        /* Skip duplicate field names if NATURAL JOIN is used */
-        if (!natural_join_table ||
-            !find_field_in_table(thd, natural_join_table, field_name,
-                                 field_name,
-                                 strlen(field_name), &not_used_item, 0, 0, 0,
-                                 &not_used_field_index, TRUE))
+        if (table)
+          table->used_keys.intersect(field->part_of_key);
+
+        if (tables->is_natural_join)
         {
-          Item *item= iterator->create_item(thd);
-          if (!item)
-            goto err;
-          thd->used_tables|= item->used_tables();
-          if (!found++)
-            (void) it->replace(item);		// Replace '*'
-          else
-            it->after(item);
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-          if (any_privileges)
+          bool is_created;
+          TABLE *field_table;
+          /*
+            In this case we are shure that the column ref will not be created
+            because it was already created and stored with the natural join.
+           */
+          Natural_join_column *nj_col;
+          if (!(nj_col= field_iterator.get_or_create_column_ref(thd, &is_created)))
+            DBUG_RETURN(TRUE);
+          DBUG_ASSERT(nj_col->table_field && !is_created);
+          field_table= nj_col->table_ref->table;
+          if (field_table)
           {
-            /*
-              In time of view creation MEGRGE algorithm for underlying
-              VIEWs can't be used => it should be Item_field
-            */
-            DBUG_ASSERT(item->type() == Item::FIELD_ITEM);
-            Item_field *fld= (Item_field*)item;
-            char *db, *tab;
-            if (tables->view)
-            {
-              db= tables->view_db.str;
-              tab= tables->view_name.str;
-            }
-            else
-            {
-              db= tables->db;
-              tab= tables->table_name;
-            }
-            if (!tables->schema_table && 
-                !(fld->have_privileges= (get_column_grant(thd,
-                                                          &table->grant,
-                                                          db,
-                                                          tab,
-                                                          fld->field_name) &
-                                         VIEW_ANY_ACL)))
-            {
-              my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
-                       "ANY",
-                       thd->priv_user,
-                       thd->host_or_ip,
-                       fld->field_name,
-                       tab);
-              goto err;
-            }
+            thd->used_tables|= field_table->map;
+            field_table->used_keys.intersect(field->part_of_key);
+            field_table->used_fields++;
           }
-#endif
-        }
-        if ((field= iterator->field()))
-        {
-          /*
-            Mark if field used before in this select.
-            Used by 'insert' to verify if a field name is used twice
-          */
-          if (field->query_id == thd->query_id)
-            thd->dupp_field=field;
-          field->query_id=thd->query_id;
-          table->used_keys.intersect(field->part_of_key);
-        }
-        else
-        {
-          Item *item= ((Field_iterator_view *) iterator)->item();
-          item->walk(&Item::reset_query_id_processor,
-                     (byte *)(&thd->query_id));
         }
       }
-      /*
-	All fields are used in case if usual tables (in case of view used
-	fields marked in setup_tables during fix_fields of view columns
-      */
-      if (table)
-	table->used_fields= table->s->fields;
+      else
+      {
+        thd->used_tables|= item->used_tables();
+        item->walk(&Item::reset_query_id_processor,
+                   (byte *)(&thd->query_id));
+      }
     }
+    /*
+      In case of stored tables, all fields are considered as used,
+      while in the case of views, the fields considered as used are the
+      ones marked in setup_tables during fix_fields of view columns.
+      For NATURAL joins, used_tables is updated in the IF above.
+    */
+    if (table)
+      table->used_fields= table->s->fields;
   }
   if (found)
-    DBUG_RETURN(0);
+    DBUG_RETURN(FALSE);
 
+  /*
+    TODO: in the case when we skipped all columns because there was a qualified
+    '*', and all columns were coalesced, we have to give a more meaningful message
+    than ER_BAD_TABLE_ERROR.
+  */
   if (!table_name)
     my_message(ER_NO_TABLES_USED, ER(ER_NO_TABLES_USED), MYF(0));
   else
     my_error(ER_BAD_TABLE_ERROR, MYF(0), table_name);
-err:
-  DBUG_RETURN(1);
+
+  DBUG_RETURN(TRUE);
 }
 
 
 /*
-  Fix all conditions and outer join expressions
+  Fix all conditions and outer join expressions.
 
   SYNOPSIS
     setup_conds()
     thd     thread handler
-    leaves  list of leaves of join table tree
+    tables  list of tables for name resolving (select_lex->table_list)
+    leaves  list of leaves of join table tree (select_lex->leaf_tables)
+    conds   WHERE clause
+
+  DESCRIPTION
+    TODO
+
+  RETURN
+    TRUE  if some error occured (e.g. out of memory)
+    FALSE if all is OK
 */
 
 int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
@@ -3675,11 +4519,14 @@
       goto err_no_arena;
   }
 
-  /* Check if we are using outer joins */
+  /*
+    Apply fix_fields() to all ON clauses at all levels of nesting,
+    including the ones inside view definitions.
+  */
   for (table= leaves; table; table= table->next_leaf)
   {
-    TABLE_LIST *embedded;
-    TABLE_LIST *embedding= table;
+    TABLE_LIST *embedded; /* The table at the current level of nesting. */
+    TABLE_LIST *embedding= table; /* The parent nested table reference. */
     do
     {
       embedded= embedding;
@@ -3692,144 +4539,6 @@
 	    embedded->on_expr->check_cols(1))
 	  goto err_no_arena;
         select_lex->cond_count++;
-      }
-
-      if (embedded->natural_join)
-      {
-        /* Make a join of all fields wich have the same name */
-        TABLE_LIST *tab1= embedded;
-        TABLE_LIST *tab2= embedded->natural_join;
-        if (!(embedded->outer_join & JOIN_TYPE_RIGHT))
-        {
-          while (tab1->nested_join)
-          {
-            TABLE_LIST *next;
-            List_iterator_fast<TABLE_LIST> it(tab1->nested_join->join_list);
-            tab1= it++;
-            while ((next= it++))
-              tab1= next;
-          }
-        }
-        else
-        {
-          while (tab1->nested_join)
-            tab1= tab1->nested_join->join_list.head();
-        }
-        if (embedded->outer_join & JOIN_TYPE_RIGHT)
-        {
-          while (tab2->nested_join)
-          {
-            TABLE_LIST *next;
-            List_iterator_fast<TABLE_LIST> it(tab2->nested_join->join_list);
-            tab2= it++;
-            while ((next= it++))
-              tab2= next;
-          }
-        }
-        else
-        {
-          while (tab2->nested_join)
-            tab2= tab2->nested_join->join_list.head();
-        }
-
-        if (arena)
-	  arena= thd->change_arena_if_needed(&backup);
-
-        TABLE *t1=tab1->table;
-        TABLE *t2=tab2->table;
-        Field_iterator_table table_iter;
-        Field_iterator_view view_iter;
-        Field_iterator *iterator;
-        Field *t1_field, *t2_field;
-        Item *item_t2= 0;
-        Item_cond_and *cond_and= new Item_cond_and();
-
-        if (!cond_and)				// If not out of memory
-	  goto err_no_arena;
-        cond_and->top_level_item();
-
-        if (table->field_translation)
-        {
-          iterator= &view_iter;
-          view_iter.set(tab1);
-        }
-        else
-        {
-          iterator= &table_iter;
-          table_iter.set(tab1);
-        }
-
-        for (; !iterator->end_of_fields(); iterator->next())
-        {
-          const char *t1_field_name= iterator->name();
-          uint not_used_field_index= NO_CACHED_FIELD_INDEX;
-
-          if ((t2_field= find_field_in_table(thd, tab2, t1_field_name,
-                                             t1_field_name,
-                                             strlen(t1_field_name), &item_t2,
-                                             0, 0, 0,
-                                             &not_used_field_index,
-                                             FALSE)))
-          {
-            if (t2_field != view_ref_found)
-            {
-              if (!(item_t2= new Item_field(thd, &select_lex->context,
-                                            t2_field)))
-                goto err;
-              /* Mark field used for table cache */
-              t2_field->query_id= thd->query_id;
-              t2->used_keys.intersect(t2_field->part_of_key);
-            }
-            if ((t1_field= iterator->field()))
-            {
-              /* Mark field used for table cache */
-              t1_field->query_id= thd->query_id;
-              t1->used_keys.intersect(t1_field->part_of_key);
-            }
-            Item_func_eq *tmp= new Item_func_eq(iterator->create_item(thd),
-                                                item_t2);
-            if (!tmp)
-              goto err;
-            cond_and->list.push_back(tmp);
-          }
-        }
-        select_lex->cond_count+= cond_and->list.elements;
-
-        // to prevent natural join processing during PS re-execution
-        embedding->natural_join= 0;
-
-        if (cond_and->list.elements)
-        {
-          COND *on_expr= cond_and;
-          if (!on_expr->fixed)
-            on_expr->fix_fields(thd, &on_expr);
-          if (!embedded->outer_join)			// Not left join
-          {
-            *conds= and_conds(*conds, cond_and);
-            // fix_fields() should be made with temporary memory pool
-            if (arena)
-              thd->restore_backup_item_arena(arena, &backup);
-            if (*conds && !(*conds)->fixed)
-            {
-              if ((*conds)->fix_fields(thd, conds))
-                goto err_no_arena;
-            }
-          }
-          else
-          {
-            embedded->on_expr= and_conds(embedded->on_expr, cond_and);
-            // fix_fields() should be made with temporary memory pool
-            if (arena)
-              thd->restore_backup_item_arena(arena, &backup);
-            if (embedded->on_expr && !embedded->on_expr->fixed)
-            {
-              if (embedded->on_expr->fix_fields(thd, &embedded->on_expr))
-                goto err_no_arena;
-            }
-          }
-        }
-        else if (arena)
-	  thd->restore_backup_item_arena(arena, &backup);
       }
       embedding= embedded->embedding;
     }

--- 1.200/sql/sql_class.cc	2005-08-15 00:19:58 +03:00
+++ 1.201/sql/sql_class.cc	2005-08-15 18:35:44 +03:00
@@ -174,7 +174,7 @@
   :Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0),
    Open_tables_state(refresh_version),
    lock_id(&main_lock_id),
-   user_time(0), in_sub_stmt(FALSE), global_read_lock(0), is_fatal_error(0),
+   user_time(0), in_sub_stmt(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),
    in_lock_tables(0), bootstrap(0), derived_tables_processing(FALSE),
@@ -1844,6 +1844,94 @@
   DBUG_VOID_RETURN;
 }
 
+
+
+/****************************************************************************
+  Handling of statement states in functions and triggers.
+
+  This is used to ensure that the function/trigger gets a clean state
+  to work with and does not cause any side effects of the calling statement.
+
+  It also allows most stored functions and triggers to replicate even
+  if they are used items that would normally be stored in the binary
+  replication (like last_insert_id() etc...)
+
+  The following things is done
+  - Disable binary logging for the duration of the statement
+  - Disable multi-result-sets for the duration of the statement
+  - Value of last_insert_id() is reset and restored
+  - Value set by 'SET INSERT_ID=#' is reset and restored
+  - Value for found_rows() is reset and restored
+  - examined_row_count is added to the total
+  - cuted_fields is added to the total
+
+  NOTES:
+    Seed for random() is saved for the first! usage of RAND()
+    We reset examined_row_count and cuted_fields and add these to the
+    result to ensure that if we have a bug that would reset these within
+    a function, we are not loosing any rows from the main statement.
+****************************************************************************/
+
+void THD::reset_sub_statement_state(Sub_statement_state *backup,
+                                    uint new_state)
+{
+  backup->options=         options;
+  backup->in_sub_stmt=     in_sub_stmt;
+  backup->no_send_ok=      net.no_send_ok;
+  backup->enable_slow_log= enable_slow_log;
+  backup->last_insert_id=  last_insert_id;
+  backup->next_insert_id=  next_insert_id;
+  backup->insert_id_used=  insert_id_used;
+  backup->limit_found_rows= limit_found_rows;
+  backup->examined_row_count= examined_row_count;
+  backup->sent_row_count=   sent_row_count;
+  backup->cuted_fields=     cuted_fields;
+  backup->client_capabilities= client_capabilities;
+
+  options&= ~OPTION_BIN_LOG;
+  /* Disable result sets */
+  client_capabilities &= ~CLIENT_MULTI_RESULTS;
+  in_sub_stmt|= new_state;
+  last_insert_id= 0;
+  next_insert_id= 0;
+  insert_id_used= 0;
+  examined_row_count= 0;
+  sent_row_count= 0;
+  cuted_fields= 0;
+
+#ifndef EMBEDDED_LIBRARY
+  /* Surpress OK packets in case if we will execute statements */
+  net.no_send_ok= TRUE;
+#endif
+}
+
+
+void THD::restore_sub_statement_state(Sub_statement_state *backup)
+{
+  options=          backup->options;
+  in_sub_stmt=      backup->in_sub_stmt;
+  net.no_send_ok=   backup->no_send_ok;
+  enable_slow_log=  backup->enable_slow_log;
+  last_insert_id=   backup->last_insert_id;
+  next_insert_id=   backup->next_insert_id;
+  insert_id_used=   backup->insert_id_used;
+  limit_found_rows= backup->limit_found_rows;
+  sent_row_count=   backup->sent_row_count;
+  client_capabilities= backup->client_capabilities;
+
+  /*
+    The following is added to the old values as we are interested in the
+    total complexity of the query
+  */
+  examined_row_count+= backup->examined_row_count;
+  cuted_fields+=       backup->cuted_fields;
+}
+
+
+/***************************************************************************
+  Handling of XA id cacheing
+***************************************************************************/
+
 pthread_mutex_t LOCK_xid_cache;
 HASH xid_cache;
 
@@ -1884,6 +1972,7 @@
   return res;
 }
 
+
 bool xid_cache_insert(XID *xid, enum xa_states xa_state)
 {
   XID_STATE *xs;
@@ -1904,6 +1993,7 @@
   return res;
 }
 
+
 bool xid_cache_insert(XID_STATE *xid_state)
 {
   pthread_mutex_lock(&LOCK_xid_cache);
@@ -1913,6 +2003,7 @@
   pthread_mutex_unlock(&LOCK_xid_cache);
   return res;
 }
+
 
 void xid_cache_delete(XID_STATE *xid_state)
 {

--- 1.258/sql/sql_class.h	2005-08-15 18:15:04 +03:00
+++ 1.259/sql/sql_class.h	2005-08-15 18:31:00 +03:00
@@ -351,8 +351,6 @@
   inline uint32 get_open_count() { return open_count; }
 };
 
-/* character conversion tables */
-
 
 typedef struct st_copy_info {
   ha_rows records;
@@ -564,11 +562,11 @@
   my_bool ndb_use_transactions;
 #endif /* HAVE_NDBCLUSTER_DB */
   my_bool old_passwords;
-  
+
   /* Only charset part of these variables is sensible */
-  CHARSET_INFO 	*character_set_client;
+  CHARSET_INFO  *character_set_client;
   CHARSET_INFO  *character_set_results;
-  
+
   /* Both charset and collation parts of these variables are important */
   CHARSET_INFO	*collation_server;
   CHARSET_INFO	*collation_database;
@@ -631,7 +629,7 @@
   ulong filesort_range_count;
   ulong filesort_rows;
   ulong filesort_scan_count;
-  /* Ppepared statements and binary protocol */
+  /* Prepared statements and binary protocol */
   ulong com_stmt_prepare;
   ulong com_stmt_execute;
   ulong com_stmt_send_long_data;
@@ -656,8 +654,8 @@
 /* The following macro is to make init of Query_arena simpler */
 #ifndef DBUG_OFF
 #define INIT_ARENA_DBUG_INFO is_backup_arena= 0
-#else 
-#define INIT_ARENA_DBUG_INFO  
+#else
+#define INIT_ARENA_DBUG_INFO
 #endif
 
 
@@ -925,6 +923,22 @@
 enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED};
 extern const char *xa_state_names[];
 
+typedef struct st_xid_state {
+  /* For now, this is only used to catch duplicated external xids */
+  XID  xid;                           // transaction identifier
+  enum xa_states xa_state;            // used by external XA only
+  bool in_thd;
+} XID_STATE;
+
+extern pthread_mutex_t LOCK_xid_cache;
+extern HASH xid_cache;
+bool xid_cache_init(void);
+void xid_cache_free(void);
+XID_STATE *xid_cache_search(XID *xid);
+bool xid_cache_insert(XID *xid, enum xa_states xa_state);
+bool xid_cache_insert(XID_STATE *xid_state);
+void xid_cache_delete(XID_STATE *xid_state);
+
 /*
   A registry for item tree transformations performed during
   query optimization. We register only those changes which require
@@ -946,7 +960,7 @@
 
 
 /*
-  Class that holds information about tables which were open and locked
+  Class that holds information about tables which were opened and locked
   by the thread. It is also used to save/restore this information in
   push_open_tables_state()/pop_open_tables_state().
 */
@@ -1089,7 +1103,7 @@
                                         // the lock_id of a cursor.
   pthread_mutex_t LOCK_delete;		// Locked before thd is deleted
   /* all prepared statements and cursors of this connection */
-  Statement_map stmt_map; 
+  Statement_map stmt_map;
   /*
     A pointer to the stack frame of handle_one_connection(),
     which is called first in the thread for handling a client
@@ -1158,10 +1172,10 @@
   time_t     connect_time,thr_create_time; // track down slow pthread_create
   thr_lock_type update_lock_default;
   delayed_insert *di;
-  
+
   /* <> 0 if we are inside of trigger or stored function. */
   uint in_sub_stmt;
-  
+
   /* container for handler's private per-connection data */
   void *ha_data[MAX_HA];
   struct st_transactions {
@@ -1169,8 +1183,7 @@
     THD_TRANS all;			// Trans since BEGIN WORK
     THD_TRANS stmt;			// Trans for current statement
     bool on;                            // see ha_enable_transaction()
-    XID  xid;                           // transaction identifier
-    enum xa_states xa_state;            // used by external XA only
+    XID_STATE xid_state;
     /*
        Tables changed in transaction (that must be invalidated in query cache).
        List contain only transactional tables, that not invalidated in query
@@ -1190,7 +1203,7 @@
     st_transactions()
     {
       bzero((char*)this, sizeof(*this));
-      xid.null();
+      xid_state.xid.null();
       init_sql_alloc(&mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
     }
 #endif
@@ -1247,6 +1260,12 @@
   longlong   row_count_func;	/* For the ROW_COUNT() function */
   ha_rows    cuted_fields,
              sent_row_count, examined_row_count;
+  /*
+    The set of those tables whose fields are referenced in all subqueries
+    of the query.
+    TODO: possibly this it is incorrect to have used tables in THD because
+    with more than one subquery, it is not clear what does the field mean.
+  */
   table_map  used_tables;
   USER_CONN *user_connect;
   CHARSET_INFO *db_charset;

--- 1.155/sql/sql_delete.cc	2005-08-12 13:54:35 +03:00
+++ 1.156/sql/sql_delete.cc	2005-08-15 18:31:01 +03:00
@@ -301,6 +301,7 @@
   DBUG_ENTER("mysql_prepare_delete");
 
   if (setup_tables(thd, &thd->lex->select_lex.context,
+                   &thd->lex->select_lex.top_join_list,
                    table_list, conds, &select_lex->leaf_tables,
                    FALSE) ||
       setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
@@ -359,6 +360,7 @@
     lex->query_tables also point on local list of DELETE SELECT_LEX
   */
   if (setup_tables(thd, &thd->lex->select_lex.context,
+                   &thd->lex->select_lex.top_join_list,
                    lex->query_tables, &lex->select_lex.where,
                    &lex->select_lex.leaf_tables, FALSE))
     DBUG_RETURN(TRUE);

--- 1.192/sql/sql_lex.h	2005-08-12 13:54:36 +03:00
+++ 1.193/sql/sql_lex.h	2005-08-15 18:31:01 +03:00
@@ -472,14 +472,16 @@
 {
 public:
   Name_resolution_context context;
-  char *db, *db1, *table1, *db2, *table2;      	/* For outer join using .. */
+  char *db;
   Item *where, *having;                         /* WHERE & HAVING clauses */
   Item *prep_where; /* saved WHERE clause for prepared statement processing */
   /* point on lex in which it was created, used in view subquery detection */
   st_lex *parent_lex;
   enum olap_type olap;
-  SQL_LIST	      table_list, group_list;   /* FROM & GROUP BY clauses */
-  List<Item>          item_list; /* list of fields & expressions */
+  /* FROM clause - points to the beginning of the TABLE_LIST::next_local list. */
+  SQL_LIST	      table_list;
+  SQL_LIST	      group_list; /* GROUP BY clause. */
+  List<Item>          item_list;  /* list of fields & expressions */
   List<String>        interval_list, use_index, *use_index_ptr,
 		      ignore_index, *ignore_index_ptr;
   /* 
@@ -492,7 +494,12 @@
   List<TABLE_LIST> top_join_list; /* join list of the top level          */
   List<TABLE_LIST> *join_list;    /* list for the currently parsed join  */
   TABLE_LIST *embedding;          /* table embedding to the above list   */
-  TABLE_LIST *leaf_tables;        /* list of leaves in join table tree   */
+  /*
+    Beginning of the list of leaves in a FROM clause, where the leaves
+    inlcude all base tables including view tables. The tables are connected
+    by TABLE_LIST::next_leaf, so leaf_tables points to the left-most leaf.
+  */
+  TABLE_LIST *leaf_tables;
   const char *type;               /* type of select for EXPLAIN          */
 
   SQL_LIST order_list;                /* ORDER clause */
@@ -594,7 +601,6 @@
   bool init_nested_join(THD *thd);
   TABLE_LIST *end_nested_join(THD *thd);
   TABLE_LIST *nest_last_join(THD *thd);
-  void save_names_for_using_list(TABLE_LIST *tab1, TABLE_LIST *tab2);
   void add_joined_table(TABLE_LIST *table);
   TABLE_LIST *convert_right_join();
   List<Item>* get_item_list();
@@ -736,6 +742,21 @@
   List<set_var_base>  var_list;
   List<Item_param>    param_list;
   List<LEX_STRING>    view_list; // view list (list of field names in view)
+  /*
+    A stack of name resolution contexts for the query. This stack is used
+    at parse time to set local name resolution contexts for various parts
+    of a query. For example, in a JOIN ... ON (some_condition) clause the
+    Items in 'some_condition' must be resolved only against the operands
+    of the the join, and not against the whole clause. Similarly, Items in
+    subqueries should be resolved against the subqueries (and outer queries).
+    The stack is used in the following way: when the parser detects that
+    all Items in some clause need a local context, it creates a new context
+    and pushes it on the stack. All newly created Items always store the
+    top-most context in the stack. Once the parser leaves the clause that
+    required a local context, the parser pops the top-most context.
+  */
+  List<Name_resolution_context> context_stack;
+
   SQL_LIST	      proc_list, auxilliary_table_list, save_list;
   create_field	      *last_field;
   udf_func udf;
@@ -927,6 +948,21 @@
     return ( query_tables_own_last ? *query_tables_own_last : 0);
   }
   void cleanup_after_one_table_open();
+
+  void push_context(Name_resolution_context *context)
+  {
+    context_stack.push_front(context);
+  }
+
+  void pop_context()
+  {
+    context_stack.pop();
+  }
+
+  Name_resolution_context *current_context()
+  {
+    return context_stack.head();
+  }
 } LEX;
 
 struct st_lex_local: public st_lex

--- 1.468/sql/sql_parse.cc	2005-08-15 18:15:04 +03:00
+++ 1.469/sql/sql_parse.cc	2005-08-15 18:31:01 +03:00
@@ -2019,7 +2019,7 @@
   */
   bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
   if (!thd->active_transaction())
-    thd->transaction.xid.null();
+    thd->transaction.xid_state.xid.null();
 
   /* report error issued during command execution */
   if (thd->killed_errno() && !thd->net.report_error)
@@ -2146,6 +2146,8 @@
     {
       TABLE_LIST **query_tables_last= lex->query_tables_last;
       sel= new SELECT_LEX();
+      /* 'parent_lex' is used in init_query() so it must be before it. */
+      sel->parent_lex= lex;
       sel->init_query();
       if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, 
                                  (List<String> *) 0, (List<String> *) 0))
@@ -2326,11 +2328,16 @@
     /*
       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.
-      Exception is DROP TEMPORARY TABLE IF EXISTS: we always execute it
-      (otherwise we have stale files on slave caused by exclusion of one tmp
-      table).
+
+      Exceptions are:
+
+      - SET: we always execute it (e.g., SET ONE_SHOT TIME_ZONE = 'XYZ')
+
+      - DROP TEMPORARY TABLE IF EXISTS: we always execute it (otherwise we
+        have stale files on slave caused by exclusion of one tmp table).
     */
-    if (!(lex->sql_command == SQLCOM_DROP_TABLE &&
+    if (lex->sql_command != SQLCOM_SET_OPTION &&
+        !(lex->sql_command == SQLCOM_DROP_TABLE &&
           lex->drop_temporary && lex->drop_if_exists) &&
         all_tables_not_ok(thd, all_tables))
     {
@@ -3249,19 +3256,26 @@
     if (!(res= open_and_lock_tables(thd, all_tables)))
     {
       /* Skip first table, which is the table we are inserting in */
-      select_lex->table_list.first= (byte*)first_table->next_local;
-
+      TABLE_LIST *second_table= first_table->next_local;
+      select_lex->table_list.first= (byte*) second_table;
+      select_lex->context.table_list= second_table;
+      select_lex->context.first_name_resolution_table= second_table;
       res= mysql_insert_select_prepare(thd);
-      lex->select_lex.context.table_list= first_table->next_local;
       if (!res && (result= new select_insert(first_table, first_table->table,
                                              &lex->field_list,
                                              &lex->update_list,
                                              &lex->value_list,
                                              lex->duplicates, lex->ignore)))
       {
-        /* Skip first table, which is the table we are inserting in */
+        /*
+          Skip first table, which is the table we are inserting in.
+          Below we set context.table_list again because the call above to
+          mysql_insert_select_prepare() calls resolve_in_table_list_only(),
+          which in turn resets context.table_list and
+          context.first_name_resolution_table.
+        */
         select_lex->context.table_list= first_table->next_local;
-
+        select_lex->context.first_name_resolution_table= first_table->next_local;
 	res= handle_select(thd, lex, result, OPTION_SETUP_TABLES_DONE);
         delete result;
       }
@@ -4507,14 +4521,15 @@
     break;
   }
   case SQLCOM_XA_START:
-    if (thd->transaction.xa_state == XA_IDLE && thd->lex->xa_opt == XA_RESUME)
+    if (thd->transaction.xid_state.xa_state == XA_IDLE &&
+        thd->lex->xa_opt == XA_RESUME)
     {
-      if (! thd->transaction.xid.eq(thd->lex->xid))
+      if (! thd->transaction.xid_state.xid.eq(thd->lex->xid))
       {
         my_error(ER_XAER_NOTA, MYF(0));
         break;
       }
-      thd->transaction.xa_state=XA_ACTIVE;
+      thd->transaction.xid_state.xa_state=XA_ACTIVE;
       send_ok(thd);
       break;
     }
@@ -4523,10 +4538,10 @@
       my_error(ER_XAER_INVAL, MYF(0));
       break;
     }
-    if (thd->transaction.xa_state != XA_NOTR)
+    if (thd->transaction.xid_state.xa_state != XA_NOTR)
     {
       my_error(ER_XAER_RMFAIL, MYF(0),
-               xa_state_names[thd->transaction.xa_state]);
+               xa_state_names[thd->transaction.xid_state.xa_state]);
       break;
     }
     if (thd->active_transaction() || thd->locked_tables)
@@ -4534,9 +4549,15 @@
       my_error(ER_XAER_OUTSIDE, MYF(0));
       break;
     }
-    DBUG_ASSERT(thd->transaction.xid.is_null());
-    thd->transaction.xa_state=XA_ACTIVE;
-    thd->transaction.xid.set(thd->lex->xid);
+    if (xid_cache_search(thd->lex->xid))
+    {
+      my_error(ER_XAER_DUPID, MYF(0));
+      break;
+    }
+    DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
+    thd->transaction.xid_state.xa_state=XA_ACTIVE;
+    thd->transaction.xid_state.xid.set(thd->lex->xid);
+    xid_cache_insert(&thd->transaction.xid_state);
     thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
                    OPTION_BEGIN);
     thd->server_status|= SERVER_STATUS_IN_TRANS;
@@ -4549,28 +4570,28 @@
       my_error(ER_XAER_INVAL, MYF(0));
       break;
     }
-    if (thd->transaction.xa_state != XA_ACTIVE)
+    if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
     {
       my_error(ER_XAER_RMFAIL, MYF(0),
-               xa_state_names[thd->transaction.xa_state]);
+               xa_state_names[thd->transaction.xid_state.xa_state]);
       break;
     }
-    if (!thd->transaction.xid.eq(thd->lex->xid))
+    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
     {
       my_error(ER_XAER_NOTA, MYF(0));
       break;
     }
-    thd->transaction.xa_state=XA_IDLE;
+    thd->transaction.xid_state.xa_state=XA_IDLE;
     send_ok(thd);
     break;
   case SQLCOM_XA_PREPARE:
-    if (thd->transaction.xa_state != XA_IDLE)
+    if (thd->transaction.xid_state.xa_state != XA_IDLE)
     {
       my_error(ER_XAER_RMFAIL, MYF(0),
-               xa_state_names[thd->transaction.xa_state]);
+               xa_state_names[thd->transaction.xid_state.xa_state]);
       break;
     }
-    if (!thd->transaction.xid.eq(thd->lex->xid))
+    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
     {
       my_error(ER_XAER_NOTA, MYF(0));
       break;
@@ -4578,22 +4599,28 @@
     if (ha_prepare(thd))
     {
       my_error(ER_XA_RBROLLBACK, MYF(0));
-      thd->transaction.xa_state=XA_NOTR;
+      xid_cache_delete(&thd->transaction.xid_state);
+      thd->transaction.xid_state.xa_state=XA_NOTR;
       break;
     }
-    thd->transaction.xa_state=XA_PREPARED;
+    thd->transaction.xid_state.xa_state=XA_PREPARED;
     send_ok(thd);
     break;
   case SQLCOM_XA_COMMIT:
-    if (!thd->transaction.xid.eq(thd->lex->xid))
+    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
     {
-      if (!(res= !ha_commit_or_rollback_by_xid(thd->lex->xid, 1)))
+      XID_STATE *xs=xid_cache_search(thd->lex->xid);
+      if (!xs || xs->in_thd)
         my_error(ER_XAER_NOTA, MYF(0));
       else
+      {
+        ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
+        xid_cache_delete(xs);
         send_ok(thd);
+      }
       break;
     }
-    if (thd->transaction.xa_state == XA_IDLE &&
+    if (thd->transaction.xid_state.xa_state == XA_IDLE &&
         thd->lex->xa_opt == XA_ONE_PHASE)
     {
       int r;
@@ -4602,7 +4629,7 @@
       else
         send_ok(thd);
     }
-    else if (thd->transaction.xa_state == XA_PREPARED &&
+    else if (thd->transaction.xid_state.xa_state == XA_PREPARED &&
              thd->lex->xa_opt == XA_NONE)
     {
       if (wait_if_global_read_lock(thd, 0, 0))
@@ -4622,27 +4649,33 @@
     else
     {
       my_error(ER_XAER_RMFAIL, MYF(0),
-               xa_state_names[thd->transaction.xa_state]);
+               xa_state_names[thd->transaction.xid_state.xa_state]);
       break;
     }
     thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
     thd->server_status&= ~SERVER_STATUS_IN_TRANS;
-    thd->transaction.xa_state=XA_NOTR;
+    xid_cache_delete(&thd->transaction.xid_state);
+    thd->transaction.xid_state.xa_state=XA_NOTR;
     break;
   case SQLCOM_XA_ROLLBACK:
-    if (!thd->transaction.xid.eq(thd->lex->xid))
+    if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
     {
-      if (!(res= !ha_commit_or_rollback_by_xid(thd->lex->xid, 0)))
+      XID_STATE *xs=xid_cache_search(thd->lex->xid);
+      if (!xs || xs->in_thd)
         my_error(ER_XAER_NOTA, MYF(0));
       else
+      {
+        ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
+        xid_cache_delete(xs);
         send_ok(thd);
+      }
       break;
     }
-    if (thd->transaction.xa_state != XA_IDLE &&
-        thd->transaction.xa_state != XA_PREPARED)
+    if (thd->transaction.xid_state.xa_state != XA_IDLE &&
+        thd->transaction.xid_state.xa_state != XA_PREPARED)
     {
       my_error(ER_XAER_RMFAIL, MYF(0),
-               xa_state_names[thd->transaction.xa_state]);
+               xa_state_names[thd->transaction.xid_state.xa_state]);
       break;
     }
     if (ha_rollback(thd))
@@ -4651,7 +4684,8 @@
       send_ok(thd);
     thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
     thd->server_status&= ~SERVER_STATUS_IN_TRANS;
-    thd->transaction.xa_state=XA_NOTR;
+    xid_cache_delete(&thd->transaction.xid_state);
+    thd->transaction.xid_state.xa_state=XA_NOTR;
     break;
   case SQLCOM_XA_RECOVER:
     res= mysql_xa_recover(thd);
@@ -5211,9 +5245,9 @@
   if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
     DBUG_RETURN(1);
   select_lex->select_number= ++thd->select_number;
+  select_lex->parent_lex= lex; /* Used in init_query. */
   select_lex->init_query();
   select_lex->init_select();
-  select_lex->parent_lex= lex;
   /*
     Don't evaluate this subquery during statement prepare even if
     it's a constant one. The flag is switched off in the end of
@@ -5271,6 +5305,7 @@
       fake->include_standalone(unit,
 			       (SELECT_LEX_NODE**)&unit->fake_select_lex);
       fake->select_number= INT_MAX;
+      fake->parent_lex= lex; /* Used in init_query. */
       fake->make_empty_select();
       fake->linkage= GLOBAL_OPTIONS_TYPE;
       fake->select_limit= 0;
@@ -5279,6 +5314,11 @@
       /* allow item list resolving in fake select for ORDER BY */
       fake->context.resolve_in_select_list= TRUE;
       fake->context.select_lex= fake;
+      /*
+        Remove the name resolution context of the fake select from the
+        context stack.
+       */
+      lex->pop_context();
     }
     select_lex->context.outer_context= outer_context;
   }
@@ -5972,6 +6012,7 @@
                                              LEX_STRING *option)
 {
   register TABLE_LIST *ptr;
+  TABLE_LIST *previous_table_ref; /* The table preceding the current one. */
   char *alias_str;
   LEX *lex= thd->lex;
   DBUG_ENTER("add_table_to_list");
@@ -6067,8 +6108,29 @@
       }
     }
   }
-  /* Link table in local list (list for current select) */
+  /* Store the table reference preceding the current one. */
+  if (table_list.elements > 0)
+  {
+    previous_table_ref= (TABLE_LIST*) table_list.next;
+    DBUG_ASSERT(previous_table_ref);
+  }
+  /*
+    Link the current table reference in a local list (list for current select).
+    Notice that as a side effect here we set the next_local field of the
+    previous table reference to 'ptr'. Here we also add one element to the
+    list 'table_list'.
+  */
   table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local);
+  /*
+    Set next_name_resolution_table of the previous table reference to point to
+    the current table reference. In effect the list
+    TABLE_LIST::next_name_resolution_table coincides with
+    TABLE_LIST::next_local. Later this may be changed in
+    store_top_level_join_columns() for NATURAL/USING joins.
+   */
+  if (table_list.elements > 1)
+    previous_table_ref->next_name_resolution_table= ptr;
+  ptr->next_name_resolution_table= NULL;
   /* Link table in global list (all used tables) */
   lex->add_to_query_tables(ptr);
   DBUG_RETURN(ptr);
@@ -6197,6 +6259,19 @@
     table->join_list= embedded_list;
     table->embedding= ptr;
     embedded_list->push_back(table);
+    if (table->natural_join)
+    {
+      ptr->is_natural_join= TRUE;
+      /*
+        If this is a JOIN ... USING, move the list of joined fields to the
+        table reference that describes the join.
+      */
+      if (table->join_using_fields)
+      {
+        ptr->join_using_fields= table->join_using_fields;
+        table->join_using_fields= NULL;
+      }
+    }
   }
   join_list->push_front(ptr);
   nested_join->used_tables= nested_join->not_null_tables= (table_map) 0;
@@ -6205,44 +6280,6 @@
 
 
 /*
-  Save names for a join with using clause
-
-  SYNOPSIS
-    save_names_for_using_list
-    tab1      left table in join
-    tab2      right table in join
-
-  DESCRIPTION
-    The function saves the full names of the tables in st_select_lex
-    to be able to build later an on expression to replace the using clause.
-
-  RETURN VALUE
-    None
-*/
-
-void st_select_lex::save_names_for_using_list(TABLE_LIST *tab1,
-                                              TABLE_LIST *tab2)
-{
-  while (tab1->nested_join)
-  {
-    tab1= tab1->nested_join->join_list.head();
-  }
-  db1= tab1->db;
-  table1= tab1->alias;
-  while (tab2->nested_join)
-  {
-    TABLE_LIST *next;
-    List_iterator_fast<TABLE_LIST> it(tab2->nested_join->join_list);
-    tab2= it++;
-    while ((next= it++))
-      tab2= next;
-  }
-  db2= tab2->db;
-  table2= tab2->alias;
-}
-
-
-/*
   Add a table to the current join list
 
   SYNOPSIS
@@ -6345,16 +6382,71 @@
 }
 
 
-void add_join_on(TABLE_LIST *b,Item *expr)
+/*
+  Create a new name resolution context for a JOIN ... ON clause.
+
+  SYNOPSIS
+    make_join_on_context()
+    thd       pointer to current thread
+    left_op   lefto operand of the JOIN
+    right_op  rigth operand of the JOIN
+
+  DESCRIPTION
+    Create a new name resolution context for a JOIN ... ON clause,
+    and set the first and last leaves of the list of table references
+    to be used for name resolution.
+
+  RETURN
+    A new context if all is OK
+    NULL - if a memory allocation error occured
+*/
+
+Name_resolution_context *
+make_join_on_context(THD *thd, TABLE_LIST *left_op, TABLE_LIST *right_op)
+{
+  Name_resolution_context *on_context;
+  if (!(on_context= (Name_resolution_context*)
+        thd->calloc(sizeof(Name_resolution_context))))
+    return NULL;
+  on_context->init();
+  on_context->first_name_resolution_table=
+    left_op->first_leaf_for_name_resolution();
+  on_context->last_name_resolution_table=
+    right_op->last_leaf_for_name_resolution();
+  return on_context;
+}
+
+
+/*
+  Add an ON condition to the second operand of a JOIN ... ON.
+
+  SYNOPSIS
+    add_join_on
+    b     the second operand of a JOIN ... ON
+    expr  the condition to be added to the ON clause
+
+  DESCRIPTION
+    Add an ON condition to the right operand of a JOIN ... ON clause.
+
+  RETURN
+    FALSE  if there was some error
+    TRUE   if all is OK
+*/
+
+void add_join_on(TABLE_LIST *b, Item *expr)
 {
   if (expr)
   {
     if (!b->on_expr)
-      b->on_expr=expr;
+      b->on_expr= expr;
     else
     {
-      /* This only happens if you have both a right and left join */
-      b->on_expr=new Item_cond_and(b->on_expr,expr);
+      /*
+        If called from the parser, this happens if you have both a
+        right and left join. If called later, it happens if we add more
+        than one condition to the ON clause.
+      */
+      b->on_expr= new Item_cond_and(b->on_expr,expr);
     }
     b->on_expr->top_level_item();
   }
@@ -6362,27 +6454,48 @@
 
 
 /*
-  Mark that we have a NATURAL JOIN between two tables
+  Mark that there is a NATURAL JOIN or JOIN ... USING between two
+  tables.
 
   SYNOPSIS
     add_join_natural()
-    a			Table to do normal join with
-    b			Do normal join with this table
-
+    a			Left join argument
+    b			Right join argument
+    using_fields        Field names from USING clause
+  
   IMPLEMENTATION
-    This function just marks that table b should be joined with a.
-    The function setup_cond() will create in b->on_expr a list
-    of equal condition between all fields of the same name.
+    This function marks that table b should be joined with a either via
+    a NATURAL JOIN or via JOIN ... USING. Both join types are special
+    cases of each other, so we treat them together. The function
+    setup_conds() creates a list of equal condition between all fields
+    of the same name for NATURAL JOIN or the fields in 'using_fields'
+    for JOIN ... USING. The list of equality conditions is stored
+    either in b->on_expr, or in JOIN::conds, depending on whether there
+    was an outer join.
 
+  EXAMPLE
     SELECT * FROM t1 NATURAL LEFT JOIN t2
      <=>
     SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... )
+
+    SELECT * FROM t1 NATURAL JOIN t2 WHERE <some_cond>
+     <=>
+    SELECT * FROM t1, t2 WHERE (t1.i=t2.i and t1.j=t2.j and <some_cond>)
+
+    SELECT * FROM t1 JOIN t2 USING(j) WHERE <some_cond>
+     <=>
+    SELECT * FROM t1, t2 WHERE (t1.j=t2.j and <some_cond>)
+
+  RETURN
+    None
 */
 
-void add_join_natural(TABLE_LIST *a,TABLE_LIST *b)
+void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields)
 {
-  b->natural_join=a;
+  b->natural_join= a;
+  b->join_using_fields= using_fields;
 }
+
 
 /*
   Reload/resets privileges and the different caches.

--- 1.356/sql/sql_select.cc	2005-08-12 13:55:45 +03:00
+++ 1.357/sql/sql_select.cc	2005-08-15 18:31:01 +03:00
@@ -338,7 +338,7 @@
   /* Check that all tables, fields, conds and order are ok */
 
   if ((!(select_options & OPTION_SETUP_TABLES_DONE) &&
-       setup_tables(thd, &select_lex->context,
+       setup_tables(thd, &select_lex->context, join_list,
                     tables_list, &conds, &select_lex->leaf_tables,
                     FALSE)) ||
       setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) ||
@@ -1583,7 +1583,8 @@
 	curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having,
 						   ~ (table_map) 0,
 						   ~used_tables);
-	DBUG_EXECUTE("where",print_where(conds,"having after sort"););
+	DBUG_EXECUTE("where",print_where(curr_join->tmp_having,
+                                         "having after sort"););
       }
     }
     {
@@ -11911,13 +11912,14 @@
 
   SYNOPSIS
     find_order_in_list()
-    thd		      	Pointer to current thread structure
-    ref_pointer_array   All select, group and order by fields
-    tables              List of tables to search in (usually FROM clause)
-    order               Column reference to be resolved
-    fields              List of fields to search in (usually SELECT list)
-    all_fields          All select, group and order by fields
-    is_group_field      True if order is a GROUP field, false if ORDER by field
+    thd		      [in]     Pointer to current thread structure
+    ref_pointer_array [in/out] All select, group and order by fields
+    tables            [in]     List of tables to search in (usually FROM clause)
+    order             [in]     Column reference to be resolved
+    fields            [in]     List of fields to search in (usually SELECT list)
+    all_fields        [in/out] All select, group and order by fields
+    is_group_field    [in]     True if order is a GROUP field, false if
+                               ORDER by field
 
   DESCRIPTION
     Given a column reference (represented by 'order') from a GROUP BY or ORDER
@@ -11993,7 +11995,7 @@
         order_item_type == Item::REF_ITEM)
     {
       from_field= find_field_in_tables(thd, (Item_ident*) order_item, tables,
-                                       &view_ref, IGNORE_ERRORS, TRUE,
+                                       NULL, &view_ref, IGNORE_ERRORS, TRUE,
                                        FALSE);
       if (!from_field)
         from_field= (Field*) not_found_field;

--- 1.264/sql/sql_show.cc	2005-08-12 13:54:36 +03:00
+++ 1.265/sql/sql_show.cc	2005-08-15 18:31:01 +03:00
@@ -2072,6 +2072,11 @@
           else
           {
             int res;
+            /*
+              Set the parent lex of 'sel' because it is needed by sel.init_query()
+              which is called inside make_table_list.
+            */
+            sel.parent_lex= lex;
             if (make_table_list(thd, &sel, base_name, file_name))
               goto err;
             TABLE_LIST *show_table_list= (TABLE_LIST*) sel.table_list.first;

--- 1.165/sql/sql_update.cc	2005-08-12 13:54:36 +03:00
+++ 1.166/sql/sql_update.cc	2005-08-15 18:31:01 +03:00
@@ -555,7 +555,7 @@
   tables.table= table;
   tables.alias= table_list->alias;
 
-  if (setup_tables(thd, &select_lex->context,
+  if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list,
                    table_list, conds, &select_lex->leaf_tables,
                    FALSE) ||
       setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
@@ -643,6 +643,7 @@
   */
 
   if (setup_tables(thd, &lex->select_lex.context,
+                   &lex->select_lex.top_join_list,
                    table_list, &lex->select_lex.where,
                    &lex->select_lex.leaf_tables, FALSE))
     DBUG_RETURN(TRUE);
@@ -761,6 +762,7 @@
       tbl->cleanup_items();
 
     if (setup_tables(thd, &lex->select_lex.context,
+                     &lex->select_lex.top_join_list,
                      table_list, &lex->select_lex.where,
                      &lex->select_lex.leaf_tables, FALSE) ||
         setup_fields_with_no_wrap(thd, 0, *fields, 1, 0, 0))

--- 1.413/sql/sql_yacc.yy	2005-08-15 18:15:05 +03:00
+++ 1.414/sql/sql_yacc.yy	2005-08-15 18:31:02 +03:00
@@ -717,7 +717,7 @@
 	bool_term bool_factor bool_test bool_pri 
 	predicate bit_expr bit_term bit_factor value_expr term factor
 	table_wild simple_expr udf_expr
-	using_list expr_or_default set_expr_or_default interval_expr
+	expr_or_default set_expr_or_default interval_expr
 	param_marker singlerow_subselect singlerow_subselect_init
 	exists_subselect exists_subselect_init geometry_function
 	signed_literal now_or_signed_literal opt_escape
@@ -739,7 +739,7 @@
 	key_alg opt_btree_or_rtree
 
 %type <string_list>
-	key_usage_list
+	key_usage_list using_list
 
 %type <key_part>
 	key_part
@@ -4425,10 +4425,10 @@
 	      my_error(ER_WRONG_COLUMN_NAME, MYF(0), name->str);
 	      YYABORT;
 	    }
-	    $$= new Item_default_value(&Select->context, $3);
+	    $$= new Item_default_value(Lex->current_context(), $3);
 	  }
 	| VALUES '(' simple_ident ')'
-	  { $$= new Item_insert_value(&Select->context, $3); }
+	  { $$= new Item_insert_value(Lex->current_context(), $3); }
 	| FUNC_ARG0 '(' ')'
 	  {
 	    if (!$1.symbol->create_func)
@@ -4719,9 +4719,9 @@
 	    name->init_qname(YYTHD);
 	    sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION);
 	    if ($5)
-	      $$= new Item_func_sp(&lex->current_select->context, name, *$5);
+	      $$= new Item_func_sp(Lex->current_context(), name, *$5);
 	    else
-	      $$= new Item_func_sp(&lex->current_select->context, name);
+	      $$= new Item_func_sp(Lex->current_context(), name);
 	    lex->safe_to_cache_query=0;
 	  }
 	| IDENT_sys '(' udf_expr_list ')'
@@ -4809,9 +4809,9 @@
 
               sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION);
               if ($3)
-                $$= new Item_func_sp(&lex->current_select->context, name, *$3);
+                $$= new Item_func_sp(Lex->current_context(), name, *$3);
               else
-                $$= new Item_func_sp(&lex->current_select->context, name);
+                $$= new Item_func_sp(Lex->current_context(), name);
 	      lex->safe_to_cache_query=0;
 	    }
           }
@@ -5015,7 +5015,7 @@
 	  {
             SELECT_LEX *sel= Select;
 	    sel->in_sum_expr--;
-	    $$=new Item_func_group_concat(&sel->context, $3, $5,
+	    $$=new Item_func_group_concat(Lex->current_context(), $3, $5,
                                           sel->gorder_list, $7);
 	    $5->empty();
 	  };
@@ -5151,68 +5151,116 @@
         table_ref normal_join table_ref { YYERROR_UNLESS($1 && ($$=$3)); }
 	| table_ref STRAIGHT_JOIN table_factor
 	  { YYERROR_UNLESS($1 && ($$=$3)); $3->straight=1; }
-	| table_ref normal_join table_ref ON expr
-	  { YYERROR_UNLESS($1 && ($$=$3)); add_join_on($3,$5); }
-        | table_ref STRAIGHT_JOIN table_factor ON expr
-          { YYERROR_UNLESS($1 && ($$=$3)); $3->straight=1; add_join_on($3,$5); }
+	| table_ref normal_join table_ref
+          ON
+          {
+            YYERROR_UNLESS($1 && ($$=$3));
+            /* Change the current name resolution context to a local context. */
+            Name_resolution_context *on_context;
+            if (!(on_context= make_join_on_context(YYTHD,$1,$3)))
+              YYABORT;
+            Lex->push_context(on_context);
+          }
+          expr
+	  {
+            add_join_on($3,$6);
+            Lex->pop_context();
+          }
+        | table_ref STRAIGHT_JOIN table_factor
+          ON
+          {
+            YYERROR_UNLESS($1 && ($$=$3));
+            /* Change the current name resolution context to a local context. */
+            Name_resolution_context *on_context;
+            if (!(on_context= make_join_on_context(YYTHD,$1,$3)))
+              YYABORT;
+            Lex->push_context(on_context);
+          }
+          expr
+          {
+            $3->straight=1;
+            add_join_on($3,$6);
+            Lex->pop_context();
+          }
 	| table_ref normal_join table_ref
 	  USING
 	  {
 	    SELECT_LEX *sel= Select;
             YYERROR_UNLESS($1 && $3);
-            sel->save_names_for_using_list($1, $3);
 	  }
 	  '(' using_list ')'
-	  { add_join_on($3,$7); $$=$3; }
-
-	| table_ref LEFT opt_outer JOIN_SYM table_ref ON expr
-	  { YYERROR_UNLESS($1 && $5); add_join_on($5,$7); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
+          { add_join_natural($1,$3,$7); $$=$3; }
+	| table_ref LEFT opt_outer JOIN_SYM table_ref
+          ON
+          {
+            /* Change the current name resolution context to a local context. */
+            Name_resolution_context *on_context;
+            if (!(on_context= make_join_on_context(YYTHD,$1,$5)))
+              YYABORT;
+            Lex->push_context(on_context);
+          }
+          expr
+	  {
+            YYERROR_UNLESS($1 && $5);
+            add_join_on($5,$8);
+            Lex->pop_context();
+            $5->outer_join|=JOIN_TYPE_LEFT;
+            $$=$5;
+          }
 	| table_ref LEFT opt_outer JOIN_SYM table_factor
 	  {
 	    SELECT_LEX *sel= Select;
             YYERROR_UNLESS($1 && $5);
-            sel->save_names_for_using_list($1, $5);
 	  }
 	  USING '(' using_list ')'
-	  { add_join_on($5,$9); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
+          { add_join_natural($1,$5,$9); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
 	| table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor
 	  {
             YYERROR_UNLESS($1 && $6);
-	    add_join_natural($1,$6);
+ 	    add_join_natural($1,$6,NULL);
 	    $6->outer_join|=JOIN_TYPE_LEFT;
 	    $$=$6;
 	  }
-	| table_ref RIGHT opt_outer JOIN_SYM table_ref ON expr
+	| table_ref RIGHT opt_outer JOIN_SYM table_ref
+          ON
+          {
+            /* Change the current name resolution context to a local context. */
+            Name_resolution_context *on_context;
+            if (!(on_context= make_join_on_context(YYTHD,$1,$5)))
+              YYABORT;
+            Lex->push_context(on_context);
+          }
+          expr
           {
 	    LEX *lex= Lex;
             YYERROR_UNLESS($1 && $5);
             if (!($$= lex->current_select->convert_right_join()))
               YYABORT;
-            add_join_on($$, $7);
+            add_join_on($$, $8);
+            Lex->pop_context();
           }
 	| table_ref RIGHT opt_outer JOIN_SYM table_factor
 	  {
 	    SELECT_LEX *sel= Select;
             YYERROR_UNLESS($1 && $5);
-            sel->save_names_for_using_list($1, $5);
 	  }
 	  USING '(' using_list ')'
           {
 	    LEX *lex= Lex;
             if (!($$= lex->current_select->convert_right_join()))
               YYABORT;
-            add_join_on($$, $9);
+            add_join_natural($$,$5,$9);
           }
 	| table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor
 	  {
             YYERROR_UNLESS($1 && $6);
-	    add_join_natural($6,$1);
+	    add_join_natural($6,$1,NULL);
 	    LEX *lex= Lex;
             if (!($$= lex->current_select->convert_right_join()))
               YYABORT;
 	  }
 	| table_ref NATURAL JOIN_SYM table_factor
-	  { YYERROR_UNLESS($1 && ($$=$4)); add_join_natural($1,$4); };
+	  { YYERROR_UNLESS($1 && ($$=$4)); add_join_natural($1,$4,NULL); };
 
 
 normal_join:
@@ -5240,8 +5288,23 @@
 	    YYABORT;
           sel->add_joined_table($$);
 	}
-	| '{' ident table_ref LEFT OUTER JOIN_SYM table_ref ON expr '}'
-	  { YYERROR_UNLESS($3 && $7); add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; }
+	| '{' ident table_ref LEFT OUTER JOIN_SYM table_ref
+          ON
+          {
+            /* Change the current name resolution context to a local context. */
+            Name_resolution_context *on_context;
+            if (!(on_context= make_join_on_context(YYTHD,$3,$7)))
+              YYABORT;
+            Lex->push_context(on_context);
+          }
+          expr '}'
+	  {
+            YYERROR_UNLESS($3 && $7);
+            add_join_on($7,$10);
+            Lex->pop_context();
+            $7->outer_join|=JOIN_TYPE_LEFT;
+            $$=$7;
+          }
 	| select_derived_init get_select_lex select_derived2
           {
             LEX *lex= Lex;
@@ -5295,6 +5358,7 @@
 
 	      YYABORT;
             sel->add_joined_table($$);
+            lex->pop_context();
           }
 	  else
           if ($4 || $6)
@@ -5434,32 +5498,18 @@
 using_list:
 	ident
 	  {
-	    SELECT_LEX *sel= Select;
-	    if (!($$= new Item_func_eq(new Item_field(&sel->context,
-                                                      sel->db1, sel->table1,
-						      $1.str),
-				       new Item_field(&sel->context,
-                                                      sel->db2, sel->table2,
-						      $1.str))))
+            if (!($$= new List<String>))
 	      YYABORT;
+            $$->push_back(new (YYTHD->mem_root)
+                              String((const char *) $1.str, $1.length,
+                                      system_charset_info));
 	  }
 	| using_list ',' ident
 	  {
-	    SELECT_LEX *sel= Select;
-	    if (!($$=
-                  new Item_cond_and(new
-                                    Item_func_eq(new
-                                                 Item_field(&sel->context,
-                                                            sel->db1,
-                                                            sel->table1,
-                                                            $3.str),
-                                                 new
-                                                 Item_field(&sel->context,
-                                                            sel->db2,
-                                                            sel->table2,
-                                                            $3.str)),
-                                    $1)))
-	      YYABORT;
+            $1->push_back(new (YYTHD->mem_root)
+                              String((const char *) $3.str, $3.length,
+                                      system_charset_info));
+            $$= $1;
 	  };
 
 interval:
@@ -6100,7 +6150,7 @@
 
 expr_or_default:
 	expr	  { $$= $1;}
-	| DEFAULT {$$= new Item_default_value(&Select->context); }
+	| DEFAULT {$$= new Item_default_value(Lex->current_context()); }
 	;
 
 opt_insert_update:
@@ -7061,13 +7111,13 @@
 	ident '.' '*'
 	{
           SELECT_LEX *sel= Select;
-	  $$ = new Item_field(&sel->context, NullS, $1.str, "*");
+	  $$ = new Item_field(Lex->current_context(), NullS, $1.str, "*");
 	  sel->with_wild++;
 	}
 	| ident '.' ident '.' '*'
 	{
           SELECT_LEX *sel= Select;
-	  $$ = new Item_field(&sel->context, (YYTHD->client_capabilities &
+	  $$ = new Item_field(Lex->current_context(), (YYTHD->client_capabilities &
                              CLIENT_NO_SCHEMA ? NullS : $1.str),
                              $3.str,"*");
 	  sel->with_wild++;
@@ -7095,8 +7145,8 @@
 	    SELECT_LEX *sel=Select;
 	    $$= (sel->parsing_place != IN_HAVING ||
 	         sel->get_in_sum_expr() > 0) ?
-                 (Item*) new Item_field(&sel->context, NullS, NullS, $1.str) :
-	         (Item*) new Item_ref(&sel->context, NullS, NullS, $1.str);
+                 (Item*) new Item_field(Lex->current_context(), NullS, NullS, $1.str) :
+	         (Item*) new Item_ref(Lex->current_context(), NullS, NullS, $1.str);
 	  }
         }
         | simple_ident_q { $$= $1; }
@@ -7108,8 +7158,8 @@
 	  SELECT_LEX *sel=Select;
 	  $$= (sel->parsing_place != IN_HAVING ||
 	       sel->get_in_sum_expr() > 0) ?
-              (Item*) new Item_field(&sel->context, NullS, NullS, $1.str) :
-	      (Item*) new Item_ref(&sel->context, NullS, NullS, $1.str);
+              (Item*) new Item_field(Lex->current_context(), NullS, NullS, $1.str) :
+	      (Item*) new Item_ref(Lex->current_context(), NullS, NullS, $1.str);
 	}
 	| simple_ident_q { $$= $1; }
 	;
@@ -7146,7 +7196,7 @@
               YYABORT;
             }
 
-            if (!(trg_fld= new Item_trigger_field(&lex->current_select->context,
+            if (!(trg_fld= new Item_trigger_field(Lex->current_context(),
                                                   new_row ?
                                                   Item_trigger_field::NEW_ROW:
                                                   Item_trigger_field::OLD_ROW,
@@ -7172,8 +7222,8 @@
 	    }
 	    $$= (sel->parsing_place != IN_HAVING ||
 	         sel->get_in_sum_expr() > 0) ?
-	        (Item*) new Item_field(&sel->context, NullS, $1.str, $3.str) :
-	        (Item*) new Item_ref(&sel->context, NullS, $1.str, $3.str);
+	        (Item*) new Item_field(Lex->current_context(), NullS, $1.str, $3.str) :
+	        (Item*) new Item_ref(Lex->current_context(), NullS, $1.str, $3.str);
           }
         }
 	| '.' ident '.' ident
@@ -7188,8 +7238,8 @@
 	  }
 	  $$= (sel->parsing_place != IN_HAVING ||
 	       sel->get_in_sum_expr() > 0) ?
-	      (Item*) new Item_field(&sel->context, NullS, $2.str, $4.str) :
-              (Item*) new Item_ref(&sel->context, NullS, $2.str, $4.str);
+	      (Item*) new Item_field(Lex->current_context(), NullS, $2.str, $4.str) :
+              (Item*) new Item_ref(Lex->current_context(), NullS, $2.str, $4.str);
 	}
 	| ident '.' ident '.' ident
 	{
@@ -7203,11 +7253,11 @@
 	  }
 	  $$= (sel->parsing_place != IN_HAVING ||
 	       sel->get_in_sum_expr() > 0) ?
-	      (Item*) new Item_field(&sel->context,
+	      (Item*) new Item_field(Lex->current_context(),
                                      (YYTHD->client_capabilities &
 				      CLIENT_NO_SCHEMA ? NullS : $1.str),
 				     $3.str, $5.str) :
-	      (Item*) new Item_ref(&sel->context,
+	      (Item*) new Item_ref(Lex->current_context(),
                                    (YYTHD->client_capabilities &
 				    CLIENT_NO_SCHEMA ? NullS : $1.str),
                                    $3.str, $5.str);
@@ -7772,8 +7822,7 @@
               it= new Item_null();
             }
 
-            if (!(trg_fld= new Item_trigger_field(&lex->current_select->
-                                                  context,
+            if (!(trg_fld= new Item_trigger_field(Lex->current_context(),
                                                   Item_trigger_field::NEW_ROW,
                                                   $2.base_name.str)) ||
                 !(sp_fld= new sp_instr_set_trigger_field(lex->sphead->
@@ -8630,7 +8679,12 @@
             lex->current_select->master_unit()->union_distinct=
                                                       lex->current_select;
 	}
-	select_init {}
+	select_init
+        {
+          /* Remove from the name resolution context stack the context of the
+             last select in the union. */
+          Lex->pop_context();
+        }
 	;
 
 union_opt:
@@ -8734,6 +8788,7 @@
 	')'
 	{
 	  LEX *lex=Lex;
+          lex->pop_context();
 	  lex->current_select = lex->current_select->return_after_parsing();
 	};
 

--- 1.75/sql/sql_derived.cc	2005-08-12 13:54:36 +03:00
+++ 1.76/sql/sql_derived.cc	2005-08-15 18:31:01 +03:00
@@ -125,6 +125,11 @@
     if ((res= unit->prepare(thd, derived_result, 0, orig_table_list->alias)))
       goto exit;
 
+    if (check_duplicate_names(unit->types, 0))
+    {
+      res= -1;
+      goto exit;
+    }
 
     derived_result->tmp_table_param.init();
     derived_result->tmp_table_param.field_count= unit->types.elements;

--- 1.41/sql/share/errmsg.txt	2005-08-12 23:58:05 +03:00
+++ 1.42/sql/share/errmsg.txt	2005-08-15 18:31:02 +03:00
@@ -5342,8 +5342,8 @@
 	eng "Duplicate handler declared in the same block"
 ER_SP_NOT_VAR_ARG 42000
 	eng "OUT or INOUT argument %d for routine %s is not a variable"
-ER_SP_NO_RETSET_IN_FUNC 0A000
-	eng "Not allowed to return a result set from a function"
+ER_SP_NO_RETSET 0A000
+	eng "Not allowed to return a result set from a %s"
 ER_CANT_CREATE_GEOMETRY_OBJECT 22003 
 	eng "Cannot get geometry object from data you send to the GEOMETRY field"
 ER_FAILED_ROUTINE_BREAK_BINLOG
Thread
bk commit into 5.0 tree (monty:1.1985)monty15 Aug