Skip to content

Commit 30cf5d7

Browse files
authored
feat: add support SELECT all columns from function result (#2207)
PostgreSQL supports composite type expansion using .*, for example below query is valid in PostgreSQL: SELECT (pg_stat_file('postgresql.conf')).*
1 parent 1c7c343 commit 30cf5d7

File tree

8 files changed

+127
-0
lines changed

8 files changed

+127
-0
lines changed

src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import net.sf.jsqlparser.statement.piped.FromQuery;
6161
import net.sf.jsqlparser.statement.select.AllColumns;
6262
import net.sf.jsqlparser.statement.select.AllTableColumns;
63+
import net.sf.jsqlparser.statement.select.FunctionAllColumns;
6364
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
6465
import net.sf.jsqlparser.statement.select.Select;
6566

@@ -585,6 +586,8 @@ default void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter) {
585586

586587
<S> T visit(AllColumns allColumns, S context);
587588

589+
<S> T visit(FunctionAllColumns functionColumns, S context);
590+
588591
default void visit(AllColumns allColumns) {
589592
this.visit(allColumns, null);
590593
}

src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import net.sf.jsqlparser.statement.piped.FromQuery;
6161
import net.sf.jsqlparser.statement.select.AllColumns;
6262
import net.sf.jsqlparser.statement.select.AllTableColumns;
63+
import net.sf.jsqlparser.statement.select.FunctionAllColumns;
6364
import net.sf.jsqlparser.statement.select.OrderByElement;
6465
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
6566
import net.sf.jsqlparser.statement.select.Pivot;
@@ -599,6 +600,11 @@ public <S> T visit(AllTableColumns allTableColumns, S context) {
599600
return visitExpression(allTableColumns, context);
600601
}
601602

603+
@Override
604+
public <S> T visit(FunctionAllColumns functionAllColumns, S context) {
605+
return visitExpression(functionAllColumns, context);
606+
}
607+
602608
@Override
603609
public <S> T visit(AllValue allValue, S context) {
604610
return visitExpression(allValue, context);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2025 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.select;
11+
12+
import net.sf.jsqlparser.expression.ExpressionVisitor;
13+
import net.sf.jsqlparser.expression.Function;
14+
15+
16+
public class FunctionAllColumns extends AllColumns {
17+
private Function function;
18+
19+
public FunctionAllColumns(Function function) {
20+
super(null, null, null);
21+
this.function = function;
22+
}
23+
24+
public Function getFunction() {
25+
return function;
26+
}
27+
28+
public FunctionAllColumns setFunction(Function function) {
29+
this.function = function;
30+
return this;
31+
}
32+
33+
public StringBuilder appendTo(StringBuilder builder) {
34+
builder.append("(");
35+
builder.append(function);
36+
builder.append(").*");
37+
return builder;
38+
}
39+
40+
@Override
41+
public String toString() {
42+
return appendTo(new StringBuilder()).toString();
43+
}
44+
45+
@Override
46+
public <T, S> T accept(ExpressionVisitor<T> expressionVisitor, S context) {
47+
return expressionVisitor.visit(this, context);
48+
}
49+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@
165165
import net.sf.jsqlparser.statement.select.AllColumns;
166166
import net.sf.jsqlparser.statement.select.AllTableColumns;
167167
import net.sf.jsqlparser.statement.select.FromItemVisitor;
168+
import net.sf.jsqlparser.statement.select.FunctionAllColumns;
168169
import net.sf.jsqlparser.statement.select.Join;
169170
import net.sf.jsqlparser.statement.select.LateralSubSelect;
170171
import net.sf.jsqlparser.statement.select.OrderByElement;
@@ -948,6 +949,12 @@ public <S> Void visit(AllTableColumns allTableColumns, S context) {
948949
return null;
949950
}
950951

952+
@Override
953+
public <S> Void visit(FunctionAllColumns functionAllColumns, S context) {
954+
955+
return null;
956+
}
957+
951958
@Override
952959
public <S> Void visit(AllValue allValue, S context) {
953960

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@
123123
import net.sf.jsqlparser.statement.piped.FromQuery;
124124
import net.sf.jsqlparser.statement.select.AllColumns;
125125
import net.sf.jsqlparser.statement.select.AllTableColumns;
126+
import net.sf.jsqlparser.statement.select.FunctionAllColumns;
126127
import net.sf.jsqlparser.statement.select.OrderByElement;
127128
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
128129
import net.sf.jsqlparser.statement.select.Select;
@@ -1652,6 +1653,12 @@ public <S> StringBuilder visit(AllTableColumns allTableColumns, S context) {
16521653
return builder;
16531654
}
16541655

1656+
@Override
1657+
public <S> StringBuilder visit(FunctionAllColumns functionAllColumns, S context) {
1658+
builder.append(functionAllColumns.toString());
1659+
return builder;
1660+
}
1661+
16551662
@Override
16561663
public <S> StringBuilder visit(AllValue allValue, S context) {
16571664
builder.append(allValue);

src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@
123123
import net.sf.jsqlparser.statement.piped.FromQuery;
124124
import net.sf.jsqlparser.statement.select.AllColumns;
125125
import net.sf.jsqlparser.statement.select.AllTableColumns;
126+
import net.sf.jsqlparser.statement.select.FunctionAllColumns;
126127
import net.sf.jsqlparser.statement.select.ParenthesedSelect;
127128
import net.sf.jsqlparser.statement.select.PlainSelect;
128129
import net.sf.jsqlparser.statement.select.Select;
@@ -1060,6 +1061,11 @@ public <S> Void visit(AllTableColumns allTableColumns, S context) {
10601061
return null;
10611062
}
10621063

1064+
@Override
1065+
public <S> Void visit(FunctionAllColumns functionColumns, S context) {
1066+
return null;
1067+
}
1068+
10631069
@Override
10641070
public <S> Void visit(AllValue allValue, S context) {
10651071
return null;

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3273,6 +3273,16 @@ List<SelectItem<?>> SelectItemsList():
32733273
{ return selectItemsList; }
32743274
}
32753275

3276+
FunctionAllColumns FunctionAllColumns() #FunctionAllColumns:
3277+
{
3278+
Function function;
3279+
}
3280+
{
3281+
"(" ( "(" )* function=Function() ")" ( ")" )* "." "*"
3282+
{
3283+
return new FunctionAllColumns(function);
3284+
}
3285+
}
32763286

32773287
SelectItem<?> SelectItem() #SelectItem:
32783288
{
@@ -5332,6 +5342,8 @@ Expression PrimaryExpression() #PrimaryExpression:
53325342

53335343
| LOOKAHEAD(AllTableColumns()) retval=AllTableColumns()
53345344

5345+
| LOOKAHEAD(FunctionAllColumns()) retval=FunctionAllColumns()
5346+
53355347
// support timestamp expressions
53365348
| LOOKAHEAD(2, {!interrupted}) (token=<K_TIME_KEY_EXPR> | token=<K_CURRENT>) { retval = new TimeKeyExpression(token.image); }
53375349

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6173,4 +6173,41 @@ public void testSelectWithSkylineKeywords() throws JSQLParserException {
61736173
select.getPlainSelect().getSelectItems().toString());
61746174
}
61756175

6176+
@Test
6177+
public void testSelectAllColumnsFromFunctionReturn() throws JSQLParserException {
6178+
String sql = "SELECT (pg_stat_file('postgresql.conf')).*";
6179+
Statement statement = CCJSqlParserUtil.parse(sql);
6180+
assertNotNull(statement);
6181+
assertTrue(statement instanceof Select);
6182+
6183+
// Ensure the function is recognized correctly
6184+
Select select = (Select) statement;
6185+
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
6186+
assertNotNull(plainSelect);
6187+
assertEquals(1, plainSelect.getSelectItems().size());
6188+
assertTrue(plainSelect.getSelectItems().get(0)
6189+
.getExpression() instanceof FunctionAllColumns);
6190+
assertEquals("(pg_stat_file('postgresql.conf')).*",
6191+
plainSelect.getSelectItems().get(0).toString());
6192+
}
6193+
6194+
@Test
6195+
public void testSelectAllColumnsFromFunctionReturnWithMultipleParentheses()
6196+
throws JSQLParserException {
6197+
String sql = "SELECT ( ( ( pg_stat_file('postgresql.conf') ) )) . *";
6198+
Statement statement = CCJSqlParserUtil.parse(sql);
6199+
assertNotNull(statement);
6200+
assertTrue(statement instanceof Select);
6201+
6202+
// Ensure the function is recognized correctly
6203+
Select select = (Select) statement;
6204+
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
6205+
assertNotNull(plainSelect);
6206+
assertEquals(1, plainSelect.getSelectItems().size());
6207+
assertTrue(plainSelect.getSelectItems().get(0)
6208+
.getExpression() instanceof FunctionAllColumns);
6209+
assertEquals("(pg_stat_file('postgresql.conf')).*",
6210+
plainSelect.getSelectItems().get(0).toString());
6211+
}
6212+
61766213
}

0 commit comments

Comments
 (0)