Commit 4fd41f7c authored by unknown's avatar unknown
Browse files

WL#2486 - Natural/using join according to SQL:2003.

Post-review fixes according to Monty's review.


sql/item.h:
  Unite all code that stores and restores the state of a name resolution context
  into a class to represent the state, and methods to save/restore that
  state.
sql/mysql_priv.h:
  Reorder parameters so that length is after the name of a field,
  and database is before table name.
sql/sql_acl.cc:
  Reorder parameters so that length is after the name of a field,
  and database is before table name.
sql/sql_base.cc:
  * Reorder parameters so that length is after the name of a field,
    and database is before table name.
  * Added new method - Field_iterator_table_ref::get_natural_column_ref
    to avoid unnecessary code when it is knwon that no new columns will
    be created when accessing natural join columns.
sql/sql_insert.cc:
  Unite all code that stores and restores the state of a name resolution context
  into a class to represent the state, and methods to save/restore that
  state.
sql/sql_lex.cc:
  Removed obsolete comment.
sql/sql_lex.h:
  Return error from push_contex() if there is no memory.
sql/sql_list.h:
  Extended base_list_iterator, List_iterator, and List_iterator_fast with an
  empty constructor, and init() methods, so that one doesn't have to construct
  a new iterator object every time one needs to iterate over a new list.
sql/sql_parse.cc:
  Moved common functionality from the parser into one function, and renamed
  the function to better reflect what it does.
sql/sql_yacc.yy:
  Moved common functionality from the parser into one function, and renamed
  the function to better reflect what it does.
sql/table.cc:
  * Extended base_list_iterator, List_iterator, and List_iterator_fast with an
    empty constructor, and init() methods, so that one doesn't have to construct
    a new iterator object every time one needs to iterate over a new list.
  * Added new method Field_iterator_table_ref::get_natural_column_ref to be
    used in cases when it is known for sure that no new columns should be
    created.
sql/table.h:
  - column_ref_it no longer allocated for each new list of columns
  - new method get_natural_join_column for faster/simpler access
    to natural join columns.
parent 97bfd41f
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
@@ -326,6 +326,48 @@ struct Name_resolution_context: Sql_alloc
};


/*
  Store and restore the current state of a name resolution context.
*/

class Name_resolution_context_state
{
private:
  TABLE_LIST *save_table_list;
  TABLE_LIST *save_first_name_resolution_table;
  TABLE_LIST *save_next_name_resolution_table;
  bool        save_resolve_in_select_list;

public:
  TABLE_LIST *save_next_local;

public:
  /* Save the state of a name resolution context. */
  void save_state(Name_resolution_context *context, TABLE_LIST *table_list)
  {
    save_table_list=                  context->table_list;
    save_first_name_resolution_table= context->first_name_resolution_table;
    save_next_name_resolution_table=  (context->first_name_resolution_table) ?
                                      context->first_name_resolution_table->
                                               next_name_resolution_table :
                                      NULL;
    save_resolve_in_select_list=      context->resolve_in_select_list;
    save_next_local=                  table_list->next_local;
  }

  /* Restore a name resolution context from saved state. */
  void restore_state(Name_resolution_context *context, TABLE_LIST *table_list)
  {
    table_list->next_local=                save_next_local;
    context->table_list=                   save_table_list;
    context->first_name_resolution_table=  save_first_name_resolution_table;
    if (context->first_name_resolution_table)
      context->first_name_resolution_table->
               next_name_resolution_table= save_next_name_resolution_table;
    context->resolve_in_select_list=       save_resolve_in_select_list;
  }
};

/*************************************************************************/

typedef bool (Item::*Item_processor)(byte *arg);
+7 −7
Original line number Diff line number Diff line
@@ -791,12 +791,11 @@ find_field_in_tables(THD *thd, Item_ident *item,
                     bool check_privileges, bool register_tree_change);
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,
                        const char *name, uint length,
                        const char *item_name, const char *db_name,
                        const char *table_name, Item **ref,
                        bool check_grants_table, bool check_grants_view,
                        bool allow_rowid,
                        uint *cached_field_index_ptr,
                        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,
@@ -918,7 +917,8 @@ create_field * new_create_field(THD *thd, char *field_name, enum_field_types typ
				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,
bool push_new_name_resolution_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,List<String> *using_fields);
+2 −1
Original line number Diff line number Diff line
@@ -2761,8 +2761,9 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
        uint unused_field_idx= NO_CACHED_FIELD_INDEX;
        TABLE_LIST *dummy;
        Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
                                         column->column.length(),
                                         column->column.ptr(), NULL, NULL,
                                         column->column.length(), 0, 1, 1, 0,
                                         0, 1, 1, 0,
                                         &unused_field_idx, FALSE, &dummy);
        if (f == (Field*)0)
        {
+29 −48
Original line number Diff line number Diff line
@@ -2732,8 +2732,8 @@ static bool check_grant_column_in_sctx(THD *thd, GRANT_INFO *grant,
    thd				thread handler
    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
    item_name                   name of item if it will be created (VIEW)
    ref				expression substituted in VIEW should be passed
                                using this reference (return view_ref_found)
    check_grants		do check columns grants for view?
@@ -2748,9 +2748,9 @@ static bool check_grant_column_in_sctx(THD *thd, GRANT_INFO *grant,

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)
                   const char *name, uint length,
                   const char *item_name, Item **ref,
                   bool check_grants, bool register_tree_change)
{
  DBUG_ENTER("find_field_in_view");
  DBUG_PRINT("enter",
@@ -2766,13 +2766,6 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list,
  {
    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);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
      if (check_grant_column_in_sctx(thd, &table_list->grant,
                                     table_list->view_db.str,
@@ -2784,6 +2777,10 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list,
      // in PS use own arena or data will be freed after prepare
      if (register_tree_change)
        arena= thd->activate_stmt_arena_if_needed(&backup);
      /*
        create_item() may, or may not create a new Item, depending on
        the column reference. See create_view_field() for details.
      */
      Item *item= field_it.create_item(thd);
      if (register_tree_change && arena)
        thd->restore_active_arena(arena, &backup);
@@ -2880,15 +2877,13 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
  if (nj_col->view_field)
  {
    Item *item;
    /*
      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.
    */
    if (register_tree_change)
      arena= thd->activate_stmt_arena_if_needed(&backup);

    /*
      create_item() may, or may not create a new Item, depending on the
      column reference. See create_view_field() for details.
    */
    item= nj_col->create_item(thd);

    if (register_tree_change && arena)
      thd->restore_active_arena(arena, &backup);

@@ -3006,10 +3001,10 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
    thd			   [in]  thread handler
    table_list		   [in]  table reference to search
    name		   [in]  name of field
    length		   [in]  field length of name
    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
    table_name             [in]  optional table name that qualifies the field
    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?
@@ -3043,9 +3038,9 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,

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,
                        const char *name, uint length,
                        const char *item_name, const char *db_name,
                        const char *table_name, 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)
@@ -3092,7 +3087,7 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
  if (table_list->field_translation)
  {
    /* 'table_list' is a view or an information schema table. */
    if ((fld= find_field_in_view(thd, table_list, name, item_name, length,
    if ((fld= find_field_in_view(thd, table_list, name, length, item_name,
                                 ref, check_grants_view,
                                 register_tree_change)))
      *actual_table= table_list;
@@ -3132,8 +3127,8 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
      TABLE_LIST *table;
      while ((table= it++))
      {
        if ((fld= find_field_in_table_ref(thd, table, name, item_name,
                                          table_name, db_name, length, ref,
        if ((fld= find_field_in_table_ref(thd, table, name, length, item_name,
                                          db_name, table_name, ref,
                                          check_grants_table,
                                          check_grants_view,
                                          allow_rowid, cached_field_index_ptr,
@@ -3241,8 +3236,8 @@ find_field_in_tables(THD *thd, Item_ident *item,
                                 1, &(item->cached_field_index),
                                 table_ref->security_ctx);
    else
      found= find_field_in_table_ref(thd, table_ref, name, item->name,
                                     NULL, NULL, length, ref,
      found= find_field_in_table_ref(thd, table_ref, name, length, item->name,
                                     NULL, NULL, ref,
                                     (table_ref->table &&
                                      test(table_ref->table->grant.
                                           want_privilege) &&
@@ -3289,9 +3284,8 @@ find_field_in_tables(THD *thd, Item_ident *item,
  for (; cur_table != last_table ;
       cur_table= cur_table->next_name_resolution_table)
  {
    Field *cur_field= find_field_in_table_ref(thd, cur_table, name, item->name,
                                              table_name, db,
                                              length, ref,
    Field *cur_field= find_field_in_table_ref(thd, cur_table, name, length,
                                              item->name, db, table_name, ref,
                                              (cur_table->table &&
                                               test(cur_table->table->grant.
                                                    want_privilege) &&
@@ -3707,7 +3701,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
  {
    bool is_created_1;
    bool found= FALSE;
    if (!(nj_col_1= it_1.get_or_create_column_ref(thd, &is_created_1)))
    if (!(nj_col_1= it_1.get_or_create_column_ref(&is_created_1)))
      goto err;
    field_name_1= nj_col_1->name();

@@ -3728,7 +3722,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
      bool is_created_2;
      Natural_join_column *cur_nj_col_2;
      const char *cur_field_name_2;
      if (!(cur_nj_col_2= it_2.get_or_create_column_ref(thd, &is_created_2)))
      if (!(cur_nj_col_2= it_2.get_or_create_column_ref(&is_created_2)))
        goto err;
      cur_field_name_2= cur_nj_col_2->name();

@@ -3920,13 +3914,7 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join,
  /* 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;
    /*
      The following assert checks that mark_common_columns() was run and
      we created the list table_ref_1->join_columns.
    */
    DBUG_ASSERT(!is_created);
    nj_col_1= it_1.get_natural_column_ref();
    if (nj_col_1->is_common)
    {
      natural_using_join->join_columns->push_back(nj_col_1);
@@ -3972,13 +3960,7 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join,
  /* 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;
    /*
      The following assert checks that mark_common_columns() was run and
      we created the list table_ref_2->join_columns.
    */
    DBUG_ASSERT(!is_created);
    nj_col_2= it_2.get_natural_column_ref();
    if (!nj_col_2->is_common)
      non_join_columns->push_back(nj_col_2);
    else
@@ -4712,8 +4694,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
            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)))
          if (!(nj_col= field_iterator.get_or_create_column_ref(&is_created)))
            DBUG_RETURN(TRUE);
          DBUG_ASSERT(nj_col->table_field && !is_created);
          field_table= nj_col->table_ref->table;
+18 −79
Original line number Diff line number Diff line
@@ -108,11 +108,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
  {						// Part field list
    SELECT_LEX *select_lex= &thd->lex->select_lex;
    Name_resolution_context *context= &select_lex->context;
    TABLE_LIST *save_next_local;
    TABLE_LIST *save_table_list;
    TABLE_LIST *save_first_name_resolution_table;
    TABLE_LIST *save_next_name_resolution_table;
    bool        save_resolve_in_select_list;
    Name_resolution_context_state ctx_state;
    int res;

    if (fields.elements != values.elements)
@@ -125,14 +121,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
    select_lex->no_wrap_view_item= TRUE;

    /* Save the state of the current name resolution context. */
    save_table_list=                  context->table_list;
    save_first_name_resolution_table= context->first_name_resolution_table;
    save_next_name_resolution_table=  (context->first_name_resolution_table) ?
                                      context->first_name_resolution_table->
                                               next_name_resolution_table :
                                      NULL;
    save_resolve_in_select_list=      context->resolve_in_select_list;
    save_next_local=                  table_list->next_local;
    ctx_state.save_state(context, table_list);

    /*
      Perform name resolution only in the first table - 'table_list',
@@ -143,13 +132,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
    res= setup_fields(thd, 0, fields, 1, 0, 0);

    /* Restore the current context. */
    table_list->next_local=                save_next_local;
    context->table_list=                   save_table_list;
    context->first_name_resolution_table=  save_first_name_resolution_table;
    if (context->first_name_resolution_table)
      context->first_name_resolution_table->
               next_name_resolution_table= save_next_name_resolution_table;
    context->resolve_in_select_list=       save_resolve_in_select_list;
    ctx_state.restore_state(context, table_list);
    thd->lex->select_lex.no_wrap_view_item= FALSE;

    if (res)
@@ -280,13 +263,10 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
  ulonglong id;
  COPY_INFO info;
  TABLE *table= 0;
  TABLE_LIST *save_table_list;
  TABLE_LIST *save_next_local;
  TABLE_LIST *save_first_name_resolution_table;
  TABLE_LIST *save_next_name_resolution_table;
  List_iterator_fast<List_item> its(values_list);
  List_item *values;
  Name_resolution_context *context;
  Name_resolution_context_state ctx_state;
#ifndef EMBEDDED_LIBRARY
  char *query= thd->query;
#endif
@@ -367,13 +347,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,

  context= &thd->lex->select_lex.context;
  /* Save the state of the current name resolution context. */
  save_table_list=                  context->table_list;
  save_first_name_resolution_table= context->first_name_resolution_table;
  save_next_name_resolution_table=  (context->first_name_resolution_table) ?
                                    context->first_name_resolution_table->
                                             next_name_resolution_table :
                                    NULL;
  save_next_local=                  table_list->next_local;
  ctx_state.save_state(context, table_list);

  /*
    Perform name resolution only in the first table - 'table_list',
@@ -397,16 +371,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
  its.rewind ();
 
  /* Restore the current context. */
  table_list->next_local= save_next_local;
  context->first_name_resolution_table= save_first_name_resolution_table;
  if (context->first_name_resolution_table)
    context->first_name_resolution_table->
             next_name_resolution_table= save_next_name_resolution_table;
  ctx_state.restore_state(context, table_list);

  /*
    Fill in the given fields and dump it to the table file
  */

  info.records= info.deleted= info.copied= info.updated= 0;
  info.ignore= ignore;
  info.handle_duplicates=duplic;
@@ -814,11 +783,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
{
  SELECT_LEX *select_lex= &thd->lex->select_lex;
  Name_resolution_context *context= &select_lex->context;
  TABLE_LIST *save_table_list;
  TABLE_LIST *save_next_local;
  TABLE_LIST *save_first_name_resolution_table;
  TABLE_LIST *save_next_name_resolution_table;
  bool        save_resolve_in_select_list;
  Name_resolution_context_state ctx_state;
  bool insert_into_view= (table_list->view != 0);
  bool res= 0;
  DBUG_ENTER("mysql_prepare_insert");
@@ -858,15 +823,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
    DBUG_RETURN(TRUE);

  /* Save the state of the current name resolution context. */
  save_table_list=                  context->table_list;
  /* Here first_name_resolution_table points to the first select table. */
  save_first_name_resolution_table= context->first_name_resolution_table;
  save_next_name_resolution_table=  (context->first_name_resolution_table) ?
                                    context->first_name_resolution_table->
                                             next_name_resolution_table :
                                    NULL;
  save_resolve_in_select_list=      context->resolve_in_select_list;
  save_next_local=                  table_list->next_local;
  ctx_state.save_state(context, table_list);

  /*
    Perform name resolution only in the first table - 'table_list',
@@ -891,23 +848,17 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
    */       
    if (select_lex->group_list.elements == 0)
    {
      context->table_list->next_local=       save_next_local;
      context->table_list->next_local=       ctx_state.save_next_local;
      /* first_name_resolution_table was set by resolve_in_table_list_only() */
      context->first_name_resolution_table->
        next_name_resolution_table=          save_next_local;
        next_name_resolution_table=          ctx_state.save_next_local;
    }
    if (!res)
      res= setup_fields(thd, 0, update_values, 1, 0, 0);
  }

  /* Restore the current context. */
  table_list->next_local= save_next_local;
  context->table_list= save_table_list;
  context->first_name_resolution_table= save_first_name_resolution_table;
  if (context->first_name_resolution_table)
    context->first_name_resolution_table->
             next_name_resolution_table= save_next_name_resolution_table;
  context->resolve_in_select_list= save_resolve_in_select_list;
  ctx_state.restore_state(context, table_list);

  if (res)
    DBUG_RETURN(res);
@@ -2176,17 +2127,10 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
  {
    /* Save the state of the current name resolution context. */
    Name_resolution_context *context= &lex->select_lex.context;
    TABLE_LIST *save_table_list;
    TABLE_LIST *save_next_local;
    TABLE_LIST *save_first_name_resolution_table;
    TABLE_LIST *save_next_name_resolution_table;
    save_table_list=                  context->table_list;
    save_first_name_resolution_table= context->first_name_resolution_table;
    save_next_name_resolution_table=  (context->first_name_resolution_table) ?
                                      context->first_name_resolution_table->
                                               next_name_resolution_table :
                                      NULL;
    save_next_local= table_list->next_local;
    Name_resolution_context_state ctx_state;

    /* Save the state of the current name resolution context. */
    ctx_state.save_state(context, table_list);

    /* Perform name resolution only in the first table - 'table_list'. */
    table_list->next_local= 0;
@@ -2202,20 +2146,15 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
    */       
    if (lex->select_lex.group_list.elements == 0)
    {
      context->table_list->next_local=       save_next_local;
      context->table_list->next_local=       ctx_state.save_next_local;
      /* first_name_resolution_table was set by resolve_in_table_list_only() */
      context->first_name_resolution_table->
        next_name_resolution_table=          save_next_local;
        next_name_resolution_table=          ctx_state.save_next_local;
    }
    res= res || setup_fields(thd, 0, *info.update_values, 1, 0, 0);

    /* Restore the current context. */
    table_list->next_local= save_next_local;
    context->first_name_resolution_table= save_first_name_resolution_table;
    if (context->first_name_resolution_table)
      context->first_name_resolution_table->
               next_name_resolution_table= save_next_name_resolution_table;

    ctx_state.restore_state(context, table_list);
  }

  lex->current_select= lex_current_select_save;
Loading