summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2013-01-30 19:16:44 +0000
committerTom Lane2013-01-30 19:16:44 +0000
commit969dc8cce922e58fa5dd6f945678ecd531fdb6a6 (patch)
treed009a1e10eb236e063f3b29cf7eea29a191db493
parent8879c3503726534af58e61ce5ab1e26d70877696 (diff)
Fix grammar for subscripting or field selection from a sub-SELECT result.
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.
-rw-r--r--src/backend/parser/gram.y23
-rw-r--r--src/test/regress/expected/subselect.out55
-rw-r--r--src/test/regress/sql/subselect.sql15
3 files changed, 93 insertions, 0 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c5a9a6f0d58..69c4fa442fa 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -9258,6 +9258,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);
diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out
index eae1b611dc8..850777acd5c 100644
--- a/src/test/regress/expected/subselect.out
+++ b/src/test/regress/expected/subselect.out
@@ -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,
diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql
index 7d7830dfe23..8ca7a3bd2fb 100644
--- a/src/test/regress/sql/subselect.sql
+++ b/src/test/regress/sql/subselect.sql
@@ -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 (