Commit 05a878c1 authored by Sergei Golubchik's avatar Sergei Golubchik

precedence bugfixing

fix printing precedence for BETWEEN, LIKE/ESCAPE, REGEXP, IN
don't use precedence for printing CASE/WHEN/THEN/ELSE/END

fix parsing precedence of BETWEEN, LIKE/ESCAPE, REGEXP, IN
support predicate arguments for IN, BETWEEN, SOUNDS LIKE, LIKE/ESCAPE,
REGEXP

use %nonassoc for unary operators

fix parsing of IS TRUE/FALSE/UNKNOWN/NULL

remove parser_precedence test as superseded by the precedence test
parent 7f974e5a
......@@ -10052,11 +10052,11 @@ EXPLAIN
"access_type": "ALL",
"rows": 3,
"filtered": 100,
"attached_condition": "case when (tab2.max_a = 1 or tab2.max_a = 2) then 1 else 0 end = 1",
"attached_condition": "case when tab2.max_a = 1 or tab2.max_a = 2 then 1 else 0 end = 1",
"materialized": {
"query_block": {
"select_id": 3,
"having_condition": "case when (max_a = 1 or max_a = 2) then 1 else 0 end = 1",
"having_condition": "case when max_a = 1 or max_a = 2 then 1 else 0 end = 1",
"filesort": {
"sort_key": "t1.b",
"temporary_table": {
......@@ -10101,11 +10101,11 @@ EXPLAIN
"access_type": "ALL",
"rows": 3,
"filtered": 100,
"attached_condition": "case when (tab2.max_a = 1 or tab2.max_a > 2 and tab2.max_a < 4) then 1 else 0 end = 1",
"attached_condition": "case when tab2.max_a = 1 or tab2.max_a > 2 and tab2.max_a < 4 then 1 else 0 end = 1",
"materialized": {
"query_block": {
"select_id": 3,
"having_condition": "case when (max_a = 1 or max_a > 2 and max_a < 4) then 1 else 0 end = 1",
"having_condition": "case when max_a = 1 or max_a > 2 and max_a < 4 then 1 else 0 end = 1",
"filesort": {
"sort_key": "t1.b",
"temporary_table": {
......@@ -10150,11 +10150,11 @@ EXPLAIN
"access_type": "ALL",
"rows": 3,
"filtered": 100,
"attached_condition": "case when (tab2.max_a > 1 and (tab2.max_a = 2 or tab2.max_a > 2)) then 1 else 0 end = 1",
"attached_condition": "case when tab2.max_a > 1 and (tab2.max_a = 2 or tab2.max_a > 2) then 1 else 0 end = 1",
"materialized": {
"query_block": {
"select_id": 3,
"having_condition": "case when (max_a > 1 and (max_a = 2 or max_a > 2)) then 1 else 0 end = 1",
"having_condition": "case when max_a > 1 and (max_a = 2 or max_a > 2) then 1 else 0 end = 1",
"filesort": {
"sort_key": "t1.b",
"temporary_table": {
......@@ -10199,7 +10199,7 @@ EXPLAIN
"access_type": "ALL",
"rows": 3,
"filtered": 100,
"attached_condition": "case when (tab2.b = 2 or tab2.b = 4) then 1 else 0 end = 1",
"attached_condition": "case when tab2.b = 2 or tab2.b = 4 then 1 else 0 end = 1",
"materialized": {
"query_block": {
"select_id": 3,
......@@ -10211,7 +10211,7 @@ EXPLAIN
"access_type": "ALL",
"rows": 3,
"filtered": 100,
"attached_condition": "case when (t1.b = 2 or t1.b = 4) then 1 else 0 end = 1"
"attached_condition": "case when t1.b = 2 or t1.b = 4 then 1 else 0 end = 1"
}
}
}
......
......@@ -89,15 +89,6 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
Warnings:
Note 1003 select -1 AS `- a` from dual
drop table t1;
select 5 between 0 and 10 between 0 and 1,(5 between 0 and 10) between 0 and 1;
5 between 0 and 10 between 0 and 1 (5 between 0 and 10) between 0 and 1
0 1
select 1 and 2 between 2 and 10, 2 between 2 and 10 and 1;
1 and 2 between 2 and 10 2 between 2 and 10 and 1
1 1
select 1 and 0 or 2, 2 or 1 and 0;
1 and 0 or 2 2 or 1 and 0
1 1
select _koi8r'a' = _koi8r'A';
_koi8r'a' = _koi8r'A'
1
......
......@@ -1321,3 +1321,20 @@ t1 CREATE TABLE `t1` (
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
create or replace view v1 as select 1 between (2 between 3 and 4) and 5;
Select view_definition from information_schema.views where table_schema='test' and table_name='v1';
view_definition
select 1 between 2 between 3 and 4 and 5 AS `1 between (2 between 3 and 4) and 5`
create or replace view v1 as select 1 between (2 in (3,4)) and 5;
Select view_definition from information_schema.views where table_schema='test' and table_name='v1';
view_definition
select 1 between 2 in (3,4) and 5 AS `1 between (2 in (3,4)) and 5`
create or replace view v1 as select 1 between (2 like 3) and 4;
Select view_definition from information_schema.views where table_schema='test' and table_name='v1';
view_definition
select 1 between 2 like 3 and 4 AS `1 between (2 like 3) and 4`
create or replace view v1 as select 1 not between (2 like 3) and 4;
Select view_definition from information_schema.views where table_schema='test' and table_name='v1';
view_definition
select 1 not between 2 like 3 and 4 AS `1 not between (2 like 3) and 4`
drop view v1;
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -35,14 +35,6 @@ select - a from t1;
explain extended select - a from t1;
drop table t1;
#
# Wrong usage of functions
#
select 5 between 0 and 10 between 0 and 1,(5 between 0 and 10) between 0 and 1;
select 1 and 2 between 2 and 10, 2 between 2 and 10 and 1;
select 1 and 0 or 2, 2 or 1 and 0;
#
# Coercibility
#
......
......@@ -1351,3 +1351,17 @@ create function fs() returns serial return 1;
create table t1 ( id serial );
show create table t1;
drop table t1;
#
# BETWEEN syntax
#
create or replace view v1 as select 1 between (2 between 3 and 4) and 5;
Select view_definition from information_schema.views where table_schema='test' and table_name='v1';
create or replace view v1 as select 1 between (2 in (3,4)) and 5;
Select view_definition from information_schema.views where table_schema='test' and table_name='v1';
create or replace view v1 as select 1 between (2 like 3) and 4;
Select view_definition from information_schema.views where table_schema='test' and table_name='v1';
create or replace view v1 as select 1 not between (2 like 3) and 4;
Select view_definition from information_schema.views where table_schema='test' and table_name='v1';
drop view v1;
This diff is collapsed.
......@@ -82,8 +82,9 @@ enum precedence {
XOR_PRECEDENCE, // XOR
AND_PRECEDENCE, // AND, &&
NOT_PRECEDENCE, // NOT (unless HIGH_NOT_PRECEDENCE)
BETWEEN_PRECEDENCE, // BETWEEN, CASE, WHEN, THEN, ELSE
CMP_PRECEDENCE, // =, <=>, >=, >, <=, <, <>, !=, IS, LIKE, REGEXP, IN
CMP_PRECEDENCE, // =, <=>, >=, >, <=, <, <>, !=, IS
BETWEEN_PRECEDENCE, // BETWEEN
IN_PRECEDENCE, // IN, LIKE, REGEXP
BITOR_PRECEDENCE, // |
BITAND_PRECEDENCE, // &
SHIFT_PRECEDENCE, // <<, >>
......@@ -1381,6 +1382,8 @@ class Item: public Value_source,
mysql_register_view().
*/
virtual enum precedence precedence() const { return DEFAULT_PRECEDENCE; }
enum precedence higher_precedence() const
{ return (enum precedence)(precedence() + 1); }
void print_parenthesised(String *str, enum_query_type query_type,
enum precedence parent_prec);
/**
......
......@@ -2312,7 +2312,7 @@ longlong Item_func_between::val_int()
void Item_func_between::print(String *str, enum_query_type query_type)
{
args[0]->print_parenthesised(str, query_type, precedence());
args[0]->print_parenthesised(str, query_type, higher_precedence());
if (negated)
str->append(STRING_WITH_LEN(" not"));
str->append(STRING_WITH_LEN(" between "));
......@@ -3375,15 +3375,15 @@ void Item_func_case::print(String *str, enum_query_type query_type)
for (uint i= first_expr_num + 1 ; i < nwhens + first_expr_num + 1; i++)
{
str->append(STRING_WITH_LEN("when "));
args[i]->print_parenthesised(str, query_type, precedence());
args[i]->print(str, query_type);
str->append(STRING_WITH_LEN(" then "));
args[i+nwhens]->print_parenthesised(str, query_type, precedence());
args[i+nwhens]->print(str, query_type);
str->append(' ');
}
if (else_expr_num != -1)
{
str->append(STRING_WITH_LEN("else "));
args[else_expr_num]->print_parenthesised(str, query_type, precedence());
args[else_expr_num]->print(str, query_type);
str->append(' ');
}
str->append(STRING_WITH_LEN("end"));
......@@ -5243,12 +5243,14 @@ void Item_func_like::print(String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN(" not "));
str->append(func_name());
str->append(' ');
args[1]->print_parenthesised(str, query_type, precedence());
if (escape_used_in_parsing)
{
args[1]->print_parenthesised(str, query_type, precedence());
str->append(STRING_WITH_LEN(" escape "));
escape_item->print(str, query_type);
escape_item->print_parenthesised(str, query_type, higher_precedence());
}
else
args[1]->print_parenthesised(str, query_type, higher_precedence());
}
......
......@@ -1590,7 +1590,6 @@ class Item_func_case :public Item_func_hybrid_field_type
uint decimal_precision() const;
table_map not_null_tables() const { return 0; }
const char *func_name() const { return "case"; }
enum precedence precedence() const { return BETWEEN_PRECEDENCE; }
virtual void print(String *str, enum_query_type query_type);
Item *find_item(String *str);
CHARSET_INFO *compare_collation() const { return cmp_collation.collation; }
......@@ -1704,7 +1703,7 @@ class Item_func_in :public Item_func_opt_neg
virtual void print(String *str, enum_query_type query_type);
enum Functype functype() const { return IN_FUNC; }
const char *func_name() const { return "in"; }
enum precedence precedence() const { return CMP_PRECEDENCE; }
enum precedence precedence() const { return IN_PRECEDENCE; }
bool eval_not_null_tables(void *opt_arg);
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
bool count_sargable_conds(void *arg);
......@@ -1999,7 +1998,7 @@ class Item_func_like :public Item_bool_func2
return this;
}
const char *func_name() const { return "like"; }
enum precedence precedence() const { return CMP_PRECEDENCE; }
enum precedence precedence() const { return IN_PRECEDENCE; }
bool fix_fields(THD *thd, Item **ref);
bool fix_length_and_dec()
{
......@@ -2127,7 +2126,7 @@ class Item_func_regex :public Item_bool_func
bool fix_fields(THD *thd, Item **ref);
bool fix_length_and_dec();
const char *func_name() const { return "regexp"; }
enum precedence precedence() const { return CMP_PRECEDENCE; }
enum precedence precedence() const { return IN_PRECEDENCE; }
Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
void print(String *str, enum_query_type query_type)
{
......
......@@ -481,8 +481,7 @@ void Item_func::print_op(String *str, enum_query_type query_type)
str->append(func_name());
str->append(' ');
}
args[arg_count-1]->print_parenthesised(str, query_type,
(enum precedence)(precedence() + 1));
args[arg_count-1]->print_parenthesised(str, query_type, higher_precedence());
}
......
......@@ -3283,7 +3283,7 @@ void Item_in_subselect::print(String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN("<exists>"));
else
{
left_expr->print(str, query_type);
left_expr->print_parenthesised(str, query_type, precedence());
str->append(STRING_WITH_LEN(" in "));
}
Item_subselect::print(str, query_type);
......
......@@ -628,7 +628,7 @@ class Item_in_subselect :public Item_exists_subselect
bool val_bool();
bool test_limit(st_select_lex_unit *unit);
void print(String *str, enum_query_type query_type);
enum precedence precedence() const { return CMP_PRECEDENCE; }
enum precedence precedence() const { return IN_PRECEDENCE; }
bool fix_fields(THD *thd, Item **ref);
bool fix_length_and_dec();
void fix_after_pullout(st_select_lex *new_parent, Item **ref, bool merge);
......
......@@ -149,6 +149,14 @@ void LEX::parse_error()
}
static Item* escape(THD *thd)
{
thd->lex->escape_used= false;
const char *esc= thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES ? "" : "\\";
return new (thd->mem_root) Item_string_ascii(thd, esc, MY_TEST(esc[0]));
}
/**
@brief Bison callback to report a syntax/OOM error
......@@ -1022,10 +1030,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%parse-param { THD *thd }
%lex-param { THD *thd }
/*
Currently there are 102 shift/reduce conflicts.
Currently there are 105 shift/reduce conflicts.
We should not introduce new conflicts any more.
*/
%expect 101
%expect 105
/*
Comments for TOKENS.
......@@ -1721,17 +1729,19 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%left OR_OR_SYM OR_SYM OR2_SYM
%left XOR
%left AND_SYM AND_AND_SYM
%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE
%left '=' EQUAL_SYM GE '>' LE '<' NE IS LIKE REGEXP IN_SYM
%nonassoc NOT_SYM
%left '=' EQUAL_SYM GE '>' LE '<' NE
%nonassoc IS
%right BETWEEN_SYM
%left LIKE REGEXP IN_SYM
%left '|'
%left '&'
%left SHIFT_LEFT SHIFT_RIGHT
%left '-' '+'
%left '*' '/' '%' DIV_SYM MOD_SYM
%left '^'
%left NEG '~'
%right NOT_SYM NOT2_SYM
%right BINARY COLLATE_SYM
%nonassoc NEG '~' NOT2_SYM BINARY
%nonassoc COLLATE_SYM
%left INTERVAL_SYM
%type <lex_str>
......@@ -1831,7 +1841,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
table_wild simple_expr column_default_non_parenthesized_expr udf_expr
expr_or_default set_expr_or_default
geometry_function signed_literal expr_or_literal
opt_escape
sp_opt_default
simple_ident_nospvar simple_ident_q
field_or_var limit_option
......@@ -8915,59 +8924,59 @@ expr:
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS TRUE_SYM %prec IS
| expr IS TRUE_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_istrue(thd, $1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS not TRUE_SYM %prec IS
| expr IS not TRUE_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnottrue(thd, $1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS FALSE_SYM %prec IS
| expr IS FALSE_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isfalse(thd, $1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS not FALSE_SYM %prec IS
| expr IS not FALSE_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnotfalse(thd, $1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS UNKNOWN_SYM %prec IS
| expr IS UNKNOWN_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnull(thd, $1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS not UNKNOWN_SYM %prec IS
| expr IS not UNKNOWN_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnotnull(thd, $1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri
;
bool_pri:
bool_pri IS NULL_SYM %prec IS
| expr IS NULL_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnull(thd, $1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri IS not NULL_SYM %prec IS
| expr IS not NULL_SYM %prec IS
{
$$= new (thd->mem_root) Item_func_isnotnull(thd, $1);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bool_pri EQUAL_SYM predicate %prec EQUAL_SYM
| bool_pri
;
bool_pri:
bool_pri EQUAL_SYM predicate %prec EQUAL_SYM
{
$$= new (thd->mem_root) Item_func_equal(thd, $1, $3);
if ($$ == NULL)
......@@ -8989,13 +8998,13 @@ bool_pri:
;
predicate:
bit_expr IN_SYM '(' subselect ')'
predicate IN_SYM '(' subselect ')'
{
$$= new (thd->mem_root) Item_in_subselect(thd, $1, $4);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr not IN_SYM '(' subselect ')'
| predicate not IN_SYM '(' subselect ')'
{
Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $5);
if (item == NULL)
......@@ -9004,13 +9013,13 @@ predicate:
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr IN_SYM '(' expr ')'
| predicate IN_SYM '(' expr ')'
{
$$= handle_sql2003_note184_exception(thd, $1, true, $4);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr IN_SYM '(' expr ',' expr_list ')'
| predicate IN_SYM '(' expr ',' expr_list ')'
{
$6->push_front($4, thd->mem_root);
$6->push_front($1, thd->mem_root);
......@@ -9018,13 +9027,13 @@ predicate:
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr not IN_SYM '(' expr ')'
| predicate not IN_SYM '(' expr ')'
{
$$= handle_sql2003_note184_exception(thd, $1, false, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr not IN_SYM '(' expr ',' expr_list ')'
| predicate not IN_SYM '(' expr ',' expr_list ')'
{
$7->push_front($5, thd->mem_root);
$7->push_front($1, thd->mem_root);
......@@ -9033,13 +9042,13 @@ predicate:
MYSQL_YYABORT;
$$= item->neg_transformer(thd);
}
| bit_expr BETWEEN_SYM bit_expr AND_SYM predicate
| predicate BETWEEN_SYM predicate AND_SYM predicate %prec BETWEEN_SYM
{
$$= new (thd->mem_root) Item_func_between(thd, $1, $3, $5);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate
| predicate not BETWEEN_SYM predicate AND_SYM predicate %prec BETWEEN_SYM
{
Item_func_between *item;
item= new (thd->mem_root) Item_func_between(thd, $1, $4, $6);
......@@ -9047,7 +9056,7 @@ predicate:
MYSQL_YYABORT;
$$= item->neg_transformer(thd);
}
| bit_expr SOUNDS_SYM LIKE bit_expr
| predicate SOUNDS_SYM LIKE predicate
{
Item *item1= new (thd->mem_root) Item_func_soundex(thd, $1);
Item *item4= new (thd->mem_root) Item_func_soundex(thd, $4);
......@@ -9057,28 +9066,41 @@ predicate:
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr LIKE simple_expr opt_escape
| predicate LIKE predicate
{
$$= new (thd->mem_root) Item_func_like(thd, $1, $3, escape(thd), false);
if ($$ == NULL)
MYSQL_YYABORT;
}
| predicate LIKE predicate ESCAPE_SYM predicate %prec LIKE
{
$$= new (thd->mem_root) Item_func_like(thd, $1, $3, $4,
Lex->escape_used);
Lex->escape_used= true;
$$= new (thd->mem_root) Item_func_like(thd, $1, $3, $5, true);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr not LIKE simple_expr opt_escape
| predicate not LIKE predicate
{
Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, escape(thd), false);
if (item == NULL)
MYSQL_YYABORT;
$$= item->neg_transformer(thd);
}
| predicate not LIKE predicate ESCAPE_SYM predicate %prec LIKE
{
Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $5,
Lex->escape_used);
Lex->escape_used= true;
Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $6, true);
if (item == NULL)
MYSQL_YYABORT;
$$= item->neg_transformer(thd);
}
| bit_expr REGEXP bit_expr
| predicate REGEXP predicate
{
$$= new (thd->mem_root) Item_func_regex(thd, $1, $3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| bit_expr not REGEXP bit_expr
| predicate not REGEXP predicate
{
Item *item= new (thd->mem_root) Item_func_regex(thd, $1, $4);
if (item == NULL)
......@@ -11468,23 +11490,6 @@ opt_having_clause:
}
;
opt_escape:
ESCAPE_SYM simple_expr
{
Lex->escape_used= TRUE;
$$= $2;
}
| /* empty */
{
Lex->escape_used= FALSE;
$$= ((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ?
new (thd->mem_root) Item_string_ascii(thd, "", 0) :
new (thd->mem_root) Item_string_ascii(thd, "\\", 1));
if ($$ == NULL)
MYSQL_YYABORT;
}
;
/*
group by statement in select
*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment