Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 1b4f7f9

Browse files
committed
Allow empty target list in SELECT.
This fixes a problem noted as a followup to bug #8648: if a query has a semantically-empty target list, e.g. SELECT * FROM zero_column_table, ruleutils.c will dump it as a syntactically-empty target list, which was not allowed. There doesn't seem to be any reliable way to fix this by hacking ruleutils (note in particular that the originally zero-column table might since have had columns added to it); and even if we had such a fix, it would do nothing for existing dump files that might contain bad syntax. The best bet seems to be to relax the syntactic restriction. Also, add parse-analysis errors for SELECT DISTINCT with no columns (after *-expansion) and RETURNING with no columns. These cases previously produced unexpected behavior because the parsed Query looked like it had no DISTINCT or RETURNING clause, respectively. If anyone ever offers a plausible use-case for this, we could work a bit harder on making the situation distinguishable. Arguably this is a bug fix that should be back-patched, but I'm worried that there may be client apps or PLs that expect "SELECT ;" to throw a syntax error. The issue doesn't seem important enough to risk changing behavior in minor releases.
1 parent c03ad56 commit 1b4f7f9

File tree

6 files changed

+73
-33
lines changed

6 files changed

+73
-33
lines changed

doc/src/sgml/ref/select.sgml

+21-7
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ PostgreSQL documentation
3434
<synopsis>
3535
[ WITH [ RECURSIVE ] <replaceable class="parameter">with_query</replaceable> [, ...] ]
3636
SELECT [ ALL | DISTINCT [ ON ( <replaceable class="parameter">expression</replaceable> [, ...] ) ] ]
37-
* | <replaceable class="parameter">expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...]
37+
[ * | <replaceable class="parameter">expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
3838
[ FROM <replaceable class="parameter">from_item</replaceable> [, ...] ]
3939
[ WHERE <replaceable class="parameter">condition</replaceable> ]
4040
[ GROUP BY <replaceable class="parameter">expression</replaceable> [, ...] ]
@@ -1740,13 +1740,27 @@ SELECT 2+2;
17401740
following query is invalid:
17411741
<programlisting>
17421742
SELECT distributors.* WHERE distributors.name = 'Westward';
1743-
</programlisting><productname>PostgreSQL</productname> releases prior to
1743+
</programlisting>
1744+
<productname>PostgreSQL</productname> releases prior to
17441745
8.1 would accept queries of this form, and add an implicit entry
17451746
to the query's <literal>FROM</literal> clause for each table
17461747
referenced by the query. This is no longer allowed.
17471748
</para>
17481749
</refsect2>
17491750

1751+
<refsect2>
1752+
<title>Empty <literal>SELECT</literal> Lists</title>
1753+
1754+
<para>
1755+
The list of output expressions after <literal>SELECT</literal> can be
1756+
empty, producing a zero-column result table.
1757+
This is not valid syntax according to the SQL standard.
1758+
<productname>PostgreSQL</productname> allows it to be consistent with
1759+
allowing zero-column tables.
1760+
However, an empty list is not allowed when <literal>DISTINCT</> is used.
1761+
</para>
1762+
</refsect2>
1763+
17501764
<refsect2>
17511765
<title>Omitting the <literal>AS</literal> Key Word</title>
17521766

@@ -1809,10 +1823,6 @@ SELECT distributors.* WHERE distributors.name = 'Westward';
18091823
<productname>PostgreSQL</productname> treats <literal>UNNEST()</> the
18101824
same as other set-returning functions.
18111825
</para>
1812-
1813-
<para>
1814-
<literal>ROWS FROM( ... )</> is an extension of the SQL standard.
1815-
</para>
18161826
</refsect2>
18171827

18181828
<refsect2>
@@ -1910,9 +1920,13 @@ SELECT distributors.* WHERE distributors.name = 'Westward';
19101920
<title>Nonstandard Clauses</title>
19111921

19121922
<para>
1913-
The clause <literal>DISTINCT ON</literal> is not defined in the
1923+
<literal>DISTINCT ON ( ... )</literal> is an extension of the
19141924
SQL standard.
19151925
</para>
1926+
1927+
<para>
1928+
<literal>ROWS FROM( ... )</> is an extension of the SQL standard.
1929+
</para>
19161930
</refsect2>
19171931
</refsect1>
19181932
</refentry>

src/backend/parser/analyze.c

+13
Original file line numberDiff line numberDiff line change
@@ -2018,6 +2018,19 @@ transformReturningList(ParseState *pstate, List *returningList)
20182018
/* transform RETURNING identically to a SELECT targetlist */
20192019
rlist = transformTargetList(pstate, returningList, EXPR_KIND_RETURNING);
20202020

2021+
/*
2022+
* Complain if the nonempty tlist expanded to nothing (which is possible
2023+
* if it contains only a star-expansion of a zero-column table). If we
2024+
* allow this, the parsed Query will look like it didn't have RETURNING,
2025+
* with results that would probably surprise the user.
2026+
*/
2027+
if (rlist == NIL)
2028+
ereport(ERROR,
2029+
(errcode(ERRCODE_SYNTAX_ERROR),
2030+
errmsg("RETURNING must have at least one column"),
2031+
parser_errposition(pstate,
2032+
exprLocation(linitial(returningList)))));
2033+
20212034
/* mark column origins */
20222035
markTargetListOrigins(pstate, rlist);
20232036

src/backend/parser/gram.y

+6-2
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
334334
name_list from_clause from_list opt_array_bounds
335335
qualified_name_list any_name any_name_list
336336
any_operator expr_list attrs
337-
target_list insert_column_list set_target_list
337+
target_list opt_target_list insert_column_list set_target_list
338338
set_clause_list set_clause multiple_set_clause
339339
ctext_expr_list ctext_row def_list indirection opt_indirection
340340
reloption_list group_clause TriggerFuncArgs select_limit
@@ -9259,7 +9259,7 @@ select_clause:
92599259
* However, this is not checked by the grammar; parse analysis must check it.
92609260
*/
92619261
simple_select:
9262-
SELECT opt_distinct target_list
9262+
SELECT opt_distinct opt_target_list
92639263
into_clause from_clause where_clause
92649264
group_clause having_clause window_clause
92659265
{
@@ -12215,6 +12215,10 @@ ctext_row: '(' ctext_expr_list ')' { $$ = $2; }
1221512215
*
1221612216
*****************************************************************************/
1221712217

12218+
opt_target_list: target_list { $$ = $1; }
12219+
| /* EMPTY */ { $$ = NIL; }
12220+
;
12221+
1221812222
target_list:
1221912223
target_el { $$ = list_make1($1); }
1222012224
| target_list ',' target_el { $$ = lappend($1, $3); }

src/backend/parser/parse_clause.c

+19
Original file line numberDiff line numberDiff line change
@@ -2011,6 +2011,20 @@ transformDistinctClause(ParseState *pstate,
20112011
true);
20122012
}
20132013

2014+
/*
2015+
* Complain if we found nothing to make DISTINCT. Returning an empty list
2016+
* would cause the parsed Query to look like it didn't have DISTINCT, with
2017+
* results that would probably surprise the user. Note: this case is
2018+
* presently impossible for aggregates because of grammar restrictions,
2019+
* but we check anyway.
2020+
*/
2021+
if (result == NIL)
2022+
ereport(ERROR,
2023+
(errcode(ERRCODE_SYNTAX_ERROR),
2024+
is_agg ?
2025+
errmsg("an aggregate with DISTINCT must have at least one argument") :
2026+
errmsg("SELECT DISTINCT must have at least one column")));
2027+
20142028
return result;
20152029
}
20162030

@@ -2115,6 +2129,11 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist,
21152129
true);
21162130
}
21172131

2132+
/*
2133+
* An empty result list is impossible here because of grammar restrictions.
2134+
*/
2135+
Assert(result != NIL);
2136+
21182137
return result;
21192138
}
21202139

src/test/regress/expected/errors.out

+8-15
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,24 @@ select 1;
1515
--
1616
--
1717
-- SELECT
18-
-- missing relation name
18+
-- this used to be a syntax error, but now we allow an empty target list
1919
select;
20-
ERROR: syntax error at or near ";"
21-
LINE 1: select;
22-
^
20+
--
21+
(1 row)
22+
2323
-- no such relation
2424
select * from nonesuch;
2525
ERROR: relation "nonesuch" does not exist
2626
LINE 1: select * from nonesuch;
2727
^
28-
-- missing target list
29-
select from pg_database;
30-
ERROR: syntax error at or near "from"
31-
LINE 1: select from pg_database;
32-
^
3328
-- bad name in target list
3429
select nonesuch from pg_database;
3530
ERROR: column "nonesuch" does not exist
3631
LINE 1: select nonesuch from pg_database;
3732
^
33+
-- empty distinct list isn't OK
34+
select distinct from pg_database;
35+
ERROR: SELECT DISTINCT must have at least one column
3836
-- bad attribute name on lhs of operator
3937
select * from pg_database where nonesuch = pg_database.datname;
4038
ERROR: column "nonesuch" does not exist
@@ -45,12 +43,7 @@ select * from pg_database where pg_database.datname = nonesuch;
4543
ERROR: column "nonesuch" does not exist
4644
LINE 1: ...ect * from pg_database where pg_database.datname = nonesuch;
4745
^
48-
-- bad select distinct on syntax, distinct attribute missing
49-
select distinct on (foobar) from pg_database;
50-
ERROR: syntax error at or near "from"
51-
LINE 1: select distinct on (foobar) from pg_database;
52-
^
53-
-- bad select distinct on syntax, distinct attribute not in target list
46+
-- bad attribute name in select distinct on
5447
select distinct on (foobar) * from pg_database;
5548
ERROR: column "foobar" does not exist
5649
LINE 1: select distinct on (foobar) * from pg_database;

src/test/regress/sql/errors.sql

+6-9
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,25 @@ select 1;
1616
--
1717
-- SELECT
1818

19-
-- missing relation name
19+
-- this used to be a syntax error, but now we allow an empty target list
2020
select;
2121

2222
-- no such relation
2323
select * from nonesuch;
2424

25-
-- missing target list
26-
select from pg_database;
2725
-- bad name in target list
2826
select nonesuch from pg_database;
27+
28+
-- empty distinct list isn't OK
29+
select distinct from pg_database;
30+
2931
-- bad attribute name on lhs of operator
3032
select * from pg_database where nonesuch = pg_database.datname;
3133

3234
-- bad attribute name on rhs of operator
3335
select * from pg_database where pg_database.datname = nonesuch;
3436

35-
36-
-- bad select distinct on syntax, distinct attribute missing
37-
select distinct on (foobar) from pg_database;
38-
39-
40-
-- bad select distinct on syntax, distinct attribute not in target list
37+
-- bad attribute name in select distinct on
4138
select distinct on (foobar) * from pg_database;
4239

4340

0 commit comments

Comments
 (0)