Skip to content

Commit 544b168

Browse files
feat: Databricks IGNORE/RESPECT NULLS
- IGNORE/RESPECT NULLS for aggregate functions - `VALUES` can have an `Alias` Signed-off-by: Andreas Reichel <andreas@manticore-projects.com>
1 parent e07f8d0 commit 544b168

File tree

20 files changed

+112
-30
lines changed

20 files changed

+112
-30
lines changed

settings.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
* This file was generated by the Gradle 'init' task.
33
*/
44

5-
rootProject.name = 'jsqlparser'
5+
rootProject.name = 'JSQLFormatter'

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public AnalyticExpression(Function function) {
7575
}
7676
}
7777
this.havingClause = function.getHavingClause();
78+
this.ignoreNullsOutside = function.isIgnoreNullsOutside();
7879
this.nullHandling = function.getNullHandling();
7980
this.funcOrderBy = function.getOrderByElements();
8081
this.limit = function.getLimit();

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public String toString() {
8686
private Column attributeColumn = null;
8787
private List<OrderByElement> orderByElements;
8888
private NullHandling nullHandling = null;
89+
private boolean ignoreNullsOutside = false; // IGNORE NULLS outside function parameters
8990
private Limit limit = null;
9091

9192
private KeepExpression keep = null;
@@ -148,6 +149,15 @@ public Function setNullHandling(NullHandling nullHandling) {
148149
return this;
149150
}
150151

152+
public boolean isIgnoreNullsOutside() {
153+
return ignoreNullsOutside;
154+
}
155+
156+
public Function setIgnoreNullsOutside(boolean ignoreNullsOutside) {
157+
this.ignoreNullsOutside = ignoreNullsOutside;
158+
return this;
159+
}
160+
151161
public Limit getLimit() {
152162
return limit;
153163
}
@@ -321,7 +331,7 @@ public String toString() {
321331
havingClause.appendTo(b);
322332
}
323333

324-
if (nullHandling != null) {
334+
if (nullHandling != null && !isIgnoreNullsOutside()) {
325335
switch (nullHandling) {
326336
case IGNORE_NULLS:
327337
b.append(" IGNORE NULLS");
@@ -357,6 +367,17 @@ public String toString() {
357367

358368
String ans = getName() + params;
359369

370+
if (nullHandling != null && isIgnoreNullsOutside()) {
371+
switch (nullHandling) {
372+
case IGNORE_NULLS:
373+
ans += " IGNORE NULLS";
374+
break;
375+
case RESPECT_NULLS:
376+
ans += " RESPECT NULLS";
377+
break;
378+
}
379+
}
380+
360381
if (attributeExpression != null) {
361382
ans += "." + attributeExpression;
362383
} else if (attributeColumn != null) {

src/main/java/net/sf/jsqlparser/statement/select/Values.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,21 @@
2020
public class Values extends Select implements FromItem {
2121

2222
private ExpressionList<Expression> expressions;
23+
private Alias alias;
2324

2425
public Values() {
25-
// empty constructor
26+
this(null, null);
2627
}
2728

2829
public Values(ExpressionList<Expression> expressions) {
2930
this.expressions = expressions;
3031
}
3132

33+
public Values(ExpressionList<Expression> expressions, Alias alias) {
34+
this.expressions = expressions;
35+
this.alias = alias;
36+
}
37+
3238
public ExpressionList<?> getExpressions() {
3339
return expressions;
3440
}
@@ -42,6 +48,9 @@ public void setExpressions(ExpressionList<Expression> expressions) {
4248
public StringBuilder appendSelectBodyTo(StringBuilder builder) {
4349
builder.append("VALUES ");
4450
builder.append(expressions.toString());
51+
if (alias != null) {
52+
builder.append(" ").append(alias);
53+
}
4554
return builder;
4655
}
4756

@@ -74,12 +83,12 @@ public void accept(FromItemVisitor fromItemVisitor) {
7483

7584
@Override
7685
public Alias getAlias() {
77-
return null;
86+
return alias;
7887
}
7988

8089
@Override
8190
public void setAlias(Alias alias) {
82-
91+
this.alias = alias;
8392
}
8493

8594
@Override

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ public void visit(Function function) {
613613
havingClause.getExpression().accept(this);
614614
}
615615

616-
if (function.getNullHandling() != null) {
616+
if (function.getNullHandling() != null && !function.isIgnoreNullsOutside()) {
617617
switch (function.getNullHandling()) {
618618
case IGNORE_NULLS:
619619
buffer.append(" IGNORE NULLS");
@@ -643,6 +643,17 @@ public void visit(Function function) {
643643
buffer.append(")");
644644
}
645645

646+
if (function.getNullHandling() != null && function.isIgnoreNullsOutside()) {
647+
switch (function.getNullHandling()) {
648+
case IGNORE_NULLS:
649+
buffer.append(" IGNORE NULLS");
650+
break;
651+
case RESPECT_NULLS:
652+
buffer.append(" RESPECT NULLS");
653+
break;
654+
}
655+
}
656+
646657
if (function.getAttribute() != null) {
647658
buffer.append(".").append(function.getAttribute());
648659
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,8 @@ public ValuesStatementDeParser(ExpressionVisitor expressionVisitor, StringBuilde
2525
public void deParse(Values values) {
2626
buffer.append("VALUES ");
2727
values.getExpressions().accept(expressionVisitor);
28+
if (values.getAlias() != null) {
29+
buffer.append(" ").append(values.getAlias());
30+
}
2831
}
2932
}

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

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5086,16 +5086,6 @@ void windowFun(AnalyticExpression retval):{
50865086
WindowDefinition winDef;
50875087
} {
50885088
(
5089-
[
5090-
(
5091-
<K_IGNORE> <K_NULLS> { retval.setNullHandling(Function.NullHandling.IGNORE_NULLS); retval.setIgnoreNullsOutside(true); }
5092-
)
5093-
|
5094-
(
5095-
<K_RESPECT> <K_NULLS> { retval.setNullHandling(Function.NullHandling.RESPECT_NULLS); retval.setIgnoreNullsOutside(true); }
5096-
)
5097-
]
5098-
50995089
<K_OVER> {retval.setType(AnalyticType.OVER);}
51005090
|
51015091
<K_WITHIN> <K_GROUP> {retval.setType(AnalyticType.WITHIN_GROUP);}
@@ -5146,8 +5136,13 @@ AnalyticExpression AnalyticExpression(Function function) :
51465136
Expression filter = null;
51475137
}
51485138
{
5149-
((<K_FILTER> "(" <K_WHERE> {retval.setType(AnalyticType.FILTER_ONLY);} filter = Expression() ")" [ LOOKAHEAD(2) windowFun(retval) ] )
5150-
| windowFun(retval))
5139+
(
5140+
(
5141+
<K_FILTER> "(" <K_WHERE> {retval.setType(AnalyticType.FILTER_ONLY);} filter = Expression() ")"
5142+
[ LOOKAHEAD(2) windowFun(retval) ]
5143+
)
5144+
| windowFun(retval)
5145+
)
51515146
{
51525147
retval.setFilterExpression(filter);
51535148
return retval;
@@ -5610,6 +5605,7 @@ Function InternalFunction(boolean escaped):
56105605

56115606
")"
56125607

5608+
56135609
[ "." (
56145610
// tricky lookahead since we do need to support the following constructs
56155611
// schema.f1().f2() - Function with Function Column
@@ -5620,6 +5616,24 @@ Function InternalFunction(boolean escaped):
56205616
)
56215617
]
56225618

5619+
[
5620+
(
5621+
<K_IGNORE> <K_NULLS>
5622+
{
5623+
retval.setNullHandling(Function.NullHandling.IGNORE_NULLS);
5624+
retval.setIgnoreNullsOutside(true);
5625+
}
5626+
)
5627+
|
5628+
(
5629+
<K_RESPECT> <K_NULLS>
5630+
{
5631+
retval.setNullHandling(Function.NullHandling.RESPECT_NULLS);
5632+
retval.setIgnoreNullsOutside(true);
5633+
}
5634+
)
5635+
]
5636+
56235637
[ LOOKAHEAD(2) keep = KeepExpression() ]
56245638

56255639
{

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,15 @@ void testRedshiftApproximate() throws JSQLParserException {
3232

3333
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
3434
}
35+
36+
@Test
37+
void testDatabricks() throws JSQLParserException {
38+
String sqlStr = "SELECT any_value(col) IGNORE NULLS FROM test;";
39+
40+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
41+
42+
sqlStr = "SELECT any_value(col) IGNORE NULLS FROM VALUES (NULL), (5), (20) AS tab(col);";
43+
44+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
45+
}
3546
}

src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/analytic_query04.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ select deptno
1414
from emp
1515

1616
--@FAILURE: Encountered unexpected token: "by" "BY" recorded first on Aug 3, 2021, 7:20:08 AM
17-
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Nov 14, 2022, 11:44:23 AM
17+
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Nov 14, 2022, 11:44:23 AM
18+
--@FAILURE: Encountered unexpected token: "group" "GROUP" recorded first on May 27, 2024, 9:38:25 AM

src/test/resources/net/sf/jsqlparser/statement/select/oracle-tests/analytic_query05.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@
2626

2727

2828
--@FAILURE: Encountered unexpected token: "by" "BY" recorded first on Aug 3, 2021, 7:20:08 AM
29-
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Nov 14, 2022, 11:44:22 AM
29+
--@SUCCESSFULLY_PARSED_AND_DEPARSED first on Nov 14, 2022, 11:44:22 AM
30+
--@FAILURE: Encountered unexpected token: "(" "(" recorded first on May 27, 2024, 9:38:28 AM

0 commit comments

Comments
 (0)