Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
*/
package net.sf.jsqlparser.statement.select;

import java.util.List;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.operators.relational.ParenthesedExpressionList;

@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"})
public class TableFunction extends Function implements FromItem {
Expand All @@ -20,6 +22,7 @@ public class TableFunction extends Function implements FromItem {
private Pivot pivot = null;
private UnPivot unPivot = null;
private Function function;
private ParenthesedExpressionList<Function> rowsFromFunctions;
private String withClause = null;

public TableFunction(Function function) {
Expand All @@ -42,6 +45,27 @@ public TableFunction(String prefix, Function function, String withClause) {
this.withClause = withClause;
}

public TableFunction(ParenthesedExpressionList<Function> rowsFromFunctions) {
this.rowsFromFunctions = rowsFromFunctions;
}

public TableFunction(String prefix, ParenthesedExpressionList<Function> rowsFromFunctions) {
this.prefix = prefix;
this.rowsFromFunctions = rowsFromFunctions;
}

public TableFunction(ParenthesedExpressionList<Function> rowsFromFunctions, String withClause) {
this.rowsFromFunctions = rowsFromFunctions;
this.withClause = withClause;
}

public TableFunction(String prefix, ParenthesedExpressionList<Function> rowsFromFunctions,
String withClause) {
this.prefix = prefix;
this.rowsFromFunctions = rowsFromFunctions;
this.withClause = withClause;
}

public TableFunction(String prefix, String name, Expression... parameters) {
this.prefix = prefix;
this.function = new Function(name, parameters);
Expand All @@ -57,9 +81,32 @@ public Function getFunction() {

public TableFunction setFunction(Function function) {
this.function = function;
this.rowsFromFunctions = null;
return this;
}

public ParenthesedExpressionList<Function> getRowsFromFunctions() {
return rowsFromFunctions;
}

public TableFunction setRowsFromFunctions(
ParenthesedExpressionList<Function> rowsFromFunctions) {
this.rowsFromFunctions = rowsFromFunctions;
this.function = null;
return this;
}

public boolean isRowsFrom() {
return rowsFromFunctions != null;
}

public List<Function> getFunctions() {
if (rowsFromFunctions != null) {
return rowsFromFunctions;
}
return function != null ? List.of(function) : null;
}

@Deprecated
public Function getExpression() {
return getFunction();
Expand Down Expand Up @@ -151,7 +198,11 @@ public StringBuilder appendTo(StringBuilder builder) {
if (prefix != null) {
builder.append(prefix).append(" ");
}
builder.append(function.toString());
if (rowsFromFunctions != null) {
builder.append("ROWS FROM ").append(rowsFromFunctions);
} else {
builder.append(function);
}

if (withClause != null) {
builder.append(" WITH ").append(withClause);
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -1302,7 +1302,11 @@ public <S> Void visit(OracleHint hint, S context) {

@Override
public <S> Void visit(TableFunction tableFunction, S context) {
visit(tableFunction.getFunction(), null);
if (tableFunction.getFunctions() != null) {
for (var function : tableFunction.getFunctions()) {
visit(function, null);
}
}
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -780,18 +780,7 @@ public <S> StringBuilder visit(TableStatement tableStatement, S context) {

@Override
public <S> StringBuilder visit(TableFunction tableFunction, S context) {
if (tableFunction.getPrefix() != null) {
builder.append(tableFunction.getPrefix()).append(" ");
}
tableFunction.getFunction().accept(this.expressionVisitor, context);

if (tableFunction.getWithClause() != null) {
builder.append(" WITH ").append(tableFunction.getWithClause());
}

if (tableFunction.getAlias() != null) {
builder.append(tableFunction.getAlias());
}
tableFunction.appendTo(builder);
return builder;
}

Expand Down
45 changes: 40 additions & 5 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -5677,7 +5677,11 @@ FromItem FromItem() #FromItem:
&& getToken(3).kind == OPENING_BRACKET
}) fromItem=TableFunction()
|
LOOKAHEAD({ (isFunctionAhead() && getToken(1).kind != K_LATERAL)
LOOKAHEAD({
(getToken(1).kind == K_ROWS
&& getToken(2).kind == K_FROM
&& getToken(3).kind == OPENING_BRACKET)
|| (isFunctionAhead() && getToken(1).kind != K_LATERAL)
|| (getToken(1).kind == K_LATERAL && getToken(2).kind != OPENING_BRACKET) })
fromItem=TableFunction()
|
Expand Down Expand Up @@ -9704,12 +9708,15 @@ JsonTableFunction JsonTableBody() : {
TableFunction TableFunction():
{
Token prefix = null;
Function function;
Function function = null;
ParenthesedExpressionList<Function> rowsFromFunctions = null;
Token withClause = null;
}
{
[ prefix = <K_LATERAL> ]
(
LOOKAHEAD(3) <K_ROWS> <K_FROM> rowsFromFunctions = RowsFromFunctionList()
|
LOOKAHEAD({
getToken(1).kind == S_IDENTIFIER
&& getToken(1).image.equalsIgnoreCase("JSON_TABLE")
Expand All @@ -9721,16 +9728,44 @@ TableFunction TableFunction():
)
[ LOOKAHEAD(2) <K_WITH> ( withClause = <K_OFFSET> | withClause = <K_ORDINALITY> ) ]
{
return prefix!=null
? withClause!=null
if (rowsFromFunctions != null) {
return prefix != null
? withClause != null
? new TableFunction(prefix.image, rowsFromFunctions, withClause.image)
: new TableFunction(prefix.image, rowsFromFunctions)
: withClause != null
? new TableFunction(rowsFromFunctions, withClause.image)
: new TableFunction(rowsFromFunctions);
}

return prefix != null
? withClause != null
? new TableFunction(prefix.image, function, withClause.image)
: new TableFunction(prefix.image, function)
: withClause!=null
: withClause != null
? new TableFunction(function, withClause.image)
: new TableFunction(function);
}
}

ParenthesedExpressionList<Function> RowsFromFunctionList():
{
ParenthesedExpressionList<Function> functions = new ParenthesedExpressionList<Function>();
Function function;
}
{
"("
function = Function() { functions.add(function); }
(
","
function = Function() { functions.add(function); }
)*
")"
{
return functions;
}
}

List<Index.ColumnParams> ColumnNamesWithParamsList() : {
List<Index.ColumnParams> colNames = new ArrayList<Index.ColumnParams>();
String columnName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
*/
package net.sf.jsqlparser.statement.select;

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

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.test.TestUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

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

class TableFunctionTest {

@Test
Expand Down Expand Up @@ -59,4 +59,22 @@ void testTableFunctionWithSupportedWithClauses(String withClause) throws JSQLPar
String sqlStr = "SELECT * FROM UNNEST(ARRAY[1, 2, 3]) WITH " + withClause + " AS t(a, b)";
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
}

@Test
void testRowsFromTableFunction() throws JSQLParserException {
String sqlStr = "SELECT *\n"
+ "FROM ROWS FROM (\n"
+ " generate_series(1,3),\n"
+ " generate_series(10,12)\n"
+ ") AS t(a,b)";

PlainSelect select = (PlainSelect) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
TableFunction tableFunction = select.getFromItem(TableFunction.class);

assertTrue(tableFunction.isRowsFrom());
assertNotNull(tableFunction.getRowsFromFunctions());
assertEquals(2, tableFunction.getRowsFromFunctions().size());
assertEquals("generate_series", tableFunction.getRowsFromFunctions().get(0).getName());
assertEquals("generate_series", tableFunction.getRowsFromFunctions().get(1).getName());
}
}