Skip to content

Commit c8b12f0

Browse files
authored
Implement template parser. (#6474)
1 parent b1fda6d commit c8b12f0

File tree

7 files changed

+158
-47
lines changed

7 files changed

+158
-47
lines changed

core/src/main/java/com/alibaba/druid/sql/ast/expr/SQLVariantRefExpr.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,30 @@ public class SQLVariantRefExpr extends SQLExprImpl {
2929

3030
private boolean global;
3131
private boolean session;
32+
private boolean templateParameter;
33+
private boolean hasPrefixComma;
3234

3335
private int index = -1;
3436

3537
public SQLVariantRefExpr(String name) {
3638
this.name = name;
39+
if (name.startsWith("${") && name.endsWith("}")) {
40+
this.templateParameter = true;
41+
} else {
42+
this.templateParameter = false;
43+
}
44+
this.hasPrefixComma = true;
3745
}
3846

3947
public SQLVariantRefExpr(String name, SQLObject parent) {
4048
this.name = name;
4149
this.parent = parent;
50+
if (name.startsWith("${") && name.endsWith("}")) {
51+
this.templateParameter = true;
52+
} else {
53+
this.templateParameter = false;
54+
}
55+
this.hasPrefixComma = true;
4256
}
4357

4458
public SQLVariantRefExpr(String name, boolean global) {
@@ -70,6 +84,22 @@ public void setName(String name) {
7084
this.name = name;
7185
}
7286

87+
public boolean isTemplateParameter() {
88+
return templateParameter;
89+
}
90+
91+
public void setTemplateParameter(boolean templateParameter) {
92+
this.templateParameter = templateParameter;
93+
}
94+
95+
public boolean isHasPrefixComma() {
96+
return hasPrefixComma;
97+
}
98+
99+
public void setHasPrefixComma(boolean hasPrefixComma) {
100+
this.hasPrefixComma = hasPrefixComma;
101+
}
102+
73103
public void output(StringBuilder buf) {
74104
buf.append(this.name);
75105
}

core/src/main/java/com/alibaba/druid/sql/parser/Lexer.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,7 +2278,7 @@ public void scanVariable() {
22782278
if (ch != ':' && ch != '#' && ch != '$' && !(ch == '@' && dialectFeatureEnabled(ScanVariableAt))) {
22792279
throw new ParserException("illegal variable. " + info());
22802280
}
2281-
2281+
boolean templateParameter = false;
22822282
mark = pos;
22832283
bufPos = 1;
22842284
char ch;
@@ -2296,13 +2296,14 @@ public void scanVariable() {
22962296
boolean ident = false;
22972297
for (; ; ) {
22982298
ch = charAt(++pos);
2299-
if (isEOF() || ch == ';' || ch == ';' || ch == '\r' || ch == '\n') {
2299+
if (isEOF() || (templateParameter && (ch == ';' || ch == ';' || ch == '\r'))) {
23002300
pos--;
23012301
bufPos--;
23022302
break;
23032303
}
23042304

23052305
if (ch == '}' && !ident) {
2306+
templateParameter = false;
23062307
if (isIdentifierChar(charAt(pos + 1))) {
23072308
bufPos++;
23082309
ident = true;
@@ -2313,6 +2314,7 @@ public void scanVariable() {
23132314

23142315
if (ident && ch == '$') {
23152316
if (charAt(pos + 1) == '{') {
2317+
templateParameter = true;
23162318
bufPos++;
23172319
ident = false;
23182320
continue;
@@ -2323,7 +2325,7 @@ public void scanVariable() {
23232325
if (isWhitespace(ch)) {
23242326
pos--;
23252327
break;
2326-
} else if (ch == ',' || ch == ')' || ch == '(' || ch == ';') {
2328+
} else if (ch == ',' || ch == ')' || ch == '(') {
23272329
pos--;
23282330
break;
23292331
}

core/src/main/java/com/alibaba/druid/sql/parser/SQLExprParser.java

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2927,8 +2927,7 @@ public void orderBy(List<SQLSelectOrderByItem> items, SQLObject parent) {
29272927
SQLSelectOrderByItem item = parseSelectOrderByItem();
29282928
item.setParent(parent);
29292929
items.add(item);
2930-
while (lexer.token == Token.COMMA) {
2931-
lexer.nextToken();
2930+
while (lexer.nextIf(Token.COMMA) || (item.getExpr() instanceof SQLVariantRefExpr && lexer.token == IDENTIFIER)) {
29322931
item = parseSelectOrderByItem();
29332932
item.setParent(parent);
29342933
items.add(item);
@@ -3532,9 +3531,14 @@ public SQLExpr andRest(SQLExpr expr) {
35323531
SQLBinaryOperator operator = andRestGetAndOperator();
35333532

35343533
expr = new SQLBinaryOpExpr(expr, operator, rightExp, dbType);
3535-
} else if (token == VARIANT) {
3536-
expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Blank, new SQLVariantRefExpr(lexer.stringVal()), dbType);
3534+
} else if (token == Token.VARIANT) {
3535+
String value = lexer.stringVal();
35373536
lexer.nextToken();
3537+
SQLExpr variantExpr = new SQLVariantRefExpr(value);
3538+
if (lexer.token == Token.IN) {
3539+
variantExpr = inRest(variantExpr);
3540+
}
3541+
expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Blank, variantExpr, dbType);
35383542
} else {
35393543
break;
35403544
}
@@ -3622,6 +3626,14 @@ public SQLExpr orRest(SQLExpr expr) {
36223626
SQLBinaryOperator op = orRestGetOrOperator();
36233627

36243628
expr = new SQLBinaryOpExpr(expr, op, rightExp, dbType);
3629+
} else if (lexer.token == Token.VARIANT) {
3630+
String value = lexer.stringVal();
3631+
lexer.nextToken();
3632+
SQLExpr variantExpr = new SQLVariantRefExpr(value);
3633+
if (lexer.token == Token.IN) {
3634+
variantExpr = inRest(variantExpr);
3635+
}
3636+
expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Blank, variantExpr, dbType);
36253637
} else {
36263638
break;
36273639
}
@@ -5954,46 +5966,47 @@ public SQLSelectItem parseSelectItem() {
59545966

59555967
String alias;
59565968
List<String> aliasList = null;
5957-
switch (lexer.token) {
5958-
case FULL:
5959-
case TABLESPACE:
5960-
alias = lexer.stringVal();
5961-
lexer.nextToken();
5962-
break;
5963-
case AS:
5964-
lexer.nextTokenAlias();
5965-
if (lexer.token == Token.LITERAL_INT) {
5966-
alias = '"' + lexer.stringVal() + '"';
5967-
lexer.nextToken();
5968-
} else if (lexer.token == Token.LPAREN) {
5969+
if (expr instanceof SQLVariantRefExpr && ((SQLVariantRefExpr) expr).isTemplateParameter() && lexer.token != AS) {
5970+
alias = null;
5971+
} else {
5972+
switch (lexer.token) {
5973+
case FULL:
5974+
case TABLESPACE:
5975+
alias = lexer.stringVal();
59695976
lexer.nextToken();
5970-
aliasList = new ArrayList<String>();
5971-
5972-
for (; ; ) {
5973-
String stringVal = lexer.stringVal();
5977+
break;
5978+
case AS:
5979+
lexer.nextTokenAlias();
5980+
if (lexer.token == Token.LITERAL_INT) {
5981+
alias = '"' + lexer.stringVal() + '"';
59745982
lexer.nextToken();
5975-
5976-
aliasList.add(stringVal);
5977-
5978-
if (lexer.token() == Token.COMMA) {
5983+
} else if (lexer.token == Token.LPAREN) {
5984+
lexer.nextToken();
5985+
aliasList = new ArrayList<String>();
5986+
for (; ; ) {
5987+
String stringVal = lexer.stringVal();
59795988
lexer.nextToken();
5980-
continue;
5989+
aliasList.add(stringVal);
5990+
if (lexer.token() == Token.COMMA) {
5991+
lexer.nextToken();
5992+
continue;
5993+
}
5994+
break;
59815995
}
5982-
break;
5983-
}
5984-
accept(Token.RPAREN);
5996+
accept(Token.RPAREN);
59855997

5998+
alias = null;
5999+
} else {
6000+
alias = alias();
6001+
}
6002+
break;
6003+
case EOF:
59866004
alias = null;
5987-
} else {
5988-
alias = alias();
5989-
}
5990-
break;
5991-
case EOF:
5992-
alias = null;
5993-
break;
5994-
default:
5995-
alias = as();
5996-
break;
6005+
break;
6006+
default:
6007+
alias = as();
6008+
break;
6009+
}
59976010
}
59986011

59996012
if (alias == null && isEnabled(SQLParserFeature.SelectItemGenerateAlias)

core/src/main/java/com/alibaba/druid/sql/parser/SQLSelectParser.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,11 +1123,16 @@ protected SQLExpr parseGroupByItem() {
11231123

11241124
protected void parseSelectList(SQLSelectQueryBlock queryBlock) {
11251125
final List<SQLSelectItem> selectList = queryBlock.getSelectList();
1126+
boolean hasComma = true;
11261127
for (; ; ) {
11271128
final SQLSelectItem selectItem = this.exprParser.parseSelectItem();
11281129
selectList.add(selectItem);
11291130
selectItem.setParent(queryBlock);
1130-
1131+
if (!hasComma && selectItem.getExpr() instanceof SQLVariantRefExpr) {
1132+
SQLVariantRefExpr sqlVariantRefExpr = (SQLVariantRefExpr) selectItem.getExpr();
1133+
sqlVariantRefExpr.setHasPrefixComma(false);
1134+
hasComma = true;
1135+
}
11311136
//https://github.com/alibaba/druid/issues/5708
11321137
if (lexer.hasComment()
11331138
&& lexer.isKeepComments()
@@ -1137,7 +1142,14 @@ protected void parseSelectList(SQLSelectQueryBlock queryBlock) {
11371142
}
11381143

11391144
if (lexer.token != Token.COMMA) {
1140-
break;
1145+
if (lexer.token == Token.VARIANT) {
1146+
hasComma = false;
1147+
continue;
1148+
} else if (selectItem.getExpr() instanceof SQLVariantRefExpr && ((SQLVariantRefExpr) selectItem.getExpr()).isTemplateParameter() && lexer.token != Token.FROM) {
1149+
continue;
1150+
} else {
1151+
break;
1152+
}
11411153
}
11421154

11431155
int line = lexer.line;

core/src/main/java/com/alibaba/druid/sql/parser/SQLStatementParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,11 @@ public void parseStatementList(List<SQLStatement> statementList, int max, SQLObj
222222
case SELECT: {
223223
MySqlHintStatement hintStatement = null;
224224
if (i == 1
225-
&& statementList.size() > 0
225+
&& !statementList.isEmpty()
226226
&& statementList.get(statementList.size() - i) instanceof MySqlHintStatement) {
227227
hintStatement = (MySqlHintStatement) statementList.get(statementList.size() - i);
228228
} else if (i > 0 && dialectFeatureEnabled(ParseStatementListSelectUnsupportedSyntax) && !semi
229-
&& !(statementList.size() > 0 && statementList.get(statementList.size() - i).isAfterSemi())
229+
&& !(!statementList.isEmpty() && statementList.size() - i >= 0 && statementList.get(statementList.size() - i).isAfterSemi())
230230
) {
231231
throw new ParserException("syntax error. " + lexer.info());
232232
}

core/src/main/java/com/alibaba/druid/sql/visitor/SQLASTOutputVisitor.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -586,8 +586,11 @@ protected void printSelectList(List<SQLSelectItem> selectList) {
586586
lineItemCount = 0;
587587
println();
588588
}
589-
590-
print0(", ");
589+
if (selectItem.getExpr() instanceof SQLVariantRefExpr && !((SQLVariantRefExpr) selectItem.getExpr()).isHasPrefixComma()) {
590+
print0(" ");
591+
} else {
592+
print0(", ");
593+
}
591594
}
592595

593596
if (selectItem.getClass() == SQLSelectItem.class) {

core/src/test/resources/bvt/parser/clickhouse/0.txt

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,57 @@ select ${if(len(a)=0,' 1=1 ',"c")}, ${if(len(a)=0,' 1=1 ',"c")} from b
99
SELECT ${if(len(a)=0,' 1=1 ',"c")}, ${if(len(a)=0,' 1=1 ',"c")}
1010
FROM b
1111
------------------------------------------------------------------------------------------------------------------------
12+
select a from test where 1=1 ${if(a=1, "and b","and c")} in d
13+
--------------------
14+
SELECT a
15+
FROM test
16+
WHERE 1 = 1 ${if(a=1, "and b","and c")} IN (d)
17+
------------------------------------------------------------------------------------------------------------------------
18+
select a from test where 1=1
19+
and (
20+
a.report_item_code in ('${gFinReportItem}') or a.report_item_code ='B002'
21+
${if(find('E069',gFinReportItem)>0," or (c.yn_offset_report = 'Y' and a.report_item_code ='E06802') "," ")}
22+
)
23+
--------------------
24+
SELECT a
25+
FROM test
26+
WHERE 1 = 1
27+
AND (a.report_item_code IN ('${gFinReportItem}')
28+
OR a.report_item_code = 'B002' ${if(find('E069',gFinReportItem)>0," or (c.yn_offset_report = 'Y' and a.report_item_code ='E06802') "," ")})
29+
------------------------------------------------------------------------------------------------------------------------
30+
select ${if(len(a)=0,' 1=1 ',"c")}, ${if(len(a)=0,' 1=1 ',"c")} from b
31+
--------------------
32+
SELECT ${if(len(a)=0,' 1=1 ',"c")}, ${if(len(a)=0,' 1=1 ',"c")}
33+
FROM b
34+
------------------------------------------------------------------------------------------------------------------------
35+
select b.merge_region_code as region_code,b.merge_region_name as region_name,b.govern_region_area_union as region_area_subtotal
36+
${if(gRegionAreaLevel='合并营运区',",b.govern_region_area_union as region_area_code,b.govern_region_area_union as region_area_name",",b.region_area_code as region_area_code,b.region_area_name as region_area_name")}
37+
-- ${if(len(gQtrMonth)=5,"/*","")}
38+
,sum(case when a.stat_date = ${gQtrMonth} then a.period_loc_amt else 0 end) as bq_amt
39+
from b
40+
--------------------
41+
SELECT b.merge_region_code AS region_code, b.merge_region_name AS region_name, b.govern_region_area_union AS region_area_subtotal ${if(gRegionAreaLevel='合并营运区',",b.govern_region_area_union as region_area_code,b.govern_region_area_union as region_area_name",",b.region_area_code as region_area_code,b.region_area_name as region_area_name")} -- ${if(len(gQtrMonth)=5,"/*","")}
42+
, sum(CASE
43+
WHEN a.stat_date = ${gQtrMonth} THEN a.period_loc_amt
44+
ELSE 0
45+
END) AS bq_amt
46+
FROM b
47+
------------------------------------------------------------------------------------------------------------------------
48+
select
49+
${if(gGyzy_hz=='G'," buyer_name as buyer_name, "," syr as buyer_name, ")}
50+
substringUTF8(buyer_name,1,positionUTF8(buyer_name,'(')-1) as tg
51+
from a
52+
--------------------
53+
SELECT ${if(gGyzy_hz=='G'," buyer_name as buyer_name, "," syr as buyer_name, ")}
54+
, substringUTF8(buyer_name, 1, positionUTF8(buyer_name, '(') - 1) AS tg
55+
FROM a
56+
------------------------------------------------------------------------------------------------------------------------
57+
select c from b where c=1 ${if(len(a)=0,'and 1=1',"and a>0")} ${if(len(a)=0,'and 1=1',"and a>0")}
58+
--------------------
59+
SELECT c
60+
FROM b
61+
WHERE c = 1 ${if(len(a)=0,'and 1=1',"and a>0")} ${if(len(a)=0,'and 1=1',"and a>0")}
62+
------------------------------------------------------------------------------------------------------------------------
1263
--test
1364
select a from b
1465
--------------------

0 commit comments

Comments
 (0)