From cab36bdf57b9611d880ed76c0c5397aea2c35f36 Mon Sep 17 00:00:00 2001 From: "davi@mysql.com/endora.local" <> Date: Wed, 19 Dec 2007 20:59:57 -0200 Subject: [PATCH] Bug#28317 Left Outer Join with {oj outer-join} Parser rejects ODBC's escape sequences for outer joins other than left outer join, yet the escape sequence BNF specifies that this syntax can be used for left, right, and full outer join syntax. The problem is that although the MySQL Connector/ODBC advertises "Outer Join Escape Sequence" capabilities, the parsing is done in the server and historically it only supported this syntax for left outer joins and applications such as Crystal Reports 11 tries to use this syntax for inner joins. The chosen solution is to reorganize a couple of parser rules to ignore any kind of SQL escape sequence. Ignoring the escape sequences is harmless because the various SQL join clauses are supported by the server. --- mysql-test/r/bdb_notembedded.result | 35 ++++++++++++++++++++++++ mysql-test/r/parser.result | 20 ++++++++++++++ mysql-test/t/bdb_notembedded.test | 38 ++++++++++++++++++++++++++ mysql-test/t/parser.test | 20 ++++++++++++++ sql/sql_yacc.yy | 41 ++++++++++++----------------- 5 files changed, 130 insertions(+), 24 deletions(-) create mode 100644 mysql-test/r/bdb_notembedded.result create mode 100644 mysql-test/t/bdb_notembedded.test diff --git a/mysql-test/r/bdb_notembedded.result b/mysql-test/r/bdb_notembedded.result new file mode 100644 index 00000000000..14cb5fad915 --- /dev/null +++ b/mysql-test/r/bdb_notembedded.result @@ -0,0 +1,35 @@ +set autocommit=1; +reset master; +create table bug16206 (a int); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4 +f n Query 1 n use `test`; create table bug16206 (a int) +f n Query 1 n use `test`; insert into bug16206 values(1) +f n Query 1 n use `test`; insert into bug16206 values(2) +drop table bug16206; +reset master; +create table bug16206 (a int) engine= bdb; +insert into bug16206 values(0); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +insert into bug16206 values(3); +show binlog events; +Log_name Pos Event_type Server_id End_log_pos Info +f n Format_desc 1 n Server ver: VERSION, Binlog ver: 4 +f n Query 1 n use `test`; create table bug16206 (a int) engine= bdb +f n Query 1 n use `test`; insert into bug16206 values(0) +f n Query 1 n use `test`; insert into bug16206 values(1) +f n Query 1 n use `test`; BEGIN +f n Query 1 n use `test`; insert into bug16206 values(2) +f n Query 1 n use `test`; COMMIT +f n Query 1 n use `test`; insert into bug16206 values(3) +drop table bug16206; +set autocommit=0; +End of 5.0 tests diff --git a/mysql-test/r/parser.result b/mysql-test/r/parser.result index ef53f227ec0..e10bcba36c2 100644 --- a/mysql-test/r/parser.result +++ b/mysql-test/r/parser.result @@ -527,3 +527,23 @@ SELECT * FROM t1 WHERE a = INTERVAL(3,2,1) + 1; a b 3 1998-01-01 00:00:00 DROP TABLE t1; +DROP TABLE IF EXISTS t1,t2,t3; +CREATE TABLE t1 (a1 INT, a2 INT, a3 INT, a4 DATETIME); +CREATE TABLE t2 LIKE t1; +CREATE TABLE t3 LIKE t1; +SELECT t1.* FROM t1 AS t0, { OJ t2 INNER JOIN t1 ON (t1.a1=t2.a1) } WHERE t0.a3=2; +a1 a2 a3 a4 +SELECT t1.*,t2.* FROM { OJ ((t1 INNER JOIN t2 ON (t1.a1=t2.a2)) LEFT OUTER JOIN t3 ON t3.a3=t2.a1)}; +a1 a2 a3 a4 a1 a2 a3 a4 +SELECT t1.*,t2.* FROM { OJ ((t1 LEFT OUTER JOIN t2 ON t1.a3=t2.a2) INNER JOIN t3 ON (t3.a1=t2.a2))}; +a1 a2 a3 a4 a1 a2 a3 a4 +SELECT t1.*,t2.* FROM { OJ (t1 LEFT OUTER JOIN t2 ON t1.a1=t2.a2) CROSS JOIN t3 ON (t3.a2=t2.a3)}; +a1 a2 a3 a4 a1 a2 a3 a4 +SELECT * FROM {oj t1 LEFT OUTER JOIN t2 ON t1.a1=t2.a3} WHERE t1.a2 > 10; +a1 a2 a3 a4 a1 a2 a3 a4 +SELECT {fn CONCAT(a1,a2)} FROM t1; +{fn CONCAT(a1,a2)} +UPDATE t3 SET a4={d '1789-07-14'} WHERE a1=0; +SELECT a1, a4 FROM t2 WHERE a4 LIKE {fn UCASE('1789-07-14')}; +a1 a4 +DROP TABLE t1, t2, t3; diff --git a/mysql-test/t/bdb_notembedded.test b/mysql-test/t/bdb_notembedded.test new file mode 100644 index 00000000000..24e64ebbfb2 --- /dev/null +++ b/mysql-test/t/bdb_notembedded.test @@ -0,0 +1,38 @@ +-- source include/not_embedded.inc +-- source include/have_bdb.inc + +# +# Bug #16206: Superfluous COMMIT event in binlog when updating BDB in autocommit mode +# +set autocommit=1; + +let $VERSION=`select version()`; + +reset master; +create table bug16206 (a int); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +--replace_result $VERSION VERSION +--replace_column 1 f 2 n 5 n +show binlog events; +drop table bug16206; + +reset master; +create table bug16206 (a int) engine= bdb; +insert into bug16206 values(0); +insert into bug16206 values(1); +start transaction; +insert into bug16206 values(2); +commit; +insert into bug16206 values(3); +--replace_result $VERSION VERSION +--replace_column 1 f 2 n 5 n +show binlog events; +drop table bug16206; + +set autocommit=0; + + +--echo End of 5.0 tests diff --git a/mysql-test/t/parser.test b/mysql-test/t/parser.test index 9170308a4f2..800d717cf6b 100644 --- a/mysql-test/t/parser.test +++ b/mysql-test/t/parser.test @@ -657,3 +657,23 @@ CREATE TABLE t1 (a INT, b DATETIME); INSERT INTO t1 VALUES (INTERVAL(3,2,1) + 1, "1997-12-31 23:59:59" + INTERVAL 1 SECOND); SELECT * FROM t1 WHERE a = INTERVAL(3,2,1) + 1; DROP TABLE t1; + +# +# Bug#28317 Left Outer Join with {oj outer-join} +# + +--disable_warnings +DROP TABLE IF EXISTS t1,t2,t3; +--enable_warnings +CREATE TABLE t1 (a1 INT, a2 INT, a3 INT, a4 DATETIME); +CREATE TABLE t2 LIKE t1; +CREATE TABLE t3 LIKE t1; +SELECT t1.* FROM t1 AS t0, { OJ t2 INNER JOIN t1 ON (t1.a1=t2.a1) } WHERE t0.a3=2; +SELECT t1.*,t2.* FROM { OJ ((t1 INNER JOIN t2 ON (t1.a1=t2.a2)) LEFT OUTER JOIN t3 ON t3.a3=t2.a1)}; +SELECT t1.*,t2.* FROM { OJ ((t1 LEFT OUTER JOIN t2 ON t1.a3=t2.a2) INNER JOIN t3 ON (t3.a1=t2.a2))}; +SELECT t1.*,t2.* FROM { OJ (t1 LEFT OUTER JOIN t2 ON t1.a1=t2.a2) CROSS JOIN t3 ON (t3.a2=t2.a3)}; +SELECT * FROM {oj t1 LEFT OUTER JOIN t2 ON t1.a1=t2.a3} WHERE t1.a2 > 10; +SELECT {fn CONCAT(a1,a2)} FROM t1; +UPDATE t3 SET a4={d '1789-07-14'} WHERE a1=0; +SELECT a1, a4 FROM t2 WHERE a4 LIKE {fn UCASE('1789-07-14')}; +DROP TABLE t1, t2, t3; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 80e354aef98..c2f99f75c92 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -508,10 +508,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %pure_parser /* We have threads */ /* - Currently there are 177 shift/reduce conflicts. + Currently there are 169 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 177 +%expect 169 /* Comments for TOKENS. @@ -1193,7 +1193,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type join_table_list join_table - table_factor table_ref + table_factor table_ref esc_table_ref select_derived derived_table_list %type date_time_type; @@ -7444,10 +7444,22 @@ join_table_list: derived_table_list { MYSQL_YYABORT_UNLESS($$=$1); } ; +/* + The ODBC escape syntax for Outer Join is: '{' OJ join_table '}' + The parser does not define OJ as a token, any ident is accepted + instead in $2 (ident). Also, all productions from table_ref can + be escaped, not only join_table. Both syntax extensions are safe + and are ignored. +*/ +esc_table_ref: + table_ref { $$=$1; } + | '{' ident table_ref '}' { $$=$3; } + ; + /* Warning - may return NULL in case of incomplete SELECT */ derived_table_list: - table_ref { $$=$1; } - | derived_table_list ',' table_ref + esc_table_ref { $$=$1; } + | derived_table_list ',' esc_table_ref { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); } @@ -7612,25 +7624,6 @@ table_factor: MYSQL_YYABORT; Select->add_joined_table($$); } - | '{' ident table_ref LEFT OUTER JOIN_SYM table_ref - ON - { - /* Change the current name resolution context to a local context. */ - if (push_new_name_resolution_context(YYTHD, $3, $7)) - MYSQL_YYABORT; - - } - expr '}' - { - LEX *lex= Lex; - MYSQL_YYABORT_UNLESS($3 && $7); - add_join_on($7,$10); - Lex->pop_context(); - $7->outer_join|=JOIN_TYPE_LEFT; - $$=$7; - if (!($$= lex->current_select->nest_last_join(lex->thd))) - MYSQL_YYABORT; - } | select_derived_init get_select_lex select_derived2 { LEX *lex= Lex;