Commit e61b4454 authored by timour@mysql.com's avatar timour@mysql.com

Merge of WL#1972 "Evaluate HAVING before SELECT select-list"

parents f65e0547 9a4ffb5a
drop table if exists t1,t2; drop table if exists t1,t2,t3;
create table t1 (a int); create table t1 (a int);
select count(a) as b from t1 where a=0 having b > 0; select count(a) as b from t1 where a=0 having b > 0;
b b
...@@ -128,3 +128,203 @@ id description c ...@@ -128,3 +128,203 @@ id description c
1 test 0 1 test 0
2 test2 0 2 test2 0
drop table t1,t2,t3; drop table t1,t2,t3;
create table t1 (col1 int, col2 varchar(5), col_t1 int);
create table t2 (col1 int, col2 varchar(5), col_t2 int);
create table t3 (col1 int, col2 varchar(5), col_t3 int);
insert into t1 values(10,'hello',10);
insert into t1 values(20,'hello',20);
insert into t1 values(30,'hello',30);
insert into t1 values(10,'bye',10);
insert into t1 values(10,'sam',10);
insert into t1 values(10,'bob',10);
insert into t2 select * from t1;
insert into t3 select * from t1;
select count(*) from t1 group by col1 having col1 = 10;
count(*)
4
select count(*) as count_col1 from t1 group by col1 having col1 = 10;
count_col1
4
select count(*) as count_col1 from t1 as tmp1 group by col1 having col1 = 10;
count_col1
4
select count(*) from t1 group by col2 having col2 = 'hello';
count(*)
3
select count(*) from t1 group by col2 having col1 = 10;
ERROR 42S22: Unknown column 'col1' in 'having clause'
select col1 as count_col1 from t1 as tmp1 group by col1 having col1 = 10;
count_col1
10
select col1 as count_col1 from t1 as tmp1 group by col1 having count_col1 = 10;
count_col1
10
select col1 as count_col1 from t1 as tmp1 group by count_col1 having col1 = 10;
count_col1
10
select col1 as count_col1 from t1 as tmp1 group by count_col1 having count_col1 = 10;
count_col1
10
select col1 as count_col1,col2 from t1 as tmp1 group by col1,col2 having col1 = 10;
count_col1 col2
10 bob
10 bye
10 hello
10 sam
select col1 as count_col1,col2 from t1 as tmp1 group by col1,col2 having count_col1 = 10;
count_col1 col2
10 bob
10 bye
10 hello
10 sam
select col1 as count_col1,col2 from t1 as tmp1 group by col1,col2 having col2 = 'hello';
count_col1 col2
10 hello
20 hello
30 hello
select col1 as count_col1,col2 as group_col2 from t1 as tmp1 group by col1,col2 having group_col2 = 'hello';
count_col1 group_col2
10 hello
20 hello
30 hello
select sum(col1) as co12 from t1 group by col2 having col2 10;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '10' at line 1
select sum(col1) as co2, count(col2) as cc from t1 group by col1 having col1 =10;
co2 cc
40 4
select t2.col2 from t2 group by t2.col1, t2.col2 having t1.col1 <= 10;
ERROR 42S22: Unknown column 't1.col1' in 'having clause'
select t1.col1 from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2 having t2.col1 <= 10);
col1
10
20
30
10
10
10
select t1.col1 from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2
having t2.col1 <=
(select min(t3.col1) from t3));
col1
10
20
30
10
10
10
select t1.col1 from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2 having t1.col1 <= 10);
col1
10
10
10
10
select t1.col1 as tmp_col from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2 having tmp_col <= 10);
tmp_col
10
10
10
10
select t1.col1 from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2 having col_t1 <= 10);
col1
10
10
10
10
select sum(col1) from t1
group by col_t1
having (select col_t1 from t2 where col_t1 = col_t2 order by col_t2 limit 1);
sum(col1)
40
20
30
select t1.col1 from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2 having col_t1 <= 10)
having col_t1 <= 20;
ERROR 42S22: Unknown column 'col_t1' in 'having clause'
select t1.col1 from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2 having col_t1 <= 10)
group by col_t1
having col_t1 <= 20;
col1
10
select col_t1, sum(col1) from t1
group by col_t1
having col_t1 > 10 and
exists (select sum(t2.col1) from t2
group by t2.col2 having t2.col2 > 'b');
col_t1 sum(col1)
20 20
30 30
select sum(col1) from t1
group by col_t1
having col_t1 in (select sum(t2.col1) from t2
group by t2.col2, t2.col1 having t2.col1 = t1.col1);
ERROR 42S22: Unknown column 't1.col1' in 'having clause'
select sum(col1) from t1
group by col_t1
having col_t1 in (select sum(t2.col1) from t2
group by t2.col2, t2.col1 having t2.col1 = col_t1);
sum(col1)
40
20
30
select t1.col1, t2.col1 from t1, t2 where t1.col1 = t2.col1
group by t1.col1, t2.col1 having col1 = 2;
ERROR 23000: Column 'col1' in having clause is ambiguous
select t1.col1*10+t2.col1 from t1,t2 where t1.col1=t2.col1
group by t1.col1, t2.col1 having col1 = 2;
ERROR 23000: Column 'col1' in having clause is ambiguous
drop table t1, t2, t3;
create table t1 (s1 int);
insert into t1 values (1),(2),(3);
select count(*) from t1 group by s1 having s1 is null;
count(*)
select s1*0 as s1 from t1 group by s1 having s1 <> 0;
s1
0
0
0
Warnings:
Warning 1052 Column 's1' in having clause is ambiguous
select s1*0 from t1 group by s1 having s1 = 0;
s1*0
select s1 from t1 group by 1 having 1 = 0;
s1
select count(s1) from t1 group by s1 having count(1+1)=2;
count(s1)
select count(s1) from t1 group by s1 having s1*0=0;
count(s1)
1
1
1
select * from t1 a, t1 b group by a.s1 having s1 is null;
ERROR 23000: Column 's1' in having clause is ambiguous
drop table t1;
create table t1 (s1 char character set latin1 collate latin1_german1_ci);
insert into t1 values ('ü'),('y');
Warnings:
Warning 1265 Data truncated for column 's1' at row 1
select s1,count(s1) from t1
group by s1 collate latin1_swedish_ci having s1 = 'y';
s1 count(s1)
y 1
drop table t1;
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# #
--disable_warnings --disable_warnings
drop table if exists t1,t2; drop table if exists t1,t2,t3;
--enable_warnings --enable_warnings
create table t1 (a int); create table t1 (a int);
...@@ -122,3 +122,196 @@ from t1 a left join t3 b on a.id=b.order_id ...@@ -122,3 +122,196 @@ from t1 a left join t3 b on a.id=b.order_id
group by a.id, a.description group by a.id, a.description
having (a.description is not null) and (c=0); having (a.description is not null) and (c=0);
drop table t1,t2,t3; drop table t1,t2,t3;
#
# Tests for WL#1972 CORRECT EVALUATION OF COLUMN REFERENCES IN THE HAVING CLAUSE
# Per the SAP VERI tests and WL#1972, MySQL must ensure that HAVING can
# correctly evaluate column references from the GROUP BY clause, even if the
# same references are not also found in the select list.
#
# set global sql_mode='ansi';
# set session sql_mode='ansi';
create table t1 (col1 int, col2 varchar(5), col_t1 int);
create table t2 (col1 int, col2 varchar(5), col_t2 int);
create table t3 (col1 int, col2 varchar(5), col_t3 int);
insert into t1 values(10,'hello',10);
insert into t1 values(20,'hello',20);
insert into t1 values(30,'hello',30);
insert into t1 values(10,'bye',10);
insert into t1 values(10,'sam',10);
insert into t1 values(10,'bob',10);
insert into t2 select * from t1;
insert into t3 select * from t1;
select count(*) from t1 group by col1 having col1 = 10;
select count(*) as count_col1 from t1 group by col1 having col1 = 10;
select count(*) as count_col1 from t1 as tmp1 group by col1 having col1 = 10;
select count(*) from t1 group by col2 having col2 = 'hello';
--error 1054
select count(*) from t1 group by col2 having col1 = 10;
select col1 as count_col1 from t1 as tmp1 group by col1 having col1 = 10;
select col1 as count_col1 from t1 as tmp1 group by col1 having count_col1 = 10;
select col1 as count_col1 from t1 as tmp1 group by count_col1 having col1 = 10;
# ANSI: should return SQLSTATE 42000 Syntax error or access violation
# MySQL: returns 10 - because of GROUP BY name resolution
select col1 as count_col1 from t1 as tmp1 group by count_col1 having count_col1 = 10;
# ANSI: should return SQLSTATE 42000 Syntax error or access violation
# MySQL: returns 10 - because of GROUP BY name resolution
select col1 as count_col1,col2 from t1 as tmp1 group by col1,col2 having col1 = 10;
select col1 as count_col1,col2 from t1 as tmp1 group by col1,col2 having count_col1 = 10;
select col1 as count_col1,col2 from t1 as tmp1 group by col1,col2 having col2 = 'hello';
select col1 as count_col1,col2 as group_col2 from t1 as tmp1 group by col1,col2 having group_col2 = 'hello';
--error 1064
select sum(col1) as co12 from t1 group by col2 having col2 10;
select sum(col1) as co2, count(col2) as cc from t1 group by col1 having col1 =10;
--error 1054
select t2.col2 from t2 group by t2.col1, t2.col2 having t1.col1 <= 10;
#
# queries with nested sub-queries
#
# the having column is resolved in the same query
select t1.col1 from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2 having t2.col1 <= 10);
select t1.col1 from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2
having t2.col1 <=
(select min(t3.col1) from t3));
# the having column is resolved in the SELECT clause of the outer query -
# works in ANSI
select t1.col1 from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2 having t1.col1 <= 10);
# the having column is resolved in the SELECT clause of the outer query -
# error in ANSI, works with MySQL extension
select t1.col1 as tmp_col from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2 having tmp_col <= 10);
# the having column is resolved in the FROM clause of the outer query -
# works in ANSI
select t1.col1 from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2 having col_t1 <= 10);
# Item_field must be resolved in the same way as Item_ref
select sum(col1) from t1
group by col_t1
having (select col_t1 from t2 where col_t1 = col_t2 order by col_t2 limit 1);
# nested queries with HAVING, inner having column resolved in outer FROM clause
# the outer having column is not referenced in GROUP BY which results in an error
--error 1054
select t1.col1 from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2 having col_t1 <= 10)
having col_t1 <= 20;
# both having columns are resolved in the GROUP clause of the outer query
select t1.col1 from t1
where t1.col2 in
(select t2.col2 from t2
group by t2.col1, t2.col2 having col_t1 <= 10)
group by col_t1
having col_t1 <= 20;
#
# nested HAVING clauses
#
# non-correlated subqueries
select col_t1, sum(col1) from t1
group by col_t1
having col_t1 > 10 and
exists (select sum(t2.col1) from t2
group by t2.col2 having t2.col2 > 'b');
# correlated subqueries - inner having column 't1.col2' resolves to
# the outer FROM clause, which cannot be used because the outer query
# is grouped
--error 1054
select sum(col1) from t1
group by col_t1
having col_t1 in (select sum(t2.col1) from t2
group by t2.col2, t2.col1 having t2.col1 = t1.col1);
# correlated subqueries - inner having column 'col_t1' resolves to
# the outer GROUP clause
select sum(col1) from t1
group by col_t1
having col_t1 in (select sum(t2.col1) from t2
group by t2.col2, t2.col1 having t2.col1 = col_t1);
#
# queries with joins and ambiguous column names
#
--error 1052
select t1.col1, t2.col1 from t1, t2 where t1.col1 = t2.col1
group by t1.col1, t2.col1 having col1 = 2;
--error 1052
select t1.col1*10+t2.col1 from t1,t2 where t1.col1=t2.col1
group by t1.col1, t2.col1 having col1 = 2;
drop table t1, t2, t3;
# More queries to test ANSI compatibility
create table t1 (s1 int);
insert into t1 values (1),(2),(3);
select count(*) from t1 group by s1 having s1 is null;
select s1*0 as s1 from t1 group by s1 having s1 <> 0;
# ANSI requires: 3 rows
# MySQL returns: 0 rows - because of GROUP BY name resolution
select s1*0 from t1 group by s1 having s1 = 0;
select s1 from t1 group by 1 having 1 = 0;
select count(s1) from t1 group by s1 having count(1+1)=2;
# ANSI requires: 3 rows
# MySQL returns: 0 rows - because of GROUP BY name resolution
select count(s1) from t1 group by s1 having s1*0=0;
-- error 1052
select * from t1 a, t1 b group by a.s1 having s1 is null;
# ANSI requires: 0 rows
# MySQL returns:
# "ERROR 1052 (23000): Column 's1' in having clause is ambiguous"
# I think the column is ambiguous in ANSI too.
# It is the same as:
# select a.s1, b.s1 from t1 a, t1 b group by a.s1 having s1 is null;
# currently we first check SELECT, thus s1 is ambiguous.
drop table t1;
create table t1 (s1 char character set latin1 collate latin1_german1_ci);
insert into t1 values ('ü'),('y');
select s1,count(s1) from t1
group by s1 collate latin1_swedish_ci having s1 = 'y';
# ANSI requires: 1 row, with count(s1) = 2
# MySQL returns: 1 row, with count(s1) = 1
drop table t1;
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "sql_acl.h" #include "sql_acl.h"
#include "sp_head.h" #include "sp_head.h"
#include "sql_trigger.h" #include "sql_trigger.h"
#include "sql_select.h"
static void mark_as_dependent(THD *thd, static void mark_as_dependent(THD *thd,
SELECT_LEX *last, SELECT_LEX *current, SELECT_LEX *last, SELECT_LEX *current,
...@@ -1532,170 +1533,420 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, ...@@ -1532,170 +1533,420 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
} }
bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
/*
Search a GROUP BY clause for a field with a certain name.
SYNOPSIS
find_field_in_group_list()
find_item the item being searched for
group_list GROUP BY clause
DESCRIPTION
Search the GROUP BY list for a column named as find_item. When searching
preference is given to columns that are qualified with the same table (and
database) name as the one being searched for.
RETURN
- the found item on success
- NULL if find_item is not in group_list
*/
static Item** find_field_in_group_list(Item *find_item, ORDER *group_list)
{
const char *db_name;
const char *table_name;
const char *field_name;
ORDER *found_group= NULL;
int found_match_degree= 0;
Item_field *cur_field;
int cur_match_degree= 0;
if (find_item->type() == Item::FIELD_ITEM ||
find_item->type() == Item::REF_ITEM)
{
db_name= ((Item_ident*) find_item)->db_name;
table_name= ((Item_ident*) find_item)->table_name;
field_name= ((Item_ident*) find_item)->field_name;
}
else
return NULL;
DBUG_ASSERT(field_name);
for (ORDER *cur_group= group_list ; cur_group ; cur_group= cur_group->next)
{
if ((*(cur_group->item))->type() == Item::FIELD_ITEM)
{
cur_field= (Item_field*) *cur_group->item;
cur_match_degree= 0;
DBUG_ASSERT(cur_field->field_name);
if (!my_strcasecmp(system_charset_info,
cur_field->field_name, field_name))
++cur_match_degree;
else
continue;
if (cur_field->table_name && table_name)
{
/* If field_name is qualified by a table name. */
if (strcmp(cur_field->table_name, table_name))
/* Same field names, different tables. */
return NULL;
++cur_match_degree;
if (cur_field->db_name && db_name)
{
/* If field_name is also qualified by a database name. */
if (strcmp(cur_field->db_name, db_name))
/* Same field names, different databases. */
return NULL;
++cur_match_degree;
}
}
if (cur_match_degree > found_match_degree)
{
found_match_degree= cur_match_degree;
found_group= cur_group;
}
else if (found_group && (cur_match_degree == found_match_degree) &&
! (*(found_group->item))->eq(cur_field, 0))
{
/*
If the current resolve candidate matches equally well as the current
best match, they must reference the same column, otherwise the field
is ambiguous.
*/
my_printf_error(ER_NON_UNIQ_ERROR, ER(ER_NON_UNIQ_ERROR),
MYF(0), find_item->full_name(), current_thd->where);
return NULL;
}
}
}
if (found_group)
return found_group->item;
else
return NULL;
}
/*
Resolve a column reference in a sub-select.
SYNOPSIS
resolve_ref_in_select_and_group()
thd current thread
ref column reference being resolved
select the sub-select that ref is resolved against
DESCRIPTION
Resolve a column reference (usually inside a HAVING clause) against the
SELECT and GROUP BY clauses of the query described by 'select'. The name
resolution algorithm searches both the SELECT and GROUP BY clauses, and in
case of a name conflict prefers GROUP BY column names over SELECT names. If
both clauses contain different fields with the same names, a warning is
issued that name of 'ref' is ambiguous. We extend ANSI SQL in that when no
GROUP BY column is found, then a HAVING name is resolved as a possibly
derived SELECT column.
NOTES
The resolution procedure is:
- Search for a column or derived column named col_ref_i [in table T_j]
in the SELECT clause of Q.
- Search for a column named col_ref_i [in table T_j]
in the GROUP BY clause of Q.
- If found different columns with the same name in GROUP BY and SELECT
- issue a warning and return the GROUP BY column,
- otherwise return the found SELECT column.
RETURN
NULL - there was an error, and the error was already reported
not_found_item - the item was not resolved, no error was reported
resolved item - if the item was resolved
*/
static Item**
resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
{
Item **group_by_ref= NULL;
Item **select_ref= NULL;
ORDER *group_list= (ORDER*) select->group_list.first;
bool ambiguous_fields= FALSE;
uint counter;
bool not_used;
/*
Search for a column or derived column named as 'ref' in the SELECT
clause of the current select.
*/
if (!(select_ref= find_item_in_list(ref, *(select->get_item_list()), &counter,
REPORT_EXCEPT_NOT_FOUND, &not_used)))
return NULL; /* Some error occurred. */
/* If this is a non-aggregated field inside HAVING, search in GROUP BY. */
if (select->having_fix_field && !ref->with_sum_func && group_list)
{
group_by_ref= find_field_in_group_list(ref, group_list);
/* Check if the fields found in SELECT and GROUP BY are the same field. */
if (group_by_ref && (select_ref != not_found_item) &&
!((*group_by_ref)->eq(*select_ref, 0)))
{
ambiguous_fields= TRUE;
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR,
ER(ER_NON_UNIQ_ERROR), ref->full_name(),
current_thd->where);
}
}
if (select_ref != not_found_item || group_by_ref)
{
if (select_ref != not_found_item && !ambiguous_fields)
{
DBUG_ASSERT(*select_ref);
if (! (*select_ref)->fixed)
{
my_error(ER_ILLEGAL_REFERENCE, MYF(0), ref->name,
"forward reference in item list");
return NULL;
}
return (select->ref_pointer_array + counter);
}
else if (group_by_ref)
return group_by_ref;
else
DBUG_ASSERT(FALSE);
}
else
return (Item**) not_found_item;
}
/*
Resolve the name of a column reference.
SYNOPSIS
Item_field::fix_fields()
thd [in] current thread
tables [in] the tables in a FROM clause
reference [in/out] view column if this item was resolved to a view column
DESCRIPTION
The method resolves the column reference represented by 'this' as a column
present in one of: FROM clause, SELECT clause, GROUP BY clause of a query
Q, or in outer queries that contain Q.
NOTES
The name resolution algorithm used is (where [T_j] is an optional table
name that qualifies the column name):
resolve_column_reference([T_j].col_ref_i)
{
search for a column or derived column named col_ref_i
[in table T_j] in the FROM clause of Q;
if such a column is NOT found AND // Lookup in outer queries.
there are outer queries
{
for each outer query Q_k beginning from the inner-most one
{
if - Q_k is not a group query AND
- Q_k is not inside an aggregate function
OR
- Q_(k-1) is not in a HAVING or SELECT clause of Q_k
{
search for a column or derived column named col_ref_i
[in table T_j] in the FROM clause of Q_k;
}
if such a column is not found
Search for a column or derived column named col_ref_i
[in table T_j] in the SELECT and GROUP clauses of Q_k.
}
}
}
Notice that compared to Item_ref::fix_fields, here we first search the FROM
clause, and then we search the SELECT and GROUP BY clauses.
RETURN
TRUE if error
FALSE on success
*/
bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
{ {
DBUG_ASSERT(fixed == 0); DBUG_ASSERT(fixed == 0);
if (!field) // If field is not checked if (!field) // If field is not checked
{ {
bool upward_lookup= 0; bool upward_lookup= FALSE;
Field *tmp= (Field *)not_found_field; Field *from_field= (Field *)not_found_field;
if ((tmp= find_field_in_tables(thd, this, tables, ref, 0, if ((from_field= find_field_in_tables(thd, this, tables, reference,
IGNORE_EXCEPT_NON_UNIQUE,
!any_privileges)) == !any_privileges)) ==
not_found_field) not_found_field)
{ {
/*
We can't find table field in table 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
field 'field' it is not mistake to refer to this field without
mention of table name, but if we join tables in one list it will
cause error ER_NON_UNIQ_ERROR in find_field_in_tables.
*/
SELECT_LEX *last= 0;
#ifdef EMBEDDED_LIBRARY #ifdef EMBEDDED_LIBRARY
thd->net.last_errno= 0; thd->net.last_errno= 0;
#endif #endif
SELECT_LEX *last= 0;
TABLE_LIST *table_list; TABLE_LIST *table_list;
Item **refer= (Item **)not_found_item; Item **ref= (Item **) not_found_item;
uint counter; SELECT_LEX *current_sel= (SELECT_LEX *) thd->lex->current_select;
bool not_used; /*
// Prevent using outer fields in subselects, that is not supported now If there is an outer select, and it is not a derived table (which do
SELECT_LEX *cursel= (SELECT_LEX *) thd->lex->current_select; not support the use of outer fields for now), try to resolve this
if (cursel->master_unit()->first_select()->linkage != DERIVED_TABLE_TYPE) reference in the outer select(s).
{
SELECT_LEX_UNIT *prev_unit= cursel->master_unit(); We treat each subselect as a separate namespace, so that different
for (SELECT_LEX *sl= prev_unit->outer_select(); subselects may contain columns with the same names. The subselects are
sl; searched starting from the innermost.
sl= (prev_unit= sl->master_unit())->outer_select()) */
if (current_sel->master_unit()->first_select()->linkage !=
DERIVED_TABLE_TYPE)
{ {
upward_lookup= 1; SELECT_LEX_UNIT *prev_unit= current_sel->master_unit();
table_list= (last= sl)->get_table_list(); SELECT_LEX *outer_sel= prev_unit->outer_select();
if (sl->resolve_mode == SELECT_LEX::INSERT_MODE && table_list) for ( ; outer_sel ;
outer_sel= (prev_unit= outer_sel->master_unit())->outer_select())
{ {
last= outer_sel;
Item_subselect *prev_subselect_item= prev_unit->item;
upward_lookup= TRUE;
/* Search in the tables of the FROM clause of the outer select. */
table_list= outer_sel->get_table_list();
if (outer_sel->resolve_mode == SELECT_LEX::INSERT_MODE && table_list)
/* /*
it is primary INSERT st_select_lex => skip first table It is a primary INSERT st_select_lex => do not resolve against the
resolving first table.
*/ */
table_list= table_list->next_local; table_list= table_list->next_local;
}
Item_subselect *prev_subselect_item= prev_unit->item;
enum_parsing_place place= prev_subselect_item->parsing_place; enum_parsing_place place= prev_subselect_item->parsing_place;
/* /*
check table fields only if subquery used somewhere out of HAVING Check table fields only if the subquery is used somewhere out of
or outer SELECT do not use groupping (i.e. tables are accessable) HAVING, or the outer SELECT does not use grouping (i.e. tables are
accessible).
*/ */
if ((place != IN_HAVING || if ((place != IN_HAVING ||
(sl->with_sum_func == 0 && sl->group_list.elements == 0)) && (outer_sel->with_sum_func == 0 &&
(tmp= find_field_in_tables(thd, this, outer_sel->group_list.elements == 0)) &&
table_list, ref, (from_field= find_field_in_tables(thd, this, table_list,
0, 1)) != not_found_field) reference,
IGNORE_EXCEPT_NON_UNIQUE,
TRUE)) !=
not_found_field)
{ {
if (tmp) if (from_field)
{ {
if (tmp != view_ref_found) if (from_field != view_ref_found)
{ {
prev_subselect_item->used_tables_cache|= tmp->table->map; prev_subselect_item->used_tables_cache|= from_field->table->map;
prev_subselect_item->const_item_cache= 0; prev_subselect_item->const_item_cache= 0;
} }
else else
{ {
prev_subselect_item->used_tables_cache|= prev_subselect_item->used_tables_cache|=
(*ref)->used_tables(); (*reference)->used_tables();
prev_subselect_item->const_item_cache&= prev_subselect_item->const_item_cache&=
(*ref)->const_item(); (*reference)->const_item();
} }
} }
break; break;
} }
if (sl->resolve_mode == SELECT_LEX::SELECT_MODE &&
(refer= find_item_in_list(this, sl->item_list, &counter, /* Search in the SELECT and GROUP lists of the outer select. */
REPORT_EXCEPT_NOT_FOUND, if (outer_sel->resolve_mode == SELECT_LEX::SELECT_MODE)
&not_used)) !=
(Item **) not_found_item)
{ {
if (*refer && (*refer)->fixed) // Avoid crash in case of error if (!(ref= resolve_ref_in_select_and_group(thd, this, outer_sel)))
return TRUE; /* Some error occured (e.g. ambigous names). */
if (ref != not_found_item)
{ {
prev_subselect_item->used_tables_cache|= (*refer)->used_tables(); DBUG_ASSERT(*ref && (*ref)->fixed);
prev_subselect_item->const_item_cache&= (*refer)->const_item(); prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
} prev_subselect_item->const_item_cache&= (*ref)->const_item();
break; break;
} }
}
// Reference is not found => depend from outer (or just error) // Reference is not found => depend from outer (or just error)
prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT;
prev_subselect_item->const_item_cache= 0; prev_subselect_item->const_item_cache= 0;
if (sl->master_unit()->first_select()->linkage == if (outer_sel->master_unit()->first_select()->linkage ==
DERIVED_TABLE_TYPE) DERIVED_TABLE_TYPE)
break; // do not look over derived table break; // do not look over derived table
} }
} }
if (!tmp)
return -1; DBUG_ASSERT(ref);
if (!refer) if (!from_field)
return 1; return TRUE;
if (tmp == not_found_field && refer == (Item **)not_found_item) if (ref == not_found_item && from_field == not_found_field)
{ {
if (upward_lookup) if (upward_lookup)
{ {
// We can't say exactly what absend table or field // We can't say exactly what absent table or field
my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0), my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0),
full_name(), thd->where); full_name(), thd->where);
} }
else else
{ {
// Call to report error // Call to report error
find_field_in_tables(thd, this, tables, ref, 1, 1); find_field_in_tables(thd, this, tables, reference, REPORT_ALL_ERRORS,
TRUE);
} }
return -1; return TRUE;
} }
else if (refer != (Item **)not_found_item) else if (ref != not_found_item)
{
if (!(*refer)->fixed)
{ {
my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, /* Should have been checked in resolve_ref_in_select_and_group(). */
"forward reference in item list"); DBUG_ASSERT(*ref && (*ref)->fixed);
return -1;
}
Item_ref *rf= new Item_ref(last->ref_pointer_array + counter, Item_ref *rf= new Item_ref(last->ref_pointer_array + counter,
(char *)table_name, (char *)field_name); (char *)table_name, (char *)field_name);
if (!rf) if (!rf)
return 1; return TRUE;
thd->change_item_tree(ref, rf); thd->change_item_tree(reference, rf);
/* /*
rf is Item_ref => never substitute other items (in this case) rf is Item_ref => never substitute other items (in this case)
during fix_fields() => we can use rf after fix_fields() during fix_fields() => we can use rf after fix_fields()
*/ */
if (rf->fix_fields(thd, tables, ref) || rf->check_cols(1)) if (rf->fix_fields(thd, tables, reference) || rf->check_cols(1))
return 1; return TRUE;
mark_as_dependent(thd, last, cursel, rf); mark_as_dependent(thd, last, current_sel, rf);
return 0; return FALSE;
} }
else else
{ {
mark_as_dependent(thd, last, cursel, this); mark_as_dependent(thd, last, current_sel, this);
if (last->having_fix_field) if (last->having_fix_field)
{ {
Item_ref *rf; Item_ref *rf;
rf= new Item_ref((cached_table->db[0] ? cached_table->db : 0), rf= new Item_ref((cached_table->db[0] ? cached_table->db : 0),
(char*) cached_table->alias, (char*) field_name); (char*) cached_table->alias, (char*) field_name);
if (!rf) if (!rf)
return 1; return TRUE;
thd->change_item_tree(ref, rf); thd->change_item_tree(reference, rf);
/* /*
rf is Item_ref => never substitute other items (in this case) rf is Item_ref => never substitute other items (in this case)
during fix_fields() => we can use rf after fix_fields() during fix_fields() => we can use rf after fix_fields()
*/ */
return rf->fix_fields(thd, tables, ref) || rf->check_cols(1); return rf->fix_fields(thd, tables, reference) || rf->check_cols(1);
} }
} }
} }
else if (!tmp) else if (!from_field)
return -1; return TRUE;
/* /*
if it is not expression from merged VIEW we will set this field. if it is not expression from merged VIEW we will set this field.
...@@ -1709,8 +1960,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) ...@@ -1709,8 +1960,8 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
Also we suppose that view can't be changed during PS/SP life. Also we suppose that view can't be changed during PS/SP life.
*/ */
if (tmp != view_ref_found) if (from_field != view_ref_found)
set_field(tmp); set_field(from_field);
} }
else if (thd->set_query_id && field->query_id != thd->query_id) else if (thd->set_query_id && field->query_id != thd->query_id)
{ {
...@@ -1746,14 +1997,15 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) ...@@ -1746,14 +1997,15 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
thd->host_or_ip, thd->host_or_ip,
field_name, field_name,
tab); tab);
return 1; return TRUE;
} }
} }
#endif #endif
fixed= 1; fixed= 1;
return 0; return FALSE;
} }
Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs) Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs)
{ {
no_const_subst= 1; no_const_subst= 1;
...@@ -2428,89 +2680,147 @@ bool Item_field::send(Protocol *protocol, String *buffer) ...@@ -2428,89 +2680,147 @@ bool Item_field::send(Protocol *protocol, String *buffer)
/* /*
This is used for HAVING clause Resolve the name of a reference to a column reference.
Find field in select list having the same name
SYNOPSIS
Item_ref::fix_fields()
thd [in] current thread
tables [in] the tables in a FROM clause
reference [in/out] view column if this item was resolved to a view column
DESCRIPTION
The method resolves the column reference represented by 'this' as a column
present in one of: GROUP BY clause, SELECT clause, outer queries. It is
used typically for columns in the HAVING clause which are not under
aggregate functions.
NOTES
The name resolution algorithm used is (where [T_j] is an optional table
name that qualifies the column name):
resolve_extended([T_j].col_ref_i)
{
Search for a column or derived column named col_ref_i [in table T_j]
in the SELECT and GROUP clauses of Q.
if such a column is NOT found AND // Lookup in outer queries.
there are outer queries
{
for each outer query Q_k beginning from the inner-most one
{
Search for a column or derived column named col_ref_i
[in table T_j] in the SELECT and GROUP clauses of Q_k.
if such a column is not found AND
- Q_k is not a group query AND
- Q_k is not inside an aggregate function
OR
- Q_(k-1) is not in a HAVING or SELECT clause of Q_k
{
search for a column or derived column named col_ref_i
[in table T_j] in the FROM clause of Q_k;
}
}
}
}
This procedure treats GROUP BY and SELECT clauses as one namespace for
column references in HAVING. Notice that compared to
Item_field::fix_fields, here we first search the SELECT and GROUP BY
clauses, and then we search the FROM clause.
RETURN
TRUE if error
FALSE on success
*/ */
bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
{ {
DBUG_ASSERT(fixed == 0); DBUG_ASSERT(fixed == 0);
uint counter; SELECT_LEX *current_sel= thd->lex->current_select;
bool not_used;
if (!ref) if (!ref)
{ {
TABLE_LIST *table_list; SELECT_LEX_UNIT *prev_unit= current_sel->master_unit();
bool upward_lookup= 0; SELECT_LEX *outer_sel= prev_unit->outer_select();
SELECT_LEX_UNIT *prev_unit= thd->lex->current_select->master_unit(); ORDER *group_list= (ORDER*) current_sel->group_list.first;
SELECT_LEX *sl= prev_unit->outer_select(); bool ambiguous_fields= FALSE;
Item **group_by_ref= NULL;
if (!(ref= resolve_ref_in_select_and_group(thd, this, current_sel)))
return TRUE; /* Some error occured (e.g. ambigous names). */
if (ref == not_found_item) /* This reference was not resolved. */
{
/* /*
Finding only in current select will be performed for selects that have If there is an outer select, and it is not a derived table (which do
not outer one and for derived tables (which not support using outer not support the use of outer fields for now), try to resolve this
fields for now) reference in the outer select(s).
We treat each subselect as a separate namespace, so that different
subselects may contain columns with the same names. The subselects are
searched starting from the innermost.
*/ */
if ((ref= find_item_in_list(this, if (outer_sel && (current_sel->master_unit()->first_select()->linkage !=
*(thd->lex->current_select->get_item_list()), DERIVED_TABLE_TYPE))
&counter, {
((sl && TABLE_LIST *table_list;
thd->lex->current_select->master_unit()-> Field *from_field= (Field*) not_found_field;
first_select()->linkage !=
DERIVED_TABLE_TYPE) ?
REPORT_EXCEPT_NOT_FOUND :
REPORT_ALL_ERRORS), &not_used)) ==
(Item **)not_found_item)
{
Field *tmp= (Field*) not_found_field;
SELECT_LEX *last= 0; SELECT_LEX *last= 0;
upward_lookup= 1;
/* for ( ; outer_sel ;
We can't find table field in select list of current select, outer_sel= (prev_unit= outer_sel->master_unit())->outer_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
field 'field' it is not mistake to refer to this field without
mention of table name, but if we join tables in one list it will
cause error ER_NON_UNIQ_ERROR in find_item_in_list.
*/
for ( ; sl ; sl= (prev_unit= sl->master_unit())->outer_select())
{ {
last= sl; last= outer_sel;
Item_subselect *prev_subselect_item= prev_unit->item; 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, /* Search in the SELECT and GROUP lists of the outer select. */
&counter, REPORT_EXCEPT_NOT_FOUND, if (outer_sel->resolve_mode == SELECT_LEX::SELECT_MODE)
&not_used)) !=
(Item **)not_found_item)
{ {
if (*ref && (*ref)->fixed) // Avoid crash in case of error if (!(ref= resolve_ref_in_select_and_group(thd, this, outer_sel)))
return TRUE; /* Some error occured (e.g. ambigous names). */
if (ref != not_found_item)
{ {
DBUG_ASSERT(*ref && (*ref)->fixed);
prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
prev_subselect_item->const_item_cache&= (*ref)->const_item(); prev_subselect_item->const_item_cache&= (*ref)->const_item();
}
break; break;
} }
table_list= sl->get_table_list();
if (sl->resolve_mode == SELECT_LEX::INSERT_MODE && table_list)
{
// it is primary INSERT st_select_lex => skip first table resolving
table_list= table_list->next_local;
} }
/* Search in the tables of the FROM clause of the outer select. */
table_list= outer_sel->get_table_list();
if (outer_sel->resolve_mode == SELECT_LEX::INSERT_MODE && table_list)
/*
It is a primary INSERT st_select_lex => do not resolve against the
first table.
*/
table_list= table_list->next_local;
enum_parsing_place place= prev_subselect_item->parsing_place; enum_parsing_place place= prev_subselect_item->parsing_place;
/* /*
Check table fields only if subquery used somewhere out of HAVING Check table fields only if the subquery is used somewhere out of
or SELECT list or outer SELECT do not use groupping (i.e. tables HAVING or the outer SELECT does not use grouping (i.e. tables are
are accessable) accessible).
TODO:
Here we could first find the field anyway, and then test this
condition, so that we can give a better error message -
ER_WRONG_FIELD_WITH_GROUP, instead of the less informative
ER_BAD_FIELD_ERROR which we produce now.
*/ */
if ((place != IN_HAVING || if ((place != IN_HAVING ||
(sl->with_sum_func == 0 && sl->group_list.elements == 0)) && (!outer_sel->with_sum_func &&
(tmp= find_field_in_tables(thd, this, outer_sel->group_list.elements == 0)))
table_list, reference,
0, 1)) != not_found_field)
{ {
if (tmp) if ((from_field= find_field_in_tables(thd, this, table_list,
reference,
IGNORE_EXCEPT_NON_UNIQUE,
TRUE)) !=
not_found_field)
{ {
if (tmp != view_ref_found) if (from_field != view_ref_found)
{ {
prev_subselect_item->used_tables_cache|= tmp->table->map; prev_subselect_item->used_tables_cache|= from_field->table->map;
prev_subselect_item->const_item_cache= 0; prev_subselect_item->const_item_cache= 0;
} }
else else
...@@ -2520,115 +2830,90 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) ...@@ -2520,115 +2830,90 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
prev_subselect_item->const_item_cache&= prev_subselect_item->const_item_cache&=
(*reference)->const_item(); (*reference)->const_item();
} }
}
break; break;
} }
}
// Reference is not found => depend from outer (or just error) /* Reference is not found => depend on outer (or just error). */
prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT;
prev_subselect_item->const_item_cache= 0; prev_subselect_item->const_item_cache= 0;
if (sl->master_unit()->first_select()->linkage == if (outer_sel->master_unit()->first_select()->linkage ==
DERIVED_TABLE_TYPE) DERIVED_TABLE_TYPE)
break; // do not look over derived table break; /* Do not consider derived tables. */
} }
if (!ref) DBUG_ASSERT(ref);
return 1; if (!from_field)
if (!tmp) return TRUE;
return -1; if (ref == not_found_item && from_field == not_found_field)
if (ref == (Item **)not_found_item && tmp == not_found_field)
{
if (upward_lookup)
{ {
// We can't say exactly what absend (table or field)
my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0), my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0),
full_name(), thd->where); this->full_name(), current_thd->where);
}
else
{
// Call to report error
find_item_in_list(this,
*(thd->lex->current_select->get_item_list()),
&counter, REPORT_ALL_ERRORS, &not_used);
}
ref= 0; // Safety ref= 0; // Safety
return 1; return TRUE;
} }
if (tmp != not_found_field) if (from_field != not_found_field)
{ {
/* /*
Set ref to 0 as we are replacing this item with the found item Set ref to 0 as we are replacing this item with the found item and
and this will ensure we get an error if this item would be this will ensure we get an error if this item would be used
used elsewhere elsewhere
*/ */
ref= 0; // Safety ref= 0; // Safety
if (tmp != view_ref_found) if (from_field != view_ref_found)
{ {
Item_field* fld; Item_field* fld;
if (!(fld= new Item_field(tmp))) if (!(fld= new Item_field(tmp)))
return 1; return TRUE;
thd->change_item_tree(reference, fld); thd->change_item_tree(reference, fld);
mark_as_dependent(thd, last, thd->lex->current_select, fld); mark_as_dependent(thd, last, thd->lex->current_select, fld);
return 0; return FALSE;
} }
/* /*
We can leave expression substituted from view for next PS/SP We can leave expression substituted from view for next PS/SP
rexecution (i.e. do not register this substitution for reverting re-execution (i.e. do not register this substitution for reverting
on cleupup() (register_item_tree_changing())), because this on cleanup() (register_item_tree_changing())), because this subtree
subtree will be fix_field'ed during will be fix_field'ed during setup_tables()->setup_ancestor()
setup_tables()->setup_ancestor() (i.e. before all other (i.e. before all other expressions of query, and references on
expressions of query, and references on tables which do not tables which do not present in query will not make problems.
present in query will not make problems.
Also we suppose that view can't be changed during PS/SP life. Also we suppose that view can't be changed during PS/SP life.
*/ */
} }
else else
{ {
if (!(*ref)->fixed) /* Should be checked in resolve_ref_in_select_and_group(). */
{ DBUG_ASSERT(*ref && (*ref)->fixed);
my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, mark_as_dependent(thd, last, current_sel, this);
"forward reference in item list");
return -1;
}
mark_as_dependent(thd, last, thd->lex->current_select,
this);
ref= last->ref_pointer_array + counter;
} }
} }
else if (!ref)
return 1;
else else
{ {
if (!(*ref)->fixed) /* The current reference cannot be resolved in this query. */
{ my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0),
my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, this->full_name(), current_thd->where);
"forward reference in item list"); return TRUE;
return -1;
} }
ref= thd->lex->current_select->ref_pointer_array + counter;
} }
} }
/* /*
The following conditional is changed as to correctly identify Check if this is an incorrect reference in a group function or forward
incorrect references in group functions or forward references reference. Do not issue an error if this is an unnamed reference inside an
with sub-select's / derived tables, while it prevents this aggregate function.
check when Item_ref is created in an expression involving
summing function, which is to be placed in the user variable.
*/ */
if (((*ref)->with_sum_func && name && if (((*ref)->with_sum_func && name &&
(depended_from || (depended_from ||
!(thd->lex->current_select->linkage != GLOBAL_OPTIONS_TYPE && !(current_sel->linkage != GLOBAL_OPTIONS_TYPE &&
thd->lex->current_select->having_fix_field))) || current_sel->having_fix_field))) ||
!(*ref)->fixed) !(*ref)->fixed)
{ {
my_error(ER_ILLEGAL_REFERENCE, MYF(0), name, my_error(ER_ILLEGAL_REFERENCE, MYF(0), name,
((*ref)->with_sum_func? ((*ref)->with_sum_func?
"reference on group function": "reference to group function":
"forward reference in item list")); "forward reference in item list"));
return 1; return TRUE;
} }
max_length= (*ref)->max_length; max_length= (*ref)->max_length;
maybe_null= (*ref)->maybe_null; maybe_null= (*ref)->maybe_null;
...@@ -2638,8 +2923,8 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) ...@@ -2638,8 +2923,8 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
fixed= 1; fixed= 1;
if (ref && (*ref)->check_cols(1)) if (ref && (*ref)->check_cols(1))
return 1; return TRUE;
return 0; return FALSE;
} }
......
...@@ -651,8 +651,13 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var, ...@@ -651,8 +651,13 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var,
rw_lock_t *var_mutex); rw_lock_t *var_mutex);
extern const Field *not_found_field; extern const Field *not_found_field;
extern const Field *view_ref_found; extern const Field *view_ref_found;
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, Field *find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
Item **ref, bool report_error, Item **ref,
find_item_error_report_type report_error,
bool check_privileges); bool check_privileges);
Field * Field *
find_field_in_table(THD *thd, TABLE_LIST *table_list, find_field_in_table(THD *thd, TABLE_LIST *table_list,
...@@ -771,8 +776,6 @@ TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find); ...@@ -771,8 +776,6 @@ TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find);
SQL_SELECT *make_select(TABLE *head, table_map const_tables, SQL_SELECT *make_select(TABLE *head, table_map const_tables,
table_map read_tables, COND *conds, int *error, table_map read_tables, COND *conds, int *error,
bool allow_null_cond= false); bool allow_null_cond= false);
enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
IGNORE_ERRORS};
extern const Item **not_found_item; extern const Item **not_found_item;
Item ** find_item_in_list(Item *item, List<Item> &items, uint *counter, 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,
......
...@@ -615,7 +615,7 @@ db_show_routine_status(THD *thd, int type, const char *wild) ...@@ -615,7 +615,7 @@ db_show_routine_status(THD *thd, int type, const char *wild)
Item_field *field= new Item_field("mysql", "proc", Item_field *field= new Item_field("mysql", "proc",
used_field->field_name); used_field->field_name);
if (!(used_field->field= find_field_in_tables(thd, field, &tables, if (!(used_field->field= find_field_in_tables(thd, field, &tables,
0, TRUE, 1))) 0, REPORT_ALL_ERRORS, 1)))
{ {
res= SP_INTERNAL_ERROR; res= SP_INTERNAL_ERROR;
goto err_case1; goto err_case1;
......
...@@ -2225,25 +2225,32 @@ Field *find_field_in_real_table(THD *thd, TABLE *table, ...@@ -2225,25 +2225,32 @@ Field *find_field_in_real_table(THD *thd, TABLE *table,
find_field_in_tables() find_field_in_tables()
thd Pointer to current thread structure thd Pointer to current thread structure
item Field item that should be found item Field item that should be found
tables Tables for scanning tables Tables to be searched for item
ref if view field is found, pointer to view item will ref If 'item' is resolved to a view field, ref is set to
be returned via this parameter point to the found view field
report_error If FALSE then do not report error if item not found report_error Degree of error reporting:
and return not_found_field - IGNORE_ERRORS then do not report any error
- IGNORE_EXCEPT_NON_UNIQUE report only non-unique
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 check_privileges need to check privileges
RETURN VALUES RETURN VALUES
0 Field is not found or field is not unique- error 0 If error: the found field is not unique, or there are
message is reported no sufficient access priviliges for the found field,
not_found_field Function was called with report_error == FALSE and or the field is qualified with non-existing table.
field was not found. no error message reported. not_found_field The function was called with report_error ==
view_ref_found view field is found, item passed through ref parameter (IGNORE_ERRORS || IGNORE_EXCEPT_NON_UNIQUE) and a
found field field was not found.
view_ref_found View field is found, item passed through ref parameter
found field If a item was resolved to some field
*/ */
Field * Field *
find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
Item **ref, bool report_error, Item **ref, find_item_error_report_type report_error,
bool check_privileges) bool check_privileges)
{ {
Field *found=0; Field *found=0;
...@@ -2261,8 +2268,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, ...@@ -2261,8 +2268,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
TABLE_LIST *tables is not changed during query execution (which TABLE_LIST *tables is not changed during query execution (which
is true for all queries except RENAME but luckily RENAME doesn't is true for all queries except RENAME but luckily RENAME doesn't
use fields...) so we can rely on reusing pointer to its member. use fields...) so we can rely on reusing pointer to its member.
With this optimisation we also miss case when addition of one more With this optimization we also miss case when addition of one more
field makes some prepared query ambiguous and so erronous, but we field makes some prepared query ambiguous and so erroneous, but we
accept this trade off. accept this trade off.
*/ */
found= find_field_in_real_table(thd, item->cached_table->table, found= find_field_in_real_table(thd, item->cached_table->table,
...@@ -2283,7 +2290,7 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, ...@@ -2283,7 +2290,7 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
if (db && lower_case_table_names) if (db && lower_case_table_names)
{ {
/* /*
convert database to lower case for comparision. convert database to lower case for comparison.
We can't do this in Item_field as this would change the We can't do this in Item_field as this would change the
'name' of the item which may be used in the select list 'name' of the item which may be used in the select list
*/ */
...@@ -2320,6 +2327,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, ...@@ -2320,6 +2327,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
return find; return find;
if (found) if (found)
{ {
if (report_error == REPORT_ALL_ERRORS ||
report_error == IGNORE_EXCEPT_NON_UNIQUE)
my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0), my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0),
item->full_name(),thd->where); item->full_name(),thd->where);
return (Field*) 0; return (Field*) 0;
...@@ -2330,7 +2339,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, ...@@ -2330,7 +2339,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
} }
if (found) if (found)
return found; return found;
if (!found_table && report_error) if (!found_table && (report_error == REPORT_ALL_ERRORS ||
report_error == REPORT_EXCEPT_NON_UNIQUE))
{ {
char buff[NAME_LEN*2+1]; char buff[NAME_LEN*2+1];
if (db && db[0]) if (db && db[0])
...@@ -2338,28 +2348,30 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, ...@@ -2338,28 +2348,30 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
strxnmov(buff,sizeof(buff)-1,db,".",table_name,NullS); strxnmov(buff,sizeof(buff)-1,db,".",table_name,NullS);
table_name=buff; table_name=buff;
} }
if (report_error) if (report_error == REPORT_ALL_ERRORS ||
{ report_error == REPORT_EXCEPT_NON_UNIQUE)
my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0), my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0),
table_name, thd->where); table_name, thd->where);
}
else else
return (Field*) not_found_field; return (Field*) not_found_field;
} }
else else
if (report_error) if (report_error == REPORT_ALL_ERRORS ||
report_error == REPORT_EXCEPT_NON_UNIQUE)
my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0), my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0),
item->full_name(),thd->where); item->full_name(),thd->where);
else else
return (Field*) not_found_field; return (Field*) not_found_field;
return (Field*) 0; return (Field*) 0;
} }
bool allow_rowid= tables && !tables->next_local; // Only one table bool allow_rowid= tables && !tables->next_local; // Only one table
for (; tables ; tables= tables->next_local) for (; tables ; tables= tables->next_local)
{ {
if (!tables->table) if (!tables->table)
{ {
if (report_error) if (report_error == REPORT_ALL_ERRORS ||
report_error == REPORT_EXCEPT_NON_UNIQUE)
my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0), my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0),
item->full_name(),thd->where); item->full_name(),thd->where);
return (Field*) not_found_field; return (Field*) not_found_field;
...@@ -2384,6 +2396,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, ...@@ -2384,6 +2396,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
{ {
if (!thd->where) // Returns first found if (!thd->where) // Returns first found
break; break;
if (report_error == REPORT_ALL_ERRORS ||
report_error == IGNORE_EXCEPT_NON_UNIQUE)
my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0), my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0),
name,thd->where); name,thd->where);
return (Field*) 0; return (Field*) 0;
...@@ -2393,7 +2407,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, ...@@ -2393,7 +2407,8 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
} }
if (found) if (found)
return found; return found;
if (report_error) if (report_error == REPORT_ALL_ERRORS ||
report_error == REPORT_EXCEPT_NON_UNIQUE)
my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR),
MYF(0), item->full_name(), thd->where); MYF(0), item->full_name(), thd->where);
else else
...@@ -2432,7 +2447,7 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables, ...@@ -2432,7 +2447,7 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
found field found field
*/ */
// Special Item pointer for find_item_in_list returning /* Special Item pointer to serve as a return value from find_item_in_list(). */
const Item **not_found_item= (const Item**) 0x1; const Item **not_found_item= (const Item**) 0x1;
...@@ -2830,7 +2845,8 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, ...@@ -2830,7 +2845,8 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table,
any_privileges 0 If we should ensure that we have SELECT privileges any_privileges 0 If we should ensure that we have SELECT privileges
for all columns for all columns
1 If any privilege is ok 1 If any privilege is ok
allocate_view_names if true view names will be copied to current Item_arena memory (made for SP/PS) allocate_view_names if true view names will be copied to current Item_arena
memory (made for SP/PS)
RETURN RETURN
0 ok 0 ok
'it' is updated to point at last inserted 'it' is updated to point at last inserted
......
...@@ -88,7 +88,7 @@ static bool init_fields(THD *thd, TABLE_LIST *tables, ...@@ -88,7 +88,7 @@ static bool init_fields(THD *thd, TABLE_LIST *tables,
Item_field *field= new Item_field("mysql", find_fields->table_name, Item_field *field= new Item_field("mysql", find_fields->table_name,
find_fields->field_name); find_fields->field_name);
if (!(find_fields->field= find_field_in_tables(thd, field, tables, if (!(find_fields->field= find_field_in_tables(thd, field, tables,
0, TRUE, 1))) 0, REPORT_ALL_ERRORS, 1)))
DBUG_RETURN(1); DBUG_RETURN(1);
} }
DBUG_RETURN(0); DBUG_RETURN(0);
......
...@@ -11168,24 +11168,53 @@ cp_buffer_from_ref(TABLE_REF *ref) ...@@ -11168,24 +11168,53 @@ cp_buffer_from_ref(TABLE_REF *ref)
*****************************************************************************/ *****************************************************************************/
/* /*
Find order/group item in requested columns and change the item to point at Resolve an ORDER BY or GROUP BY column reference.
it. If item doesn't exists, add it first in the field list
Return 0 if ok. SYNOPSIS
find_order_in_list()
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
BY clause, find the actual column it represents. If the column being
resolved is from the GROUP BY clause, the procedure searches the SELECT
list 'fields' and the columns in the FROM list 'tables'. If 'order' is from
the ORDER BY clause, only the SELECT list is being searched.
If 'order' is resolved to an Item, then order->item is set to the found
Item. If there is no item for the found column (that is, it was resolved
into a table field), order->item is 'fixed' and is added to all_fields and
ref_pointer_array.
RETURN
0 if OK
1 if error occurred
*/ */
static int static int
find_order_in_list(THD *thd, Item **ref_pointer_array, find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
TABLE_LIST *tables,ORDER *order, List<Item> &fields, ORDER *order, List<Item> &fields, List<Item> &all_fields,
List<Item> &all_fields) bool is_group_field)
{ {
Item *it= *order->item; Item *order_item= *order->item; /* The item from the GROUP/ORDER caluse. */
if (it->type() == Item::INT_ITEM) Item::Type order_item_type;
Item **select_item; /* The corresponding item from the SELECT clause. */
Field *from_field; /* The corresponding field from the FROM clause. */
if (order_item->type() == Item::INT_ITEM)
{ /* Order by position */ { /* Order by position */
uint count= (uint) it->val_int(); uint count= (uint) order_item->val_int();
if (!count || count > fields.elements) if (!count || count > fields.elements)
{ {
my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR), my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),
MYF(0), it->full_name(), thd->where); MYF(0), order_item->full_name(), thd->where);
return 1; return 1;
} }
order->item= ref_pointer_array + count - 1; order->item= ref_pointer_array + count - 1;
...@@ -11194,38 +11223,68 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, ...@@ -11194,38 +11223,68 @@ find_order_in_list(THD *thd, Item **ref_pointer_array,
order->counter_used= 1; order->counter_used= 1;
return 0; return 0;
} }
/* Lookup the current GROUP/ORDER field in the SELECT clause. */
uint counter; uint counter;
bool unaliased; bool unaliased;
Item **item= find_item_in_list(it, fields, &counter, select_item= find_item_in_list(order_item, fields, &counter,
REPORT_EXCEPT_NOT_FOUND, &unaliased); REPORT_EXCEPT_NOT_FOUND, &unaliased);
if (!item) if (!select_item)
return 1; return 1; /* Some error occured. */
if (item != (Item **)not_found_item)
/* Check whether the resolved field is not ambiguos. */
if (select_item != not_found_item)
{ {
/* /*
If we have found field not by its alias in select list but by its If we have found field not by its alias in select list but by its
original field name, we should additionaly check if we have conflict original field name, we should additionaly check if we have conflict
for this name (in case if we would perform lookup in all tables). for this name (in case if we would perform lookup in all tables).
*/ */
if (unaliased && !it->fixed && it->fix_fields(thd, tables, order->item)) if (unaliased && !order_item->fixed && order_item->fix_fields(thd, tables, order->item))
return 1; return 1;
/* Lookup the current GROUP field in the FROM clause. */
order_item_type= order_item->type();
if (is_group_field &&
order_item_type == Item::FIELD_ITEM || order_item_type == Item::REF_ITEM)
{
Item **view_ref= NULL;
from_field= find_field_in_tables(thd, (Item_ident*) order_item, tables,
view_ref, IGNORE_ERRORS, TRUE);
if(!from_field)
from_field= (Field*) not_found_field;
}
else
from_field= (Field*) not_found_field;
if (from_field == not_found_field ||
from_field && from_field != view_ref_found &&
(*select_item)->type() == Item::FIELD_ITEM &&
((Item_field*) (*select_item))->field->eq(from_field))
/*
If there is no such field in the FROM clause, or it is the same field as
the one found in the SELECT clause, then use the Item created for the
SELECT field. As a result if there was a derived field that 'shadowed'
a table field with the same name, the table field will be chosen over
the derived field.
*/
{
order->item= ref_pointer_array + counter; order->item= ref_pointer_array + counter;
order->in_field_list=1; order->in_field_list=1;
return 0; return 0;
} }
}
order->in_field_list=0; order->in_field_list=0;
/* /*
We check it->fixed because Item_func_group_concat can put We check order_item->fixed because Item_func_group_concat can put
arguments for which fix_fields already was called. arguments for which fix_fields already was called.
'it' reassigned in if condition because fix_field can change it. 'it' reassigned in if condition because fix_field can change it.
*/ */
if (!it->fixed && if (!order_item->fixed &&
(it->fix_fields(thd, tables, order->item) || (order_item->fix_fields(thd, tables, order->item) ||
(it= *order->item)->check_cols(1) || (order_item= *order->item)->check_cols(1) ||
thd->is_fatal_error)) thd->is_fatal_error))
return 1; // Wrong field return 1; // Wrong field
uint el= all_fields.elements; uint el= all_fields.elements;
...@@ -11235,6 +11294,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, ...@@ -11235,6 +11294,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array,
return 0; return 0;
} }
/* /*
Change order to point at item in select list. If item isn't a number Change order to point at item in select list. If item isn't a number
and doesn't exits in the select list, add it the the field list. and doesn't exits in the select list, add it the the field list.
...@@ -11247,7 +11307,7 @@ int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, ...@@ -11247,7 +11307,7 @@ int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
for (; order; order=order->next) for (; order; order=order->next)
{ {
if (find_order_in_list(thd, ref_pointer_array, tables, order, fields, if (find_order_in_list(thd, ref_pointer_array, tables, order, fields,
all_fields)) all_fields, FALSE))
return 1; return 1;
} }
return 0; return 0;
...@@ -11299,7 +11359,7 @@ setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, ...@@ -11299,7 +11359,7 @@ setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
for (; order; order=order->next) for (; order; order=order->next)
{ {
if (find_order_in_list(thd, ref_pointer_array, tables, order, fields, if (find_order_in_list(thd, ref_pointer_array, tables, order, fields,
all_fields)) all_fields, TRUE))
return 1; return 1;
(*order->item)->marker=1; /* Mark found */ (*order->item)->marker=1; /* Mark found */
if ((*order->item)->with_sum_func) if ((*order->item)->with_sum_func)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment