aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2014-07-11 19:12:56 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2014-07-11 19:12:56 -0400
commita81fbcfb33b44e6a0d189f771108677260621753 (patch)
tree3b1f7f1f96f338946106ab19d1bc8827db22d69e
parent2e7469dc8b3bac4fe0f9bd042aaf802132efde85 (diff)
downloadpostgresql-a81fbcfb33b44e6a0d189f771108677260621753.tar.gz
postgresql-a81fbcfb33b44e6a0d189f771108677260621753.zip
Fix bug with whole-row references to append subplans.
ExecEvalWholeRowVar incorrectly supposed that it could "bless" the source TupleTableSlot just once per query. But if the input is coming from an Append (or, perhaps, other cases?) more than one slot might be returned over the query run. This led to "record type has not been registered" errors when a composite datum was extracted from a non-blessed slot. This bug has been there a long time; I guess it escaped notice because when dealing with subqueries the planner tends to expand whole-row Vars into RowExprs, which don't have the same problem. It is possible to trigger the problem in all active branches, though, as illustrated by the added regression test.
-rw-r--r--src/backend/executor/execQual.c33
-rw-r--r--src/test/regress/expected/subselect.out18
-rw-r--r--src/test/regress/sql/subselect.sql10
3 files changed, 47 insertions, 14 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 8410032cf50..61c8d61530e 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -712,7 +712,6 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
{
Var *variable = (Var *) wrvstate->xprstate.expr;
TupleTableSlot *slot;
- TupleDesc slot_tupdesc;
bool needslow = false;
if (isDone)
@@ -790,25 +789,14 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
if (wrvstate->wrv_junkFilter != NULL)
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
- slot_tupdesc = slot->tts_tupleDescriptor;
-
/*
- * If it's a RECORD Var, we'll use the slot's type ID info. It's likely
- * that the slot's type is also RECORD; if so, make sure it's been
- * "blessed", so that the Datum can be interpreted later.
- *
* If the Var identifies a named composite type, we must check that the
* actual tuple type is compatible with it.
*/
- if (variable->vartype == RECORDOID)
- {
- if (slot_tupdesc->tdtypeid == RECORDOID &&
- slot_tupdesc->tdtypmod < 0)
- assign_record_type_typmod(slot_tupdesc);
- }
- else
+ if (variable->vartype != RECORDOID)
{
TupleDesc var_tupdesc;
+ TupleDesc slot_tupdesc;
int i;
/*
@@ -825,6 +813,8 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
*/
var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
+ slot_tupdesc = slot->tts_tupleDescriptor;
+
if (var_tupdesc->natts != slot_tupdesc->natts)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -882,6 +872,7 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
{
Var *variable = (Var *) wrvstate->xprstate.expr;
TupleTableSlot *slot = econtext->ecxt_scantuple;
+ TupleDesc slot_tupdesc;
HeapTupleHeader dtuple;
if (isDone)
@@ -893,6 +884,20 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
/*
+ * If it's a RECORD Var, we'll use the slot's type ID info. It's likely
+ * that the slot's type is also RECORD; if so, make sure it's been
+ * "blessed", so that the Datum can be interpreted later. (Note: we must
+ * do this here, not in ExecEvalWholeRowVar, because some plan trees may
+ * return different slots at different times. We have to be ready to
+ * bless additional slots during the run.)
+ */
+ slot_tupdesc = slot->tts_tupleDescriptor;
+ if (variable->vartype == RECORDOID &&
+ slot_tupdesc->tdtypeid == RECORDOID &&
+ slot_tupdesc->tdtypmod < 0)
+ assign_record_type_typmod(slot_tupdesc);
+
+ /*
* Copy the slot tuple and make sure any toasted fields get detoasted.
*/
dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out
index c9d80bc045a..5d9aa4e2eb9 100644
--- a/src/test/regress/expected/subselect.out
+++ b/src/test/regress/expected/subselect.out
@@ -662,3 +662,21 @@ select * from int4_tbl o where (f1, f1) in
(1 row)
reset enable_hashjoin;
+--
+-- check for over-optimization of whole-row Var referencing an Append plan
+--
+select (select q from
+ (select 1,2,3 where f1 > 0
+ union all
+ select 4,5,6.0 where f1 <= 0
+ ) q )
+from int4_tbl;
+ ?column?
+-----------
+ (4,5,6.0)
+ (1,2,3)
+ (4,5,6.0)
+ (1,2,3)
+ (4,5,6.0)
+(5 rows)
+
diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql
index a748889b23d..2862bf791bc 100644
--- a/src/test/regress/sql/subselect.sql
+++ b/src/test/regress/sql/subselect.sql
@@ -404,3 +404,13 @@ set enable_hashjoin to 0;
select * from int4_tbl o where (f1, f1) in
(select f1, generate_series(1,2) / 10 g from int4_tbl i group by f1);
reset enable_hashjoin;
+
+--
+-- check for over-optimization of whole-row Var referencing an Append plan
+--
+select (select q from
+ (select 1,2,3 where f1 > 0
+ union all
+ select 4,5,6.0 where f1 <= 0
+ ) q )
+from int4_tbl;