Skip to content

Commit a81fbcf

Browse files
committed
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.
1 parent 2e7469d commit a81fbcf

File tree

3 files changed

+47
-14
lines changed

3 files changed

+47
-14
lines changed

src/backend/executor/execQual.c

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,6 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
712712
{
713713
Var *variable = (Var *) wrvstate->xprstate.expr;
714714
TupleTableSlot *slot;
715-
TupleDesc slot_tupdesc;
716715
bool needslow = false;
717716

718717
if (isDone)
@@ -790,25 +789,14 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
790789
if (wrvstate->wrv_junkFilter != NULL)
791790
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
792791

793-
slot_tupdesc = slot->tts_tupleDescriptor;
794-
795792
/*
796-
* If it's a RECORD Var, we'll use the slot's type ID info. It's likely
797-
* that the slot's type is also RECORD; if so, make sure it's been
798-
* "blessed", so that the Datum can be interpreted later.
799-
*
800793
* If the Var identifies a named composite type, we must check that the
801794
* actual tuple type is compatible with it.
802795
*/
803-
if (variable->vartype == RECORDOID)
804-
{
805-
if (slot_tupdesc->tdtypeid == RECORDOID &&
806-
slot_tupdesc->tdtypmod < 0)
807-
assign_record_type_typmod(slot_tupdesc);
808-
}
809-
else
796+
if (variable->vartype != RECORDOID)
810797
{
811798
TupleDesc var_tupdesc;
799+
TupleDesc slot_tupdesc;
812800
int i;
813801

814802
/*
@@ -825,6 +813,8 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
825813
*/
826814
var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
827815

816+
slot_tupdesc = slot->tts_tupleDescriptor;
817+
828818
if (var_tupdesc->natts != slot_tupdesc->natts)
829819
ereport(ERROR,
830820
(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -882,6 +872,7 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
882872
{
883873
Var *variable = (Var *) wrvstate->xprstate.expr;
884874
TupleTableSlot *slot = econtext->ecxt_scantuple;
875+
TupleDesc slot_tupdesc;
885876
HeapTupleHeader dtuple;
886877

887878
if (isDone)
@@ -892,6 +883,20 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
892883
if (wrvstate->wrv_junkFilter != NULL)
893884
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
894885

886+
/*
887+
* If it's a RECORD Var, we'll use the slot's type ID info. It's likely
888+
* that the slot's type is also RECORD; if so, make sure it's been
889+
* "blessed", so that the Datum can be interpreted later. (Note: we must
890+
* do this here, not in ExecEvalWholeRowVar, because some plan trees may
891+
* return different slots at different times. We have to be ready to
892+
* bless additional slots during the run.)
893+
*/
894+
slot_tupdesc = slot->tts_tupleDescriptor;
895+
if (variable->vartype == RECORDOID &&
896+
slot_tupdesc->tdtypeid == RECORDOID &&
897+
slot_tupdesc->tdtypmod < 0)
898+
assign_record_type_typmod(slot_tupdesc);
899+
895900
/*
896901
* Copy the slot tuple and make sure any toasted fields get detoasted.
897902
*/

src/test/regress/expected/subselect.out

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,3 +662,21 @@ select * from int4_tbl o where (f1, f1) in
662662
(1 row)
663663

664664
reset enable_hashjoin;
665+
--
666+
-- check for over-optimization of whole-row Var referencing an Append plan
667+
--
668+
select (select q from
669+
(select 1,2,3 where f1 > 0
670+
union all
671+
select 4,5,6.0 where f1 <= 0
672+
) q )
673+
from int4_tbl;
674+
?column?
675+
-----------
676+
(4,5,6.0)
677+
(1,2,3)
678+
(4,5,6.0)
679+
(1,2,3)
680+
(4,5,6.0)
681+
(5 rows)
682+

src/test/regress/sql/subselect.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,3 +404,13 @@ set enable_hashjoin to 0;
404404
select * from int4_tbl o where (f1, f1) in
405405
(select f1, generate_series(1,2) / 10 g from int4_tbl i group by f1);
406406
reset enable_hashjoin;
407+
408+
--
409+
-- check for over-optimization of whole-row Var referencing an Append plan
410+
--
411+
select (select q from
412+
(select 1,2,3 where f1 > 0
413+
union all
414+
select 4,5,6.0 where f1 <= 0
415+
) q )
416+
from int4_tbl;

0 commit comments

Comments
 (0)