From ee9bafc1c5021d92cdadb690970ab01ccfce7c27 Mon Sep 17 00:00:00 2001 From: "davi@mysql.com/endora.local" <> Date: Fri, 30 Nov 2007 09:34:25 -0200 Subject: [PATCH] Bug#22312 Syntax error in expression with INTERVAL() Parser rejects valid INTERVAL() expressions when associated with arithmetic operators. The problem is the way in which the expression and interval grammar rules were organized caused shift/reduce conflicts. The solution is to tweak the interval rules to avoid shift/reduce conflicts by removing the broken interval_expr rule and explicitly specify it's content where necessary. Original fix by Davi Arnaut, revised and improved rules by Marc Alff --- mysql-test/r/parser.result | 43 ++++++++++++++++++++++++++++ mysql-test/t/parser.test | 28 +++++++++++++++++++ sql/sql_yacc.yy | 57 ++++++++++++++++++++------------------ 3 files changed, 101 insertions(+), 27 deletions(-) diff --git a/mysql-test/r/parser.result b/mysql-test/r/parser.result index 76326f65f08..ef53f227ec0 100644 --- a/mysql-test/r/parser.result +++ b/mysql-test/r/parser.result @@ -484,3 +484,46 @@ select atan(10, 20 "p2"); ERROR 42000: Incorrect parameters in the call to native function 'atan' select atan(10 AS p1, 20 AS p2); ERROR 42000: Incorrect parameters in the call to native function 'atan' +DROP TABLE IF EXISTS t1; +SELECT STR_TO_DATE('10:00 PM', '%h:%i %p') + INTERVAL 10 MINUTE; +STR_TO_DATE('10:00 PM', '%h:%i %p') + INTERVAL 10 MINUTE +NULL +SELECT STR_TO_DATE('10:00 PM', '%h:%i %p') + INTERVAL (INTERVAL(1,2,3) + 1) MINUTE; +STR_TO_DATE('10:00 PM', '%h:%i %p') + INTERVAL (INTERVAL(1,2,3) + 1) MINUTE +NULL +SELECT "1997-12-31 23:59:59" + INTERVAL 1 SECOND; +"1997-12-31 23:59:59" + INTERVAL 1 SECOND +1998-01-01 00:00:00 +SELECT 1 + INTERVAL(1,0,1,2) + 1; +1 + INTERVAL(1,0,1,2) + 1 +4 +SELECT INTERVAL(1^1,0,1,2) + 1; +INTERVAL(1^1,0,1,2) + 1 +2 +SELECT INTERVAL(1,0+1,2,3) * 5.5; +INTERVAL(1,0+1,2,3) * 5.5 +5.5 +SELECT INTERVAL(3,3,1+3,4+4) / 0.5; +INTERVAL(3,3,1+3,4+4) / 0.5 +2.0000 +SELECT (INTERVAL(1,0,1,2) + 5) * 7 + INTERVAL(1,0,1,2) / 2; +(INTERVAL(1,0,1,2) + 5) * 7 + INTERVAL(1,0,1,2) / 2 +50.0000 +SELECT INTERVAL(1,0,1,2) + 1, 5 * INTERVAL(1,0,1,2); +INTERVAL(1,0,1,2) + 1 5 * INTERVAL(1,0,1,2) +3 10 +SELECT INTERVAL(0,(1*5)/2) + INTERVAL(5,4,3); +INTERVAL(0,(1*5)/2) + INTERVAL(5,4,3) +2 +SELECT 1^1 + INTERVAL 1+1 SECOND & 1 + INTERVAL 1+1 SECOND; +1^1 + INTERVAL 1+1 SECOND & 1 + INTERVAL 1+1 SECOND +NULL +SELECT 1%2 - INTERVAL 1^1 SECOND | 1%2 - INTERVAL 1^1 SECOND; +1%2 - INTERVAL 1^1 SECOND | 1%2 - INTERVAL 1^1 SECOND +NULL +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; +a b +3 1998-01-01 00:00:00 +DROP TABLE t1; diff --git a/mysql-test/t/parser.test b/mysql-test/t/parser.test index 79c9129bb74..9170308a4f2 100644 --- a/mysql-test/t/parser.test +++ b/mysql-test/t/parser.test @@ -629,3 +629,31 @@ select atan(10, 20 "p2"); -- error ER_WRONG_PARAMETERS_TO_NATIVE_FCT select atan(10 AS p1, 20 AS p2); +# +# Bug#22312 Syntax error in expression with INTERVAL() +# + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +SELECT STR_TO_DATE('10:00 PM', '%h:%i %p') + INTERVAL 10 MINUTE; +SELECT STR_TO_DATE('10:00 PM', '%h:%i %p') + INTERVAL (INTERVAL(1,2,3) + 1) MINUTE; +SELECT "1997-12-31 23:59:59" + INTERVAL 1 SECOND; +SELECT 1 + INTERVAL(1,0,1,2) + 1; +SELECT INTERVAL(1^1,0,1,2) + 1; +SELECT INTERVAL(1,0+1,2,3) * 5.5; +SELECT INTERVAL(3,3,1+3,4+4) / 0.5; +SELECT (INTERVAL(1,0,1,2) + 5) * 7 + INTERVAL(1,0,1,2) / 2; +SELECT INTERVAL(1,0,1,2) + 1, 5 * INTERVAL(1,0,1,2); +SELECT INTERVAL(0,(1*5)/2) + INTERVAL(5,4,3); + +--disable_warnings +SELECT 1^1 + INTERVAL 1+1 SECOND & 1 + INTERVAL 1+1 SECOND; +SELECT 1%2 - INTERVAL 1^1 SECOND | 1%2 - INTERVAL 1^1 SECOND; +--enable_warnings + +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; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5de142ca83a..80e354aef98 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 280 shift/reduce conflicts. + Currently there are 177 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 280 +%expect 177 /* Comments for TOKENS. @@ -1157,7 +1157,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); variable variable_aux bool_pri predicate bit_expr table_wild simple_expr udf_expr - expr_or_default set_expr_or_default interval_expr + expr_or_default set_expr_or_default param_marker geometry_function signed_literal now_or_signed_literal opt_escape sp_opt_default @@ -6569,10 +6569,10 @@ bit_expr: { $$= new Item_func_plus($1,$3); } | bit_expr '-' bit_expr %prec '-' { $$= new Item_func_minus($1,$3); } - | bit_expr '+' interval_expr interval %prec '+' - { $$= new Item_date_add_interval($1,$3,$4,0); } - | bit_expr '-' interval_expr interval %prec '-' - { $$= new Item_date_add_interval($1,$3,$4,1); } + | bit_expr '+' INTERVAL_SYM expr interval %prec '+' + { $$= new Item_date_add_interval($1,$4,$5,0); } + | bit_expr '-' INTERVAL_SYM expr interval %prec '-' + { $$= new Item_date_add_interval($1,$4,$5,1); } | bit_expr '*' bit_expr %prec '*' { $$= new Item_func_mul($1,$3); } | bit_expr '/' bit_expr %prec '/' @@ -6622,11 +6622,6 @@ all_or_any: | ANY_SYM { $$ = 0; } ; -interval_expr: - INTERVAL_SYM expr %prec INTERVAL_SYM - { $$=$2; } - ; - simple_expr: simple_ident | function_call_keyword @@ -6722,18 +6717,9 @@ simple_expr: $$= new (YYTHD->mem_root) Item_insert_value(Lex->current_context(), $3); } - | interval_expr interval '+' expr + | INTERVAL_SYM expr interval '+' expr %prec INTERVAL_SYM /* we cannot put interval before - */ - { $$= new (YYTHD->mem_root) Item_date_add_interval($4,$1,$2,0); } - | interval_expr - { - if ($1->type() != Item::ROW_ITEM) - { - my_parse_error(ER(ER_SYNTAX_ERROR)); - MYSQL_YYABORT; - } - $$= new (YYTHD->mem_root) Item_func_interval((Item_row *)$1); - } + { $$= new (YYTHD->mem_root) Item_date_add_interval($5,$2,$3,0); } ; /* @@ -6761,6 +6747,23 @@ function_call_keyword: { $$= new (YYTHD->mem_root) Item_func_hour($3); } | INSERT '(' expr ',' expr ',' expr ',' expr ')' { $$= new (YYTHD->mem_root) Item_func_insert($3,$5,$7,$9); } + | INTERVAL_SYM '(' expr ',' expr ')' %prec INTERVAL_SYM + { + THD *thd= YYTHD; + List *list= new (thd->mem_root) List; + list->push_front($5); + list->push_front($3); + Item_row *item= new (thd->mem_root) Item_row(*list); + $$= new (thd->mem_root) Item_func_interval(item); + } + | INTERVAL_SYM '(' expr ',' expr ',' expr_list ')' %prec INTERVAL_SYM + { + THD *thd= YYTHD; + $7->push_front($5); + $7->push_front($3); + Item_row *item= new (thd->mem_root) Item_row(*$7); + $$= new (thd->mem_root) Item_func_interval(item); + } | LEFT '(' expr ',' expr ')' { $$= new (YYTHD->mem_root) Item_func_left($3,$5); } | MINUTE_SYM '(' expr ')' @@ -6838,10 +6841,10 @@ function_call_nonkeyword: $$= new (YYTHD->mem_root) Item_func_curtime_local($3); Lex->safe_to_cache_query=0; } - | DATE_ADD_INTERVAL '(' expr ',' interval_expr interval ')' - { $$= new (YYTHD->mem_root) Item_date_add_interval($3,$5,$6,0); } - | DATE_SUB_INTERVAL '(' expr ',' interval_expr interval ')' - { $$= new (YYTHD->mem_root) Item_date_add_interval($3,$5,$6,1); } + | DATE_ADD_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')' %prec INTERVAL_SYM + { $$= new (YYTHD->mem_root) Item_date_add_interval($3,$6,$7,0); } + | DATE_SUB_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')' %prec INTERVAL_SYM + { $$= new (YYTHD->mem_root) Item_date_add_interval($3,$6,$7,1); } | EXTRACT_SYM '(' interval FROM expr ')' { $$=new (YYTHD->mem_root) Item_extract( $3, $5); } | GET_FORMAT '(' date_time_type ',' expr ')'