Commit 9e724306 authored by Tom Lane's avatar Tom Lane

When using a junkfilter, the output tuple should NOT be stored back into

the same tuple slot that the raw tuple came from, because that slot has
the wrong tuple descriptor.  Store it into its own slot with the correct
descriptor, instead.  This repairs problems with SPI functions seeing
inappropriate tuple descriptors --- for example, plpgsql code failing to
cope with SELECT FOR UPDATE.
parent a3855c57
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.27 2001/03/22 06:16:12 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.28 2001/05/27 20:48:51 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -56,10 +56,12 @@ ...@@ -56,10 +56,12 @@
* Initialize the Junk filter. * Initialize the Junk filter.
* *
* The initial targetlist and associated tuple descriptor are passed in. * The initial targetlist and associated tuple descriptor are passed in.
* An optional resultSlot can be passed as well.
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
JunkFilter * JunkFilter *
ExecInitJunkFilter(List *targetList, TupleDesc tupType) ExecInitJunkFilter(List *targetList, TupleDesc tupType,
TupleTableSlot *slot)
{ {
MemoryContext oldContext; MemoryContext oldContext;
MemoryContext junkContext; MemoryContext junkContext;
...@@ -245,6 +247,10 @@ ExecInitJunkFilter(List *targetList, TupleDesc tupType) ...@@ -245,6 +247,10 @@ ExecInitJunkFilter(List *targetList, TupleDesc tupType)
junkfilter->jf_cleanTupType = cleanTupType; junkfilter->jf_cleanTupType = cleanTupType;
junkfilter->jf_cleanMap = cleanMap; junkfilter->jf_cleanMap = cleanMap;
junkfilter->jf_junkContext = junkContext; junkfilter->jf_junkContext = junkContext;
junkfilter->jf_resultSlot = slot;
if (slot)
ExecSetSlotDescriptor(slot, cleanTupType, false);
MemoryContextSwitchTo(oldContext); MemoryContextSwitchTo(oldContext);
...@@ -260,7 +266,6 @@ ExecInitJunkFilter(List *targetList, TupleDesc tupType) ...@@ -260,7 +266,6 @@ ExecInitJunkFilter(List *targetList, TupleDesc tupType)
void void
ExecFreeJunkFilter(JunkFilter *junkfilter) ExecFreeJunkFilter(JunkFilter *junkfilter)
{ {
/* /*
* Since the junkfilter is inside its own context, we just have to * Since the junkfilter is inside its own context, we just have to
* delete the context and we're set. * delete the context and we're set.
...@@ -336,6 +341,10 @@ ExecGetJunkAttribute(JunkFilter *junkfilter, ...@@ -336,6 +341,10 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
* ExecRemoveJunk * ExecRemoveJunk
* *
* Construct and return a tuple with all the junk attributes removed. * Construct and return a tuple with all the junk attributes removed.
*
* Note: for historical reasons, this does not store the constructed
* tuple into the junkfilter's resultSlot. The caller should do that
* if it wants to.
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
HeapTuple HeapTuple
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.141 2001/05/27 09:59:29 petere Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.142 2001/05/27 20:48:51 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -598,12 +598,19 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ...@@ -598,12 +598,19 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
} }
/* /*
* initialize the executor "tuple" table. * initialize the executor "tuple" table. We need slots for all the
* plan nodes, plus possibly output slots for the junkfilter(s).
* At this point we aren't sure if we need junkfilters, so just add
* slots for them unconditionally.
*/ */
{ {
int nSlots = ExecCountSlotsNode(plan); int nSlots = ExecCountSlotsNode(plan);
estate->es_tupleTable = ExecCreateTupleTable(nSlots + 10); /* why add ten? - jolly */ if (parseTree->resultRelations != NIL)
nSlots += length(parseTree->resultRelations);
else
nSlots += 1;
estate->es_tupleTable = ExecCreateTupleTable(nSlots);
} }
/* mark EvalPlanQual not active */ /* mark EvalPlanQual not active */
...@@ -686,7 +693,8 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ...@@ -686,7 +693,8 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
JunkFilter *j; JunkFilter *j;
j = ExecInitJunkFilter(subplan->targetlist, j = ExecInitJunkFilter(subplan->targetlist,
ExecGetTupType(subplan)); ExecGetTupType(subplan),
ExecAllocTableSlot(estate->es_tupleTable));
resultRelInfo->ri_junkFilter = j; resultRelInfo->ri_junkFilter = j;
resultRelInfo++; resultRelInfo++;
subplans = lnext(subplans); subplans = lnext(subplans);
...@@ -702,9 +710,11 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) ...@@ -702,9 +710,11 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
else else
{ {
/* Normal case with just one JunkFilter */ /* Normal case with just one JunkFilter */
JunkFilter *j = ExecInitJunkFilter(plan->targetlist, JunkFilter *j;
tupType);
j = ExecInitJunkFilter(plan->targetlist,
tupType,
ExecAllocTableSlot(estate->es_tupleTable));
estate->es_junkFilter = j; estate->es_junkFilter = j;
if (estate->es_result_relation_info) if (estate->es_result_relation_info)
estate->es_result_relation_info->ri_junkFilter = j; estate->es_result_relation_info->ri_junkFilter = j;
...@@ -986,7 +996,9 @@ lnext: ; ...@@ -986,7 +996,9 @@ lnext: ;
* if we have a junk filter, then project a new tuple with the * if we have a junk filter, then project a new tuple with the
* junk removed. * junk removed.
* *
* Store this new "clean" tuple in the place of the original tuple. * Store this new "clean" tuple in the junkfilter's resultSlot.
* (Formerly, we stored it back over the "dirty" tuple, which is
* WRONG because that tuple slot has the wrong descriptor.)
* *
* Also, extract all the junk information we need. * Also, extract all the junk information we need.
*/ */
...@@ -1088,7 +1100,7 @@ lnext: ; ...@@ -1088,7 +1100,7 @@ lnext: ;
newTuple = ExecRemoveJunk(junkfilter, slot); newTuple = ExecRemoveJunk(junkfilter, slot);
slot = ExecStoreTuple(newTuple, /* tuple to store */ slot = ExecStoreTuple(newTuple, /* tuple to store */
slot, /* destination slot */ junkfilter->jf_resultSlot, /* dest slot */
InvalidBuffer, /* this tuple has no InvalidBuffer, /* this tuple has no
* buffer */ * buffer */
true); /* tuple should be pfreed */ true); /* tuple should be pfreed */
...@@ -1467,7 +1479,9 @@ lreplace:; ...@@ -1467,7 +1479,9 @@ lreplace:;
{ {
*tupleid = ctid; *tupleid = ctid;
tuple = ExecRemoveJunk(estate->es_junkFilter, epqslot); tuple = ExecRemoveJunk(estate->es_junkFilter, epqslot);
slot = ExecStoreTuple(tuple, slot, InvalidBuffer, true); slot = ExecStoreTuple(tuple,
estate->es_junkFilter->jf_resultSlot,
InvalidBuffer, true);
goto lreplace; goto lreplace;
} }
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: executor.h,v 1.57 2001/03/22 04:00:44 momjian Exp $ * $Id: executor.h,v 1.58 2001/05/27 20:48:51 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -43,7 +43,8 @@ extern void ExecRestrPos(Plan *node); ...@@ -43,7 +43,8 @@ extern void ExecRestrPos(Plan *node);
/* /*
* prototypes from functions in execJunk.c * prototypes from functions in execJunk.c
*/ */
extern JunkFilter *ExecInitJunkFilter(List *targetList, TupleDesc tupType); extern JunkFilter *ExecInitJunkFilter(List *targetList, TupleDesc tupType,
TupleTableSlot *slot);
extern void ExecFreeJunkFilter(JunkFilter *junkfilter); extern void ExecFreeJunkFilter(JunkFilter *junkfilter);
extern bool ExecGetJunkAttribute(JunkFilter *junkfilter, TupleTableSlot *slot, extern bool ExecGetJunkAttribute(JunkFilter *junkfilter, TupleTableSlot *slot,
char *attrName, Datum *value, bool *isNull); char *attrName, Datum *value, bool *isNull);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: execnodes.h,v 1.59 2001/05/15 00:33:36 tgl Exp $ * $Id: execnodes.h,v 1.60 2001/05/27 20:48:51 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -166,17 +166,19 @@ typedef struct ProjectionInfo ...@@ -166,17 +166,19 @@ typedef struct ProjectionInfo
* cleanLength: the length of 'cleanTargetList' * cleanLength: the length of 'cleanTargetList'
* cleanTupType: the tuple descriptor of the "clean" tuple (with * cleanTupType: the tuple descriptor of the "clean" tuple (with
* junk attributes removed). * junk attributes removed).
* cleanMap: A map with the correspondance between the non-junk * cleanMap: A map with the correspondence between the non-junk
* attributes of the "original" tuple and the * attribute numbers of the "original" tuple and the
* attributes of the "clean" tuple. * attribute numbers of the "clean" tuple.
* junkContext: memory context holding the JunkFilter node and all * junkContext: memory context holding the JunkFilter node and all
* its subsidiary data structures. * its subsidiary data structures.
* * resultSlot: tuple slot that can be used to hold cleaned tuple.
* NOTE: the original targetList and tupType are passed to ExecInitJunkFilter *
* and do not belong to the JunkFilter. All the other subsidiary structures * NOTE: the original targetList and tupType are passed to ExecInitJunkFilter,
* are created during ExecInitJunkFilter, and all of them can be freed by * as is the resultSlot. These items do not belong to the JunkFilter. All
* deleting the memory context junkContext. This would not be needed if we * the other subsidiary structures are created during ExecInitJunkFilter,
* had a cleaner approach to managing query-lifetime data structures... * and all of them can be freed by deleting the memory context junkContext.
* This would not be needed if we had a cleaner approach to managing
* query-lifetime data structures...
* ---------------- * ----------------
*/ */
typedef struct JunkFilter typedef struct JunkFilter
...@@ -190,6 +192,7 @@ typedef struct JunkFilter ...@@ -190,6 +192,7 @@ typedef struct JunkFilter
TupleDesc jf_cleanTupType; TupleDesc jf_cleanTupType;
AttrNumber *jf_cleanMap; AttrNumber *jf_cleanMap;
MemoryContext jf_junkContext; MemoryContext jf_junkContext;
TupleTableSlot *jf_resultSlot;
} JunkFilter; } JunkFilter;
/* ---------------- /* ----------------
......
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