diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java index 9054ece46..a87b4594a 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java @@ -60,6 +60,7 @@ import net.sf.jsqlparser.statement.piped.FromQuery; import net.sf.jsqlparser.statement.select.AllColumns; import net.sf.jsqlparser.statement.select.AllTableColumns; +import net.sf.jsqlparser.statement.select.FunctionAllColumns; import net.sf.jsqlparser.statement.select.ParenthesedSelect; import net.sf.jsqlparser.statement.select.Select; @@ -585,6 +586,8 @@ default void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter) { T visit(AllColumns allColumns, S context); + T visit(FunctionAllColumns functionColumns, S context); + default void visit(AllColumns allColumns) { this.visit(allColumns, null); } diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java index 8aaadd9a8..3be6d1348 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -60,6 +60,7 @@ import net.sf.jsqlparser.statement.piped.FromQuery; import net.sf.jsqlparser.statement.select.AllColumns; import net.sf.jsqlparser.statement.select.AllTableColumns; +import net.sf.jsqlparser.statement.select.FunctionAllColumns; import net.sf.jsqlparser.statement.select.OrderByElement; import net.sf.jsqlparser.statement.select.ParenthesedSelect; import net.sf.jsqlparser.statement.select.Pivot; @@ -599,6 +600,11 @@ public T visit(AllTableColumns allTableColumns, S context) { return visitExpression(allTableColumns, context); } + @Override + public T visit(FunctionAllColumns functionAllColumns, S context) { + return visitExpression(functionAllColumns, context); + } + @Override public T visit(AllValue allValue, S context) { return visitExpression(allValue, context); diff --git a/src/main/java/net/sf/jsqlparser/statement/select/FunctionAllColumns.java b/src/main/java/net/sf/jsqlparser/statement/select/FunctionAllColumns.java new file mode 100644 index 000000000..7a4fa5ce2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/select/FunctionAllColumns.java @@ -0,0 +1,49 @@ +/*- + * #%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.ExpressionVisitor; +import net.sf.jsqlparser.expression.Function; + + +public class FunctionAllColumns extends AllColumns { + private Function function; + + public FunctionAllColumns(Function function) { + super(null, null, null); + this.function = function; + } + + public Function getFunction() { + return function; + } + + public FunctionAllColumns setFunction(Function function) { + this.function = function; + return this; + } + + public StringBuilder appendTo(StringBuilder builder) { + builder.append("("); + builder.append(function); + builder.append(").*"); + return builder; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + @Override + public T accept(ExpressionVisitor expressionVisitor, S context) { + return expressionVisitor.visit(this, context); + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index 339803f8d..ea9f1645b 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -165,6 +165,7 @@ import net.sf.jsqlparser.statement.select.AllColumns; import net.sf.jsqlparser.statement.select.AllTableColumns; import net.sf.jsqlparser.statement.select.FromItemVisitor; +import net.sf.jsqlparser.statement.select.FunctionAllColumns; import net.sf.jsqlparser.statement.select.Join; import net.sf.jsqlparser.statement.select.LateralSubSelect; import net.sf.jsqlparser.statement.select.OrderByElement; @@ -948,6 +949,12 @@ public Void visit(AllTableColumns allTableColumns, S context) { return null; } + @Override + public Void visit(FunctionAllColumns functionAllColumns, S context) { + + return null; + } + @Override public Void visit(AllValue allValue, S context) { diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java index 175e7da07..c93960539 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -123,6 +123,7 @@ import net.sf.jsqlparser.statement.piped.FromQuery; import net.sf.jsqlparser.statement.select.AllColumns; import net.sf.jsqlparser.statement.select.AllTableColumns; +import net.sf.jsqlparser.statement.select.FunctionAllColumns; import net.sf.jsqlparser.statement.select.OrderByElement; import net.sf.jsqlparser.statement.select.ParenthesedSelect; import net.sf.jsqlparser.statement.select.Select; @@ -1652,6 +1653,12 @@ public StringBuilder visit(AllTableColumns allTableColumns, S context) { return builder; } + @Override + public StringBuilder visit(FunctionAllColumns functionAllColumns, S context) { + builder.append(functionAllColumns.toString()); + return builder; + } + @Override public StringBuilder visit(AllValue allValue, S context) { builder.append(allValue); diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java index f1f1b019b..a4b27d765 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java @@ -123,6 +123,7 @@ import net.sf.jsqlparser.statement.piped.FromQuery; import net.sf.jsqlparser.statement.select.AllColumns; import net.sf.jsqlparser.statement.select.AllTableColumns; +import net.sf.jsqlparser.statement.select.FunctionAllColumns; import net.sf.jsqlparser.statement.select.ParenthesedSelect; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; @@ -1060,6 +1061,11 @@ public Void visit(AllTableColumns allTableColumns, S context) { return null; } + @Override + public Void visit(FunctionAllColumns functionColumns, S context) { + return null; + } + @Override public Void visit(AllValue allValue, S 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 9c774070f..29c6f173f 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -3273,6 +3273,16 @@ List> SelectItemsList(): { return selectItemsList; } } +FunctionAllColumns FunctionAllColumns() #FunctionAllColumns: +{ + Function function; +} +{ + "(" ( "(" )* function=Function() ")" ( ")" )* "." "*" + { + return new FunctionAllColumns(function); + } +} SelectItem SelectItem() #SelectItem: { @@ -5332,6 +5342,8 @@ Expression PrimaryExpression() #PrimaryExpression: | LOOKAHEAD(AllTableColumns()) retval=AllTableColumns() + | LOOKAHEAD(FunctionAllColumns()) retval=FunctionAllColumns() + // support timestamp expressions | LOOKAHEAD(2, {!interrupted}) (token= | token=) { retval = new TimeKeyExpression(token.image); } diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java index 25515f6ef..cc0cbda66 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -6173,4 +6173,41 @@ public void testSelectWithSkylineKeywords() throws JSQLParserException { select.getPlainSelect().getSelectItems().toString()); } + @Test + public void testSelectAllColumnsFromFunctionReturn() throws JSQLParserException { + String sql = "SELECT (pg_stat_file('postgresql.conf')).*"; + Statement statement = CCJSqlParserUtil.parse(sql); + assertNotNull(statement); + assertTrue(statement instanceof Select); + + // Ensure the function is recognized correctly + Select select = (Select) statement; + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertNotNull(plainSelect); + assertEquals(1, plainSelect.getSelectItems().size()); + assertTrue(plainSelect.getSelectItems().get(0) + .getExpression() instanceof FunctionAllColumns); + assertEquals("(pg_stat_file('postgresql.conf')).*", + plainSelect.getSelectItems().get(0).toString()); + } + + @Test + public void testSelectAllColumnsFromFunctionReturnWithMultipleParentheses() + throws JSQLParserException { + String sql = "SELECT ( ( ( pg_stat_file('postgresql.conf') ) )) . *"; + Statement statement = CCJSqlParserUtil.parse(sql); + assertNotNull(statement); + assertTrue(statement instanceof Select); + + // Ensure the function is recognized correctly + Select select = (Select) statement; + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + assertNotNull(plainSelect); + assertEquals(1, plainSelect.getSelectItems().size()); + assertTrue(plainSelect.getSelectItems().get(0) + .getExpression() instanceof FunctionAllColumns); + assertEquals("(pg_stat_file('postgresql.conf')).*", + plainSelect.getSelectItems().get(0).toString()); + } + }