diff --git a/src/main/java/net/sf/jsqlparser/statement/select/WithFunctionDeclaration.java b/src/main/java/net/sf/jsqlparser/statement/select/WithFunctionDeclaration.java index f842e8282..c24d8a37f 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/WithFunctionDeclaration.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/WithFunctionDeclaration.java @@ -10,6 +10,7 @@ package net.sf.jsqlparser.statement.select; import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; import java.io.Serializable; import java.util.List; @@ -100,6 +101,13 @@ public StringBuilder appendTo(StringBuilder builder) { .append(returnExpression); } + public T accept(ExpressionVisitor expressionVisitor, S context) { + if (returnExpression != null) { + return returnExpression.accept(expressionVisitor, context); + } + return null; + } + @Override public String toString() { return appendTo(new StringBuilder()).toString(); diff --git a/src/main/java/net/sf/jsqlparser/statement/select/WithItem.java b/src/main/java/net/sf/jsqlparser/statement/select/WithItem.java index db633b2d2..8789a6875 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/WithItem.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/WithItem.java @@ -171,7 +171,10 @@ public T accept(SelectVisitor selectVisitor, S context) { } public T accept(StatementVisitor statementVisitor, S context) { - return statement.accept(statementVisitor, context); + if (statement != null) { + return statement.accept(statementVisitor, context); + } + return null; } public WithItem withWithItemList(List> withItemList) { diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index 1c3b9bf9e..bbb1b40f5 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -318,8 +318,12 @@ public Set getTables(Expression expr) { @Override public Void visit(WithItem withItem, S context) { - otherItemNames.add(withItem.getAlias().getName()); - withItem.getSelect().accept((SelectVisitor) this, context); + if (withItem.getAlias() != null) { + otherItemNames.add(withItem.getAlias().getName()); + } + if (withItem.getSelect() != null) { + withItem.getSelect().accept((SelectVisitor) this, context); + } return null; } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index af765b3ca..f44a57dda 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -4405,11 +4405,20 @@ WithFunctionDeclaration WithFunctionDeclaration() #WithFunctionDeclaration: WithFunctionParameter WithFunctionParameter() #WithFunctionParameter: { String name; - String type; + String type = null; + String arrayType = null; } { - name = RelObjectName() type = RelObjectName() + name = RelObjectName() + ( + LOOKAHEAD(2) "<" arrayType = RelObjectName() ">" + | + type = RelObjectName() + ) { + if (arrayType != null) { + type = "ARRAY<" + arrayType + ">"; + } return new WithFunctionParameter(name, type); } } diff --git a/src/test/java/net/sf/jsqlparser/statement/select/WithFunctionDeclarationTest.java b/src/test/java/net/sf/jsqlparser/statement/select/WithFunctionDeclarationTest.java index 81e4c16fb..ed84b4aa7 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/WithFunctionDeclarationTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/WithFunctionDeclarationTest.java @@ -1,6 +1,16 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2025 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ package net.sf.jsqlparser.statement.select; import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.ExpressionVisitor; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -10,6 +20,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -93,4 +105,26 @@ void toStringTestWithNoParameters() { assertThat(withFunctionDeclaration.toString()) .isEqualTo("FUNCTION func1() RETURNS integer RETURN 1 + 1"); } + + @Test + void expressionVisitorIsNotCalledWhenNoReturnExpressionDeclared( + @Mock ExpressionVisitor expressionVisitor) { + withFunctionDeclaration = new WithFunctionDeclaration(); + + withFunctionDeclaration.accept(expressionVisitor, "RANDOM_CONTEXT"); + + verifyNoInteractions(expressionVisitor); + } + + @Test + void expressionVisitorCalledWhenReturnExpressionDeclared( + @Mock ExpressionVisitor expressionVisitor) { + String context = "RANDOM_CONTEXT"; + withFunctionDeclaration = new WithFunctionDeclaration() + .withReturnExpression(expression); + + withFunctionDeclaration.accept(expressionVisitor, context); + + verify(expression).accept(expressionVisitor, context); + } } diff --git a/src/test/java/net/sf/jsqlparser/statement/select/WithFunctionParameterTest.java b/src/test/java/net/sf/jsqlparser/statement/select/WithFunctionParameterTest.java index bd632ed1d..9e29552ad 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/WithFunctionParameterTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/WithFunctionParameterTest.java @@ -1,3 +1,12 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2025 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ package net.sf.jsqlparser.statement.select; import org.junit.jupiter.api.Test; diff --git a/src/test/java/net/sf/jsqlparser/statement/select/WithItemTest.java b/src/test/java/net/sf/jsqlparser/statement/select/WithItemTest.java index 0e8f9e546..00224497f 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/WithItemTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/WithItemTest.java @@ -42,7 +42,13 @@ void testNotMaterializedIssue2251() throws JSQLParserException { " FUNCTION doubleupplusone(x integer)\n" + " RETURNS integer\n" + " RETURN doubleup(x) + 1\n" + - "SELECT doubleupplusone(21);" + "SELECT doubleupplusone(21);", + "WITH\n" + + " FUNCTION takesArray(x array)\n" + + " RETURNS double\n" + + " RETURN x[1] + x[2] + x[3]\n" + + "SELECT takesArray(ARRAY[1.0, 2.0, 3.0]);" + }) void testWithFunction(String sqlStr) throws JSQLParserException { TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); diff --git a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java index a3da6cb85..a40d52509 100644 --- a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java +++ b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java @@ -26,6 +26,7 @@ import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -697,5 +698,20 @@ void testIssue2305() throws JSQLParserException { Set tables = TablesNamesFinder.findTables(sqlStr); assertThat(tables).containsExactlyInAnyOrder("tbl"); } + + @Test + void assertWithItemWithFunctionDeclarationDoesNotThrowException() throws JSQLParserException { + String sqlStr = + "WITH FUNCTION my_with_item(param1 INT) RETURNS INT RETURN param1 + 1 SELECT * FROM my_table;"; + assertThatCode(() -> TablesNamesFinder.findTables(sqlStr)) + .doesNotThrowAnyException(); + } + + @Test + void assertWithItemWithFunctionDeclarationReturnsTableInSelect() throws JSQLParserException { + String sqlStr = + "WITH FUNCTION my_with_item(param1 INT) RETURNS INT RETURN param1 + 1 SELECT * FROM my_table;"; + assertThat(TablesNamesFinder.findTables(sqlStr)).containsExactly("my_table"); + } } diff --git a/src/test/resources/simple_parsing.txt b/src/test/resources/simple_parsing.txt index 657d43789..7fc390fab 100644 --- a/src/test/resources/simple_parsing.txt +++ b/src/test/resources/simple_parsing.txt @@ -242,4 +242,10 @@ WITH FUNCTION bye(name varchar) RETURNS varchar RETURN format('Bye %s!', 'name') -SELECT hello('Finn') || ' and ' || bye('Joe'); \ No newline at end of file +SELECT hello('Finn') || ' and ' || bye('Joe'); + +WITH + FUNCTION takesArray(x array) + RETURNS double + RETURN x[1] + x[2] + x[3] +SELECT takesArray(array[1.0, 2.0, 3.0]); \ No newline at end of file