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:
@ -60,7 +60,9 @@ show session variables
|
||||
|
||||
## 已支持mode
|
||||
|
||||
(后续补充)
|
||||
1. `PIPES_AS_CONCAT`
|
||||
|
||||
在此模式下,'||'符号是一种字符串连接符号(同CONCAT()函数),而不是'OR'符号的同义词。(e.g., `'a'||'b' = 'ab'`, `1||0 = '10'`)
|
||||
|
||||
## 复合mode
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
{:
|
||||
|
||||
@ -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);
|
||||
|
||||
79
fe/src/test/java/org/apache/doris/analysis/SqlModeTest.java
Normal file
79
fe/src/test/java/org/apache/doris/analysis/SqlModeTest.java
Normal 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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user