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

Commit f280eff

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 6fe2440 commit f280eff

File tree

3 files changed

+47
-14
lines changed

3 files changed

+47
-14
lines changed

src/backend/executor/execQual.c

+19-14
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)
@@ -804,25 +803,14 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
804803
if (wrvstate->wrv_junkFilter != NULL)
805804
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
806805

807-
slot_tupdesc = slot->tts_tupleDescriptor;
808-
809806
/*
810-
* If it's a RECORD Var, we'll use the slot's type ID info. It's likely
811-
* that the slot's type is also RECORD; if so, make sure it's been
812-
* "blessed", so that the Datum can be interpreted later.
813-
*
814807
* If the Var identifies a named composite type, we must check that the
815808
* actual tuple type is compatible with it.
816809
*/
817-
if (variable->vartype == RECORDOID)
818-
{
819-
if (slot_tupdesc->tdtypeid == RECORDOID &&
820-
slot_tupdesc->tdtypmod < 0)
821-
assign_record_type_typmod(slot_tupdesc);
822-
}
823-
else
810+
if (variable->vartype != RECORDOID)
824811
{
825812
TupleDesc var_tupdesc;
813+
TupleDesc slot_tupdesc;
826814
int i;
827815

828816
/*
@@ -839,6 +827,8 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
839827
*/
840828
var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
841829

830+
slot_tupdesc = slot->tts_tupleDescriptor;
831+
842832
if (var_tupdesc->natts != slot_tupdesc->natts)
843833
ereport(ERROR,
844834
(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -896,6 +886,7 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
896886
{
897887
Var *variable = (Var *) wrvstate->xprstate.expr;
898888
TupleTableSlot *slot;
889+
TupleDesc slot_tupdesc;
899890
HeapTupleHeader dtuple;
900891

901892
if (isDone)
@@ -925,6 +916,20 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
925916
if (wrvstate->wrv_junkFilter != NULL)
926917
slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
927918

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

src/test/regress/expected/subselect.out

+18
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,24 @@ select * from int4_tbl o where (f1, f1) in
774774
0
775775
(1 row)
776776

777+
--
778+
-- check for over-optimization of whole-row Var referencing an Append plan
779+
--
780+
select (select q from
781+
(select 1,2,3 where f1 > 0
782+
union all
783+
select 4,5,6.0 where f1 <= 0
784+
) q )
785+
from int4_tbl;
786+
q
787+
-----------
788+
(4,5,6.0)
789+
(1,2,3)
790+
(4,5,6.0)
791+
(1,2,3)
792+
(4,5,6.0)
793+
(5 rows)
794+
777795
--
778796
-- Check that volatile quals aren't pushed down past a DISTINCT:
779797
-- nextval() should not be called more than the nominal number of times

src/test/regress/sql/subselect.sql

+10
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,16 @@ select * from int4_tbl o where (f1, f1) in
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);
434434

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;
444+
435445
--
436446
-- Check that volatile quals aren't pushed down past a DISTINCT:
437447
-- nextval() should not be called more than the nominal number of times

0 commit comments

Comments
 (0)