Skip to content

Commit 348e82f

Browse files
author
David Hayes
committed
Add support for TableFunction [WITH OFFSET|ORDINALITY]
This PR fixes #2218, by adding a new `withClause` to a TableFunction, which allows for support of SQL-99's `UNNEST(...) WITH ORDINALITY` or GoogleSQL's `UNNEST(...) WITH OFFSET`. Currently, this is a feeler PR, as I'm getting a test failure, as the output of the parsing differs in my tests: ``` org.opentest4j.AssertionFailedError: Output from Deparser does not match. ==> Expected :select*from unnest(array[1,2,3])with offset as t(a,b) Actual :select*from unnest(array[1,2,3])as t(a,b) <Click to see difference> at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151) at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132) at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197) at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182) at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1156) at [email protected]/net.sf.jsqlparser.test.TestUtils.assertStatementCanBeDeparsedAs(TestUtils.java:126) at [email protected]/net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed(TestUtils.java:99) at [email protected]/net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed(TestUtils.java:85) at [email protected]/net.sf.jsqlparser.statement.select.TableFunctionTest.testTableFunctionWithSupportedWithClauses(TableFunctionTest.java:60) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ```
1 parent 090bb60 commit 348e82f

File tree

4 files changed

+54
-3
lines changed

4 files changed

+54
-3
lines changed

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class TableFunction extends Function implements FromItem {
2020
private Pivot pivot = null;
2121
private UnPivot unPivot = null;
2222
private Function function;
23+
private String withClause = null;
2324

2425
public TableFunction(Function function) {
2526
this.function = function;
@@ -30,6 +31,17 @@ public TableFunction(String prefix, Function function) {
3031
this.function = function;
3132
}
3233

34+
public TableFunction(Function function, String withClause) {
35+
this.function = function;
36+
this.withClause = withClause;
37+
}
38+
39+
public TableFunction(String prefix, Function function, String withClause) {
40+
this.prefix = prefix;
41+
this.function = function;
42+
this.withClause = withClause;
43+
}
44+
3345
public TableFunction(String prefix, String name, Expression... parameters) {
3446
this.prefix = prefix;
3547
this.function = new Function(name, parameters);
@@ -62,6 +74,19 @@ public TableFunction setPrefix(String prefix) {
6274
return this;
6375
}
6476

77+
public String getWithClause() {
78+
return withClause;
79+
}
80+
81+
public void setWithClause(String withClause) {
82+
this.withClause = withClause;
83+
}
84+
85+
public TableFunction withClause(String clause) {
86+
this.withClause = clause;
87+
return this;
88+
}
89+
6590
@Override
6691
public <T, S> T accept(FromItemVisitor<T> fromItemVisitor, S context) {
6792
return fromItemVisitor.visit(this, context);
@@ -128,6 +153,10 @@ public StringBuilder appendTo(StringBuilder builder) {
128153
}
129154
builder.append(function.toString());
130155

156+
if (withClause != null) {
157+
builder.append(" ").append(withClause);
158+
}
159+
131160
if (alias != null) {
132161
builder.append(alias);
133162
}

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,8 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
545545
| <K_WHERE:"WHERE">
546546
| <K_WINDOW:"WINDOW">
547547
| <K_WITH:"WITH">
548+
| <K_WITH_OFFSET:"WITH OFFSET">
549+
| <K_WITH_ORDINALITY:"WITH ORDINALITY">
548550
| <K_WITH_TIES:"WITH TIES">
549551
| <K_WITHIN:"WITHIN">
550552
| <K_WITHOUT:"WITHOUT">
@@ -6636,14 +6638,20 @@ TableFunction TableFunction():
66366638
Token prefix = null;
66376639
Function function;
66386640
TableFunction functionItem;
6641+
Token withClause = null;
66396642
}
66406643
{
66416644
[ prefix = <K_LATERAL> ]
66426645
function=Function()
6646+
[ withClause = <K_WITH_OFFSET> | <K_WITH_ORDINALITY> ]
66436647
{
66446648
return prefix!=null
6645-
? new TableFunction(prefix.image, function)
6646-
: new TableFunction(function);
6649+
? withClause!=null
6650+
? new TableFunction(prefix.image, function, withClause.image)
6651+
: new TableFunction(prefix.image, function)
6652+
: withClause!=null
6653+
? new TableFunction(function, withClause.image)
6654+
: new TableFunction(function);
66476655
}
66486656
}
66496657

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import net.sf.jsqlparser.JSQLParserException;
1313
import net.sf.jsqlparser.test.TestUtils;
1414
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.params.ParameterizedTest;
16+
import org.junit.jupiter.params.provider.ValueSource;
1517

1618
import static org.junit.jupiter.api.Assertions.*;
1719

@@ -47,4 +49,14 @@ void testTableFunctionWithNamedParameterWhereNameIsOuterKeyword() throws JSQLPar
4749
" lateral flatten(input => i.DATA_VALUE:Friends, outer => true) AS f1;";
4850
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
4951
}
52+
53+
@ParameterizedTest
54+
@ValueSource(strings = {
55+
"WITH OFFSET",
56+
"WITH ORDINALITY"
57+
})
58+
void testTableFunctionWithSupportedWithClauses(String withClause) throws JSQLParserException {
59+
String sqlStr = "SELECT * FROM UNNEST(ARRAY[1, 2, 3]) " + withClause + " AS t(a, b)";
60+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
61+
}
5062
}

src/test/resources/simple_parsing.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,4 +208,6 @@ FROM EMPLOYEE THIS_EMP, DINFO, DINFOMAX
208208
WHERE THIS_EMP.JOB = 'SALESREP'
209209
AND THIS_EMP.WORKDEPT = DINFO.DEPTNO
210210

211-
select * from Person where deptname='it' AND NOT (age=24)
211+
select * from Person where deptname='it' AND NOT (age=24)
212+
213+
select * from unnest(array[4,5,6]) with ordinality;

0 commit comments

Comments
 (0)