Skip to content

Commit bfcf00f

Browse files
Fix issue in parsing TRY_CAST() function (#1391)
* Fix issue in parsing TRY_CAST() function * Fix issue in parsing TRY_CAST() function * Add parser, deparser, validator and vistior implementation for try_cast function * Update toString() method of TryCastExpression class
1 parent 93b8c8b commit bfcf00f

File tree

9 files changed

+189
-2
lines changed

9 files changed

+189
-2
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@
4343
<version>5.8.1</version>
4444
<scope>test</scope>
4545
</dependency>
46+
<dependency>
47+
<groupId>org.junit.jupiter</groupId>
48+
<artifactId>junit-jupiter-api</artifactId>
49+
<version>5.7.1</version>
50+
<scope>test</scope>
51+
</dependency>
4652
<dependency>
4753
<groupId>org.mockito</groupId>
4854
<artifactId>mockito-core</artifactId>

src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ public interface ExpressionVisitor {
115115

116116
void visit(CastExpression cast);
117117

118+
void visit(TryCastExpression cast);
119+
118120
void visit(Modulo modulo);
119121

120122
void visit(AnalyticExpression aexpr);

src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,11 @@ public void visit(CastExpression expr) {
304304
expr.getLeftExpression().accept(this);
305305
}
306306

307+
@Override
308+
public void visit(TryCastExpression expr) {
309+
expr.getLeftExpression().accept(this);
310+
}
311+
307312
@Override
308313
public void visit(Modulo expr) {
309314
visitBinaryExpression(expr);
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2019 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.expression;
11+
12+
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
13+
import net.sf.jsqlparser.statement.create.table.ColDataType;
14+
15+
public class TryCastExpression extends ASTNodeAccessImpl implements Expression {
16+
17+
private Expression leftExpression;
18+
private ColDataType type;
19+
private RowConstructor rowConstructor;
20+
private boolean useCastKeyword = true;
21+
22+
public RowConstructor getRowConstructor() {
23+
return rowConstructor;
24+
}
25+
26+
public void setRowConstructor(RowConstructor rowConstructor) {
27+
this.rowConstructor = rowConstructor;
28+
this.type = null;
29+
}
30+
31+
public TryCastExpression withRowConstructor(RowConstructor rowConstructor) {
32+
setRowConstructor(rowConstructor);
33+
return this;
34+
}
35+
36+
public ColDataType getType() {
37+
return type;
38+
}
39+
40+
public void setType(ColDataType type) {
41+
this.type = type;
42+
this.rowConstructor = null;
43+
}
44+
45+
public Expression getLeftExpression() {
46+
return leftExpression;
47+
}
48+
49+
public void setLeftExpression(Expression expression) {
50+
leftExpression = expression;
51+
}
52+
53+
@Override
54+
public void accept(ExpressionVisitor expressionVisitor) {
55+
expressionVisitor.visit(this);
56+
}
57+
58+
public boolean isUseCastKeyword() {
59+
return useCastKeyword;
60+
}
61+
62+
public void setUseCastKeyword(boolean useCastKeyword) {
63+
this.useCastKeyword = useCastKeyword;
64+
}
65+
66+
@Override
67+
public String toString() {
68+
if (useCastKeyword) {
69+
return rowConstructor!=null
70+
? "TRY_CAST(" + leftExpression + " AS " + rowConstructor.toString() + ")"
71+
: "TRY_CAST(" + leftExpression + " AS " + type.toString() + ")";
72+
} else {
73+
return leftExpression + "::" + type.toString();
74+
}
75+
}
76+
77+
public TryCastExpression withType(ColDataType type) {
78+
this.setType(type);
79+
return this;
80+
}
81+
82+
public TryCastExpression withUseCastKeyword(boolean useCastKeyword) {
83+
this.setUseCastKeyword(useCastKeyword);
84+
return this;
85+
}
86+
87+
public TryCastExpression withLeftExpression(Expression leftExpression) {
88+
this.setLeftExpression(leftExpression);
89+
return this;
90+
}
91+
92+
public <E extends Expression> E getLeftExpression(Class<E> type) {
93+
return type.cast(getLeftExpression());
94+
}
95+
}

src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,11 @@ public void visit(CastExpression cast) {
462462
cast.getLeftExpression().accept(this);
463463
}
464464

465+
@Override
466+
public void visit(TryCastExpression cast) {
467+
cast.getLeftExpression().accept(this);
468+
}
469+
465470
@Override
466471
public void visit(Modulo modulo) {
467472
visitBinaryExpression(modulo);

src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,21 @@ public void visit(CastExpression cast) {
599599
}
600600
}
601601

602+
@Override
603+
public void visit(TryCastExpression cast) {
604+
if (cast.isUseCastKeyword()) {
605+
buffer.append("TRY_CAST(");
606+
cast.getLeftExpression().accept(this);
607+
buffer.append(" AS ");
608+
buffer.append( cast.getRowConstructor()!=null ? cast.getRowConstructor() : cast.getType() );
609+
buffer.append(")");
610+
} else {
611+
cast.getLeftExpression().accept(this);
612+
buffer.append("::");
613+
buffer.append(cast.getType());
614+
}
615+
}
616+
602617
@Override
603618
public void visit(Modulo modulo) {
604619
visitBinaryExpression(modulo, " % ");

src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,11 @@ public void visit(CastExpression cast) {
348348
cast.getLeftExpression().accept(this);
349349
}
350350

351+
@Override
352+
public void visit(TryCastExpression cast) {
353+
cast.getLeftExpression().accept(this);
354+
}
355+
351356
@Override
352357
public void visit(Modulo modulo) {
353358
visitBinaryExpression(modulo, " % ");

src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
153153
| <K_CASCADE: "CASCADE">
154154
| <K_CASE:"CASE">
155155
| <K_CASEWHEN:"CASEWHEN"> /* H2 casewhen function */
156-
| <K_CAST:"CAST">
156+
| <K_CAST: "CAST">
157+
| <K_TRY_CAST: "TRY_CAST">
157158
| <K_CHARACTER:"CHARACTER">
158159
| <K_CHANGE:"CHANGE">
159160
| <K_CHECK:"CHECK">
@@ -1635,7 +1636,7 @@ String RelObjectNameWithoutValue() :
16351636
(tk=<S_IDENTIFIER> | tk=<S_QUOTED_IDENTIFIER>
16361637
| tk=<K_ALGORITHM> | tk=<K_AT>
16371638
| tk=<K_BYTE> | tk=<K_CHAR> | tk=<K_CHANGE> | tk=<K_CHARACTER>
1638-
| tk=<K_CAST> | tk=<K_COMMENT> | tk=<K_COSTS> | tk=<K_DISABLE> | tk=<K_DESC>
1639+
| tk=<K_CAST> | tk =<K_TRY_CAST> | tk=<K_COMMENT> | tk=<K_COSTS> | tk=<K_DISABLE> | tk=<K_DESC>
16391640
| tk=<K_DO> | tk=<K_EXTRACT> | tk=<K_FILTER> | tk=<K_FIRST> | tk=<K_FOLLOWING> | tk=<K_JSON>
16401641
| tk=<K_LAST> | tk=<K_LEADING> | tk=<K_MATERIALIZED> | tk=<K_NULLS> | tk=<K_PARTITION> | tk=<K_RANGE>
16411642
| tk=<K_ROW> | tk=<K_ROWS> | tk=<K_SIBLINGS> | tk=<K_XML>
@@ -3692,6 +3693,8 @@ Expression PrimaryExpression() #PrimaryExpression:
36923693

36933694
| LOOKAHEAD(2) retval=CastExpression()
36943695

3696+
| LOOKAHEAD(2) retval=TryCastExpression()
3697+
36953698
//| LOOKAHEAD(2) retval=RowConstructor()
36963699

36973700
// support timestamp expressions
@@ -4303,6 +4306,31 @@ CastExpression CastExpression():
43034306
}
43044307
}
43054308

4309+
TryCastExpression TryCastExpression():
4310+
{
4311+
TryCastExpression retval = new TryCastExpression();
4312+
ColDataType type = null;
4313+
RowConstructor rowConstructor = null;
4314+
Expression expression = null;
4315+
boolean useCastKeyword;
4316+
}
4317+
{
4318+
<K_TRY_CAST>
4319+
"("
4320+
expression=SimpleExpression()
4321+
<K_AS> { retval.setUseCastKeyword(true); }
4322+
(
4323+
LOOKAHEAD(3) rowConstructor = RowConstructor() { retval.setRowConstructor(rowConstructor); }
4324+
| type=ColDataType() { retval.setType(type); }
4325+
)
4326+
")"
4327+
4328+
{
4329+
retval.setLeftExpression(expression);
4330+
return retval;
4331+
}
4332+
}
4333+
43064334
Expression CaseWhenExpression() #CaseWhenExpression:
43074335
{
43084336
CaseExpression caseExp = new CaseExpression();

src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1756,6 +1756,32 @@ public void testCastTypeProblem2() throws JSQLParserException {
17561756
assertSqlCanBeParsedAndDeparsed(stmt);
17571757
}
17581758

1759+
@Test
1760+
public void testTryCast() throws JSQLParserException {
1761+
String stmt = "SELECT TRY_CAST(a AS varchar) FROM tabelle1";
1762+
assertSqlCanBeParsedAndDeparsed(stmt);
1763+
stmt = "SELECT CAST(a AS varchar2) FROM tabelle1";
1764+
assertSqlCanBeParsedAndDeparsed(stmt);
1765+
}
1766+
1767+
@Test
1768+
public void testTryCastInTryCast() throws JSQLParserException {
1769+
String stmt = "SELECT TRY_CAST(TRY_CAST(a AS numeric) AS varchar) FROM tabelle1";
1770+
assertSqlCanBeParsedAndDeparsed(stmt);
1771+
}
1772+
1773+
@Test
1774+
public void testTryCastInTryCast2() throws JSQLParserException {
1775+
String stmt = "SELECT TRY_CAST('test' + TRY_CAST(assertEqual AS numeric) AS varchar) FROM tabelle1";
1776+
assertSqlCanBeParsedAndDeparsed(stmt);
1777+
}
1778+
1779+
@Test
1780+
public void testTryCastTypeProblem() throws JSQLParserException {
1781+
String stmt = "SELECT TRY_CAST(col1 AS varchar (256)) FROM tabelle1";
1782+
assertSqlCanBeParsedAndDeparsed(stmt);
1783+
}
1784+
17591785
@Test
17601786
public void testMySQLHintStraightJoin() throws JSQLParserException {
17611787
String stmt = "SELECT col FROM tbl STRAIGHT_JOIN tbl2 ON tbl.id = tbl2.id";

0 commit comments

Comments
 (0)