Skip to content

Commit a5204f6

Browse files
authored
Add support for AT TIME ZONE expressions (#1196)
* Add support for AT TIME ZONE expressions * adding tests * Fixing imports
1 parent ed089f1 commit a5204f6

File tree

11 files changed

+131
-1
lines changed

11 files changed

+131
-1
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,6 @@ public interface ExpressionVisitor {
163163
void visit(VariableAssignment aThis);
164164

165165
void visit(XMLSerializeExpr aThis);
166+
167+
void visit(TimezoneExpression aThis);
166168
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,4 +576,9 @@ public void visit(XMLSerializeExpr expr) {
576576
elm.getExpression().accept(this);
577577
}
578578
}
579+
580+
@Override
581+
public void visit(TimezoneExpression expr) {
582+
expr.getLeftExpression().accept(this);
583+
}
579584
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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+
14+
import java.util.ArrayList;
15+
import java.util.List;
16+
17+
public class TimezoneExpression extends ASTNodeAccessImpl implements Expression {
18+
19+
private Expression leftExpression;
20+
private ArrayList<String> timezoneExpressions = new ArrayList<>();
21+
22+
public Expression getLeftExpression() {
23+
return leftExpression;
24+
}
25+
26+
public void setLeftExpression(Expression expression) {
27+
leftExpression = expression;
28+
}
29+
30+
@Override
31+
public void accept(ExpressionVisitor expressionVisitor) {
32+
expressionVisitor.visit(this);
33+
}
34+
35+
public List<String> getTimezoneExpressions() {
36+
return timezoneExpressions;
37+
}
38+
39+
public void addTimezoneExpression(String timezoneExpr) {
40+
this.timezoneExpressions.add(timezoneExpr);
41+
}
42+
43+
@Override
44+
public String toString() {
45+
String returnValue = getLeftExpression().toString();
46+
for (String expr : timezoneExpressions) {
47+
returnValue += " AT TIME ZONE " + expr;
48+
}
49+
50+
return returnValue;
51+
}
52+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import java.util.ArrayList;
1313
import java.util.List;
14+
1415
import net.sf.jsqlparser.expression.AllComparisonExpression;
1516
import net.sf.jsqlparser.expression.AnalyticExpression;
1617
import net.sf.jsqlparser.expression.AnyComparisonExpression;
@@ -48,6 +49,7 @@
4849
import net.sf.jsqlparser.expression.TimeKeyExpression;
4950
import net.sf.jsqlparser.expression.TimeValue;
5051
import net.sf.jsqlparser.expression.TimestampValue;
52+
import net.sf.jsqlparser.expression.TimezoneExpression;
5153
import net.sf.jsqlparser.expression.UserVariable;
5254
import net.sf.jsqlparser.expression.ValueListExpression;
5355
import net.sf.jsqlparser.expression.VariableAssignment;
@@ -928,4 +930,9 @@ public void visit(CreateSynonym createSynonym) {
928930
private static <T> void throwUnsupported(T type){
929931
throw new UnsupportedOperationException(String.format("Finding tables from %s is not supported", type.getClass().getSimpleName()));
930932
}
933+
934+
@Override
935+
public void visit(TimezoneExpression aThis) {
936+
aThis.getLeftExpression().accept(this);
937+
}
931938
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import net.sf.jsqlparser.expression.TimeKeyExpression;
5252
import net.sf.jsqlparser.expression.TimeValue;
5353
import net.sf.jsqlparser.expression.TimestampValue;
54+
import net.sf.jsqlparser.expression.TimezoneExpression;
5455
import net.sf.jsqlparser.expression.UserVariable;
5556
import net.sf.jsqlparser.expression.ValueListExpression;
5657
import net.sf.jsqlparser.expression.VariableAssignment;
@@ -989,4 +990,13 @@ public void visit(XMLSerializeExpr expr) {
989990
}
990991
buffer.append(") AS ").append(expr.getDataType()).append(")");
991992
}
993+
994+
@Override
995+
public void visit(TimezoneExpression var) {
996+
var.getLeftExpression().accept(this);
997+
998+
for (String expr : var.getTimezoneExpressions()) {
999+
buffer.append(" AT TIME ZONE " + expr);
1000+
}
1001+
}
9921002
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import net.sf.jsqlparser.expression.TimeKeyExpression;
4747
import net.sf.jsqlparser.expression.TimeValue;
4848
import net.sf.jsqlparser.expression.TimestampValue;
49+
import net.sf.jsqlparser.expression.TimezoneExpression;
4950
import net.sf.jsqlparser.expression.UserVariable;
5051
import net.sf.jsqlparser.expression.ValueListExpression;
5152
import net.sf.jsqlparser.expression.VariableAssignment;
@@ -563,6 +564,11 @@ public void visit(VariableAssignment a) {
563564
}
564565
}
565566

567+
@Override
568+
public void visit(TimezoneExpression a) {
569+
validateOptionalExpression(a.getLeftExpression());
570+
}
571+
566572
@Override
567573
public void visit(XMLSerializeExpr xml) {
568574
// TODO this feature seams very close to a jsqlparser-user usecase

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
133133
| <K_APPLY:"APPLY">
134134
| <K_ARRAY_LITERAL: "ARRAY" >
135135
| <K_AS: "AS">
136+
| <K_AT: "AT">
136137
| <K_ASC:"ASC">
137138
| <K_AUTHORIZATION:"AUTHORIZATION">
138139
| <K_BEGIN:"BEGIN">
@@ -3301,6 +3302,7 @@ Expression PrimaryExpression() #PrimaryExpression:
33013302
{
33023303
Expression retval = null;
33033304
CastExpression castExpr = null;
3305+
TimezoneExpression timezoneExpr = null;
33043306
Token token = null;
33053307
Token sign = null;
33063308
String tmp = "";
@@ -3399,6 +3401,20 @@ Expression PrimaryExpression() #PrimaryExpression:
33993401
retval=castExpr;
34003402
} )*
34013403

3404+
( <K_AT> <K_DATETIMELITERAL> <K_ZONE> token=<S_CHAR_LITERAL> {
3405+
if (timezoneExpr == null)
3406+
timezoneExpr = new TimezoneExpression();
3407+
3408+
timezoneExpr.addTimezoneExpression(token.image);
3409+
} )*
3410+
3411+
{
3412+
if (timezoneExpr != null && !timezoneExpr.getTimezoneExpressions().isEmpty()) {
3413+
timezoneExpr.setLeftExpression(retval);
3414+
retval=timezoneExpr;
3415+
}
3416+
}
3417+
34023418
{
34033419
if (sign != null) {
34043420
retval = new SignedExpression(sign.image.charAt(0), retval);

src/test/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapterTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,4 +208,10 @@ public void testAnalyticFunctionWithoutExpression502() throws JSQLParserExceptio
208208
expr.accept(adapter);
209209
}
210210

211+
@Test
212+
public void testAtTimeZoneExpression() throws JSQLParserException {
213+
Expression expr = CCJSqlParserUtil.parseExpression("DATE(date1 AT TIME ZONE 'UTC' AT TIME ZONE 'australia/sydney')");
214+
ExpressionVisitorAdapter adapter = new ExpressionVisitorAdapter();
215+
expr.accept(adapter);
216+
}
211217
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,18 @@ public void testSelectItems() throws JSQLParserException {
882882
assertStatementCanBeDeparsedAs(select, statement);
883883
}
884884

885+
@Test
886+
public void testTimezoneExpression() throws JSQLParserException {
887+
String stmt = "SELECT creation_date AT TIME ZONE 'UTC'";
888+
assertSqlCanBeParsedAndDeparsed(stmt);
889+
}
890+
891+
@Test
892+
public void testTimezoneExpressionWithTwoTransformations() throws JSQLParserException {
893+
String stmt = "SELECT DATE(date1 AT TIME ZONE 'UTC' AT TIME ZONE 'australia/sydney') AS another_date";
894+
assertSqlCanBeParsedAndDeparsed(stmt);
895+
}
896+
885897
@Test
886898
public void testUnionWithOrderByAndLimitAndNoBrackets() throws JSQLParserException {
887899
String stmt = "SELECT id FROM table1 UNION SELECT id FROM table2 ORDER BY id ASC LIMIT 55";

src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,4 +642,14 @@ public void testNPEIssue1009() throws JSQLParserException {
642642

643643
assertThat(tablesNamesFinder.getTableList(stmt)).containsExactly("biz_fund_info");
644644
}
645+
646+
@Test
647+
public void testAtTimeZoneExpression() throws JSQLParserException {
648+
String sql = "SELECT DATE(date1 AT TIME ZONE 'UTC' AT TIME ZONE 'australia/sydney') AS another_date FROM mytbl";
649+
Statement stmt = CCJSqlParserUtil.parse(sql);
650+
TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
651+
List<String> tableList = tablesNamesFinder.getTableList(stmt);
652+
assertEquals(1, tableList.size());
653+
assertTrue(tableList.contains("mytbl"));
654+
}
645655
}

0 commit comments

Comments
 (0)