Commit 53edc92c authored by unknown's avatar unknown
Browse files

Final solution for bug# 4302 "Ambiguos order by when renamed column is

identical to another in result"
According to SQL standard queries like 
"select t1.a as col from t1, t2 order by a" should return an error if
both tables contain field a.


mysql-test/r/order_by.result:
  Updated test to conform SQL-standard.
mysql-test/t/order_by.test:
  Updated test to conform SQL-standard.
sql/item.cc:
  find_item_in_list() has now one more out parameter which is not used
  in item.cc functions.
sql/mysql_priv.h:
  find_item_in_list(): Added boolean out parameter "unaliased" which
  indicates that we have found field by its original name and not by
  its alias in item (select) list.
sql/sql_base.cc:
  find_item_in_list(): Added boolean out parameter "unaliased" which
  indicates that we have found field by its original name and not by
  its alias in item (select) list. This means that additional check is
  required to ensure there will be no ambiguity if we would search for this
  field in all tables.
sql/sql_select.cc:
  find_order_in_list(): If we have found field in select list by its
  original name and not by its alias then we should perform additional
  check to ensure that there will be no ambiguity if we will search for
  this field in all tables. Also small cleanup.
parent ccf52b4f
Loading
Loading
Loading
Loading
+3 −6
Original line number Diff line number Diff line
@@ -680,6 +680,9 @@ order by col;
ERROR 23000: Column 'col' in order clause is ambiguous
select col1 from t1, t2 where t1.col1=t2.col2 order by col;
ERROR 23000: Column 'col' in order clause is ambiguous
select t1.col as t1_col, t2.col2 from t1, t2 where t1.col1=t2.col2
order by col;
ERROR 23000: Column 'col' in order clause is ambiguous
select t1.col as t1_col, t2.col from t1, t2 where t1.col1=t2.col2
order by col;
t1_col	col
@@ -696,12 +699,6 @@ col col2
1	3
2	2
3	1
select t1.col as t1_col, t2.col2 from t1, t2 where t1.col1=t2.col2
order by col;
t1_col	col2
1	1
2	2
3	3
select t2.col2, t2.col, t2.col from t2 order by col;
col2	col	col
3	1	1
+3 −2
Original line number Diff line number Diff line
@@ -472,13 +472,14 @@ select t1.col as c1, t2.col as c2 from t1, t2 where t1.col1=t2.col2
  order by col;
--error 1052
select col1 from t1, t2 where t1.col1=t2.col2 order by col;
--error 1052
select t1.col as t1_col, t2.col2 from t1, t2 where t1.col1=t2.col2
  order by col;

select t1.col as t1_col, t2.col from t1, t2 where t1.col1=t2.col2
  order by col;
select col2 as c, col as c from t2 order by col;
select col2 as col, col as col2 from t2 order by col; 
select t1.col as t1_col, t2.col2 from t1, t2 where t1.col1=t2.col2
  order by col;
select t2.col2, t2.col, t2.col from t2 order by col;

select t2.col2 as col from t2 order by t2.col;
+9 −7
Original line number Diff line number Diff line
@@ -1246,6 +1246,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
      TABLE_LIST *table_list;
      Item **refer= (Item **)not_found_item;
      uint counter;
      bool not_used;
      // Prevent using outer fields in subselects, that is not supported now
      SELECT_LEX *cursel= (SELECT_LEX *) thd->lex->current_select;
      if (cursel->master_unit()->first_select()->linkage != DERIVED_TABLE_TYPE)
@@ -1288,7 +1289,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
          }
	  if (sl->resolve_mode == SELECT_LEX::SELECT_MODE &&
	      (refer= find_item_in_list(this, sl->item_list, &counter,
					 REPORT_EXCEPT_NOT_FOUND)) !=
                                        REPORT_EXCEPT_NOT_FOUND,
                                        &not_used)) !=
	       (Item **) not_found_item)
	  {
	    if (*refer && (*refer)->fixed) // Avoid crash in case of error
@@ -1889,6 +1891,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
{
  DBUG_ASSERT(fixed == 0);
  uint counter;
  bool not_used;
  if (!ref)
  {
    TABLE_LIST *where= 0, *table_list;
@@ -1908,13 +1911,13 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
				  first_select()->linkage !=
				  DERIVED_TABLE_TYPE) ? 
				  REPORT_EXCEPT_NOT_FOUND :
				  REPORT_ALL_ERRORS))) ==
				  REPORT_ALL_ERRORS ), &not_used)) ==
	(Item **)not_found_item)
    {
      upward_lookup= 1;
      Field *tmp= (Field*) not_found_field;
      /*
	We can't find table field in table list of current select,
	We can't find table field in select list of current select,
	consequently we have to find it in outer subselect(s).
	We can't join lists of outer & current select, because of scope
	of view rules. For example if both tables (outer & current) have
@@ -1929,8 +1932,8 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
	Item_subselect *prev_subselect_item= prev_unit->item;
	if (sl->resolve_mode == SELECT_LEX::SELECT_MODE &&
	    (ref= find_item_in_list(this, sl->item_list,
				    &counter,
				    REPORT_EXCEPT_NOT_FOUND)) !=
                                    &counter, REPORT_EXCEPT_NOT_FOUND,
                                    &not_used)) !=
	   (Item **)not_found_item)
	{
	  if (*ref && (*ref)->fixed) // Avoid crash in case of error
@@ -1989,8 +1992,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
	  // Call to report error
	  find_item_in_list(this,
			    *(thd->lex->current_select->get_item_list()),
			    &counter,
			    REPORT_ALL_ERRORS);
			    &counter, REPORT_ALL_ERRORS, &not_used);
	}
        ref= 0;
	return 1;
+2 −1
Original line number Diff line number Diff line
@@ -705,7 +705,8 @@ enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
				  IGNORE_ERRORS};
extern const Item **not_found_item;
Item ** find_item_in_list(Item *item, List<Item> &items, uint *counter,
			  find_item_error_report_type report_error);
                          find_item_error_report_type report_error,
                          bool *unaliased);
bool get_key_map_from_key_list(key_map *map, TABLE *table,
                               List<String> *index_list);
bool insert_fields(THD *thd,TABLE_LIST *tables,
+28 −15
Original line number Diff line number Diff line
@@ -2082,6 +2082,9 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
				return not_found_item, report other errors,
				return 0
      IGNORE_ERRORS		Do not report errors, return 0 if error
    unaliased                   Set to true if item is field which was found
                                by original field name and not by its alias
                                in item list. Set to false otherwise.

  RETURN VALUES
    0			Item is not found or item is not unique,
@@ -2098,7 +2101,7 @@ const Item **not_found_item= (const Item**) 0x1;

Item **
find_item_in_list(Item *find, List<Item> &items, uint *counter,
		  find_item_error_report_type report_error)
                  find_item_error_report_type report_error, bool *unaliased)
{
  List_iterator<Item> li(items);
  Item **found=0, **found_unaliased= 0, *item;
@@ -2107,6 +2110,9 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
  const char *table_name=0;
  bool found_unaliased_non_uniq= 0;
  uint unaliased_counter;

  *unaliased= FALSE;

  if (find->type() == Item::FIELD_ITEM	|| find->type() == Item::REF_ITEM)
  {
    field_name= ((Item_ident*) find)->field_name;
@@ -2134,17 +2140,18 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
        /*
          If table name is specified we should find field 'field_name' in
          table 'table_name'. According to SQL-standard we should ignore
          aliases in this case. Note that we should prefer fields from the
          select list over other fields from the tables participating in
          this select in case of ambiguity.
          aliases in this case.

          Since we should NOT prefer fields from the select list over
          other fields from the tables participating in this select in
          case of ambiguity we have to do extra check outside this function.

          We use strcmp for table names and database names as these may be
          case sensitive.
          In cases where they are not case sensitive, they are always in lower
          case.
          case sensitive. In cases where they are not case sensitive, they
          are always in lower case.

	  item_field->field_name and item_field->table_name can be 0x0 if
	  item is not fix fielded yet.
	  item is not fix_field()'ed yet.
        */
        if (item_field->field_name && item_field->table_name &&
	    !my_strcasecmp(system_charset_info, item_field->field_name,
@@ -2153,17 +2160,22 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
            (!db_name || (item_field->db_name &&
                          !strcmp(item_field->db_name, db_name))))
        {
          if (found)
          if (found_unaliased)
          {
            if ((*found)->eq(item, 0))
              continue;                         // Same field twice
            if ((*found_unaliased)->eq(item, 0))
              continue;
            /*
              Two matching fields in select list.
              We already can bail out because we are searching through
              unaliased names only and will have duplicate error anyway.
            */
            if (report_error != IGNORE_ERRORS)
              my_printf_error(ER_NON_UNIQ_ERROR, ER(ER_NON_UNIQ_ERROR),
                              MYF(0), find->full_name(), current_thd->where);
            return (Item**) 0;
          }
          found= li.ref();
          *counter= i;
          found_unaliased= li.ref();
          unaliased_counter= i;
          if (db_name)
            break;                              // Perfect match
        }
@@ -2235,6 +2247,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
    {
      found= found_unaliased;
      *counter= unaliased_counter;
      *unaliased= TRUE;
    }
  }
  if (found)
Loading