Merge of WL#1972 "Evaluate HAVING before SELECT select-list"
This commit is contained in:
commit
e61b4454da
@ -1,4 +1,4 @@
|
|||||||
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
|
|||||||
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 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
--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
|
|||||||
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;
|
||||||
|
793
sql/item.cc
793
sql/item.cc
@ -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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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, ¬_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).
|
||||||
|
|
||||||
|
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 (current_sel->master_unit()->first_select()->linkage !=
|
||||||
|
DERIVED_TABLE_TYPE)
|
||||||
{
|
{
|
||||||
SELECT_LEX_UNIT *prev_unit= cursel->master_unit();
|
SELECT_LEX_UNIT *prev_unit= current_sel->master_unit();
|
||||||
for (SELECT_LEX *sl= prev_unit->outer_select();
|
SELECT_LEX *outer_sel= prev_unit->outer_select();
|
||||||
sl;
|
for ( ; outer_sel ;
|
||||||
sl= (prev_unit= sl->master_unit())->outer_select())
|
outer_sel= (prev_unit= outer_sel->master_unit())->outer_select())
|
||||||
{
|
{
|
||||||
upward_lookup= 1;
|
last= outer_sel;
|
||||||
table_list= (last= sl)->get_table_list();
|
Item_subselect *prev_subselect_item= prev_unit->item;
|
||||||
if (sl->resolve_mode == SELECT_LEX::INSERT_MODE && table_list)
|
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)
|
||||||
¬_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). */
|
||||||
prev_subselect_item->used_tables_cache|= (*refer)->used_tables();
|
if (ref != not_found_item)
|
||||||
prev_subselect_item->const_item_cache&= (*refer)->const_item();
|
{
|
||||||
}
|
DBUG_ASSERT(*ref && (*ref)->fixed);
|
||||||
break;
|
prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
|
||||||
|
prev_subselect_item->const_item_cache&= (*ref)->const_item();
|
||||||
|
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)
|
/* Should have been checked in resolve_ref_in_select_and_group(). */
|
||||||
{
|
DBUG_ASSERT(*ref && (*ref)->fixed);
|
||||||
my_error(ER_ILLEGAL_REFERENCE, MYF(0), name,
|
|
||||||
"forward reference in item list");
|
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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,207 +2680,240 @@ 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;
|
||||||
Finding only in current select will be performed for selects that have
|
|
||||||
not outer one and for derived tables (which not support using outer
|
if (!(ref= resolve_ref_in_select_and_group(thd, this, current_sel)))
|
||||||
fields for now)
|
return TRUE; /* Some error occured (e.g. ambigous names). */
|
||||||
*/
|
|
||||||
if ((ref= find_item_in_list(this,
|
if (ref == not_found_item) /* This reference was not resolved. */
|
||||||
*(thd->lex->current_select->get_item_list()),
|
|
||||||
&counter,
|
|
||||||
((sl &&
|
|
||||||
thd->lex->current_select->master_unit()->
|
|
||||||
first_select()->linkage !=
|
|
||||||
DERIVED_TABLE_TYPE) ?
|
|
||||||
REPORT_EXCEPT_NOT_FOUND :
|
|
||||||
REPORT_ALL_ERRORS), ¬_used)) ==
|
|
||||||
(Item **)not_found_item)
|
|
||||||
{
|
{
|
||||||
Field *tmp= (Field*) not_found_field;
|
|
||||||
SELECT_LEX *last= 0;
|
|
||||||
upward_lookup= 1;
|
|
||||||
/*
|
/*
|
||||||
We can't find table field in select list of current select,
|
If there is an outer select, and it is not a derived table (which do
|
||||||
consequently we have to find it in outer subselect(s).
|
not support the use of outer fields for now), try to resolve this
|
||||||
We can't join lists of outer & current select, because of scope
|
reference in the outer select(s).
|
||||||
of view rules. For example if both tables (outer & current) have
|
|
||||||
field 'field' it is not mistake to refer to this field without
|
We treat each subselect as a separate namespace, so that different
|
||||||
mention of table name, but if we join tables in one list it will
|
subselects may contain columns with the same names. The subselects are
|
||||||
cause error ER_NON_UNIQ_ERROR in find_item_in_list.
|
searched starting from the innermost.
|
||||||
*/
|
*/
|
||||||
for ( ; sl ; sl= (prev_unit= sl->master_unit())->outer_select())
|
if (outer_sel && (current_sel->master_unit()->first_select()->linkage !=
|
||||||
|
DERIVED_TABLE_TYPE))
|
||||||
{
|
{
|
||||||
last= sl;
|
TABLE_LIST *table_list;
|
||||||
Item_subselect *prev_subselect_item= prev_unit->item;
|
Field *from_field= (Field*) not_found_field;
|
||||||
if (sl->resolve_mode == SELECT_LEX::SELECT_MODE &&
|
SELECT_LEX *last= 0;
|
||||||
(ref= find_item_in_list(this, sl->item_list,
|
|
||||||
&counter, REPORT_EXCEPT_NOT_FOUND,
|
for ( ; outer_sel ;
|
||||||
¬_used)) !=
|
outer_sel= (prev_unit= outer_sel->master_unit())->outer_select())
|
||||||
(Item **)not_found_item)
|
{
|
||||||
{
|
last= outer_sel;
|
||||||
if (*ref && (*ref)->fixed) // Avoid crash in case of error
|
Item_subselect *prev_subselect_item= prev_unit->item;
|
||||||
{
|
|
||||||
prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
|
/* Search in the SELECT and GROUP lists of the outer select. */
|
||||||
prev_subselect_item->const_item_cache&= (*ref)->const_item();
|
if (outer_sel->resolve_mode == SELECT_LEX::SELECT_MODE)
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
enum_parsing_place place= prev_subselect_item->parsing_place;
|
|
||||||
/*
|
|
||||||
Check table fields only if subquery used somewhere out of HAVING
|
|
||||||
or SELECT list or outer SELECT do not use groupping (i.e. tables
|
|
||||||
are accessable)
|
|
||||||
*/
|
|
||||||
if ((place != IN_HAVING ||
|
|
||||||
(sl->with_sum_func == 0 && sl->group_list.elements == 0)) &&
|
|
||||||
(tmp= find_field_in_tables(thd, this,
|
|
||||||
table_list, reference,
|
|
||||||
0, 1)) != not_found_field)
|
|
||||||
{
|
|
||||||
if (tmp)
|
|
||||||
{
|
{
|
||||||
if (tmp != view_ref_found)
|
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|= tmp->table->map;
|
DBUG_ASSERT(*ref && (*ref)->fixed);
|
||||||
prev_subselect_item->const_item_cache= 0;
|
prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
|
||||||
}
|
prev_subselect_item->const_item_cache&= (*ref)->const_item();
|
||||||
else
|
break;
|
||||||
{
|
|
||||||
prev_subselect_item->used_tables_cache|=
|
|
||||||
(*reference)->used_tables();
|
|
||||||
prev_subselect_item->const_item_cache&=
|
|
||||||
(*reference)->const_item();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference is not found => depend from outer (or just error)
|
/* Search in the tables of the FROM clause of the outer select. */
|
||||||
prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT;
|
table_list= outer_sel->get_table_list();
|
||||||
prev_subselect_item->const_item_cache= 0;
|
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;
|
||||||
|
|
||||||
if (sl->master_unit()->first_select()->linkage ==
|
enum_parsing_place place= prev_subselect_item->parsing_place;
|
||||||
DERIVED_TABLE_TYPE)
|
/*
|
||||||
break; // do not look over derived table
|
Check table fields only if the subquery is used somewhere out of
|
||||||
}
|
HAVING or the outer SELECT does not use grouping (i.e. tables are
|
||||||
|
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 ||
|
||||||
|
(!outer_sel->with_sum_func &&
|
||||||
|
outer_sel->group_list.elements == 0)))
|
||||||
|
{
|
||||||
|
if ((from_field= find_field_in_tables(thd, this, table_list,
|
||||||
|
reference,
|
||||||
|
IGNORE_EXCEPT_NON_UNIQUE,
|
||||||
|
TRUE)) !=
|
||||||
|
not_found_field)
|
||||||
|
{
|
||||||
|
if (from_field != view_ref_found)
|
||||||
|
{
|
||||||
|
prev_subselect_item->used_tables_cache|= from_field->table->map;
|
||||||
|
prev_subselect_item->const_item_cache= 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prev_subselect_item->used_tables_cache|=
|
||||||
|
(*reference)->used_tables();
|
||||||
|
prev_subselect_item->const_item_cache&=
|
||||||
|
(*reference)->const_item();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!ref)
|
/* Reference is not found => depend on outer (or just error). */
|
||||||
return 1;
|
prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT;
|
||||||
if (!tmp)
|
prev_subselect_item->const_item_cache= 0;
|
||||||
return -1;
|
|
||||||
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),
|
|
||||||
full_name(), thd->where);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Call to report error
|
|
||||||
find_item_in_list(this,
|
|
||||||
*(thd->lex->current_select->get_item_list()),
|
|
||||||
&counter, REPORT_ALL_ERRORS, ¬_used);
|
|
||||||
}
|
|
||||||
ref= 0; // Safety
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (tmp != not_found_field)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Set ref to 0 as we are replacing this item with the found item
|
|
||||||
and this will ensure we get an error if this item would be
|
|
||||||
used elsewhere
|
|
||||||
*/
|
|
||||||
ref= 0; // Safety
|
|
||||||
if (tmp != view_ref_found)
|
|
||||||
{
|
|
||||||
Item_field* fld;
|
|
||||||
if (!(fld= new Item_field(tmp)))
|
|
||||||
return 1;
|
|
||||||
thd->change_item_tree(reference, fld);
|
|
||||||
mark_as_dependent(thd, last, thd->lex->current_select, fld);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
We can leave expression substituted from view for next PS/SP
|
|
||||||
rexecution (i.e. do not register this substitution for reverting
|
|
||||||
on cleupup() (register_item_tree_changing())), because this
|
|
||||||
subtree will be fix_field'ed during
|
|
||||||
setup_tables()->setup_ancestor() (i.e. before all other
|
|
||||||
expressions of query, and references on tables which do not
|
|
||||||
present in query will not make problems.
|
|
||||||
|
|
||||||
Also we suppose that view can't be changed during PS/SP life.
|
if (outer_sel->master_unit()->first_select()->linkage ==
|
||||||
*/
|
DERIVED_TABLE_TYPE)
|
||||||
|
break; /* Do not consider derived tables. */
|
||||||
|
}
|
||||||
|
|
||||||
|
DBUG_ASSERT(ref);
|
||||||
|
if (!from_field)
|
||||||
|
return TRUE;
|
||||||
|
if (ref == not_found_item && from_field == not_found_field)
|
||||||
|
{
|
||||||
|
my_printf_error(ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR), MYF(0),
|
||||||
|
this->full_name(), current_thd->where);
|
||||||
|
ref= 0; // Safety
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
if (from_field != not_found_field)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Set ref to 0 as we are replacing this item with the found item and
|
||||||
|
this will ensure we get an error if this item would be used
|
||||||
|
elsewhere
|
||||||
|
*/
|
||||||
|
ref= 0; // Safety
|
||||||
|
if (from_field != view_ref_found)
|
||||||
|
{
|
||||||
|
Item_field* fld;
|
||||||
|
if (!(fld= new Item_field(tmp)))
|
||||||
|
return TRUE;
|
||||||
|
thd->change_item_tree(reference, fld);
|
||||||
|
mark_as_dependent(thd, last, thd->lex->current_select, fld);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
We can leave expression substituted from view for next PS/SP
|
||||||
|
re-execution (i.e. do not register this substitution for reverting
|
||||||
|
on cleanup() (register_item_tree_changing())), because this subtree
|
||||||
|
will be fix_field'ed during setup_tables()->setup_ancestor()
|
||||||
|
(i.e. before all other expressions of query, and references on
|
||||||
|
tables which do not present in query will not make problems.
|
||||||
|
|
||||||
|
Also we suppose that view can't be changed during PS/SP life.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Should be checked in resolve_ref_in_select_and_group(). */
|
||||||
|
DBUG_ASSERT(*ref && (*ref)->fixed);
|
||||||
|
mark_as_dependent(thd, last, current_sel, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
}
|
|
||||||
mark_as_dependent(thd, last, thd->lex->current_select,
|
|
||||||
this);
|
|
||||||
ref= last->ref_pointer_array + counter;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!ref)
|
|
||||||
return 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!(*ref)->fixed)
|
|
||||||
{
|
|
||||||
my_error(ER_ILLEGAL_REFERENCE, MYF(0), name,
|
|
||||||
"forward reference in item list");
|
|
||||||
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)
|
|||||||
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,
|
|||||||
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);
|
|||||||
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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,8 +2327,10 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
|
|||||||
return find;
|
return find;
|
||||||
if (found)
|
if (found)
|
||||||
{
|
{
|
||||||
my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0),
|
if (report_error == REPORT_ALL_ERRORS ||
|
||||||
item->full_name(),thd->where);
|
report_error == IGNORE_EXCEPT_NON_UNIQUE)
|
||||||
|
my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0),
|
||||||
|
item->full_name(),thd->where);
|
||||||
return (Field*) 0;
|
return (Field*) 0;
|
||||||
}
|
}
|
||||||
found=find;
|
found=find;
|
||||||
@ -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,
|
|||||||
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,8 +2396,10 @@ 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;
|
||||||
my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0),
|
if (report_error == REPORT_ALL_ERRORS ||
|
||||||
name,thd->where);
|
report_error == IGNORE_EXCEPT_NON_UNIQUE)
|
||||||
|
my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0),
|
||||||
|
name,thd->where);
|
||||||
return (Field*) 0;
|
return (Field*) 0;
|
||||||
}
|
}
|
||||||
found=field;
|
found=field;
|
||||||
@ -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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
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,
|
|||||||
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;
|
||||||
|
|
||||||
order->item= ref_pointer_array + counter;
|
/* Lookup the current GROUP field in the FROM clause. */
|
||||||
order->in_field_list=1;
|
order_item_type= order_item->type();
|
||||||
return 0;
|
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->in_field_list=1;
|
||||||
|
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user