Skip to content

Commit 7d321f9

Browse files
committed
Add support PostgreSQL ROWS FROM table functions
1 parent bd1a146 commit 7d321f9

File tree

5 files changed

+118
-21
lines changed

5 files changed

+118
-21
lines changed

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

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
*/
1010
package net.sf.jsqlparser.statement.select;
1111

12+
import java.util.List;
1213
import net.sf.jsqlparser.expression.Alias;
1314
import net.sf.jsqlparser.expression.Expression;
1415
import net.sf.jsqlparser.expression.Function;
16+
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;
1517

1618
@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"})
1719
public class TableFunction extends Function implements FromItem {
@@ -20,6 +22,7 @@ public class TableFunction extends Function implements FromItem {
2022
private Pivot pivot = null;
2123
private UnPivot unPivot = null;
2224
private Function function;
25+
private ParenthesedExpressionList<Function> rowsFromFunctions;
2326
private String withClause = null;
2427

2528
public TableFunction(Function function) {
@@ -42,6 +45,27 @@ public TableFunction(String prefix, Function function, String withClause) {
4245
this.withClause = withClause;
4346
}
4447

48+
public TableFunction(ParenthesedExpressionList<Function> rowsFromFunctions) {
49+
this.rowsFromFunctions = rowsFromFunctions;
50+
}
51+
52+
public TableFunction(String prefix, ParenthesedExpressionList<Function> rowsFromFunctions) {
53+
this.prefix = prefix;
54+
this.rowsFromFunctions = rowsFromFunctions;
55+
}
56+
57+
public TableFunction(ParenthesedExpressionList<Function> rowsFromFunctions, String withClause) {
58+
this.rowsFromFunctions = rowsFromFunctions;
59+
this.withClause = withClause;
60+
}
61+
62+
public TableFunction(String prefix, ParenthesedExpressionList<Function> rowsFromFunctions,
63+
String withClause) {
64+
this.prefix = prefix;
65+
this.rowsFromFunctions = rowsFromFunctions;
66+
this.withClause = withClause;
67+
}
68+
4569
public TableFunction(String prefix, String name, Expression... parameters) {
4670
this.prefix = prefix;
4771
this.function = new Function(name, parameters);
@@ -57,9 +81,32 @@ public Function getFunction() {
5781

5882
public TableFunction setFunction(Function function) {
5983
this.function = function;
84+
this.rowsFromFunctions = null;
6085
return this;
6186
}
6287

88+
public ParenthesedExpressionList<Function> getRowsFromFunctions() {
89+
return rowsFromFunctions;
90+
}
91+
92+
public TableFunction setRowsFromFunctions(
93+
ParenthesedExpressionList<Function> rowsFromFunctions) {
94+
this.rowsFromFunctions = rowsFromFunctions;
95+
this.function = null;
96+
return this;
97+
}
98+
99+
public boolean isRowsFrom() {
100+
return rowsFromFunctions != null;
101+
}
102+
103+
public List<Function> getFunctions() {
104+
if (rowsFromFunctions != null) {
105+
return rowsFromFunctions;
106+
}
107+
return function != null ? List.of(function) : null;
108+
}
109+
63110
@Deprecated
64111
public Function getExpression() {
65112
return getFunction();
@@ -151,7 +198,11 @@ public StringBuilder appendTo(StringBuilder builder) {
151198
if (prefix != null) {
152199
builder.append(prefix).append(" ");
153200
}
154-
builder.append(function.toString());
201+
if (rowsFromFunctions != null) {
202+
builder.append("ROWS FROM ").append(rowsFromFunctions);
203+
} else {
204+
builder.append(function);
205+
}
155206

156207
if (withClause != null) {
157208
builder.append(" WITH ").append(withClause);

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1302,7 +1302,11 @@ public <S> Void visit(OracleHint hint, S context) {
13021302

13031303
@Override
13041304
public <S> Void visit(TableFunction tableFunction, S context) {
1305-
visit(tableFunction.getFunction(), null);
1305+
if (tableFunction.getFunctions() != null) {
1306+
for (var function : tableFunction.getFunctions()) {
1307+
visit(function, null);
1308+
}
1309+
}
13061310
return null;
13071311
}
13081312

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

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -780,18 +780,7 @@ public <S> StringBuilder visit(TableStatement tableStatement, S context) {
780780

781781
@Override
782782
public <S> StringBuilder visit(TableFunction tableFunction, S context) {
783-
if (tableFunction.getPrefix() != null) {
784-
builder.append(tableFunction.getPrefix()).append(" ");
785-
}
786-
tableFunction.getFunction().accept(this.expressionVisitor, context);
787-
788-
if (tableFunction.getWithClause() != null) {
789-
builder.append(" WITH ").append(tableFunction.getWithClause());
790-
}
791-
792-
if (tableFunction.getAlias() != null) {
793-
builder.append(tableFunction.getAlias());
794-
}
783+
tableFunction.appendTo(builder);
795784
return builder;
796785
}
797786

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

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5677,7 +5677,11 @@ FromItem FromItem() #FromItem:
56775677
&& getToken(3).kind == OPENING_BRACKET
56785678
}) fromItem=TableFunction()
56795679
|
5680-
LOOKAHEAD({ (isFunctionAhead() && getToken(1).kind != K_LATERAL)
5680+
LOOKAHEAD({
5681+
(getToken(1).kind == K_ROWS
5682+
&& getToken(2).kind == K_FROM
5683+
&& getToken(3).kind == OPENING_BRACKET)
5684+
|| (isFunctionAhead() && getToken(1).kind != K_LATERAL)
56815685
|| (getToken(1).kind == K_LATERAL && getToken(2).kind != OPENING_BRACKET) })
56825686
fromItem=TableFunction()
56835687
|
@@ -9704,12 +9708,15 @@ JsonTableFunction JsonTableBody() : {
97049708
TableFunction TableFunction():
97059709
{
97069710
Token prefix = null;
9707-
Function function;
9711+
Function function = null;
9712+
ParenthesedExpressionList<Function> rowsFromFunctions = null;
97089713
Token withClause = null;
97099714
}
97109715
{
97119716
[ prefix = <K_LATERAL> ]
97129717
(
9718+
LOOKAHEAD(3) <K_ROWS> <K_FROM> rowsFromFunctions = RowsFromFunctionList()
9719+
|
97139720
LOOKAHEAD({
97149721
getToken(1).kind == S_IDENTIFIER
97159722
&& getToken(1).image.equalsIgnoreCase("JSON_TABLE")
@@ -9721,16 +9728,44 @@ TableFunction TableFunction():
97219728
)
97229729
[ LOOKAHEAD(2) <K_WITH> ( withClause = <K_OFFSET> | withClause = <K_ORDINALITY> ) ]
97239730
{
9724-
return prefix!=null
9725-
? withClause!=null
9731+
if (rowsFromFunctions != null) {
9732+
return prefix != null
9733+
? withClause != null
9734+
? new TableFunction(prefix.image, rowsFromFunctions, withClause.image)
9735+
: new TableFunction(prefix.image, rowsFromFunctions)
9736+
: withClause != null
9737+
? new TableFunction(rowsFromFunctions, withClause.image)
9738+
: new TableFunction(rowsFromFunctions);
9739+
}
9740+
9741+
return prefix != null
9742+
? withClause != null
97269743
? new TableFunction(prefix.image, function, withClause.image)
97279744
: new TableFunction(prefix.image, function)
9728-
: withClause!=null
9745+
: withClause != null
97299746
? new TableFunction(function, withClause.image)
97309747
: new TableFunction(function);
97319748
}
97329749
}
97339750

9751+
ParenthesedExpressionList<Function> RowsFromFunctionList():
9752+
{
9753+
ParenthesedExpressionList<Function> functions = new ParenthesedExpressionList<Function>();
9754+
Function function;
9755+
}
9756+
{
9757+
"("
9758+
function = Function() { functions.add(function); }
9759+
(
9760+
","
9761+
function = Function() { functions.add(function); }
9762+
)*
9763+
")"
9764+
{
9765+
return functions;
9766+
}
9767+
}
9768+
97349769
List<Index.ColumnParams> ColumnNamesWithParamsList() : {
97359770
List<Index.ColumnParams> colNames = new ArrayList<Index.ColumnParams>();
97369771
String columnName;

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
*/
1010
package net.sf.jsqlparser.statement.select;
1111

12+
import static org.junit.jupiter.api.Assertions.*;
13+
1214
import net.sf.jsqlparser.JSQLParserException;
1315
import net.sf.jsqlparser.test.TestUtils;
1416
import org.junit.jupiter.api.Test;
1517
import org.junit.jupiter.params.ParameterizedTest;
1618
import org.junit.jupiter.params.provider.ValueSource;
1719

18-
import static org.junit.jupiter.api.Assertions.*;
19-
2020
class TableFunctionTest {
2121

2222
@Test
@@ -59,4 +59,22 @@ void testTableFunctionWithSupportedWithClauses(String withClause) throws JSQLPar
5959
String sqlStr = "SELECT * FROM UNNEST(ARRAY[1, 2, 3]) WITH " + withClause + " AS t(a, b)";
6060
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
6161
}
62+
63+
@Test
64+
void testRowsFromTableFunction() throws JSQLParserException {
65+
String sqlStr = "SELECT *\n"
66+
+ "FROM ROWS FROM (\n"
67+
+ " generate_series(1,3),\n"
68+
+ " generate_series(10,12)\n"
69+
+ ") AS t(a,b)";
70+
71+
PlainSelect select = (PlainSelect) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
72+
TableFunction tableFunction = select.getFromItem(TableFunction.class);
73+
74+
assertTrue(tableFunction.isRowsFrom());
75+
assertNotNull(tableFunction.getRowsFromFunctions());
76+
assertEquals(2, tableFunction.getRowsFromFunctions().size());
77+
assertEquals("generate_series", tableFunction.getRowsFromFunctions().get(0).getName());
78+
assertEquals("generate_series", tableFunction.getRowsFromFunctions().get(1).getName());
79+
}
6280
}

0 commit comments

Comments
 (0)