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

Commit 52fc007

Browse files
committed
Avoid a premature coercion failure in transformSetOperationTree() when
presented with an UNKNOWN-type Var, which can happen in cases where an unknown literal appeared in a subquery. While many such cases will fail later on anyway in the planner, there are some cases where the planner is able to flatten the query and replace the Var by the constant before it has to coerce the union column to the final type. I had added this check in 8.4 to provide earlier/better error detection, but it causes a regression for some cases that worked OK before. Fix by not making the check if the input node is UNKNOWN type and not a Const or Param. If it isn't going to work, it will fail anyway at plan time, with the only real loss being inability to provide an error cursor. Per gripe from Britt Piehler. In passing, rename a couple of variables to remove confusion from an inner scope masking the same variable names in an outer scope.
1 parent ff49961 commit 52fc007

File tree

3 files changed

+71
-20
lines changed

3 files changed

+71
-20
lines changed

src/backend/parser/analyze.c

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
1818
* Portions Copyright (c) 1994, Regents of the University of California
1919
*
20-
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.397 2009/12/15 17:57:47 tgl Exp $
20+
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.398 2009/12/16 22:24:13 tgl Exp $
2121
*
2222
*-------------------------------------------------------------------------
2323
*/
@@ -1533,20 +1533,20 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
15331533
op->groupClauses = NIL;
15341534
forboth(lci, lcolinfo, rci, rcolinfo)
15351535
{
1536-
Node *lcolinfo = (Node *) lfirst(lci);
1537-
Node *rcolinfo = (Node *) lfirst(rci);
1538-
Oid lcoltype = exprType(lcolinfo);
1539-
Oid rcoltype = exprType(rcolinfo);
1540-
int32 lcoltypmod = exprTypmod(lcolinfo);
1541-
int32 rcoltypmod = exprTypmod(rcolinfo);
1536+
Node *lcolnode = (Node *) lfirst(lci);
1537+
Node *rcolnode = (Node *) lfirst(rci);
1538+
Oid lcoltype = exprType(lcolnode);
1539+
Oid rcoltype = exprType(rcolnode);
1540+
int32 lcoltypmod = exprTypmod(lcolnode);
1541+
int32 rcoltypmod = exprTypmod(rcolnode);
15421542
Node *bestexpr;
1543-
SetToDefault *rescolinfo;
1543+
SetToDefault *rescolnode;
15441544
Oid rescoltype;
15451545
int32 rescoltypmod;
15461546

15471547
/* select common type, same as CASE et al */
15481548
rescoltype = select_common_type(pstate,
1549-
list_make2(lcolinfo, rcolinfo),
1549+
list_make2(lcolnode, rcolnode),
15501550
context,
15511551
&bestexpr);
15521552
/* if same type and same typmod, use typmod; else default */
@@ -1555,18 +1555,35 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
15551555
else
15561556
rescoltypmod = -1;
15571557

1558-
/* verify the coercions are actually possible */
1559-
(void) coerce_to_common_type(pstate, lcolinfo,
1560-
rescoltype, context);
1561-
(void) coerce_to_common_type(pstate, rcolinfo,
1562-
rescoltype, context);
1558+
/*
1559+
* Verify the coercions are actually possible. If not, we'd
1560+
* fail later anyway, but we want to fail now while we have
1561+
* sufficient context to produce an error cursor position.
1562+
*
1563+
* The if-tests might look wrong, but they are correct: we should
1564+
* verify if the input is non-UNKNOWN *or* if it is an UNKNOWN
1565+
* Const (to verify the literal is valid for the target data type)
1566+
* or Param (to possibly resolve the Param's type). We should do
1567+
* nothing if the input is say an UNKNOWN Var, which can happen in
1568+
* some cases. The planner is sometimes able to fold the Var to a
1569+
* constant before it has to coerce the type, so failing now would
1570+
* just break cases that might work.
1571+
*/
1572+
if (lcoltype != UNKNOWNOID ||
1573+
IsA(lcolnode, Const) || IsA(lcolnode, Param))
1574+
(void) coerce_to_common_type(pstate, lcolnode,
1575+
rescoltype, context);
1576+
if (rcoltype != UNKNOWNOID ||
1577+
IsA(rcolnode, Const) || IsA(rcolnode, Param))
1578+
(void) coerce_to_common_type(pstate, rcolnode,
1579+
rescoltype, context);
15631580

15641581
/* emit results */
1565-
rescolinfo = makeNode(SetToDefault);
1566-
rescolinfo->typeId = rescoltype;
1567-
rescolinfo->typeMod = rescoltypmod;
1568-
rescolinfo->location = exprLocation(bestexpr);
1569-
*colInfo = lappend(*colInfo, rescolinfo);
1582+
rescolnode = makeNode(SetToDefault);
1583+
rescolnode->typeId = rescoltype;
1584+
rescolnode->typeMod = rescoltypmod;
1585+
rescolnode->location = exprLocation(bestexpr);
1586+
*colInfo = lappend(*colInfo, rescolnode);
15701587

15711588
op->colTypes = lappend_oid(op->colTypes, rescoltype);
15721589
op->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
@@ -1584,7 +1601,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
15841601
ParseCallbackState pcbstate;
15851602

15861603
setup_parser_errposition_callback(&pcbstate, pstate,
1587-
rescolinfo->location);
1604+
rescolnode->location);
15881605

15891606
/* determine the eqop and optional sortop */
15901607
get_sort_group_operators(rescoltype,

src/test/regress/expected/union.out

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,3 +433,25 @@ SELECT q1 FROM int8_tbl EXCEPT (((SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1)))
433433
4567890123456789 | -4567890123456789
434434
(5 rows)
435435

436+
--
437+
-- Check handling of a case with unknown constants. We don't guarantee
438+
-- an undecorated constant will work in all cases, but historically this
439+
-- usage has worked, so test we don't break it.
440+
--
441+
SELECT a.f1 FROM (SELECT 'test' AS f1 FROM varchar_tbl) a
442+
UNION
443+
SELECT b.f1 FROM (SELECT f1 FROM varchar_tbl) b
444+
ORDER BY 1;
445+
f1
446+
------
447+
a
448+
ab
449+
abcd
450+
test
451+
(4 rows)
452+
453+
-- This should fail, but it should produce an error cursor
454+
SELECT '3.4'::numeric UNION SELECT 'foo';
455+
ERROR: invalid input syntax for type numeric: "foo"
456+
LINE 1: SELECT '3.4'::numeric UNION SELECT 'foo';
457+
^

src/test/regress/sql/union.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,16 @@ SELECT q1 FROM int8_tbl EXCEPT (((SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1)))
153153

154154
(((((select * from int8_tbl)))));
155155

156+
--
157+
-- Check handling of a case with unknown constants. We don't guarantee
158+
-- an undecorated constant will work in all cases, but historically this
159+
-- usage has worked, so test we don't break it.
160+
--
161+
162+
SELECT a.f1 FROM (SELECT 'test' AS f1 FROM varchar_tbl) a
163+
UNION
164+
SELECT b.f1 FROM (SELECT f1 FROM varchar_tbl) b
165+
ORDER BY 1;
156166

167+
-- This should fail, but it should produce an error cursor
168+
SELECT '3.4'::numeric UNION SELECT 'foo';

0 commit comments

Comments
 (0)