]> granicus.if.org Git - postgresql/commitdiff
Fix grammar for subscripting or field selection from a sub-SELECT result.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 30 Jan 2013 19:16:34 +0000 (14:16 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 30 Jan 2013 19:16:34 +0000 (14:16 -0500)
Such cases should work, but the grammar failed to accept them because of
our ancient precedence hacks to convince bison that extra parentheses
around a sub-SELECT in an expression are unambiguous.  (Formally, they
*are* ambiguous, but we don't especially care whether they're treated as
part of the sub-SELECT or part of the expression.  Bison cares, though.)
Fix by adding a redundant-looking production for this case.

This is a fine example of why fixing shift/reduce conflicts via
precedence declarations is more dangerous than it looks: you can easily
cause the parser to reject cases that should work.

This has been wrong since commit 3db4056e22b0c6b2adc92543baf8408d2894fe91
or maybe before, and apparently some people have been working around it
by inserting no-op casts.  That method introduces a dump/reload hazard,
as illustrated in bug #7838 from Jan Mate.  Hence, back-patch to all
active branches.

src/backend/parser/gram.y
src/test/regress/expected/subselect.out
src/test/regress/sql/subselect.sql

index d0d9492bb1eb6e5e3a2829e0727219195f8d0b71..d59a6a9c5d3105724ec22891d86cd5a456fbadee 100644 (file)
@@ -10232,6 +10232,29 @@ c_expr:                columnref                                                               { $$ = $1; }
                                        n->location = @1;
                                        $$ = (Node *)n;
                                }
+                       | select_with_parens indirection
+                               {
+                                       /*
+                                        * Because the select_with_parens nonterminal is designed
+                                        * to "eat" as many levels of parens as possible, the
+                                        * '(' a_expr ')' opt_indirection production above will
+                                        * fail to match a sub-SELECT with indirection decoration;
+                                        * the sub-SELECT won't be regarded as an a_expr as long
+                                        * as there are parens around it.  To support applying
+                                        * subscripting or field selection to a sub-SELECT result,
+                                        * we need this redundant-looking production.
+                                        */
+                                       SubLink *n = makeNode(SubLink);
+                                       A_Indirection *a = makeNode(A_Indirection);
+                                       n->subLinkType = EXPR_SUBLINK;
+                                       n->testexpr = NULL;
+                                       n->operName = NIL;
+                                       n->subselect = $1;
+                                       n->location = @1;
+                                       a->arg = (Node *)n;
+                                       a->indirection = check_indirection($2, yyscanner);
+                                       $$ = (Node *)a;
+                               }
                        | EXISTS select_with_parens
                                {
                                        SubLink *n = makeNode(SubLink);
index f42c1139727b716f7ad93359313b994499f0d811..1cc573589a88c377f1eb5a72e8563ea891a90d5b 100644 (file)
@@ -17,6 +17,61 @@ SELECT 1 AS zero WHERE 1 IN (SELECT 2);
 ------
 (0 rows)
 
+-- Check grammar's handling of extra parens in assorted contexts
+SELECT * FROM (SELECT 1 AS x) ss;
+ x 
+---
+ 1
+(1 row)
+
+SELECT * FROM ((SELECT 1 AS x)) ss;
+ x 
+---
+ 1
+(1 row)
+
+(SELECT 2) UNION SELECT 2;
+ ?column? 
+----------
+        2
+(1 row)
+
+((SELECT 2)) UNION SELECT 2;
+ ?column? 
+----------
+        2
+(1 row)
+
+SELECT ((SELECT 2) UNION SELECT 2);
+ ?column? 
+----------
+        2
+(1 row)
+
+SELECT (((SELECT 2)) UNION SELECT 2);
+ ?column? 
+----------
+        2
+(1 row)
+
+SELECT (SELECT ARRAY[1,2,3])[1];
+ ?column? 
+----------
+        1
+(1 row)
+
+SELECT ((SELECT ARRAY[1,2,3]))[2];
+ ?column? 
+----------
+        2
+(1 row)
+
+SELECT (((SELECT ARRAY[1,2,3])))[3];
+ ?column? 
+----------
+        3
+(1 row)
+
 -- Set up some simple test tables
 CREATE TABLE SUBSELECT_TBL (
   f1 integer,
index cba449a203df65838e16bbddf0d1e880a10e42b6..8a55474b54a0ea5699011a47545277154835e40d 100644 (file)
@@ -8,6 +8,21 @@ SELECT 1 AS zero WHERE 1 NOT IN (SELECT 1);
 
 SELECT 1 AS zero WHERE 1 IN (SELECT 2);
 
+-- Check grammar's handling of extra parens in assorted contexts
+
+SELECT * FROM (SELECT 1 AS x) ss;
+SELECT * FROM ((SELECT 1 AS x)) ss;
+
+(SELECT 2) UNION SELECT 2;
+((SELECT 2)) UNION SELECT 2;
+
+SELECT ((SELECT 2) UNION SELECT 2);
+SELECT (((SELECT 2)) UNION SELECT 2);
+
+SELECT (SELECT ARRAY[1,2,3])[1];
+SELECT ((SELECT ARRAY[1,2,3]))[2];
+SELECT (((SELECT ARRAY[1,2,3])))[3];
+
 -- Set up some simple test tables
 
 CREATE TABLE SUBSELECT_TBL (