Skip to content

Commit 261f954

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 189bd09 commit 261f954

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
@@ -710,7 +710,6 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
710710
{
711711
Var *variable = (Var *) wrvstate->xprstate.expr;
712712
TupleTableSlot *slot;
713-
TupleDesc slot_tupdesc;
714713
bool needslow = false;
715714

716715
if (isDone)
@@ -802,25 +801,14 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
802801
if (wrvstate->wrv_junkFilter != NULL)
803802
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
804803

805-
slot_tupdesc = slot->tts_tupleDescriptor;
806-
807804
/*
808-
* If it's a RECORD Var, we'll use the slot's type ID info. It's likely
809-
* that the slot's type is also RECORD; if so, make sure it's been
810-
* "blessed", so that the Datum can be interpreted later.
811-
*
812805
* If the Var identifies a named composite type, we must check that the
813806
* actual tuple type is compatible with it.
814807
*/
815-
if (variable->vartype == RECORDOID)
816-
{
817-
if (slot_tupdesc->tdtypeid == RECORDOID &&
818-
slot_tupdesc->tdtypmod < 0)
819-
assign_record_type_typmod(slot_tupdesc);
820-
}
821-
else
808+
if (variable->vartype != RECORDOID)
822809
{
823810
TupleDesc var_tupdesc;
811+
TupleDesc slot_tupdesc;
824812
int i;
825813

826814
/*
@@ -837,6 +825,8 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
837825
*/
838826
var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
839827

828+
slot_tupdesc = slot->tts_tupleDescriptor;
829+
840830
if (var_tupdesc->natts != slot_tupdesc->natts)
841831
ereport(ERROR,
842832
(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -894,6 +884,7 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
894884
{
895885
Var *variable = (Var *) wrvstate->xprstate.expr;
896886
TupleTableSlot *slot;
887+
TupleDesc slot_tupdesc;
897888
HeapTupleHeader dtuple;
898889

899890
if (isDone)
@@ -923,6 +914,20 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
923914
if (wrvstate->wrv_junkFilter != NULL)
924915
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
925916

917+
/*
918+
* If it's a RECORD Var, we'll use the slot's type ID info. It's likely
919+
* that the slot's type is also RECORD; if so, make sure it's been
920+
* "blessed", so that the Datum can be interpreted later. (Note: we must
921+
* do this here, not in ExecEvalWholeRowVar, because some plan trees may
922+
* return different slots at different times. We have to be ready to
923+
* bless additional slots during the run.)
924+
*/
925+
slot_tupdesc = slot->tts_tupleDescriptor;
926+
if (variable->vartype == RECORDOID &&
927+
slot_tupdesc->tdtypeid == RECORDOID &&
928+
slot_tupdesc->tdtypmod < 0)
929+
assign_record_type_typmod(slot_tupdesc);
930+
926931
/*
927932
* Copy the slot tuple and make sure any toasted fields get detoasted.
928933
*/

src/test/regress/expected/subselect.out

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

778+
--
779+
-- check for over-optimization of whole-row Var referencing an Append plan
780+
--
781+
select (select q from
782+
(select 1,2,3 where f1 > 0
783+
union all
784+
select 4,5,6.0 where f1 <= 0
785+
) q )
786+
from int4_tbl;
787+
q
788+
-----------
789+
(4,5,6.0)
790+
(1,2,3)
791+
(4,5,6.0)
792+
(1,2,3)
793+
(4,5,6.0)
794+
(5 rows)
795+

src/test/regress/sql/subselect.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,3 +431,13 @@ select * from int4_tbl o where (f1, f1) in
431431
(select f1, generate_series(1,2) / 10 g from int4_tbl i group by f1);
432432
select * from int4_tbl o where (f1, f1) in
433433
(select f1, generate_series(1,2) / 10 g from int4_tbl i group by f1);
434+
435+
--
436+
-- check for over-optimization of whole-row Var referencing an Append plan
437+
--
438+
select (select q from
439+
(select 1,2,3 where f1 > 0
440+
union all
441+
select 4,5,6.0 where f1 <= 0
442+
) q )
443+
from int4_tbl;

0 commit comments

Comments
 (0)