Skip to content

Commit 28ad7d2

Browse files
feat: allow SELECT without target expressions for PostgreSQL (#2841)
* feat: allow SELECT without target expressions for PostgreSQL Resolves #2784 * style: rename new test to camelCase for Codacy * fix: disambiguate BigQuery AS STRUCT/VALUE qualifier from select tail Making the select-items group optional for the target-less PostgreSQL SELECT let JavaCC see K_AS in the follow set of the optional AS STRUCT/VALUE qualifier block, producing a choice conflict at the block. Guard it with an explicit lookahead so AS only opens the qualifier when STRUCT or VALUE comes next.
1 parent 8ade7b0 commit 28ad7d2

4 files changed

Lines changed: 36 additions & 2 deletions

File tree

‎src/main/java/net/sf/jsqlparser/statement/select/PlainSelect.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,10 +629,16 @@ public StringBuilder appendSelectBodyTo(StringBuilder builder) {
629629

630630
private void appendSelectClauseTo(StringBuilder builder) {
631631
builder.append("SELECT ");
632+
int afterSelect = builder.length();
632633
appendSelectHintsTo(builder);
633634
appendSelectQualifiersTo(builder);
634635
appendMySqlSelectModifiersTo(builder);
635636
builder.append(getStringList(selectItems));
637+
// PostgreSQL permits SELECT with no target expressions: keep "SELECT
638+
// FROM" instead of doubling the space.
639+
if (builder.length() == afterSelect) {
640+
builder.deleteCharAt(afterSelect - 1);
641+
}
636642
}
637643

638644
private void appendSelectHintsTo(StringBuilder builder) {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,13 @@ private <S> void deparseSelectClause(PlainSelect plainSelect, S context) {
379379

380380
plainSelect.appendMySqlSelectModifiersTo(builder);
381381

382+
int beforeItems = builder.length();
382383
deparseSelectItemsClause(plainSelect.getSelectItems());
384+
// PostgreSQL permits SELECT with no target expressions: keep "SELECT
385+
// FROM" instead of doubling the space.
386+
if (builder.length() == beforeItems && builder.charAt(beforeItems - 1) == ' ') {
387+
builder.deleteCharAt(beforeItems - 1);
388+
}
383389

384390
if (plainSelect.getIntoTables() != null) {
385391
builder.append(" INTO ");

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9241,7 +9241,7 @@ PlainSelect PlainSelect() #PlainSelect:
92419241
}
92429242
}
92439243

9244-
[
9244+
[ LOOKAHEAD(<K_AS> (<K_STRUCT> | <K_VALUE>))
92459245
<K_AS>
92469246
(
92479247
<K_STRUCT> { plainSelect.setBigQuerySelectQualifier( PlainSelect.BigQuerySelectQualifier.AS_STRUCT ); }
@@ -9252,7 +9252,14 @@ PlainSelect PlainSelect() #PlainSelect:
92529252

92539253
[ LOOKAHEAD(2) top = Top() { plainSelect.setTop(top); } ]
92549254

9255-
selectItems=SelectItemsList()
9255+
(
9256+
// PostgreSQL allows SELECT with no target expressions.
9257+
LOOKAHEAD({ Dialect.POSTGRESQL.name().equals(getAsString(Feature.dialect))
9258+
&& getToken(1).kind == K_FROM })
9259+
{ selectItems = new ArrayList<SelectItem<?>>(); }
9260+
|
9261+
selectItems=SelectItemsList()
9262+
)
92569263

92579264
[ LOOKAHEAD(<K_INTO>)
92589265
(

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,6 +2398,21 @@ public void testBooleanTestAfterPredicate() throws JSQLParserException {
23982398
assertSqlCanBeParsedAndDeparsed("SELECT * FROM t WHERE a IS TRUE IS FALSE");
23992399
}
24002400

2401+
@Test
2402+
public void testSelectWithoutTargetListPostgreSql() throws JSQLParserException {
2403+
// PostgreSQL permits SELECT with no target expressions
2404+
Statement stmt = CCJSqlParserUtil.parse(
2405+
"SELECT FROM t", parser -> parser.withDialect(Dialect.POSTGRESQL));
2406+
PlainSelect select = (PlainSelect) ((Select) stmt).getSelectBody();
2407+
assertTrue(select.getSelectItems().isEmpty());
2408+
assertDeparse(select, "SELECT FROM t");
2409+
2410+
// the reported case: empty target list inside a derived table
2411+
assertSqlCanBeParsedAndDeparsed(
2412+
"SELECT count(*) FROM (SELECT FROM (VALUES (1, 'a'), (2, 'b')) AS t(id, label)) AS q",
2413+
false, parser -> parser.withDialect(Dialect.POSTGRESQL));
2414+
}
2415+
24012416
@Test
24022417
public void testIsUnknown() throws JSQLParserException {
24032418
String statement = "SELECT col FROM tbl WHERE col IS UNKNOWN";

0 commit comments

Comments
 (0)