Skip to content

Commit 16d2d68

Browse files
author
gkodinov/kgeorge@macbook.gmz
committed
BUG#25575: ERROR 1052 (Column in from clause is ambiguous) with sub-join
Two problems here: Problem 1: While constructing the join columns list the optimizer does as follows: 1. Sets the join_using_fields/natural_join members of the right JOIN operand. 2. Makes a "table reference" (TABLE_LIST) to parent the two tables. 3. Assigns the join_using_fields/is_natural_join of the wrapper table using join_using_fields/natural_join of the rightmost table 4. Sets join_using_fields to NULL for the right JOIN operand. 5. Passes the parent table up to the same procedure on the upper level. Step 1 overrides the the join_using_fields that are set for a nested join wrapping table in step 4. Fixed by making a designated variable SELECT_LEX::prev_join_using to pass the data from step 1 to step 4 without destroying the wrapping table data. Problem 2: The optimizer checks for ambiguous columns while transforming NATURAL JOIN/JOIN USING to JOIN ON. While doing that there was no distinction between columns that are used in the generated join condition (where ambiguity can be checked) and the other columns (where ambiguity can be checked only when resolving references coming from outside the JOIN construct itself). Fixed by allowing the non-USING columns to be present in multiple copies in both sides of the join and moving the ambiguity check to the place where unqualified references to the join columns are resolved (find_field_in_natural_join()).
1 parent f3b3f1e commit 16d2d68

File tree

8 files changed

+140
-25
lines changed

8 files changed

+140
-25
lines changed

mysql-test/r/join_nested.result

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,3 +1605,31 @@ WHERE t1.id='5';
16051605
id ct pc nm
16061606
5 NULL NULL NULL
16071607
DROP TABLE t1,t2,t3,t4;
1608+
CREATE TABLE t1 (a INT, b INT);
1609+
CREATE TABLE t2 (a INT);
1610+
CREATE TABLE t3 (a INT, c INT);
1611+
CREATE TABLE t4 (a INT, c INT);
1612+
CREATE TABLE t5 (a INT, c INT);
1613+
SELECT b FROM t1 JOIN (t2 LEFT JOIN t3 USING (a) LEFT JOIN t4 USING (a)
1614+
LEFT JOIN t5 USING (a)) USING (a);
1615+
b
1616+
SELECT c FROM t1 JOIN (t2 LEFT JOIN t3 USING (a) LEFT JOIN t4 USING (a)
1617+
LEFT JOIN t5 USING (a)) USING (a);
1618+
ERROR 23000: Column 'c' in field list is ambiguous
1619+
SELECT b FROM t1 JOIN (t2 JOIN t3 USING (a) JOIN t4 USING (a)
1620+
JOIN t5 USING (a)) USING (a);
1621+
b
1622+
SELECT c FROM t1 JOIN (t2 JOIN t3 USING (a) JOIN t4 USING (a)
1623+
JOIN t5 USING (a)) USING (a);
1624+
ERROR 23000: Column 'c' in field list is ambiguous
1625+
DROP TABLE t1,t2,t3,t4,t5;
1626+
CREATE TABLE t1 (a INT, b INT);
1627+
CREATE TABLE t2 (a INT, b INT);
1628+
CREATE TABLE t3 (a INT, b INT);
1629+
INSERT INTO t1 VALUES (1,1);
1630+
INSERT INTO t2 VALUES (1,1);
1631+
INSERT INTO t3 VALUES (1,1);
1632+
SELECT * FROM t1 JOIN (t2 JOIN t3 USING (b)) USING (a);
1633+
ERROR 23000: Column 'a' in from clause is ambiguous
1634+
DROP TABLE t1,t2,t3;
1635+
End of 5.0 tests

mysql-test/t/join_nested.test

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,3 +1045,42 @@ SELECT t1.*, t4.nm
10451045
WHERE t1.id='5';
10461046

10471047
DROP TABLE t1,t2,t3,t4;
1048+
1049+
#
1050+
# BUG#25575: ERROR 1052 (Column in from clause is ambiguous) with sub-join
1051+
#
1052+
CREATE TABLE t1 (a INT, b INT);
1053+
CREATE TABLE t2 (a INT);
1054+
CREATE TABLE t3 (a INT, c INT);
1055+
CREATE TABLE t4 (a INT, c INT);
1056+
CREATE TABLE t5 (a INT, c INT);
1057+
1058+
SELECT b FROM t1 JOIN (t2 LEFT JOIN t3 USING (a) LEFT JOIN t4 USING (a)
1059+
LEFT JOIN t5 USING (a)) USING (a);
1060+
1061+
--error ER_NON_UNIQ_ERROR
1062+
SELECT c FROM t1 JOIN (t2 LEFT JOIN t3 USING (a) LEFT JOIN t4 USING (a)
1063+
LEFT JOIN t5 USING (a)) USING (a);
1064+
1065+
SELECT b FROM t1 JOIN (t2 JOIN t3 USING (a) JOIN t4 USING (a)
1066+
JOIN t5 USING (a)) USING (a);
1067+
1068+
--error ER_NON_UNIQ_ERROR
1069+
SELECT c FROM t1 JOIN (t2 JOIN t3 USING (a) JOIN t4 USING (a)
1070+
JOIN t5 USING (a)) USING (a);
1071+
1072+
DROP TABLE t1,t2,t3,t4,t5;
1073+
CREATE TABLE t1 (a INT, b INT);
1074+
CREATE TABLE t2 (a INT, b INT);
1075+
CREATE TABLE t3 (a INT, b INT);
1076+
1077+
INSERT INTO t1 VALUES (1,1);
1078+
INSERT INTO t2 VALUES (1,1);
1079+
INSERT INTO t3 VALUES (1,1);
1080+
1081+
--error ER_NON_UNIQ_ERROR
1082+
SELECT * FROM t1 JOIN (t2 JOIN t3 USING (b)) USING (a);
1083+
1084+
DROP TABLE t1,t2,t3;
1085+
1086+
--echo End of 5.0 tests

sql/mysql_priv.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,8 @@ bool push_new_name_resolution_context(THD *thd,
982982
TABLE_LIST *left_op,
983983
TABLE_LIST *right_op);
984984
void add_join_on(TABLE_LIST *b,Item *expr);
985-
void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields);
985+
void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields,
986+
SELECT_LEX *lex);
986987
bool add_proc_to_list(THD *thd, Item *item);
987988
TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find);
988989
void update_non_unique_table_error(TABLE_LIST *update,

sql/sql_base.cc

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2945,7 +2945,7 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
29452945
{
29462946
List_iterator_fast<Natural_join_column>
29472947
field_it(*(table_ref->join_columns));
2948-
Natural_join_column *nj_col;
2948+
Natural_join_column *nj_col, *curr_nj_col;
29492949
Field *found_field;
29502950
Query_arena *arena, backup;
29512951
DBUG_ENTER("find_field_in_natural_join");
@@ -2956,14 +2956,21 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
29562956

29572957
LINT_INIT(found_field);
29582958

2959-
for (;;)
2959+
for (nj_col= NULL, curr_nj_col= field_it++; curr_nj_col;
2960+
curr_nj_col= field_it++)
29602961
{
2961-
if (!(nj_col= field_it++))
2962-
DBUG_RETURN(NULL);
2963-
2964-
if (!my_strcasecmp(system_charset_info, nj_col->name(), name))
2965-
break;
2962+
if (!my_strcasecmp(system_charset_info, curr_nj_col->name(), name))
2963+
{
2964+
if (nj_col)
2965+
{
2966+
my_error(ER_NON_UNIQ_ERROR, MYF(0), name, thd->where);
2967+
DBUG_RETURN(NULL);
2968+
}
2969+
nj_col= curr_nj_col;
2970+
}
29662971
}
2972+
if (!nj_col)
2973+
DBUG_RETURN(NULL);
29672974

29682975
if (nj_col->view_field)
29692976
{
@@ -3774,9 +3781,16 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
37743781
{
37753782
bool found= FALSE;
37763783
const char *field_name_1;
3784+
/* true if field_name_1 is a member of using_fields */
3785+
bool is_using_column_1;
37773786
if (!(nj_col_1= it_1.get_or_create_column_ref(leaf_1)))
37783787
goto err;
37793788
field_name_1= nj_col_1->name();
3789+
is_using_column_1= using_fields &&
3790+
test_if_string_in_list(field_name_1, using_fields);
3791+
DBUG_PRINT ("info", ("field_name_1=%s.%s",
3792+
nj_col_1->table_name() ? nj_col_1->table_name() : "",
3793+
field_name_1));
37803794

37813795
/*
37823796
Find a field with the same name in table_ref_2.
@@ -3793,17 +3807,27 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
37933807
if (!(cur_nj_col_2= it_2.get_or_create_column_ref(leaf_2)))
37943808
goto err;
37953809
cur_field_name_2= cur_nj_col_2->name();
3810+
DBUG_PRINT ("info", ("cur_field_name_2=%s.%s",
3811+
cur_nj_col_2->table_name() ?
3812+
cur_nj_col_2->table_name() : "",
3813+
cur_field_name_2));
37963814

37973815
/*
37983816
Compare the two columns and check for duplicate common fields.
37993817
A common field is duplicate either if it was already found in
38003818
table_ref_2 (then found == TRUE), or if a field in table_ref_2
38013819
was already matched by some previous field in table_ref_1
38023820
(then cur_nj_col_2->is_common == TRUE).
3821+
Note that it is too early to check the columns outside of the
3822+
USING list for ambiguity because they are not actually "referenced"
3823+
here. These columns must be checked only on unqualified reference
3824+
by name (e.g. in SELECT list).
38033825
*/
38043826
if (!my_strcasecmp(system_charset_info, field_name_1, cur_field_name_2))
38053827
{
3806-
if (found || cur_nj_col_2->is_common)
3828+
DBUG_PRINT ("info", ("match c1.is_common=%d", nj_col_1->is_common));
3829+
if (cur_nj_col_2->is_common ||
3830+
(found && (!using_fields || is_using_column_1)))
38073831
{
38083832
my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1, thd->where);
38093833
goto err;
@@ -3829,9 +3853,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
38293853
clause (if present), mark them as common fields, and add a new
38303854
equi-join condition to the ON clause.
38313855
*/
3832-
if (nj_col_2 &&
3833-
(!using_fields ||
3834-
test_if_string_in_list(field_name_1, using_fields)))
3856+
if (nj_col_2 && (!using_fields ||is_using_column_1))
38353857
{
38363858
Item *item_1= nj_col_1->create_item(thd);
38373859
Item *item_2= nj_col_2->create_item(thd);
@@ -3886,6 +3908,13 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
38863908
eq_cond);
38873909

38883910
nj_col_1->is_common= nj_col_2->is_common= TRUE;
3911+
DBUG_PRINT ("info", ("%s.%s and %s.%s are common",
3912+
nj_col_1->table_name() ?
3913+
nj_col_1->table_name() : "",
3914+
nj_col_1->name(),
3915+
nj_col_2->table_name() ?
3916+
nj_col_2->table_name() : "",
3917+
nj_col_2->name()));
38893918

38903919
if (field_1)
38913920
{

sql/sql_lex.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,20 @@ class st_select_lex: public st_select_lex_node
587587
int cur_pos_in_select_list;
588588

589589
List<udf_func> udf_list; /* udf function calls stack */
590+
/*
591+
This is a copy of the original JOIN USING list that comes from
592+
the parser. The parser :
593+
1. Sets the natural_join of the second TABLE_LIST in the join
594+
and the st_select_lex::prev_join_using.
595+
2. Makes a parent TABLE_LIST and sets its is_natural_join/
596+
join_using_fields members.
597+
3. Uses the wrapper TABLE_LIST as a table in the upper level.
598+
We cannot assign directly to join_using_fields in the parser because
599+
at stage (1.) the parent TABLE_LIST is not constructed yet and
600+
the assignment will override the JOIN USING fields of the lower level
601+
joins on the right.
602+
*/
603+
List<String> *prev_join_using;
590604

591605
void init_query();
592606
void init_select();

sql/sql_parse.cc

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6365,11 +6365,8 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
63656365
If this is a JOIN ... USING, move the list of joined fields to the
63666366
table reference that describes the join.
63676367
*/
6368-
if (table->join_using_fields)
6369-
{
6370-
ptr->join_using_fields= table->join_using_fields;
6371-
table->join_using_fields= NULL;
6372-
}
6368+
if (prev_join_using)
6369+
ptr->join_using_fields= prev_join_using;
63736370
}
63746371
}
63756372
join_list->push_front(ptr);
@@ -6625,6 +6622,7 @@ void add_join_on(TABLE_LIST *b, Item *expr)
66256622
a Left join argument
66266623
b Right join argument
66276624
using_fields Field names from USING clause
6625+
lex The current st_select_lex
66286626
66296627
IMPLEMENTATION
66306628
This function marks that table b should be joined with a either via
@@ -6653,10 +6651,11 @@ void add_join_on(TABLE_LIST *b, Item *expr)
66536651
None
66546652
*/
66556653

6656-
void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields)
6654+
void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
6655+
SELECT_LEX *lex)
66576656
{
66586657
b->natural_join= a;
6659-
b->join_using_fields= using_fields;
6658+
lex->prev_join_using= using_fields;
66606659
}
66616660

66626661

sql/sql_yacc.yy

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5280,11 +5280,11 @@ join_table:
52805280
YYERROR_UNLESS($1 && $3);
52815281
}
52825282
'(' using_list ')'
5283-
{ add_join_natural($1,$3,$7); $$=$3; }
5283+
{ add_join_natural($1,$3,$7,Select); $$=$3; }
52845284
| table_ref NATURAL JOIN_SYM table_factor
52855285
{
52865286
YYERROR_UNLESS($1 && ($$=$4));
5287-
add_join_natural($1,$4,NULL);
5287+
add_join_natural($1,$4,NULL,Select);
52885288
}
52895289

52905290
/* LEFT JOIN variants */
@@ -5311,11 +5311,15 @@ join_table:
53115311
YYERROR_UNLESS($1 && $5);
53125312
}
53135313
USING '(' using_list ')'
5314-
{ add_join_natural($1,$5,$9); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
5314+
{
5315+
add_join_natural($1,$5,$9,Select);
5316+
$5->outer_join|=JOIN_TYPE_LEFT;
5317+
$$=$5;
5318+
}
53155319
| table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor
53165320
{
53175321
YYERROR_UNLESS($1 && $6);
5318-
add_join_natural($1,$6,NULL);
5322+
add_join_natural($1,$6,NULL,Select);
53195323
$6->outer_join|=JOIN_TYPE_LEFT;
53205324
$$=$6;
53215325
}
@@ -5349,12 +5353,12 @@ join_table:
53495353
LEX *lex= Lex;
53505354
if (!($$= lex->current_select->convert_right_join()))
53515355
YYABORT;
5352-
add_join_natural($$,$5,$9);
5356+
add_join_natural($$,$5,$9,Select);
53535357
}
53545358
| table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor
53555359
{
53565360
YYERROR_UNLESS($1 && $6);
5357-
add_join_natural($6,$1,NULL);
5361+
add_join_natural($6,$1,NULL,Select);
53585362
LEX *lex= Lex;
53595363
if (!($$= lex->current_select->convert_right_join()))
53605364
YYABORT;

sql/table.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2628,6 +2628,7 @@ Field *Natural_join_column::field()
26282628

26292629
const char *Natural_join_column::table_name()
26302630
{
2631+
DBUG_ASSERT(table_ref);
26312632
return table_ref->alias;
26322633
}
26332634

0 commit comments

Comments
 (0)