diff --git a/mysql-test/main/ansi.result b/mysql-test/main/ansi.result index 527748e00d53e..810168cc3bd4e 100644 --- a/mysql-test/main/ansi.result +++ b/mysql-test/main/ansi.result @@ -46,3 +46,73 @@ t1 CREATE TABLE `t1` ( PRIMARY KEY (`i`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t1; +# +# MDEV-16186 Concatenation operator || returns wrong results in sql_mode=ORACLE +# +SET sql_mode=ANSI; +SELECT -1<<1||1 AS a FROM DUAL; +a +18446744073709549568 +SELECT -1||0<<1 AS a FROM DUAL; +a +18446744073709551596 +EXPLAIN EXTENDED SELECT -1<<1||1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select -1 << concat(1,1) AS "a" +EXPLAIN EXTENDED SELECT -1||0<<1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat(-1,0) << 1 AS "a" +SELECT -1+1||1 AS a FROM DUAL; +a +10 +SELECT -1||0+1 AS a FROM DUAL; +a +-9 +EXPLAIN EXTENDED SELECT -1+1||1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select -1 + concat(1,1) AS "a" +EXPLAIN EXTENDED SELECT -1||0+1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat(-1,0) + 1 AS "a" +SELECT 1*1||-1 AS a FROM DUAL; +a +1 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: '1-1' +SELECT 1||1*-1 AS a FROM DUAL; +a +-11 +EXPLAIN EXTENDED SELECT 1*1||-1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select 1 * concat(1,-1) AS "a" +EXPLAIN EXTENDED SELECT 1||1*-1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat(1,1) * -1 AS "a" +SELECT -1^1||1 AS a FROM DUAL; +a +18446744073709551604 +SELECT -1||0^1 AS a FROM DUAL; +a +18446744073709551607 +EXPLAIN EXTENDED SELECT -1^1||1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select -1 ^ concat(1,1) AS "a" +EXPLAIN EXTENDED SELECT -1||0^1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat(-1,0) ^ 1 AS "a" diff --git a/mysql-test/main/ansi.test b/mysql-test/main/ansi.test index fa7f999954e1a..0620465728bc6 100644 --- a/mysql-test/main/ansi.test +++ b/mysql-test/main/ansi.test @@ -39,3 +39,36 @@ SHOW CREATE TABLE t1; DROP TABLE t1; # End of 4.1 tests + + +--echo # +--echo # MDEV-16186 Concatenation operator || returns wrong results in sql_mode=ORACLE +--echo # + +SET sql_mode=ANSI; + +# Concatenation operator || is stronger than numeric dyadic operators ^ * + << + +SELECT -1<<1||1 AS a FROM DUAL; +SELECT -1||0<<1 AS a FROM DUAL; + +EXPLAIN EXTENDED SELECT -1<<1||1 AS a FROM DUAL; +EXPLAIN EXTENDED SELECT -1||0<<1 AS a FROM DUAL; + +SELECT -1+1||1 AS a FROM DUAL; +SELECT -1||0+1 AS a FROM DUAL; + +EXPLAIN EXTENDED SELECT -1+1||1 AS a FROM DUAL; +EXPLAIN EXTENDED SELECT -1||0+1 AS a FROM DUAL; + +SELECT 1*1||-1 AS a FROM DUAL; +SELECT 1||1*-1 AS a FROM DUAL; + +EXPLAIN EXTENDED SELECT 1*1||-1 AS a FROM DUAL; +EXPLAIN EXTENDED SELECT 1||1*-1 AS a FROM DUAL; + +SELECT -1^1||1 AS a FROM DUAL; +SELECT -1||0^1 AS a FROM DUAL; + +EXPLAIN EXTENDED SELECT -1^1||1 AS a FROM DUAL; +EXPLAIN EXTENDED SELECT -1||0^1 AS a FROM DUAL; diff --git a/mysql-test/suite/compat/oracle/r/func_concat.result b/mysql-test/suite/compat/oracle/r/func_concat.result index 230b36b94a5ed..b598f97006ef7 100644 --- a/mysql-test/suite/compat/oracle/r/func_concat.result +++ b/mysql-test/suite/compat/oracle/r/func_concat.result @@ -255,3 +255,70 @@ SELECT * FROM v1; test 1 DROP VIEW v1; +# +# MDEV-16186 Concatenation operator || returns wrong results in sql_mode=ORACLE +# +SELECT -1<<1||1 AS a FROM DUAL; +a +18446744073709549568 +SELECT -1||0<<1 AS a FROM DUAL; +a +18446744073709551596 +EXPLAIN EXTENDED SELECT -1<<1||1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select -1 << concat_operator_oracle(1,1) AS "a" +EXPLAIN EXTENDED SELECT -1||0<<1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat_operator_oracle(-1,0) << 1 AS "a" +SELECT -1+1||1 AS a FROM DUAL; +a +01 +SELECT -1||0+1 AS a FROM DUAL; +a +-9 +EXPLAIN EXTENDED SELECT -1+1||1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat_operator_oracle(-1 + 1,1) AS "a" +EXPLAIN EXTENDED SELECT -1||0+1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat_operator_oracle(-1,0) + 1 AS "a" +SELECT 1*1||-1 AS a FROM DUAL; +a +1-1 +SELECT 1||1*-1 AS a FROM DUAL; +a +1-1 +EXPLAIN EXTENDED SELECT 1*1||-1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat_operator_oracle(1 * 1,-1) AS "a" +EXPLAIN EXTENDED SELECT 1||1*-1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat_operator_oracle(1,1 * -1) AS "a" +SELECT -1^1||1 AS a FROM DUAL; +a +184467440737095516141 +SELECT -1||0^1 AS a FROM DUAL; +a +-11 +EXPLAIN EXTENDED SELECT -1^1||1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat_operator_oracle(-1 ^ 1,1) AS "a" +EXPLAIN EXTENDED SELECT -1||0^1 AS a FROM DUAL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select concat_operator_oracle(-1,0 ^ 1) AS "a" diff --git a/mysql-test/suite/compat/oracle/t/func_concat.test b/mysql-test/suite/compat/oracle/t/func_concat.test index e1d8a5c477fd4..7f9fec4f2fe87 100644 --- a/mysql-test/suite/compat/oracle/t/func_concat.test +++ b/mysql-test/suite/compat/oracle/t/func_concat.test @@ -114,3 +114,35 @@ SET sql_mode=ORACLE; SHOW CREATE VIEW v1; SELECT * FROM v1; DROP VIEW v1; + + +--echo # +--echo # MDEV-16186 Concatenation operator || returns wrong results in sql_mode=ORACLE +--echo # + +# Concatenation operator || has the same precedence with + +# (stronger than << and weaker than * ^) + +SELECT -1<<1||1 AS a FROM DUAL; +SELECT -1||0<<1 AS a FROM DUAL; + +EXPLAIN EXTENDED SELECT -1<<1||1 AS a FROM DUAL; +EXPLAIN EXTENDED SELECT -1||0<<1 AS a FROM DUAL; + +SELECT -1+1||1 AS a FROM DUAL; +SELECT -1||0+1 AS a FROM DUAL; + +EXPLAIN EXTENDED SELECT -1+1||1 AS a FROM DUAL; +EXPLAIN EXTENDED SELECT -1||0+1 AS a FROM DUAL; + +SELECT 1*1||-1 AS a FROM DUAL; +SELECT 1||1*-1 AS a FROM DUAL; + +EXPLAIN EXTENDED SELECT 1*1||-1 AS a FROM DUAL; +EXPLAIN EXTENDED SELECT 1||1*-1 AS a FROM DUAL; + +SELECT -1^1||1 AS a FROM DUAL; +SELECT -1||0^1 AS a FROM DUAL; + +EXPLAIN EXTENDED SELECT -1^1||1 AS a FROM DUAL; +EXPLAIN EXTENDED SELECT -1||0^1 AS a FROM DUAL; diff --git a/sql/lex.h b/sql/lex.h index 7be3e4c9251ea..d336c273a1814 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -722,7 +722,7 @@ static SYMBOL symbols[] = { { "YEAR", SYM(YEAR_SYM)}, { "YEAR_MONTH", SYM(YEAR_MONTH_SYM)}, { "ZEROFILL", SYM(ZEROFILL)}, - { "||", SYM(OR_OR_SYM)} + { "||", SYM(OR2_SYM)} }; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 95a59c341d556..d132f3a8f2b5e 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -839,9 +839,12 @@ int Lex_input_stream::find_keyword(Lex_ident_cli_st *kwd, if ((symbol->tok == NOT_SYM) && (m_thd->variables.sql_mode & MODE_HIGH_NOT_PRECEDENCE)) return NOT2_SYM; - if ((symbol->tok == OR_OR_SYM) && - !(m_thd->variables.sql_mode & MODE_PIPES_AS_CONCAT)) - return OR2_SYM; + if ((symbol->tok == OR2_SYM) && + (m_thd->variables.sql_mode & MODE_PIPES_AS_CONCAT)) + { + return (m_thd->variables.sql_mode & MODE_ORACLE) ? + ORACLE_CONCAT_SYM : MYSQL_CONCAT_SYM; + } return symbol->tok; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e4f005a048801..765cc4d41ea80 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -892,10 +892,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 127 shift/reduce conflicts. + Currently there are 122 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 127 +%expect 122 /* Comments for TOKENS. @@ -1073,6 +1073,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token MIN_SYM /* SQL-2003-N */ %token MODIFIES_SYM /* SQL-2003-R */ %token MOD_SYM /* SQL-2003-N */ +%token MYSQL_CONCAT_SYM /* OPERATOR */ %token NATURAL /* SQL-2003-R */ %token NCHAR_STRING %token NE /* OPERATOR */ @@ -1089,9 +1090,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token ON /* SQL-2003-R */ %token OPTIMIZE %token OPTIONALLY +%token ORACLE_CONCAT_SYM /* INTERNAL */ %token OR2_SYM %token ORDER_SYM /* SQL-2003-R */ -%token OR_OR_SYM /* OPERATOR */ %token OR_SYM /* SQL-2003-R */ %token OUTER %token OUTFILE @@ -1654,7 +1655,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); /* A dummy token to force the priority of table_ref production in a join. */ %left TABLE_REF_PRIORITY %left SET_VAR -%left OR_OR_SYM OR_SYM OR2_SYM +%left OR_SYM OR2_SYM %left XOR %left AND_SYM AND_AND_SYM %left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE @@ -1662,9 +1663,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %left '|' %left '&' %left SHIFT_LEFT SHIFT_RIGHT -%left '-' '+' +%left '-' '+' ORACLE_CONCAT_SYM %left '*' '/' '%' DIV_SYM MOD_SYM %left '^' +%left MYSQL_CONCAT_SYM %left NEG '~' %right NOT_SYM NOT2_SYM %right BINARY COLLATE_SYM @@ -1803,6 +1805,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); variable variable_aux bool_pri predicate bit_expr parenthesized_expr table_wild simple_expr column_default_non_parenthesized_expr udf_expr + primary_expr string_factor_expr mysql_concatenation_expr select_sublist_qualified_asterisk expr_or_default set_expr_or_default geometry_function signed_literal expr_or_literal @@ -2056,8 +2059,9 @@ END_OF_INPUT %type '-' '+' '*' '/' '%' '(' ')' - ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM + ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM BETWEEN_SYM CASE_SYM THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM + MYSQL_CONCAT_SYM ORACLE_CONCAT_SYM %type opt_with_clause with_clause @@ -9697,14 +9701,14 @@ predicate: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr LIKE simple_expr opt_escape + | bit_expr LIKE mysql_concatenation_expr opt_escape { $$= new (thd->mem_root) Item_func_like(thd, $1, $3, $4, Lex->escape_used); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr not LIKE simple_expr opt_escape + | bit_expr not LIKE mysql_concatenation_expr opt_escape { Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $5, Lex->escape_used); @@ -9755,6 +9759,13 @@ bit_expr: if (unlikely($$ == NULL)) MYSQL_YYABORT; } + | bit_expr ORACLE_CONCAT_SYM bit_expr + { + $$= new (thd->mem_root) Item_func_concat_operator_oracle(thd, + $1, $3); + if (unlikely($$ == NULL)) + MYSQL_YYABORT; + } | bit_expr '+' bit_expr %prec '+' { $$= new (thd->mem_root) Item_func_plus(thd, $1, $3); @@ -9834,7 +9845,7 @@ bit_expr: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | simple_expr + | mysql_concatenation_expr %prec '^' ; or: @@ -10113,26 +10124,28 @@ column_default_non_parenthesized_expr: } ; -simple_expr: +primary_expr: column_default_non_parenthesized_expr - | simple_expr COLLATE_SYM collation_name %prec NEG + | '(' parenthesized_expr ')' { $$= $2; } + ; + +string_factor_expr: + primary_expr + | string_factor_expr COLLATE_SYM collation_name { if (unlikely(!($$= new (thd->mem_root) Item_func_set_collation(thd, $1, $3)))) MYSQL_YYABORT; } - | '(' parenthesized_expr ')' { $$= $2; } - | BINARY simple_expr %prec NEG + ; + +simple_expr: + string_factor_expr %prec NEG + | BINARY simple_expr { Type_cast_attributes at(&my_charset_bin); if (unlikely(!($$= type_handler_long_blob.create_typecast_item(thd, $2, at)))) MYSQL_YYABORT; } - | simple_expr OR_OR_SYM simple_expr - { - $$= new (thd->mem_root) Item_func_concat(thd, $1, $3); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } | '+' simple_expr %prec NEG { $$= $2; @@ -10157,6 +10170,16 @@ simple_expr: } ; +mysql_concatenation_expr: + simple_expr + | mysql_concatenation_expr MYSQL_CONCAT_SYM simple_expr + { + $$= new (thd->mem_root) Item_func_concat(thd, $1, $3); + if (unlikely($$ == NULL)) + MYSQL_YYABORT; + } + ; + function_call_keyword_timestamp: TIMESTAMP '(' expr ')' { diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index cb1d7481efb63..736837b9ca1c8 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -278,10 +278,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 104 shift/reduce conflicts. + Currently there are 99 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 104 +%expect 99 /* Comments for TOKENS. @@ -459,6 +459,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token MIN_SYM /* SQL-2003-N */ %token MODIFIES_SYM /* SQL-2003-R */ %token MOD_SYM /* SQL-2003-N */ +%token MYSQL_CONCAT_SYM /* OPERATOR */ %token NATURAL /* SQL-2003-R */ %token NCHAR_STRING %token NE /* OPERATOR */ @@ -475,9 +476,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %token ON /* SQL-2003-R */ %token OPTIMIZE %token OPTIONALLY +%token ORACLE_CONCAT_SYM /* INTERNAL */ %token OR2_SYM %token ORDER_SYM /* SQL-2003-R */ -%token OR_OR_SYM /* OPERATOR */ %token OR_SYM /* SQL-2003-R */ %token OUTER %token OUTFILE @@ -1040,7 +1041,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); /* A dummy token to force the priority of table_ref production in a join. */ %left TABLE_REF_PRIORITY %left SET_VAR -%left OR_OR_SYM OR_SYM OR2_SYM +%left OR_SYM OR2_SYM %left XOR %left AND_SYM AND_AND_SYM %left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE @@ -1048,9 +1049,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %left '|' %left '&' %left SHIFT_LEFT SHIFT_RIGHT -%left '-' '+' +%left '-' '+' ORACLE_CONCAT_SYM %left '*' '/' DIV_SYM MOD_SYM %left '^' +%left MYSQL_CONCAT_SYM %left NEG '~' %right NOT_SYM NOT2_SYM %right BINARY COLLATE_SYM @@ -1196,6 +1198,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); variable variable_aux bool_pri predicate bit_expr parenthesized_expr table_wild simple_expr column_default_non_parenthesized_expr udf_expr + primary_expr string_factor_expr mysql_concatenation_expr select_sublist_qualified_asterisk expr_or_default set_expr_or_default geometry_function signed_literal expr_or_literal @@ -1469,8 +1472,9 @@ END_OF_INPUT %type '-' '+' '*' '/' '%' '(' ')' - ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM + ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM BETWEEN_SYM CASE_SYM THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM + MYSQL_CONCAT_SYM ORACLE_CONCAT_SYM %type opt_with_clause with_clause @@ -9443,14 +9447,14 @@ predicate: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr LIKE simple_expr opt_escape + | bit_expr LIKE mysql_concatenation_expr opt_escape { $$= new (thd->mem_root) Item_func_like(thd, $1, $3, $4, Lex->escape_used); if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | bit_expr not LIKE simple_expr opt_escape + | bit_expr not LIKE mysql_concatenation_expr opt_escape { Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $5, Lex->escape_used); @@ -9501,6 +9505,13 @@ bit_expr: if (unlikely($$ == NULL)) MYSQL_YYABORT; } + | bit_expr ORACLE_CONCAT_SYM bit_expr + { + $$= new (thd->mem_root) Item_func_concat_operator_oracle(thd, + $1, $3); + if (unlikely($$ == NULL)) + MYSQL_YYABORT; + } | bit_expr '+' bit_expr %prec '+' { $$= new (thd->mem_root) Item_func_plus(thd, $1, $3); @@ -9574,7 +9585,7 @@ bit_expr: if (unlikely($$ == NULL)) MYSQL_YYABORT; } - | simple_expr + | mysql_concatenation_expr %prec '^' ; or: @@ -9909,28 +9920,29 @@ column_default_non_parenthesized_expr: } ; -simple_expr: +primary_expr: column_default_non_parenthesized_expr | explicit_cursor_attr - | simple_expr COLLATE_SYM collation_name %prec NEG + | '(' parenthesized_expr ')' { $$= $2; } + ; + +string_factor_expr: + primary_expr + | string_factor_expr COLLATE_SYM collation_name { if (unlikely(!($$= new (thd->mem_root) Item_func_set_collation(thd, $1, $3)))) MYSQL_YYABORT; } - | '(' parenthesized_expr ')' { $$= $2; } - | BINARY simple_expr %prec NEG + ; + +simple_expr: + string_factor_expr %prec NEG + | BINARY simple_expr { Type_cast_attributes at(&my_charset_bin); if (unlikely(!($$= type_handler_long_blob.create_typecast_item(thd, $2, at)))) MYSQL_YYABORT; } - | simple_expr OR_OR_SYM simple_expr - { - $$= new (thd->mem_root) Item_func_concat_operator_oracle(thd, - $1, $3); - if (unlikely($$ == NULL)) - MYSQL_YYABORT; - } | '+' simple_expr %prec NEG { $$= $2; @@ -9955,6 +9967,17 @@ simple_expr: } ; +mysql_concatenation_expr: + simple_expr + | mysql_concatenation_expr MYSQL_CONCAT_SYM simple_expr + { + $$= new (thd->mem_root) Item_func_concat(thd, $1, $3); + if (unlikely($$ == NULL)) + MYSQL_YYABORT; + } + ; + + /* Function call syntax using official SQL 2003 keywords. Because the function name is an official token,