Commit d6858148 authored by Tom Lane's avatar Tom Lane

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.
parent 80ddd04b
...@@ -712,7 +712,6 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext, ...@@ -712,7 +712,6 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
{ {
Var *variable = (Var *) wrvstate->xprstate.expr; Var *variable = (Var *) wrvstate->xprstate.expr;
TupleTableSlot *slot; TupleTableSlot *slot;
TupleDesc slot_tupdesc;
bool needslow = false; bool needslow = false;
if (isDone) if (isDone)
...@@ -804,25 +803,14 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext, ...@@ -804,25 +803,14 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
if (wrvstate->wrv_junkFilter != NULL) if (wrvstate->wrv_junkFilter != NULL)
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot); 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 * If the Var identifies a named composite type, we must check that the
* actual tuple type is compatible with it. * actual tuple type is compatible with it.
*/ */
if (variable->vartype == RECORDOID) if (variable->vartype != RECORDOID)
{
if (slot_tupdesc->tdtypeid == RECORDOID &&
slot_tupdesc->tdtypmod < 0)
assign_record_type_typmod(slot_tupdesc);
}
else
{ {
TupleDesc var_tupdesc; TupleDesc var_tupdesc;
TupleDesc slot_tupdesc;
int i; int i;
/* /*
...@@ -839,6 +827,8 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext, ...@@ -839,6 +827,8 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
*/ */
var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
slot_tupdesc = slot->tts_tupleDescriptor;
if (var_tupdesc->natts != slot_tupdesc->natts) if (var_tupdesc->natts != slot_tupdesc->natts)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
...@@ -896,6 +886,7 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext, ...@@ -896,6 +886,7 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
{ {
Var *variable = (Var *) wrvstate->xprstate.expr; Var *variable = (Var *) wrvstate->xprstate.expr;
TupleTableSlot *slot; TupleTableSlot *slot;
TupleDesc slot_tupdesc;
HeapTupleHeader dtuple; HeapTupleHeader dtuple;
if (isDone) if (isDone)
...@@ -925,6 +916,20 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext, ...@@ -925,6 +916,20 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
if (wrvstate->wrv_junkFilter != NULL) if (wrvstate->wrv_junkFilter != NULL)
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot); 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. * Copy the slot tuple and make sure any toasted fields get detoasted.
*/ */
......
...@@ -774,6 +774,24 @@ select * from int4_tbl o where (f1, f1) in ...@@ -774,6 +774,24 @@ select * from int4_tbl o where (f1, f1) in
0 0
(1 row) (1 row)
--
-- 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;
q
-----------
(4,5,6.0)
(1,2,3)
(4,5,6.0)
(1,2,3)
(4,5,6.0)
(5 rows)
-- --
-- Check that volatile quals aren't pushed down past a DISTINCT: -- Check that volatile quals aren't pushed down past a DISTINCT:
-- nextval() should not be called more than the nominal number of times -- nextval() should not be called more than the nominal number of times
......
...@@ -432,6 +432,16 @@ select * from int4_tbl o where (f1, f1) in ...@@ -432,6 +432,16 @@ select * from int4_tbl o where (f1, f1) in
select * from int4_tbl o where (f1, f1) in select * from int4_tbl o where (f1, f1) in
(select f1, generate_series(1,2) / 10 g from int4_tbl i group by f1); (select f1, generate_series(1,2) / 10 g from int4_tbl i group by f1);
--
-- 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;
-- --
-- Check that volatile quals aren't pushed down past a DISTINCT: -- Check that volatile quals aren't pushed down past a DISTINCT:
-- nextval() should not be called more than the nominal number of times -- nextval() should not be called more than the nominal number of times
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment