Add pipes_as_concat_mode (#2252)

This commit will add a new sql mode named MODE_PIPES_AS_CONCAT:
Description:
1、If this mode is active, '||' will be handled different from the original way ('||' and 'or' are seen as the same symbols in Doris) that it can be used to concat two exps and returns a new string. For example, 'a' || 'b' = 'ab' and 1 || 0 = '10'.
2. User can active this mode by "SET sql_mode = PIPES_AS_CONCAT", and deactive it by "SET sql_mode = '' ".
This commit is contained in:
xy720
2019-11-22 15:01:53 +08:00
committed by Mingyu Chen
parent 297542bd3f
commit 79ff0ad2a4
5 changed files with 115 additions and 4 deletions

View File

@ -60,7 +60,9 @@ show session variables
## 已支持mode
(后续补充)
1. `PIPES_AS_CONCAT`
在此模式下,'||'符号是一种字符串连接符号(同CONCAT()函数),而不是'OR'符号的同义词。(e.g., `'a'||'b' = 'ab'`, `1||0 = '10'`)
## 复合mode

View File

@ -60,7 +60,9 @@ show session variables
## supported mode
(Work in progress)
1. `PIPES_AS_CONCAT`
Treat '||' as a string concatenation operator (same as CONCAT()) rather than as a synonym for OR. (e.g., `'a'||'b' = 'ab'`, `1||0 = '10'`)
## combine mode

View File

@ -210,7 +210,7 @@ terminal String KW_ADD, KW_ADMIN, KW_AFTER, KW_AGGREGATE, KW_ALL, KW_ALTER, KW_A
KW_LOCAL, KW_LOCATION,
KW_MAX, KW_MAX_VALUE, KW_MERGE, KW_MIN, KW_MIGRATE, KW_MIGRATIONS, KW_MODIFY,
KW_NAME, KW_NAMES, KW_NEGATIVE, KW_NO, KW_NOT, KW_NULL, KW_NULLS,
KW_OBSERVER, KW_OFFSET, KW_ON, KW_ONLY, KW_OPEN, KW_OR, KW_ORDER, KW_OUTER, KW_OVER,
KW_OBSERVER, KW_OFFSET, KW_ON, KW_ONLY, KW_OPEN, KW_OR, KW_PIPE, KW_ORDER, KW_OUTER, KW_OVER,
KW_PARTITION, KW_PARTITIONS, KW_PATH, KW_PRECEDING,
KW_PASSWORD, KW_PLUGIN, KW_PLUGINS,
KW_PRIMARY,
@ -294,6 +294,7 @@ nonterminal Expr expr, non_pred_expr, arithmetic_expr, timestamp_arithmetic_expr
nonterminal Expr set_expr_or_default;
nonterminal ArrayList<Expr> expr_list, values, row_value, opt_values;
nonterminal ArrayList<Expr> func_arg_list;
nonterminal ArrayList<Expr> expr_pipe_list;
nonterminal String select_alias, opt_table_alias;
nonterminal ArrayList<String> ident_list, opt_using_partition;
nonterminal ClusterName cluster_name;
@ -428,6 +429,7 @@ precedence left KW_NOT, NOT;
precedence left KW_BETWEEN, KW_IN, KW_IS, KW_EXISTS;
precedence left KW_LIKE, KW_REGEXP;
precedence left EQUAL, LESSTHAN, GREATERTHAN;
precedence left KW_PIPE;
precedence left ADD, SUBTRACT;
precedence left AT, STAR, DIVIDE, MOD, KW_DIV;
precedence left BITAND, BITOR, BITXOR, BITNOT;
@ -3500,6 +3502,11 @@ non_pred_expr ::=
/* Since "IF" is a keyword, need to special case this function */
| KW_IF LPAREN expr_list:exprs RPAREN
{: RESULT = new FunctionCallExpr("if", exprs); :}
/* For the case like e1 || e2 || e3 ... */
| expr_pipe_list:exprs
{:
RESULT = new FunctionCallExpr("concat", exprs);
:}
| cast_expr:c
{: RESULT = c; :}
| case_expr:c
@ -3538,6 +3545,21 @@ non_pred_expr ::=
{: RESULT = new BoolLiteral(false); :}
;
expr_pipe_list ::=
expr:e1 KW_PIPE expr:e2
{:
ArrayList<Expr> list = new ArrayList<Expr>();
list.add(e1);
list.add(e2);
RESULT = list;
:}
| expr_pipe_list:list KW_PIPE expr:e
{:
list.add(e);
RESULT = list;
:}
;
func_arg_list ::=
expr:item
{:

View File

@ -31,6 +31,7 @@ import java.util.ArrayList;
import org.apache.doris.analysis.SqlParserSymbols;
import org.apache.doris.common.util.SqlUtils;
import org.apache.doris.qe.SqlModeHelper;
%%
@ -352,7 +353,7 @@ import org.apache.doris.common.util.SqlUtils;
keywordMap.put("with", new Integer(SqlParserSymbols.KW_WITH));
keywordMap.put("work", new Integer(SqlParserSymbols.KW_WORK));
keywordMap.put("write", new Integer(SqlParserSymbols.KW_WRITE));
keywordMap.put("||", new Integer(SqlParserSymbols.KW_OR));
keywordMap.put("||", new Integer(SqlParserSymbols.KW_PIPE));
}
// map from token id to token description
@ -547,6 +548,11 @@ EndOfLineComment = "--" !({HintContent}|{ContainsLineTerminator}) {LineTerminato
Integer kw_id = keywordMap.get(text.toLowerCase());
/* Integer kw_id = keywordMap.get(text); */
if (kw_id != null) {
// if MODE_PIPES_AS_CONCAT is not active, treat '||' symbol as same as 'or' symbol
if ((kw_id == SqlParserSymbols.KW_PIPE) &&
((this.sql_mode & SqlModeHelper.MODE_PIPES_AS_CONCAT) == 0)) {
return newToken(SqlParserSymbols.KW_OR, text);
}
return newToken(kw_id.intValue(), text);
} else {
return newToken(SqlParserSymbols.IDENT, text);

View File

@ -0,0 +1,79 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.analysis;
import org.apache.doris.qe.SqlModeHelper;
import org.junit.Assert;
import org.junit.Test;
import java.io.StringReader;
public class SqlModeTest {
@Test
public void testScannerConstructor() {
String stmt = new String("SELECT * FROM db1.tbl1 WHERE name = 'BILL GATES'");
SqlParser parser = new SqlParser(new SqlScanner(new StringReader(stmt)));
SelectStmt selectStmt = null;
try {
selectStmt = (SelectStmt) parser.parse().value;
} catch (Exception e) {
Assert.fail(e.getMessage());
}
Assert.assertEquals("SELECT FROM `db1`.`tbl1` WHERE `name` = 'BILL GATES'", selectStmt.toSql());
parser = new SqlParser(new SqlScanner(new StringReader(stmt), SqlModeHelper.MODE_DEFAULT));
try {
selectStmt = (SelectStmt) parser.parse().value;
} catch (Exception e) {
Assert.fail(e.getMessage());
}
Assert.assertEquals("SELECT FROM `db1`.`tbl1` WHERE `name` = 'BILL GATES'", selectStmt.toSql());
}
@Test
public void testPipesAsConcatMode() {
// Mode Active
String stmt = new String("SELECT 'a' || 'b' || 'c'");
SqlParser parser = new SqlParser(new SqlScanner(new StringReader(stmt), SqlModeHelper.MODE_PIPES_AS_CONCAT));
SelectStmt selectStmt = null;
try {
selectStmt = (SelectStmt) parser.parse().value;
} catch (Exception e) {
Assert.fail(e.getMessage());
}
Expr expr = selectStmt.getSelectList().getItems().get(0).getExpr();
if (!(expr instanceof FunctionCallExpr)) {
Assert.fail("Mode not working");
}
Assert.assertEquals("concat('a', 'b', 'c')", expr.toSql());
// Mode DeActive
parser = new SqlParser(new SqlScanner(new StringReader(stmt), SqlModeHelper.MODE_DEFAULT));
try {
selectStmt = (SelectStmt) parser.parse().value;
} catch (Exception e) {
Assert.fail(e.getMessage());
}
expr = selectStmt.getSelectList().getItems().get(0).getExpr();
if (!(expr instanceof CompoundPredicate)) {
Assert.fail();
}
Assert.assertEquals("(('a') OR ('b')) OR ('c')", expr.toSql());
}
}