*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.60 2000/07/03 23:09:11 wieck Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.61 2000/07/12 02:36:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
tupleTable = ExecCreateTupleTable(1);
slot = ExecAllocTableSlot(tupleTable);
- econtext = makeNode(ExprContext);
- FillDummyExprContext(econtext, slot, hd, InvalidBuffer);
+ ExecSetSlotDescriptor(slot, hd);
+ econtext = MakeExprContext(slot, TransactionCommandContext);
}
else
-/* shut the compiler up */
{
tupleTable = NULL;
slot = NULL;
{
nh++;
+#ifndef OMIT_PARTIAL_INDEX
/*
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
* this tuple if it was already in the existing partial index
*/
if (oldPred != NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
/* SetSlotContents(slot, htup); */
slot->val = htup;
if (ExecQual((List *) oldPred, econtext, false))
ni++;
continue;
}
-#endif /* OMIT_PARTIAL_INDEX */
}
/*
*/
if (pred != NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
/* SetSlotContents(slot, htup); */
slot->val = htup;
if (!ExecQual((List *) pred, econtext, false))
continue;
-#endif /* OMIT_PARTIAL_INDEX */
}
+#endif /* OMIT_PARTIAL_INDEX */
ni++;
/* okay, all heap tuples are indexed */
heap_endscan(scan);
+#ifndef OMIT_PARTIAL_INDEX
if (pred != NULL || oldPred != NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
ExecDropTupleTable(tupleTable, true);
- pfree(econtext);
-#endif /* OMIT_PARTIAL_INDEX */
+ FreeExprContext(econtext);
}
+#endif /* OMIT_PARTIAL_INDEX */
/*
* Since we just counted the tuples in the heap, we update its stats
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.40 2000/06/17 23:41:13 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.41 2000/07/12 02:36:46 tgl Exp $
*
* NOTES
* This file contains only the public interface routines.
{
tupleTable = ExecCreateTupleTable(1);
slot = ExecAllocTableSlot(tupleTable);
- econtext = makeNode(ExprContext);
- FillDummyExprContext(econtext, slot, htupdesc, InvalidBuffer);
+ ExecSetSlotDescriptor(slot, htupdesc);
+ econtext = MakeExprContext(slot, TransactionCommandContext);
}
else
-/* quiet the compiler */
{
+ tupleTable = NULL;
+ slot = NULL;
econtext = NULL;
- tupleTable = 0;
- slot = 0;
}
#endif /* OMIT_PARTIAL_INDEX */
while (HeapTupleIsValid(htup = heap_getnext(hscan, 0)))
{
-
nhtups++;
+#ifndef OMIT_PARTIAL_INDEX
/*
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
* this tuple if it was already in the existing partial index
if (oldPred != NULL)
{
/* SetSlotContents(slot, htup); */
-#ifndef OMIT_PARTIAL_INDEX
slot->val = htup;
if (ExecQual((List *) oldPred, econtext, false))
{
nitups++;
continue;
}
-#endif /* OMIT_PARTIAL_INDEX */
}
/*
*/
if (pred != NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
/* SetSlotContents(slot, htup); */
slot->val = htup;
if (!ExecQual((List *) pred, econtext, false))
continue;
-#endif /* OMIT_PARTIAL_INDEX */
}
+#endif /* OMIT_PARTIAL_INDEX */
nitups++;
/* okay, all heap tuples are indexed */
heap_endscan(hscan);
+#ifndef OMIT_PARTIAL_INDEX
if (pred != NULL || oldPred != NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
ExecDropTupleTable(tupleTable, true);
- pfree(econtext);
-#endif /* OMIT_PARTIAL_INDEX */
+ FreeExprContext(econtext);
}
+#endif /* OMIT_PARTIAL_INDEX */
/*
* Since we just counted the tuples in the heap, we update its stats
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.38 2000/06/19 03:54:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.39 2000/07/12 02:36:48 tgl Exp $
*
* NOTES
*
* that work on 32-bit or wider datatypes can't just return "a - b".
* That could overflow and give the wrong answer.
*
+ * NOTE: these routines must not leak memory, since memory allocated
+ * during an index access won't be recovered till end of query. This
+ * primarily affects comparison routines for toastable datatypes;
+ * they have to be careful to free any detoasted copy of an input datum.
*-------------------------------------------------------------------------
*/
} while (res == 0 && len != 0);
}
+ if (res == 0 && VARSIZE(a) != VARSIZE(b))
+ {
+ /*
+ * The two strings are the same in the first len bytes,
+ * and they are of different lengths.
+ */
+ if (VARSIZE(a) < VARSIZE(b))
+ res = -1;
+ else
+ res = 1;
+ }
+
#endif
- if (res != 0 || VARSIZE(a) == VARSIZE(b))
- PG_RETURN_INT32(res);
+ /* Avoid leaking memory when handed toasted input. */
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
- /*
- * The two strings are the same in the first len bytes, and they are
- * of different lengths.
- */
-
- if (VARSIZE(a) < VARSIZE(b))
- PG_RETURN_INT32(-1);
- else
- PG_RETURN_INT32(1);
+ PG_RETURN_INT32(res);
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.59 2000/06/17 23:41:16 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.60 2000/07/12 02:36:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
tupleTable = ExecCreateTupleTable(1);
slot = ExecAllocTableSlot(tupleTable);
- econtext = makeNode(ExprContext);
- FillDummyExprContext(econtext, slot, htupdesc, InvalidBuffer);
+ ExecSetSlotDescriptor(slot, htupdesc);
+ econtext = MakeExprContext(slot, TransactionCommandContext);
/*
* we never want to use sort/build if we are extending an existing
{
nhtups++;
+#ifndef OMIT_PARTIAL_INDEX
/*
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
* this tuple if it was already in the existing partial index
*/
if (oldPred != NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
-
/* SetSlotContents(slot, htup); */
slot->val = htup;
if (ExecQual((List *) oldPred, econtext, false))
nitups++;
continue;
}
-#endif /* OMIT_PARTIAL_INDEX */
}
/*
*/
if (pred != NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
/* SetSlotContents(slot, htup); */
slot->val = htup;
if (!ExecQual((List *) pred, econtext, false))
continue;
-#endif /* OMIT_PARTIAL_INDEX */
}
+#endif /* OMIT_PARTIAL_INDEX */
nitups++;
/* okay, all heap tuples are indexed */
heap_endscan(hscan);
+#ifndef OMIT_PARTIAL_INDEX
if (pred != NULL || oldPred != NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
ExecDropTupleTable(tupleTable, true);
- pfree(econtext);
-#endif /* OMIT_PARTIAL_INDEX */
+ FreeExprContext(econtext);
}
+#endif /* OMIT_PARTIAL_INDEX */
/*
* if we are doing bottom-up btree build, finish the build by (1)
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtree.c,v 1.50 2000/06/17 23:41:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtree.c,v 1.51 2000/07/12 02:36:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
tupleTable = ExecCreateTupleTable(1);
slot = ExecAllocTableSlot(tupleTable);
- econtext = makeNode(ExprContext);
- FillDummyExprContext(econtext, slot, hd, InvalidBuffer);
+ ExecSetSlotDescriptor(slot, hd);
+ econtext = MakeExprContext(slot, TransactionCommandContext);
}
else
{
- econtext = NULL;
tupleTable = NULL;
slot = NULL;
+ econtext = NULL;
}
#endif /* OMIT_PARTIAL_INDEX */
{
nh++;
+#ifndef OMIT_PARTIAL_INDEX
/*
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
* this tuple if it was already in the existing partial index
*/
if (oldPred != NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
/* SetSlotContents(slot, htup); */
slot->val = htup;
if (ExecQual((List *) oldPred, econtext, false))
ni++;
continue;
}
-#endif /* OMIT_PARTIAL_INDEX */
}
/*
*/
if (pred != NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
/* SetSlotContents(slot, htup); */
slot->val = htup;
if (!ExecQual((List *) pred, econtext, false))
continue;
-#endif /* OMIT_PARTIAL_INDEX */
}
+#endif /* OMIT_PARTIAL_INDEX */
ni++;
/* okay, all heap tuples are indexed */
heap_endscan(scan);
+#ifndef OMIT_PARTIAL_INDEX
if (pred != NULL || oldPred != NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
ExecDropTupleTable(tupleTable, true);
- pfree(econtext);
-#endif /* OMIT_PARTIAL_INDEX */
+ FreeExprContext(econtext);
}
+#endif /* OMIT_PARTIAL_INDEX */
/*
* Since we just counted the tuples in the heap, we update its stats
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.124 2000/07/05 23:11:06 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.125 2000/07/12 02:36:55 tgl Exp $
*
*
* INTERFACE ROUTINES
}
-/* -------------------------
- * FillDummyExprContext
- * Sets up dummy ExprContext and TupleTableSlot objects for use
- * with ExecQual.
- *
- * NOTE: buffer is passed for historical reasons; it should
- * almost certainly always be InvalidBuffer.
- * -------------------------
- */
-void
-FillDummyExprContext(ExprContext *econtext,
- TupleTableSlot *slot,
- TupleDesc tupdesc,
- Buffer buffer)
-{
- econtext->ecxt_scantuple = slot;
- econtext->ecxt_innertuple = NULL;
- econtext->ecxt_outertuple = NULL;
- econtext->ecxt_param_list_info = NULL;
- econtext->ecxt_range_table = NULL;
-
- slot->ttc_tupleDescriptor = tupdesc;
- slot->ttc_buffer = buffer;
- slot->ttc_shouldFree = false;
-
-}
-
-
/* ----------------
* DefaultBuild
*
{
tupleTable = ExecCreateTupleTable(1);
slot = ExecAllocTableSlot(tupleTable);
- econtext = makeNode(ExprContext);
- FillDummyExprContext(econtext, slot, heapDescriptor, InvalidBuffer);
+ ExecSetSlotDescriptor(slot, heapDescriptor);
+ econtext = MakeExprContext(slot, TransactionCommandContext);
}
else
{
- econtext = NULL;
- tupleTable = 0;
+ tupleTable = NULL;
slot = NULL;
+ econtext = NULL;
}
#endif /* OMIT_PARTIAL_INDEX */
reltuples++;
#ifndef OMIT_PARTIAL_INDEX
-
/*
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
* this tuple if it was already in the existing partial index
{
/* parameter was 'false', almost certainly wrong --- tgl 9/21/99 */
ExecDropTupleTable(tupleTable, true);
+ FreeExprContext(econtext);
}
#endif /* OMIT_PARTIAL_INDEX */
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.117 2000/07/05 23:11:11 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.118 2000/07/12 02:36:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
char *predString;
Node **indexPred = NULL;
TupleDesc rtupdesc;
- ExprContext *econtext = NULL;
EState *estate = makeNode(EState); /* for ExecConstraints() */
-
#ifndef OMIT_PARTIAL_INDEX
+ ExprContext *econtext = NULL;
TupleTable tupleTable;
TupleTableSlot *slot = NULL;
-
#endif
int natts;
AttrNumber *attnumP;
finfo = (FuncIndexInfo *) palloc(n_indices * sizeof(FuncIndexInfo));
finfoP = (FuncIndexInfo **) palloc(n_indices * sizeof(FuncIndexInfo *));
indexPred = (Node **) palloc(n_indices * sizeof(Node *));
- econtext = NULL;
for (i = 0; i < n_indices; i++)
{
itupdescArr[i] = RelationGetDescr(index_rels[i]);
PointerGetDatum(&pgIndexP[i]->indpred)));
indexPred[i] = stringToNode(predString);
pfree(predString);
+#ifndef OMIT_PARTIAL_INDEX
/* make dummy ExprContext for use by ExecQual */
if (econtext == NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
tupleTable = ExecCreateTupleTable(1);
slot = ExecAllocTableSlot(tupleTable);
- econtext = makeNode(ExprContext);
- econtext->ecxt_scantuple = slot;
rtupdesc = RelationGetDescr(rel);
- slot->ttc_tupleDescriptor = rtupdesc;
-
- /*
- * There's no buffer associated with heap tuples
- * here, so I set the slot's buffer to NULL.
- * Currently, it appears that the only way a
- * buffer could be needed would be if the partial
- * index predicate referred to the "lock" system
- * attribute. If it did, then heap_getattr would
- * call HeapTupleGetRuleLock, which uses the
- * buffer's descriptor to get the relation id.
- * Rather than try to fix this, I'll just disallow
- * partial indexes on "lock", which wouldn't be
- * useful anyway. --Nels, Nov '92
- */
- /* SetSlotBuffer(slot, (Buffer) NULL); */
- /* SetSlotShouldFree(slot, false); */
- slot->ttc_buffer = (Buffer) NULL;
- slot->ttc_shouldFree = false;
-#endif /* OMIT_PARTIAL_INDEX */
+ ExecSetSlotDescriptor(slot, rtupdesc);
+ econtext = MakeExprContext(slot,
+ TransactionCommandContext);
}
+#endif /* OMIT_PARTIAL_INDEX */
}
else
indexPred[i] = NULL;
{
for (i = 0; i < n_indices; i++)
{
+#ifndef OMIT_PARTIAL_INDEX
if (indexPred[i] != NULL)
{
-#ifndef OMIT_PARTIAL_INDEX
-
/*
* if tuple doesn't satisfy predicate, don't
* update index
/* SetSlotContents(slot, tuple); */
if (!ExecQual((List *) indexPred[i], econtext, false))
continue;
-#endif /* OMIT_PARTIAL_INDEX */
}
+#endif /* OMIT_PARTIAL_INDEX */
FormIndexDatum(indexNatts[i],
(AttrNumber *) &(pgIndexP[i]->indkey[0]),
tuple,
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: execAmi.c,v 1.48 2000/06/18 22:44:03 tgl Exp $
+ * $Id: execAmi.c,v 1.49 2000/07/12 02:37:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* ----------------------------------------------------------------
* ExecCloseR
*
- * closes the relation and scan descriptor for a scan or sort
- * node. Also closes index relations and scans for index scans.
+ * closes the relation and scan descriptor for a scan node.
+ * Also closes index relations and scans for index scans.
* ----------------------------------------------------------------
*/
void
state = ((IndexScan *) node)->scan.scanstate;
break;
- case T_Sort:
- state = &(((Sort *) node)->sortstate->csstate);
- break;
-
- case T_Agg:
- state = &(((Agg *) node)->aggstate->csstate);
- break;
-
case T_TidScan:
state = ((TidScan *) node)->scan.scanstate;
break;
default:
- elog(DEBUG, "ExecCloseR: not a scan or sort node!");
+ elog(DEBUG, "ExecCloseR: not a scan node!");
return;
}
if (IsA(node, IndexScan))
{
IndexScan *iscan = (IndexScan *) node;
- IndexScanState *indexstate;
+ IndexScanState *indexstate = iscan->indxstate;
int numIndices;
RelationPtr indexRelationDescs;
IndexScanDescPtr indexScanDescs;
int i;
- indexstate = iscan->indxstate;
numIndices = indexstate->iss_NumIndices;
indexRelationDescs = indexstate->iss_RelationDescs;
indexScanDescs = indexstate->iss_ScanDescs;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.121 2000/07/05 16:17:43 wieck Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.122 2000/07/12 02:37:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* This routine must be called at the beginning of any execution of any
* query plan
*
- * returns (AttrInfo*) which describes the attributes of the tuples to
+ * returns a TupleDesc which describes the attributes of the tuples to
* be returned by the query.
*
+ * NB: the CurrentMemoryContext when this is called must be the context
+ * to be used as the per-query context for the query plan. ExecutorRun()
+ * and ExecutorEnd() must be called in this same memory context.
* ----------------------------------------------------------------
*/
TupleDesc
{
estate->es_param_exec_vals = (ParamExecData *)
palloc(queryDesc->plantree->nParamExec * sizeof(ParamExecData));
- memset(estate->es_param_exec_vals, 0, queryDesc->plantree->nParamExec * sizeof(ParamExecData));
+ MemSet(estate->es_param_exec_vals, 0,
+ queryDesc->plantree->nParamExec * sizeof(ParamExecData));
}
/*
* EXEC_RETONE: return one tuple but don't 'retrieve' it
* used in postquel function processing
*
- *
* ----------------------------------------------------------------
*/
TupleTableSlot *
*/
estate->es_range_table = rangeTable;
- /*
- * initialize the BaseId counter so node base_id's are assigned
- * correctly. Someday baseid's will have to be stored someplace other
- * than estate because they should be unique per query planned.
- */
- estate->es_BaseId = 1;
-
/*
* initialize result relation stuff
*/
/*
* initialize the private state information for all the nodes in the
* query tree. This opens files, allocates storage and leaves us
- * ready to start processing tuples..
+ * ready to start processing tuples.
*/
ExecInitNode(plan, estate, NULL);
{
int ndef = rel->rd_att->constr->num_defval;
AttrDefault *attrdef = rel->rd_att->constr->defval;
- ExprContext *econtext = makeNode(ExprContext);
+ ExprContext *econtext = MakeExprContext(NULL, CurrentMemoryContext);
HeapTuple newtuple;
Node *expr;
bool isnull;
char *repl = NULL;
int i;
- econtext->ecxt_scantuple = NULL; /* scan tuple slot */
- econtext->ecxt_innertuple = NULL; /* inner tuple slot */
- econtext->ecxt_outertuple = NULL; /* outer tuple slot */
- econtext->ecxt_relation = NULL; /* relation */
- econtext->ecxt_relid = 0; /* relid */
- econtext->ecxt_param_list_info = NULL; /* param list info */
- econtext->ecxt_param_exec_vals = NULL; /* exec param values */
- econtext->ecxt_range_table = NULL; /* range table */
for (i = 0; i < ndef; i++)
{
if (!heap_attisnull(tuple, attrdef[i].adnum))
continue;
expr = (Node *) stringToNode(attrdef[i].adbin);
- val = ExecEvalExpr(expr, econtext, &isnull, &isdone);
-
- pfree(expr);
+ val = ExecEvalExprSwitchContext(expr, econtext, &isnull, &isdone);
if (isnull)
continue;
}
- pfree(econtext);
-
if (repl == NULL)
- return tuple;
+ {
+ /* no changes needed */
+ newtuple = tuple;
+ }
+ else
+ {
+ newtuple = heap_modifytuple(tuple, rel, replValue, replNull, repl);
- newtuple = heap_modifytuple(tuple, rel, replValue, replNull, repl);
+ pfree(repl);
+ pfree(replNull);
+ pfree(replValue);
+ heap_freetuple(tuple);
+ }
- pfree(repl);
- heap_freetuple(tuple);
- pfree(replNull);
- pfree(replValue);
+ FreeMemoryContext(econtext);
return newtuple;
-
}
#endif
{
int ncheck = rel->rd_att->constr->num_check;
ConstrCheck *check = rel->rd_att->constr->check;
- ExprContext *econtext = makeNode(ExprContext);
TupleTableSlot *slot = makeNode(TupleTableSlot);
RangeTblEntry *rte = makeNode(RangeTblEntry);
+ ExprContext *econtext = MakeExprContext(slot,
+ TransactionCommandContext);
List *rtlist;
List *qual;
int i;
rte->relid = RelationGetRelid(rel);
/* inh, inFromCl, inJoinSet, skipAcl won't be used, leave them zero */
rtlist = lcons(rte, NIL);
- econtext->ecxt_scantuple = slot; /* scan tuple slot */
- econtext->ecxt_innertuple = NULL; /* inner tuple slot */
- econtext->ecxt_outertuple = NULL; /* outer tuple slot */
- econtext->ecxt_relation = rel; /* relation */
- econtext->ecxt_relid = 0; /* relid */
- econtext->ecxt_param_list_info = NULL; /* param list info */
- econtext->ecxt_param_exec_vals = NULL; /* exec param values */
- econtext->ecxt_range_table = rtlist; /* range table */
+ econtext->ecxt_range_table = rtlist; /* phony range table */
+ /*
+ * Save the de-stringized constraint expressions in command-level
+ * memory context. XXX should build the above stuff there too,
+ * instead of doing it over for each tuple.
+ * XXX Is it sufficient to have just one es_result_relation_constraints
+ * in an inherited insert/update?
+ */
if (estate->es_result_relation_constraints == NULL)
{
+ MemoryContext oldContext;
+
+ oldContext = MemoryContextSwitchTo(TransactionCommandContext);
+
estate->es_result_relation_constraints =
(List **) palloc(ncheck * sizeof(List *));
qual = (List *) stringToNode(check[i].ccbin);
estate->es_result_relation_constraints[i] = qual;
}
+
+ MemoryContextSwitchTo(oldContext);
}
for (i = 0; i < ncheck; i++)
pfree(slot);
pfree(rte);
pfree(rtlist);
- pfree(econtext);
- return (char *) NULL;
+ FreeExprContext(econtext);
+ return (char *) NULL;
}
void
ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate)
{
-
Assert(rel->rd_att->constr);
if (rel->rd_att->constr->has_not_null)
for (attrChk = 1; attrChk <= rel->rd_att->natts; attrChk++)
{
- if (rel->rd_att->attrs[attrChk - 1]->attnotnull && heap_attisnull(tuple, attrChk))
+ if (rel->rd_att->attrs[attrChk-1]->attnotnull &&
+ heap_attisnull(tuple, attrChk))
elog(ERROR, "%s: Fail to add null value in not null attribute %s",
- caller, NameStr(rel->rd_att->attrs[attrChk - 1]->attname));
+ caller, NameStr(rel->rd_att->attrs[attrChk-1]->attname));
}
}
char *failed;
if ((failed = ExecRelCheck(rel, tuple, estate)) != NULL)
- elog(ERROR, "%s: rejected due to CHECK constraint %s", caller, failed);
+ elog(ERROR, "%s: rejected due to CHECK constraint %s",
+ caller, failed);
}
-
- return;
}
TupleTableSlot *
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.72 2000/06/15 04:09:50 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.73 2000/07/12 02:37:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
* ExecEvalExpr - evaluate an expression and return a datum
+ * ExecEvalExprSwitchContext - same, but switch into eval memory context
* ExecQual - return true/false if qualification is satisfied
- * ExecTargetList - form a new tuple by projecting the given tuple
+ * ExecProject - form a new tuple by projecting the given tuple
*
* NOTES
* ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster
* implemented recursively. Eliminating the recursion is bound to
* improve the speed of the executor.
*
- * ExecTargetList() is used to make tuple projections. Rather then
+ * ExecProject() is used to make tuple projections. Rather then
* trying to speed it up, the execution plan should be pre-processed
* to facilitate attribute sharing between nodes wherever possible,
* instead of doing needless copying. -cim 5/31/91
#include "utils/fcache2.h"
-/*
- * externs and constants
- */
-
-/*
- * XXX Used so we can get rid of use of Const nodes in the executor.
- * Currently only used by ExecHashGetBucket and set only by ExecMakeVarConst
- * and by ExecEvalArrayRef.
- */
-bool execConstByVal;
-int execConstLen;
-
-/* static functions decls */
+/* static function decls */
static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull);
static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
bool *isNull, bool *isDone);
-static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
+static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
+ bool *isNull);
static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
bool *isNull, bool *isDone);
static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
List *argList, FunctionCallInfo fcinfo,
bool *argIsDone);
static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
- bool *isNull);
+static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
static Datum ExecMakeFunctionResult(Node *node, List *arguments,
if (arrayRef->refexpr != NULL)
{
- array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr,
- econtext,
- isNull,
- isDone);
+ array_scanner = (ArrayType *)
+ DatumGetPointer(ExecEvalExpr(arrayRef->refexpr,
+ econtext,
+ isNull,
+ isDone));
/* If refexpr yields NULL, result is always NULL, for now anyway */
if (*isNull)
return (Datum) NULL;
elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
MAXDIM);
- upper.indx[i++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
- econtext,
- isNull,
- &dummy);
+ upper.indx[i++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
+ econtext,
+ isNull,
+ &dummy));
/* If any index expr yields NULL, result is NULL */
if (*isNull)
return (Datum) NULL;
elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
MAXDIM);
- lower.indx[j++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
- econtext,
- isNull,
- &dummy);
+ lower.indx[j++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
+ econtext,
+ isNull,
+ &dummy));
/* If any index expr yields NULL, result is NULL */
if (*isNull)
return (Datum) NULL;
if (*isNull)
return (Datum) NULL;
- execConstByVal = arrayRef->refelembyval;
- execConstLen = arrayRef->refelemlength;
-
if (array_scanner == NULL)
return sourceData; /* XXX do something else? */
isNull));
}
- execConstByVal = arrayRef->refelembyval;
- execConstLen = arrayRef->refelemlength;
-
if (lIndex == NULL)
return array_ref(array_scanner, i,
upper.indx,
ExecSetSlotDescriptor(tempSlot, td);
ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
- return (Datum) tempSlot;
+ return PointerGetDatum(tempSlot);
}
result = heap_getattr(heapTuple, /* tuple containing attribute */
* return null if att is null
*/
if (*isNull)
- return (Datum) NULL;
+ return (Datum) 0;
/*
* get length and type information.. ??? what should we do about
byval = tuple_type->attrs[attnum - 1]->attbyval ? true : false;
}
- execConstByVal = byval;
- execConstLen = len;
-
return result;
}
Datum
ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
{
-
char *thisParameterName;
int thisParameterKind = expression->paramkind;
AttrNumber thisParameterId = expression->paramid;
if (thisParameterKind == PARAM_EXEC)
{
- ParamExecData *prm = &(econtext->ecxt_param_exec_vals[thisParameterId]);
+ ParamExecData *prm;
+ prm = &(econtext->ecxt_param_exec_vals[thisParameterId]);
if (prm->execPlan != NULL)
- ExecSetParamPlan(prm->execPlan);
- Assert(prm->execPlan == NULL);
+ {
+ ExecSetParamPlan(prm->execPlan, econtext);
+ /* ExecSetParamPlan should have processed this param... */
+ Assert(prm->execPlan == NULL);
+ }
*isNull = prm->isnull;
return prm->value;
}
if (paramList->isnull)
{
*isNull = true;
- return (Datum) NULL;
+ return (Datum) 0;
}
if (expression->param_tlist != NIL)
* named attribute out of the tuple from the arg slot. User defined
* C functions which take a tuple as an argument are expected
* to use this. Ex: overpaid(EMP) might call GetAttributeByNum().
+ *
+ * XXX these two functions are misdeclared: they should be declared to
+ * return Datum. They are not used anywhere in the backend proper, and
+ * exist only for use by user-defined functions. Should we change their
+ * definitions, at risk of breaking user code?
*/
-/* static but gets called from external functions */
char *
GetAttributeByNum(TupleTableSlot *slot,
AttrNumber attrno,
return (char *) retval;
}
-/* XXX name for catalogs */
-#ifdef NOT_USED
-char *
-att_by_num(TupleTableSlot *slot,
- AttrNumber attrno,
- bool *isNull)
-{
- return GetAttributeByNum(slot, attrno, isNull);
-}
-
-#endif
-
char *
GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
{
return (char *) retval;
}
-/* XXX name for catalogs */
-#ifdef NOT_USED
-char *
-att_by_name(TupleTableSlot *slot, char *attname, bool *isNull)
-{
- return GetAttributeByName(slot, attname, isNull);
-}
-
-#endif
static void
ExecEvalFuncArgs(FunctionCachePtr fcache,
if (fcache->hasSetArg && argDone)
{
/* can only get here if input is an empty set. */
- if (isDone)
- *isDone = true;
*isNull = true;
+ *isDone = true;
return (Datum) 0;
}
}
else
{
result = (Datum) 0;
- *isDone = true;
*isNull = true;
+ *isDone = true;
}
if (!*isDone)
else
{
/* A non-SQL function cannot return a set, at present. */
- if (isDone)
- *isDone = true;
+ *isDone = true;
+
/*
* If function is strict, and there are any NULL arguments,
* skip calling the function and return NULL.
* ExecEvalFunc
*
* Evaluate the functional result of a list of arguments by calling the
- * function manager. Note that in the case of operator expressions, the
- * optimizer had better have already replaced the operator OID with the
- * appropriate function OID or we're hosed.
- *
- * old comments
- * Presumably the function manager will not take null arguments, so we
- * check for null arguments before sending the arguments to (fmgr).
- *
- * Returns the value of the functional expression.
+ * function manager.
* ----------------------------------------------------------------
*/
bool isDone;
/*
- * an opclause is a list (op args). (I think)
- *
* we extract the oid of the function associated with the op and then
* pass the work onto ExecMakeFunctionResult which evaluates the
* arguments and returns the result of calling the function on the
* call ExecMakeFunctionResult() with a dummy isDone that we ignore.
* We don't have operator whose arguments are sets.
*/
- return ExecMakeFunctionResult((Node *) op, argList, econtext, isNull, &isDone);
+ return ExecMakeFunctionResult((Node *) op, argList, econtext,
+ isNull, &isDone);
}
/* ----------------------------------------------------------------
FunctionCachePtr fcache;
/*
- * an funcclause is a list (func args). (I think)
- *
* we extract the oid of the function associated with the func node and
* then pass the work onto ExecMakeFunctionResult which evaluates the
* arguments and returns the result of calling the function on the
fcache = func->func_fcache;
}
- return ExecMakeFunctionResult((Node *) func, argList, econtext, isNull, isDone);
+ return ExecMakeFunctionResult((Node *) func, argList, econtext,
+ isNull, isDone);
}
/* ----------------------------------------------------------------
* evaluation of 'not' is simple.. expr is false, then return 'true'
* and vice versa.
*/
- if (DatumGetInt32(expr_value) == 0)
- return (Datum) true;
-
- return (Datum) false;
+ return BoolGetDatum(! DatumGetBool(expr_value));
}
/* ----------------------------------------------------------------
*/
if (*isNull)
AnyNull = true; /* remember we got a null */
- else if (DatumGetInt32(clause_value) != 0)
+ else if (DatumGetBool(clause_value))
return clause_value;
}
/* AnyNull is true if at least one clause evaluated to NULL */
*isNull = AnyNull;
- return (Datum) false;
+ return BoolGetDatum(false);
}
/* ----------------------------------------------------------------
*/
if (*isNull)
AnyNull = true; /* remember we got a null */
- else if (DatumGetInt32(clause_value) == 0)
+ else if (! DatumGetBool(clause_value))
return clause_value;
}
/* AnyNull is true if at least one clause evaluated to NULL */
*isNull = AnyNull;
- return (Datum) (!AnyNull);
+ return BoolGetDatum(!AnyNull);
}
/* ----------------------------------------------------------------
* case statement is satisfied. A NULL result from the test is
* not considered true.
*/
- if (DatumGetInt32(clause_value) != 0 && !*isNull)
+ if (DatumGetBool(clause_value) && !*isNull)
{
return ExecEvalExpr(wclause->result,
econtext,
*
* Recursively evaluate a targetlist or qualification expression.
*
- * This routine is an inner loop routine and should be as fast
- * as possible.
+ * The caller should already have switched into the temporary
+ * memory context econtext->ecxt_per_tuple_memory. The convenience
+ * entry point ExecEvalExprSwitchContext() is provided for callers
+ * who don't prefer to do the switch in an outer loop. We do not
+ * do the switch here because it'd be a waste of cycles during
+ * recursive entries to ExecEvalExpr().
*
- * Node comparison functions were replaced by macros for speed and to plug
- * memory leaks incurred by using the planner's Lispy stuff for
- * comparisons. Order of evaluation of node comparisons IS IMPORTANT;
- * the macros do no checks. Order of evaluation:
- *
- * o an isnull check, largely to avoid coredumps since greg doubts this
- * routine is called with a null ptr anyway in proper operation, but is
- * not completely sure...
- * o ExactNodeType checks.
- * o clause checks or other checks where we look at the lfirst of something.
+ * This routine is an inner loop routine and must be as fast
+ * as possible.
* ----------------------------------------------------------------
*/
Datum
{
Datum retDatum;
+ /* Set default values for result flags: non-null, not a set result */
*isNull = false;
+ *isDone = true;
- /*
- * Some callers don't care about is done and only want 1 result. They
- * indicate this by passing NULL
- */
- if (isDone)
- *isDone = true;
-
- /*
- * here we dispatch the work to the appropriate type of function given
- * the type of our expression.
- */
+ /* Is this still necessary? Doubtful... */
if (expression == NULL)
{
*isNull = true;
- return (Datum) true;
+ return (Datum) 0;
}
+ /*
+ * here we dispatch the work to the appropriate type of function given
+ * the type of our expression.
+ */
switch (nodeTag(expression))
{
case T_Var:
} /* ExecEvalExpr() */
+/*
+ * Same as above, but get into the right allocation context explicitly.
+ */
+Datum
+ExecEvalExprSwitchContext(Node *expression,
+ ExprContext *econtext,
+ bool *isNull,
+ bool *isDone)
+{
+ Datum retDatum;
+ MemoryContext oldContext;
+
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+ retDatum = ExecEvalExpr(expression, econtext, isNull, isDone);
+ MemoryContextSwitchTo(oldContext);
+ return retDatum;
+}
+
+
/* ----------------------------------------------------------------
- * ExecQual / ExecTargetList
+ * ExecQual / ExecTargetList / ExecProject
* ----------------------------------------------------------------
*/
bool
ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
{
+ bool result;
+ MemoryContext oldContext;
List *qlist;
/*
IncrProcessed();
+ /*
+ * Run in short-lived per-tuple context while computing expressions.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
/*
* Evaluate the qual conditions one at a time. If we find a FALSE
* result, we can stop evaluating and return FALSE --- the AND result
* is NULL (one or more NULL subresult, with all the rest TRUE) and
* the caller has specified resultForNull = TRUE.
*/
+ result = true;
foreach(qlist, qual)
{
bool isNull;
bool isDone;
- /*
- * If there is a null clause, consider the qualification to fail.
- * XXX is this still correct for constraints? It probably
- * shouldn't happen at all ...
- */
- if (clause == NULL)
- return false;
-
/*
* pass isDone, but ignore it. We don't iterate over multiple
* returns in the qualifications.
if (isNull)
{
if (resultForNull == false)
- return false; /* treat NULL as FALSE */
+ {
+ result = false; /* treat NULL as FALSE */
+ break;
+ }
}
else
{
- if (DatumGetInt32(expr_value) == 0)
- return false; /* definitely FALSE */
+ if (! DatumGetBool(expr_value))
+ {
+ result = false; /* definitely FALSE */
+ break;
+ }
}
}
- return true;
+ MemoryContextSwitchTo(oldContext);
+
+ return result;
}
int
ExprContext *econtext,
bool *isDone)
{
+ MemoryContext oldContext;
char nulls_array[64];
bool fjNullArray[64];
bool itemIsDoneArray[64];
EV_nodeDisplay(targetlist);
EV_printf("\n");
+ /*
+ * Run in short-lived per-tuple context while computing expressions.
+ */
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
/*
* There used to be some klugy and demonstrably broken code here that
* special-cased the situation where targetlist == NIL. Now we just
resdom = tle->resdom;
resind = resdom->resno - 1;
- constvalue = (Datum) ExecEvalExpr(expr,
- econtext,
- &isNull,
- &itemIsDone[resind]);
+ constvalue = ExecEvalExpr(expr,
+ econtext,
+ &isNull,
+ &itemIsDone[resind]);
values[resind] = constvalue;
/* this is probably wrong: */
if (*isDone)
- return (HeapTuple) NULL;
+ {
+ newTuple = NULL;
+ goto exit;
+ }
/*
* get the result from the inner node
if (IsA(expr, Iter) &&itemIsDone[resind])
{
- constvalue = (Datum) ExecEvalExpr(expr,
- econtext,
- &isNull,
- &itemIsDone[resind]);
+ constvalue = ExecEvalExpr(expr,
+ econtext,
+ &isNull,
+ &itemIsDone[resind]);
if (itemIsDone[resind])
{
}
/*
- * form the new result tuple (in the "normal" context)
+ * form the new result tuple (in the caller's memory context!)
*/
+ MemoryContextSwitchTo(oldContext);
+
newTuple = (HeapTuple) heap_formtuple(targettype, values, null_head);
exit:
pfree(itemIsDone);
}
+ /* make sure we are in the right context if we did "goto exit" */
+ MemoryContextSwitchTo(oldContext);
+
return newTuple;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.11 2000/01/26 05:56:21 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.12 2000/07/12 02:37:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* returns the next qualifying tuple in the direction specified
* in the global variable ExecDirection.
* The access method returns the next tuple and execScan() is
- * responisble for checking the tuple returned against the qual-clause.
+ * responsible for checking the tuple returned against the qual-clause.
*
* Conditions:
* -- the "cursor" maintained by the AMI is positioned at the tuple
* Initial States:
* -- the relation indicated is opened for scanning so that the
* "cursor" is positioned before the first qualifying tuple.
- *
- * May need to put startmmgr and endmmgr in here.
* ----------------------------------------------------------------
*/
TupleTableSlot *
ExecScan(Scan *node,
- TupleTableSlot *(*accessMtd) ()) /* function returning a
- * tuple */
+ ExecScanAccessMtd accessMtd) /* function returning a tuple */
{
CommonScanState *scanstate;
EState *estate;
List *qual;
bool isDone;
-
- TupleTableSlot *slot;
TupleTableSlot *resultSlot;
- HeapTuple newTuple;
-
ExprContext *econtext;
ProjectionInfo *projInfo;
-
/* ----------------
- * initialize misc variables
+ * Fetch data from node
* ----------------
*/
- newTuple = NULL;
- slot = NULL;
-
estate = node->plan.state;
scanstate = node->scanstate;
+ econtext = scanstate->cstate.cs_ExprContext;
+ qual = node->plan.qual;
/* ----------------
- * get the expression context
+ * Reset per-tuple memory context to free any expression evaluation
+ * storage allocated in the previous tuple cycle.
* ----------------
*/
- econtext = scanstate->cstate.cs_ExprContext;
+ ResetExprContext(econtext);
/* ----------------
- * initialize fields in ExprContext which don't change
- * in the course of the scan..
+ * Check to see if we're still projecting out tuples from a previous
+ * scan tuple (because there is a function-returning-set in the
+ * projection expressions). If so, try to project another one.
* ----------------
*/
- qual = node->plan.qual;
- econtext->ecxt_relation = scanstate->css_currentRelation;
- econtext->ecxt_relid = node->scanrelid;
-
if (scanstate->cstate.cs_TupFromTlist)
{
projInfo = scanstate->cstate.cs_ProjInfo;
resultSlot = ExecProject(projInfo, &isDone);
if (!isDone)
return resultSlot;
+ /* Done with that source tuple... */
+ scanstate->cstate.cs_TupFromTlist = false;
}
/*
*/
for (;;)
{
- slot = (TupleTableSlot *) (*accessMtd) (node);
+ TupleTableSlot *slot;
+
+ slot = (*accessMtd) (node);
/* ----------------
* if the slot returned by the accessMtd contains
* NULL, then it means there is nothing more to scan
- * so we just return the empty slot...
- *
- * ... with invalid TupleDesc (not the same as in
- * projInfo->pi_slot) and break upper MergeJoin node.
- * New code below do what ExecProject() does. - vadim 02/26/98
+ * so we just return an empty slot, being careful to use
+ * the projection result slot so it has correct tupleDesc.
* ----------------
*/
if (TupIsNull(slot))
{
- scanstate->cstate.cs_TupFromTlist = false;
- resultSlot = scanstate->cstate.cs_ProjInfo->pi_slot;
- return (TupleTableSlot *)
- ExecStoreTuple(NULL,
- resultSlot,
- InvalidBuffer,
- true);
+ return ExecStoreTuple(NULL,
+ scanstate->cstate.cs_ProjInfo->pi_slot,
+ InvalidBuffer,
+ true);
}
/* ----------------
/* ----------------
* check that the current tuple satisfies the qual-clause
- * if our qualification succeeds then we
+ * if our qualification succeeds then we may
* leave the loop.
- * ----------------
- */
-
- /*
- * add a check for non-nil qual here to avoid a function call to
+ *
+ * check for non-nil qual here to avoid a function call to
* ExecQual() when the qual is nil ... saves only a few cycles,
* but they add up ...
+ * ----------------
*/
if (!qual || ExecQual(qual, econtext, false))
break;
+
+ /* ----------------
+ * Tuple fails qual, so free per-tuple memory and try again.
+ * ----------------
+ */
+ ResetExprContext(econtext);
}
/* ----------------
- * form a projection tuple, store it in the result tuple
+ * Found a satisfactory scan tuple.
+ *
+ * Form a projection tuple, store it in the result tuple
* slot and return it.
* ----------------
*/
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.37 2000/04/12 17:15:08 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.38 2000/07/12 02:37:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* by the access methods into the scan tuple slot.
*
* - ExecSeqScan() calls ExecStoreTuple() to take the result
- * tuple from ExecTargetList() and place it into the result tuple
- * slot.
+ * tuple from ExecProject() and place it into the result tuple slot.
*
* - ExecutePlan() calls ExecRetrieve() which gets the tuple out of
* the slot passed to it by calling ExecFetchTuple(). this tuple
/* --------------------------------
* ExecDropTupleTable
*
- * This pfrees the storage assigned to the tuple table and
- * optionally pfrees the contents of the table also.
+ * This frees the storage used by the tuple table itself
+ * and optionally frees the contents of the table also.
* It is expected that this routine be called by EndPlan().
* --------------------------------
*/
*/
pfree(array);
pfree(table);
-
}
*
* This routine is used to reserve slots in the table for
* use by the various plan nodes. It is expected to be
- * called by the node init routines (ex: ExecInitNestLoop).
+ * called by the node init routines (ex: ExecInitNestLoop)
* once per slot needed by the node. Not all nodes need
* slots (some just pass tuples around).
* --------------------------------
*/
-TupleTableSlot * /* return: the slot allocated in the tuple
- * table */
+TupleTableSlot *
ExecAllocTableSlot(TupleTable table)
{
int slotnum; /* new slot number */
* pointers into _freed_ memory. This leads to bad ends. We
* now count the number of slots we will need and create all the
* slots we will need ahead of time. The if below should never
- * happen now. Give a WARN if it does. -mer 4 Aug 1992
+ * happen now. Fail if it does. -mer 4 Aug 1992
* ----------------
*/
if (table->next >= table->size)
- {
-
- /*
- * int newsize = NewTableSize(table->size);
- *
- * pfree(table->array); table->array = (Pointer) palloc(newsize *
- * TableSlotSize); bzero(table->array, newsize * TableSlotSize);
- * table->size = newsize;
- */
- elog(NOTICE, "Plan requires more slots than are available");
- elog(ERROR, "send mail to your local executor guru to fix this");
- }
+ elog(ERROR, "Plan requires more slots than are available"
+ "\n\tsend mail to your local executor guru to fix this");
/* ----------------
* at this point, space in the table is guaranteed so we
slot->val = (HeapTuple) NULL;
- slot->ttc_shouldFree = true;/* probably useless code... */
+ slot->ttc_shouldFree = true; /* probably useless code... */
/* ----------------
* Drop the pin on the referenced buffer, if there is one.
/*-------------------------------------------------------------------------
*
* execUtils.c
- * miscellanious executor utility routines
+ * miscellaneous executor utility routines
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.62 2000/07/05 23:11:14 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.63 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
- * ExecAssignNodeBaseInfo \
- * ExecAssignDebugHooks > preforms misc work done in all the
- * ExecAssignExprContext / init node routines.
+ * ExecAssignExprContext Common code for plan node init routines.
*
* ExecGetTypeInfo | old execCStructs interface
* ExecMakeTypeInfo | code from the version 1
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+#include "utils/memutils.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
#endif
/* ----------------------------------------------------------------
- * miscellanious init node support functions
+ * miscellaneous node-init support functions
*
- * ExecAssignNodeBaseInfo - assigns the baseid field of the node
- * ExecAssignDebugHooks - assigns the node's debugging hooks
* ExecAssignExprContext - assigns the node's expression context
* ----------------------------------------------------------------
*/
/* ----------------
- * ExecAssignNodeBaseInfo
+ * ExecAssignExprContext
+ *
+ * This initializes the ExprContext field. It is only necessary
+ * to do this for nodes which use ExecQual or ExecProject
+ * because those routines depend on econtext. Other nodes that
+ * don't have to evaluate expressions don't need to do this.
*
- * as it says, this assigns the baseid field of the node and
- * increments the counter in the estate. In addition, it initializes
- * the base_parent field of the basenode.
+ * Note: we assume CurrentMemoryContext is the correct per-query context.
+ * This should be true during plan node initialization.
* ----------------
*/
void
-ExecAssignNodeBaseInfo(EState *estate, CommonState *cstate, Plan *parent)
+ExecAssignExprContext(EState *estate, CommonState *commonstate)
{
- int baseId;
+ ExprContext *econtext = makeNode(ExprContext);
- baseId = estate->es_BaseId;
- cstate->cs_base_id = baseId;
- estate->es_BaseId = baseId + 1;
+ econtext->ecxt_scantuple = NULL;
+ econtext->ecxt_innertuple = NULL;
+ econtext->ecxt_outertuple = NULL;
+ econtext->ecxt_per_query_memory = CurrentMemoryContext;
+ /*
+ * Create working memory for expression evaluation in this context.
+ */
+ econtext->ecxt_per_tuple_memory =
+ AllocSetContextCreate(CurrentMemoryContext,
+ "PlanExprContext",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
+ econtext->ecxt_param_list_info = estate->es_param_list_info;
+ econtext->ecxt_aggvalues = NULL;
+ econtext->ecxt_aggnulls = NULL;
+ econtext->ecxt_range_table = estate->es_range_table;
+
+ commonstate->cs_ExprContext = econtext;
}
/* ----------------
- * ExecAssignExprContext
+ * MakeExprContext
*
- * This initializes the ExprContext field. It is only necessary
- * to do this for nodes which use ExecQual or ExecTargetList
- * because those routines depend on econtext. Other nodes which
- * dont have to evaluate expressions don't need to do this.
+ * Build an expression context for use outside normal plan-node cases.
+ * A fake scan-tuple slot can be supplied (pass NULL if not needed).
+ * A memory context sufficiently long-lived to use as fcache context
+ * must be supplied as well.
* ----------------
*/
-void
-ExecAssignExprContext(EState *estate, CommonState *commonstate)
+ExprContext *
+MakeExprContext(TupleTableSlot *slot,
+ MemoryContext queryContext)
{
- ExprContext *econtext;
+ ExprContext *econtext = makeNode(ExprContext);
- econtext = makeNode(ExprContext);
- econtext->ecxt_scantuple = NULL; /* scan tuple slot */
- econtext->ecxt_innertuple = NULL; /* inner tuple slot */
- econtext->ecxt_outertuple = NULL; /* outer tuple slot */
- econtext->ecxt_relation = NULL; /* relation */
- econtext->ecxt_relid = 0; /* relid */
- econtext->ecxt_param_list_info = estate->es_param_list_info;
- econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
- econtext->ecxt_range_table = estate->es_range_table; /* range table */
+ econtext->ecxt_scantuple = slot;
+ econtext->ecxt_innertuple = NULL;
+ econtext->ecxt_outertuple = NULL;
+ econtext->ecxt_per_query_memory = queryContext;
+ /*
+ * We make the temporary context a child of current working context,
+ * not of the specified queryContext. This seems reasonable but I'm
+ * not totally sure about it...
+ *
+ * Expression contexts made via this routine typically don't live long
+ * enough to get reset, so specify a minsize of 0. That avoids alloc'ing
+ * any memory in the common case where expr eval doesn't use any.
+ */
+ econtext->ecxt_per_tuple_memory =
+ AllocSetContextCreate(CurrentMemoryContext,
+ "TempExprContext",
+ 0,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ econtext->ecxt_param_exec_vals = NULL;
+ econtext->ecxt_param_list_info = NULL;
+ econtext->ecxt_aggvalues = NULL;
+ econtext->ecxt_aggnulls = NULL;
+ econtext->ecxt_range_table = NIL;
+
+ return econtext;
+}
- commonstate->cs_ExprContext = econtext;
+/*
+ * Free an ExprContext made by MakeExprContext, including the temporary
+ * context used for expression evaluation. Note this will cause any
+ * pass-by-reference expression result to go away!
+ */
+void
+FreeExprContext(ExprContext *econtext)
+{
+ MemoryContextDelete(econtext->ecxt_per_tuple_memory);
+ pfree(econtext);
}
/* ----------------------------------------------------------------
* clean up memory used.
* ----------------
*/
+ MemoryContextDelete(econtext->ecxt_per_tuple_memory);
pfree(econtext);
commonstate->cs_ExprContext = NULL;
}
* ExecFreeTypeInfo
* ----------------
*/
+#ifdef NOT_USED
void
ExecFreeTypeInfo(CommonState *commonstate)
{
FreeTupleDesc(tupDesc);
commonstate->cs_ResultTupleSlot->ttc_tupleDescriptor = NULL;
}
+#endif
/* ----------------------------------------------------------------
* the following scan type support functions are for
if (predicate != NULL)
{
if (econtext == NULL)
- econtext = makeNode(ExprContext);
- econtext->ecxt_scantuple = slot;
+ econtext = MakeExprContext(slot,
+ TransactionCommandContext);
/* Skip this index-update if the predicate isn't satisfied */
if (!ExecQual((List *) predicate, econtext, false))
pfree(result);
}
if (econtext != NULL)
- pfree(econtext);
+ FreeExprContext(econtext);
}
void
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.35 2000/06/28 03:31:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.36 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
HeapTuple tup,
bool *isnullP)
{
- Datum val,
- valueP;
+ Datum val;
Var *attrVar = (Var *) tlist->expr;
AttrNumber attrno = attrVar->varattno;
val = heap_getattr(tup, attrno, TD, isnullP);
+
if (*isnullP)
- return (Datum) NULL;
+ return (Datum) 0;
- valueP = datumCopy(val,
- TD->attrs[attrno - 1]->atttypid,
- TD->attrs[attrno - 1]->attbyval,
- (Size) TD->attrs[attrno - 1]->attlen);
- return valueP;
+ return datumCopy(val,
+ TD->attrs[attrno - 1]->attbyval,
+ TD->attrs[attrno - 1]->attlen);
}
static execution_state *
List *func_tlist,
bool *isDone)
{
+ MemoryContext oldcontext;
execution_state *es;
Datum result = 0;
CommandId savedId;
+ /*
+ * Switch to context in which the fcache lives. This ensures that
+ * parsetrees, plans, etc, will have sufficient lifetime. The
+ * sub-executor is responsible for deleting per-tuple information.
+ */
+ oldcontext = MemoryContextSwitchTo(fcache->fcacheCxt);
+
/*
* Before we start do anything we must save CurrentScanCommandId to
* restore it before return to upper Executor. Also, we have to set
* Let caller know we're finished.
*/
*isDone = true;
+ MemoryContextSwitchTo(oldcontext);
return (fcache->oneResult) ? result : (Datum) NULL;
}
Assert(LAST_POSTQUEL_COMMAND(es));
*isDone = false;
+
+ MemoryContextSwitchTo(oldcontext);
+
return result;
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.68 2000/06/28 03:31:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.69 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "parser/parse_type.h"
#include "utils/syscache.h"
#include "utils/tuplesort.h"
+#include "utils/datum.h"
/*
* AggStatePerAggData - per-aggregate working state for the Agg scan
initValue2IsNull;
/*
- * We need the len and byval info for the agg's input and transition
- * data types in order to know how to copy/delete values.
+ * We need the len and byval info for the agg's input, result, and
+ * transition data types in order to know how to copy/delete values.
*/
int inputtypeLen,
+ resulttypeLen,
transtype1Len,
transtype2Len;
bool inputtypeByVal,
+ resulttypeByVal,
transtype1ByVal,
transtype2ByVal;
static void initialize_aggregate(AggStatePerAgg peraggstate);
static void advance_transition_functions(AggStatePerAgg peraggstate,
Datum newVal, bool isNull);
+static void process_sorted_aggregate(AggState *aggstate,
+ AggStatePerAgg peraggstate);
static void finalize_aggregate(AggStatePerAgg peraggstate,
Datum *resultVal, bool *resultIsNull);
-static Datum copyDatum(Datum val, int typLen, bool typByVal);
/*
* Initialize one aggregate for a new set of input values.
+ *
+ * When called, CurrentMemoryContext should be the per-query context.
*/
static void
initialize_aggregate(AggStatePerAgg peraggstate)
/*
* (Re)set value1 and value2 to their initial values.
+ *
+ * Note that when the initial values are pass-by-ref, we just reuse
+ * them without copying for each group. Hence, transition function
+ * had better not scribble on its input!
*/
- if (OidIsValid(peraggstate->xfn1_oid) &&
- !peraggstate->initValue1IsNull)
- peraggstate->value1 = copyDatum(peraggstate->initValue1,
- peraggstate->transtype1Len,
- peraggstate->transtype1ByVal);
- else
- peraggstate->value1 = (Datum) NULL;
+ peraggstate->value1 = peraggstate->initValue1;
peraggstate->value1IsNull = peraggstate->initValue1IsNull;
-
- if (OidIsValid(peraggstate->xfn2_oid) &&
- !peraggstate->initValue2IsNull)
- peraggstate->value2 = copyDatum(peraggstate->initValue2,
- peraggstate->transtype2Len,
- peraggstate->transtype2ByVal);
- else
- peraggstate->value2 = (Datum) NULL;
+ peraggstate->value2 = peraggstate->initValue2;
peraggstate->value2IsNull = peraggstate->initValue2IsNull;
/* ------------------------------------------
/*
* Given a new input value, advance the transition functions of an aggregate.
*
+ * When called, CurrentMemoryContext should be the context we want transition
+ * function results to be delivered into on this cycle.
+ *
* Note: if the agg does not have usenulls set, null inputs will be filtered
* out before reaching here.
*/
* XXX We assume, without having checked, that the agg's input
* type is binary-compatible with its transtype1!
*
- * We have to copy the datum since the tuple from which it came
+ * We had better copy the datum if it is pass-by-ref, since
+ * the given pointer may be pointing into a scan tuple that
* will be freed on the next iteration of the scan.
*/
- peraggstate->value1 = copyDatum(newVal,
- peraggstate->transtype1Len,
- peraggstate->transtype1ByVal);
+ peraggstate->value1 = datumCopy(newVal,
+ peraggstate->transtype1ByVal,
+ peraggstate->transtype1Len);
peraggstate->value1IsNull = false;
peraggstate->noInitValue = false;
}
}
else
newVal = FunctionCallInvoke(&fcinfo);
- if (!peraggstate->transtype1ByVal && !peraggstate->value1IsNull)
- pfree(DatumGetPointer(peraggstate->value1));
+ /*
+ * If the transition function was uncooperative, it may have
+ * given us a pass-by-ref result that points at the scan tuple
+ * or the prior-cycle working memory. Copy it into the active
+ * context if it doesn't look right.
+ */
+ if (!peraggstate->transtype1ByVal && !fcinfo.isnull &&
+ ! MemoryContextContains(CurrentMemoryContext,
+ DatumGetPointer(newVal)))
+ newVal = datumCopy(newVal,
+ peraggstate->transtype1ByVal,
+ peraggstate->transtype1Len);
peraggstate->value1 = newVal;
peraggstate->value1IsNull = fcinfo.isnull;
}
}
else
newVal = FunctionCallInvoke(&fcinfo);
- if (!peraggstate->transtype2ByVal && !peraggstate->value2IsNull)
- pfree(DatumGetPointer(peraggstate->value2));
+ /*
+ * If the transition function was uncooperative, it may have
+ * given us a pass-by-ref result that points at the scan tuple
+ * or the prior-cycle working memory. Copy it into the active
+ * context if it doesn't look right.
+ */
+ if (!peraggstate->transtype2ByVal && !fcinfo.isnull &&
+ ! MemoryContextContains(CurrentMemoryContext,
+ DatumGetPointer(newVal)))
+ newVal = datumCopy(newVal,
+ peraggstate->transtype2ByVal,
+ peraggstate->transtype2Len);
peraggstate->value2 = newVal;
peraggstate->value2IsNull = fcinfo.isnull;
}
}
/*
- * Compute the final value of one aggregate.
+ * Run the transition functions for a DISTINCT aggregate. This is called
+ * after we have completed entering all the input values into the sort
+ * object. We complete the sort, read out the value in sorted order, and
+ * run the transition functions on each non-duplicate value.
+ *
+ * When called, CurrentMemoryContext should be the per-query context.
*/
static void
-finalize_aggregate(AggStatePerAgg peraggstate,
- Datum *resultVal, bool *resultIsNull)
+process_sorted_aggregate(AggState *aggstate,
+ AggStatePerAgg peraggstate)
{
- Aggref *aggref = peraggstate->aggref;
- FunctionCallInfoData fcinfo;
+ Datum oldVal = (Datum) 0;
+ bool haveOldVal = false;
+ MemoryContext oldContext;
+ Datum newVal;
+ bool isNull;
- MemSet(&fcinfo, 0, sizeof(fcinfo));
+ tuplesort_performsort(peraggstate->sortstate);
/*
- * If it's a DISTINCT aggregate, all we've done so far is to stuff the
- * input values into the sort object. Complete the sort, then run the
- * transition functions on the non-duplicate values. Note that
- * DISTINCT always suppresses nulls, per SQL spec, regardless of
- * usenulls.
+ * Note: if input type is pass-by-ref, the datums returned by the sort
+ * are freshly palloc'd in the per-query context, so we must be careful
+ * to pfree them when they are no longer needed.
*/
- if (aggref->aggdistinct)
+
+ while (tuplesort_getdatum(peraggstate->sortstate, true,
+ &newVal, &isNull))
{
- Datum oldVal = (Datum) 0;
- bool haveOldVal = false;
- Datum newVal;
- bool isNull;
-
- tuplesort_performsort(peraggstate->sortstate);
- while (tuplesort_getdatum(peraggstate->sortstate, true,
- &newVal, &isNull))
+ /*
+ * DISTINCT always suppresses nulls, per SQL spec, regardless of
+ * the aggregate's usenulls setting.
+ */
+ if (isNull)
+ continue;
+ /*
+ * Clear and select the current working context for evaluation of
+ * the equality function and transition functions.
+ */
+ MemoryContextReset(aggstate->agg_cxt[aggstate->which_cxt]);
+ oldContext =
+ MemoryContextSwitchTo(aggstate->agg_cxt[aggstate->which_cxt]);
+
+ if (haveOldVal &&
+ DatumGetBool(FunctionCall2(&peraggstate->equalfn,
+ oldVal, newVal)))
+ {
+ /* equal to prior, so forget this one */
+ if (!peraggstate->inputtypeByVal)
+ pfree(DatumGetPointer(newVal));
+ /* note we do NOT flip contexts in this case... */
+ }
+ else
{
- if (isNull)
- continue;
- if (haveOldVal)
- {
- if (DatumGetBool(FunctionCall2(&peraggstate->equalfn,
- oldVal, newVal)))
- {
- /* equal to prior, so forget this one */
- if (!peraggstate->inputtypeByVal)
- pfree(DatumGetPointer(newVal));
- continue;
- }
- }
advance_transition_functions(peraggstate, newVal, false);
+ /*
+ * Make the other context current so that this transition
+ * result is preserved.
+ */
+ aggstate->which_cxt = 1 - aggstate->which_cxt;
+ /* forget the old value, if any */
if (haveOldVal && !peraggstate->inputtypeByVal)
pfree(DatumGetPointer(oldVal));
oldVal = newVal;
haveOldVal = true;
}
- if (haveOldVal && !peraggstate->inputtypeByVal)
- pfree(DatumGetPointer(oldVal));
- tuplesort_end(peraggstate->sortstate);
- peraggstate->sortstate = NULL;
+
+ MemoryContextSwitchTo(oldContext);
}
+ if (haveOldVal && !peraggstate->inputtypeByVal)
+ pfree(DatumGetPointer(oldVal));
+
+ tuplesort_end(peraggstate->sortstate);
+ peraggstate->sortstate = NULL;
+}
+
+/*
+ * Compute the final value of one aggregate.
+ *
+ * When called, CurrentMemoryContext should be the context where we want
+ * final values delivered (ie, the per-output-tuple expression context).
+ */
+static void
+finalize_aggregate(AggStatePerAgg peraggstate,
+ Datum *resultVal, bool *resultIsNull)
+{
+ FunctionCallInfoData fcinfo;
+
+ MemSet(&fcinfo, 0, sizeof(fcinfo));
+
/*
- * Now apply the agg's finalfn, or substitute the appropriate
+ * Apply the agg's finalfn, or substitute the appropriate
* transition value if there is no finalfn.
*
* XXX For now, only apply finalfn if we got at least one non-null input
/* Return value1 */
*resultVal = peraggstate->value1;
*resultIsNull = peraggstate->value1IsNull;
- /* prevent pfree below */
- peraggstate->value1IsNull = true;
}
else if (OidIsValid(peraggstate->xfn2_oid))
{
/* Return value2 */
*resultVal = peraggstate->value2;
*resultIsNull = peraggstate->value2IsNull;
- /* prevent pfree below */
- peraggstate->value2IsNull = true;
}
else
elog(ERROR, "ExecAgg: no valid transition functions??");
-
/*
- * Release any per-group working storage, unless we're passing it back
- * as the result of the aggregate.
+ * If result is pass-by-ref, make sure it is in the right context.
*/
- if (OidIsValid(peraggstate->xfn1_oid) &&
- !peraggstate->value1IsNull &&
- !peraggstate->transtype1ByVal)
- pfree(DatumGetPointer(peraggstate->value1));
-
- if (OidIsValid(peraggstate->xfn2_oid) &&
- !peraggstate->value2IsNull &&
- !peraggstate->transtype2ByVal)
- pfree(DatumGetPointer(peraggstate->value2));
+ if (!peraggstate->resulttypeByVal && ! *resultIsNull &&
+ ! MemoryContextContains(CurrentMemoryContext,
+ DatumGetPointer(*resultVal)))
+ *resultVal = datumCopy(*resultVal,
+ peraggstate->resulttypeByVal,
+ peraggstate->resulttypeLen);
}
+
/* ---------------------------------------
*
* ExecAgg -
Datum *aggvalues;
bool *aggnulls;
AggStatePerAgg peragg;
+ MemoryContext oldContext;
TupleTableSlot *resultSlot;
HeapTuple inputTuple;
int aggno;
peragg = aggstate->peragg;
/*
- * We loop retrieving groups until we find one matching
- * node->plan.qual
+ * We loop retrieving groups until we find one matching node->plan.qual
*/
do
{
if (aggstate->agg_done)
return NULL;
+ /*
+ * Clear the per-output-tuple context for each group
+ */
+ MemoryContextReset(aggstate->tup_cxt);
+
/*
* Initialize working state for a new input tuple group
*/
break;
econtext->ecxt_scantuple = outerslot;
+ /*
+ * Clear and select the current working context for evaluation
+ * of the input expressions and transition functions at this
+ * input tuple.
+ */
+ econtext->ecxt_per_tuple_memory =
+ aggstate->agg_cxt[aggstate->which_cxt];
+ ResetExprContext(econtext);
+ oldContext =
+ MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
for (aggno = 0; aggno < aggstate->numaggs; aggno++)
{
AggStatePerAgg peraggstate = &peragg[aggno];
continue; /* ignore this tuple for this agg */
if (aggref->aggdistinct)
+ {
+ /* putdatum has to be called in per-query context */
+ MemoryContextSwitchTo(oldContext);
tuplesort_putdatum(peraggstate->sortstate,
newVal, isNull);
+ MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+ }
else
advance_transition_functions(peraggstate,
newVal, isNull);
}
+ /*
+ * Make the other context current so that these transition
+ * results are preserved.
+ */
+ aggstate->which_cxt = 1 - aggstate->which_cxt;
+
+ MemoryContextSwitchTo(oldContext);
+
/*
* Keep a copy of the first input tuple for the projection.
* (We only need one since only the GROUP BY columns in it can
/*
* Done scanning input tuple group. Finalize each aggregate
- * calculation.
+ * calculation, and stash results in the per-output-tuple context.
+ *
+ * This is a bit tricky when there are both DISTINCT and plain
+ * aggregates: we must first finalize all the plain aggs and then all
+ * the DISTINCT ones. This is needed because the last transition
+ * values for the plain aggs are stored in the not-current working
+ * context, and we have to evaluate those aggs (and stash the results
+ * in the output tup_cxt!) before we start flipping contexts again
+ * in process_sorted_aggregate.
*/
+ oldContext = MemoryContextSwitchTo(aggstate->tup_cxt);
+ for (aggno = 0; aggno < aggstate->numaggs; aggno++)
+ {
+ AggStatePerAgg peraggstate = &peragg[aggno];
+
+ if (! peraggstate->aggref->aggdistinct)
+ finalize_aggregate(peraggstate,
+ &aggvalues[aggno], &aggnulls[aggno]);
+ }
+ MemoryContextSwitchTo(oldContext);
for (aggno = 0; aggno < aggstate->numaggs; aggno++)
{
AggStatePerAgg peraggstate = &peragg[aggno];
- finalize_aggregate(peraggstate,
- &aggvalues[aggno], &aggnulls[aggno]);
+ if (peraggstate->aggref->aggdistinct)
+ {
+ process_sorted_aggregate(aggstate, peraggstate);
+ oldContext = MemoryContextSwitchTo(aggstate->tup_cxt);
+ finalize_aggregate(peraggstate,
+ &aggvalues[aggno], &aggnulls[aggno]);
+ MemoryContextSwitchTo(oldContext);
+ }
}
/*
/*
* If inputtuple==NULL (ie, the outerPlan didn't return
* anything), create a dummy all-nulls input tuple for use by
- * execProject. 99.44% of the time this is a waste of cycles,
+ * ExecProject. 99.44% of the time this is a waste of cycles,
* because ordinarily the projected output tuple's targetlist
* cannot contain any direct (non-aggregated) references to
* input columns, so the dummy tuple will not be referenced.
/*
* Store the representative input tuple in the tuple table slot
- * reserved for it.
+ * reserved for it. The tuple will be deleted when it is cleared
+ * from the slot.
*/
ExecStoreTuple(inputTuple,
aggstate->csstate.css_ScanTupleSlot,
true);
econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
+ /*
+ * Do projection and qual check in the per-output-tuple context.
+ */
+ econtext->ecxt_per_tuple_memory = aggstate->tup_cxt;
+
/*
* Form a projection tuple using the aggregate results and the
* representative input tuple. Store it in the result tuple slot.
}
/*
- * assign node's base id and create expression context
+ * Create expression context
*/
- ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate, (Plan *) parent);
ExecAssignExprContext(estate, &aggstate->csstate.cstate);
+ /*
+ * We actually need three separate expression memory contexts: one
+ * for calculating per-output-tuple values (ie, the finished aggregate
+ * results), and two that we ping-pong between for per-input-tuple
+ * evaluation of input expressions and transition functions. The
+ * context made by ExecAssignExprContext() is used as the output context.
+ */
+ aggstate->tup_cxt =
+ aggstate->csstate.cstate.cs_ExprContext->ecxt_per_tuple_memory;
+ aggstate->agg_cxt[0] =
+ AllocSetContextCreate(CurrentMemoryContext,
+ "AggExprContext1",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ aggstate->agg_cxt[1] =
+ AllocSetContextCreate(CurrentMemoryContext,
+ "AggExprContext2",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ aggstate->which_cxt = 0;
+
#define AGG_NSLOTS 2
/*
/* Fill in the peraggstate data */
peraggstate->aggref = aggref;
- aggTuple = SearchSysCacheTuple(AGGNAME,
- PointerGetDatum(aggname),
- ObjectIdGetDatum(aggref->basetype),
- 0, 0);
+ aggTuple = SearchSysCacheTupleCopy(AGGNAME,
+ PointerGetDatum(aggname),
+ ObjectIdGetDatum(aggref->basetype),
+ 0, 0);
if (!HeapTupleIsValid(aggTuple))
elog(ERROR, "ExecAgg: cache lookup failed for aggregate %s(%s)",
aggname,
typeidTypeName(aggref->basetype));
aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
+ typeInfo = typeidType(aggform->aggfinaltype);
+ peraggstate->resulttypeLen = typeLen(typeInfo);
+ peraggstate->resulttypeByVal = typeByVal(typeInfo);
+
peraggstate->initValue1 =
AggNameGetInitVal(aggname,
aggform->aggbasetype,
peraggstate->sortOperator = any_ordering_op(inputType);
peraggstate->sortstate = NULL;
}
+
+ heap_freetuple(aggTuple);
}
return TRUE;
Plan *outerPlan;
ExecFreeProjectionInfo(&aggstate->csstate.cstate);
+ /*
+ * Make sure ExecFreeExprContext() frees the right expr context...
+ */
+ aggstate->csstate.cstate.cs_ExprContext->ecxt_per_tuple_memory =
+ aggstate->tup_cxt;
+ ExecFreeExprContext(&aggstate->csstate.cstate);
+ /*
+ * ... and I free the others.
+ */
+ MemoryContextDelete(aggstate->agg_cxt[0]);
+ MemoryContextDelete(aggstate->agg_cxt[1]);
outerPlan = outerPlan(node);
ExecEndNode(outerPlan, (Plan *) node);
*/
if (((Plan *) node)->lefttree->chgParam == NULL)
ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-
-}
-
-
-/*
- * Helper routine to make a copy of a Datum.
- *
- * NB: input had better not be a NULL; might cause null-pointer dereference.
- */
-static Datum
-copyDatum(Datum val, int typLen, bool typByVal)
-{
- if (typByVal)
- return val;
- else
- {
- char *newVal;
-
- if (typLen == -1) /* variable length type? */
- typLen = VARSIZE((struct varlena *) DatumGetPointer(val));
- newVal = (char *) palloc(typLen);
- memcpy(newVal, DatumGetPointer(val), typLen);
- return PointerGetDatum(newVal);
- }
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.34 2000/06/17 21:48:49 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.35 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
node->appendstate = appendstate;
/* ----------------
- * Miscellanious initialization
- *
- * + assign node's base_id
- * + assign debugging hooks
+ * Miscellaneous initialization
*
* Append plans don't have expression contexts because they
- * never call ExecQual or ExecTargetList.
+ * never call ExecQual or ExecProject.
* ----------------
*/
- ExecAssignNodeBaseInfo(estate, &appendstate->cstate, parent);
#define APPEND_NSLOTS 1
/* ----------------
*
* Handles the iteration over the multiple scans.
*
- * NOTE: Can't call this ExecAppend, that name is used in execMain.l
+ * NOTE: Can't call this ExecAppend, that name is used in execMain.
* ----------------------------------------------------------------
*/
TupleTableSlot *
* locate group boundaries.
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.36 2000/05/30 04:24:45 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.37 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
EState *estate;
ExprContext *econtext;
TupleDesc tupdesc;
-
HeapTuple outerTuple = NULL;
HeapTuple firsttuple;
TupleTableSlot *outerslot;
ProjectionInfo *projInfo;
TupleTableSlot *resultSlot;
-
bool isDone;
/* ---------------------
grpstate = node->grpstate;
if (grpstate->grp_done)
return NULL;
-
estate = node->plan.state;
-
econtext = grpstate->csstate.cstate.cs_ExprContext;
-
tupdesc = ExecGetScanType(&grpstate->csstate);
- /* if we haven't returned first tuple of new group yet ... */
+ /*
+ * We need not call ResetExprContext here because execTuplesMatch
+ * will reset the per-tuple memory context once per input tuple.
+ */
+
+ /* if we haven't returned first tuple of a new group yet ... */
if (grpstate->grp_useFirstTuple)
{
grpstate->grp_useFirstTuple = FALSE;
if (!execTuplesMatch(firsttuple, outerTuple,
tupdesc,
node->numCols, node->grpColIdx,
- grpstate->eqfunctions))
+ grpstate->eqfunctions,
+ econtext->ecxt_per_tuple_memory))
{
/*
EState *estate;
ExprContext *econtext;
TupleDesc tupdesc;
-
HeapTuple outerTuple = NULL;
HeapTuple firsttuple;
TupleTableSlot *outerslot;
ProjectionInfo *projInfo;
TupleTableSlot *resultSlot;
-
bool isDone;
/* ---------------------
grpstate = node->grpstate;
if (grpstate->grp_done)
return NULL;
-
estate = node->plan.state;
-
econtext = node->grpstate->csstate.cstate.cs_ExprContext;
-
tupdesc = ExecGetScanType(&grpstate->csstate);
+ /*
+ * We need not call ResetExprContext here because execTuplesMatch
+ * will reset the per-tuple memory context once per input tuple.
+ */
+
firsttuple = grpstate->grp_firstTuple;
if (firsttuple == NULL)
{
if (!execTuplesMatch(firsttuple, outerTuple,
tupdesc,
node->numCols, node->grpColIdx,
- grpstate->eqfunctions))
+ grpstate->eqfunctions,
+ econtext->ecxt_per_tuple_memory))
break;
}
grpstate->grp_firstTuple = NULL;
/*
- * assign node's base id and create expression context
+ * create expression context
*/
- ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
- (Plan *) parent);
ExecAssignExprContext(estate, &grpstate->csstate.cstate);
#define GROUP_NSLOTS 2
grpstate = node->grpstate;
ExecFreeProjectionInfo(&grpstate->csstate.cstate);
+ ExecFreeExprContext(&grpstate->csstate.cstate);
outerPlan = outerPlan(node);
ExecEndNode(outerPlan, (Plan *) node);
* numCols: the number of attributes to be examined
* matchColIdx: array of attribute column numbers
* eqFunctions: array of fmgr lookup info for the equality functions to use
+ * evalContext: short-term memory context for executing the functions
+ *
+ * NB: evalContext is reset each time!
*/
bool
execTuplesMatch(HeapTuple tuple1,
TupleDesc tupdesc,
int numCols,
AttrNumber *matchColIdx,
- FmgrInfo *eqfunctions)
+ FmgrInfo *eqfunctions,
+ MemoryContext evalContext)
{
+ MemoryContext oldContext;
+ bool result;
int i;
+ /* Reset and switch into the temp context. */
+ MemoryContextReset(evalContext);
+ oldContext = MemoryContextSwitchTo(evalContext);
+
/*
* We cannot report a match without checking all the fields, but we
* can report a non-match as soon as we find unequal fields. So,
* start comparing at the last field (least significant sort key).
* That's the most likely to be different...
*/
+ result = true;
+
for (i = numCols; --i >= 0;)
{
AttrNumber att = matchColIdx[i];
&isNull2);
if (isNull1 != isNull2)
- return FALSE; /* one null and one not; they aren't equal */
+ {
+ result = false; /* one null and one not; they aren't equal */
+ break;
+ }
if (isNull1)
continue; /* both are null, treat as equal */
if (! DatumGetBool(FunctionCall2(&eqfunctions[i],
attr1, attr2)))
- return FALSE;
+ {
+ result = false; /* they aren't equal */
+ break;
+ }
}
- return TRUE;
+ MemoryContextSwitchTo(oldContext);
+
+ return result;
}
/*
* Portions Copyright (c) 1994, Regents of the University of California
*
*
- * $Id: nodeHash.c,v 1.48 2000/06/28 03:31:34 tgl Exp $
+ * $Id: nodeHash.c,v 1.49 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "executor/nodeHash.h"
#include "executor/nodeHashjoin.h"
#include "miscadmin.h"
-
+#include "parser/parse_expr.h"
+#include "parser/parse_type.h"
static int hashFunc(Datum key, int len, bool byVal);
EState *estate;
HashState *hashstate;
Plan *outerNode;
- Var *hashkey;
+ Node *hashkey;
HashJoinTable hashtable;
TupleTableSlot *slot;
ExprContext *econtext;
/* ----------------
* Miscellaneous initialization
*
- * + assign node's base_id
- * + assign debugging hooks and
* + create expression context for node
* ----------------
*/
- ExecAssignNodeBaseInfo(estate, &hashstate->cstate, parent);
ExecAssignExprContext(estate, &hashstate->cstate);
/* ----------------
* ----------------
*/
ExecFreeProjectionInfo(&hashstate->cstate);
+ ExecFreeExprContext(&hashstate->cstate);
/* ----------------
* shut down the subplan
int totalbuckets;
int bucketsize;
int i;
+ Type typeInfo;
MemoryContext oldcxt;
/* ----------------
hashtable->innerBatchSize = NULL;
hashtable->outerBatchSize = NULL;
+ /* ----------------
+ * Get info about the datatype of the hash key.
+ * ----------------
+ */
+ typeInfo = typeidType(exprType(node->hashkey));
+ hashtable->typByVal = typeByVal(typeInfo);
+ hashtable->typLen = typeLen(typeInfo);
+
/* ----------------
* Create temporary memory contexts in which to keep the hashtable
* working storage. See notes in executor/hashjoin.h.
void
ExecHashTableInsert(HashJoinTable hashtable,
ExprContext *econtext,
- Var *hashkey)
+ Node *hashkey)
{
int bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
TupleTableSlot *slot = econtext->ecxt_innertuple;
int
ExecHashGetBucket(HashJoinTable hashtable,
ExprContext *econtext,
- Var *hashkey)
+ Node *hashkey)
{
int bucketno;
Datum keyval;
bool isNull;
+ bool isDone;
/* ----------------
* Get the join attribute value of the tuple
*
- * ...It's quick hack - use ExecEvalExpr instead of ExecEvalVar:
- * hashkey may be T_ArrayRef, not just T_Var. - vadim 04/22/97
+ * We reset the eval context each time to avoid any possibility
+ * of memory leaks in the hash function.
* ----------------
*/
- keyval = ExecEvalExpr((Node *) hashkey, econtext, &isNull, NULL);
+ ResetExprContext(econtext);
- /*
- * keyval could be null, so we better point it to something valid
- * before trying to run hashFunc on it. --djm 8/17/96
- */
- if (isNull)
- {
- execConstByVal = 0;
- execConstLen = 0;
- keyval = (Datum) "";
- }
+ keyval = ExecEvalExprSwitchContext(hashkey, econtext,
+ &isNull, &isDone);
/* ------------------
* compute the hash function
* ------------------
*/
- bucketno = hashFunc(keyval, execConstLen, execConstByVal) % hashtable->totalbuckets;
+ if (isNull)
+ {
+ bucketno = 0;
+ }
+ else
+ {
+ bucketno = hashFunc(keyval, hashtable->typLen, hashtable->typByVal)
+ % hashtable->totalbuckets;
+ }
#ifdef HJDEBUG
if (bucketno >= hashtable->nbuckets)
- printf("hash(%d) = %d SAVED\n", keyval, bucketno);
+ printf("hash(%ld) = %d SAVED\n", (long) keyval, bucketno);
else
- printf("hash(%d) = %d\n", keyval, bucketno);
+ printf("hash(%ld) = %d\n", (long) keyval, bucketno);
#endif
return bucketno;
false); /* do not pfree this tuple */
econtext->ecxt_innertuple = inntuple;
+ /* reset temp memory each time to avoid leaks from qual expression */
+ ResetExprContext(econtext);
+
if (ExecQual(hjclauses, econtext, false))
{
hjstate->hj_CurTuple = hashTuple;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.30 2000/01/26 05:56:23 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.31 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List *qual;
ScanDirection dir;
TupleTableSlot *inntuple;
- Var *outerVar;
+ Node *outerVar;
ExprContext *econtext;
HashJoinTable hashtable;
HeapTuple curtuple;
TupleTableSlot *outerTupleSlot;
TupleTableSlot *innerTupleSlot;
- Var *innerhashkey;
int i;
bool hashPhaseDone;
hashNode = (Hash *) innerPlan(node);
outerNode = outerPlan(node);
hashPhaseDone = node->hashdone;
-
dir = estate->es_direction;
/* -----------------
* -----------------
*/
hashtable = hjstate->hj_HashTable;
+ econtext = hjstate->jstate.cs_ExprContext;
- /* --------------------
- * initialize expression context
- * --------------------
+ /* ----------------
+ * Reset per-tuple memory context to free any expression evaluation
+ * storage allocated in the previous tuple cycle.
+ * ----------------
*/
- econtext = hjstate->jstate.cs_ExprContext;
+ ResetExprContext(econtext);
+ /* ----------------
+ * Check to see if we're still projecting out tuples from a previous
+ * join tuple (because there is a function-returning-set in the
+ * projection expressions). If so, try to project another one.
+ * ----------------
+ */
if (hjstate->jstate.cs_TupFromTlist)
{
TupleTableSlot *result;
result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
if (!isDone)
return result;
+ /* Done with that source tuple... */
+ hjstate->jstate.cs_TupFromTlist = false;
}
/* ----------------
*/
hashtable = ExecHashTableCreate(hashNode);
hjstate->hj_HashTable = hashtable;
- innerhashkey = hashNode->hashkey;
- hjstate->hj_InnerHashKey = innerhashkey;
+ hjstate->hj_InnerHashKey = hashNode->hashkey;
/* ----------------
* execute the Hash node, to build the hash table
* ----------------
*/
outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
- outerVar = get_leftop(clause);
+ outerVar = (Node *) get_leftop(clause);
for (;;)
{
InvalidBuffer,
false); /* don't pfree this tuple */
econtext->ecxt_innertuple = inntuple;
+
+ /* reset temp memory each time to avoid leaks from qpqual */
+ ResetExprContext(econtext);
+
/* ----------------
* if we pass the qual, then save state for next call and
* have ExecProject form the projection, store it
/* ----------------
* Miscellaneous initialization
*
- * + assign node's base_id
- * + assign debugging hooks and
* + create expression context for node
* ----------------
*/
- ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent);
ExecAssignExprContext(estate, &hjstate->jstate);
#define HASHJOIN_NSLOTS 2
hjstate->hj_HashTable = (HashJoinTable) NULL;
hjstate->hj_CurBucketNo = 0;
hjstate->hj_CurTuple = (HashJoinTuple) NULL;
- hjstate->hj_InnerHashKey = (Var *) NULL;
+ hjstate->hj_InnerHashKey = (Node *) NULL;
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
- hjstate->jstate.cs_TupFromTlist = (bool) false;
+ hjstate->jstate.cs_TupFromTlist = false;
return TRUE;
}
* ----------------
*/
ExecFreeProjectionInfo(&hjstate->jstate);
+ ExecFreeExprContext(&hjstate->jstate);
/* ----------------
* clean up subtrees
BufFile *innerFile;
TupleTableSlot *slot;
ExprContext *econtext;
- Var *innerhashkey;
+ Node *innerhashkey;
if (newbatch > 1)
{
hjstate->hj_CurBucketNo = 0;
hjstate->hj_CurTuple = (HashJoinTuple) NULL;
- hjstate->hj_InnerHashKey = (Var *) NULL;
+ hjstate->hj_InnerHashKey = (Node *) NULL;
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
- hjstate->jstate.cs_TupFromTlist = (bool) false;
+ hjstate->jstate.cs_TupFromTlist = false;
/*
* if chgParam of subnodes is not null then plans will be re-scanned
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.51 2000/06/15 04:09:52 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.52 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
EState *estate;
CommonScanState *scanstate;
IndexScanState *indexstate;
+ ExprContext *econtext;
ScanDirection direction;
Snapshot snapshot;
IndexScanDescPtr scanDescs;
TupleTableSlot *slot;
Buffer buffer = InvalidBuffer;
int numIndices;
-
bool bBackward;
int indexNumber;
scanDescs = indexstate->iss_ScanDescs;
heapRelation = scanstate->css_currentRelation;
numIndices = indexstate->iss_NumIndices;
+ econtext = scanstate->cstate.cs_ExprContext;
slot = scanstate->css_ScanTupleSlot;
/*
slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
slot->ttc_shouldFree = false;
- scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
+ econtext->ecxt_scantuple = slot;
/* Does the tuple meet any of the OR'd indxqual conditions? */
+
+ ResetExprContext(econtext);
+
foreach(qual, node->indxqualorig)
{
- if (ExecQual((List *) lfirst(qual),
- scanstate->cstate.cs_ExprContext,
- false))
+ if (ExecQual((List *) lfirst(qual), econtext, false))
break;
}
if (qual == NIL) /* would not be returned by indices */
* and checking for failure with all previous
* qualifications.
*/
- scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
+ econtext->ecxt_scantuple = slot;
+ ResetExprContext(econtext);
qual = node->indxqualorig;
for (prev_index = 0; prev_index < indexstate->iss_IndexPtr;
prev_index++)
{
- if (ExecQual((List *) lfirst(qual),
- scanstate->cstate.cs_ExprContext,
- false))
+ if (ExecQual((List *) lfirst(qual), econtext, false))
{
prev_matches = true;
break;
qual = lnext(qual);
}
if (!prev_matches)
- return slot;/* OK to return tuple */
+ return slot; /* OK to return tuple */
/* Duplicate tuple, so drop it and loop back for another */
ExecClearTuple(slot);
}
* use IndexNext as access method
* ----------------
*/
- return ExecScan(&node->scan, IndexNext);
+ return ExecScan(&node->scan, (ExecScanAccessMtd) IndexNext);
}
/* ----------------------------------------------------------------
* Recalculates the value of the scan keys whose value depends on
* information known at runtime and rescans the indexed relation.
* Updating the scan key was formerly done separately in
- * ExecUpdateIndexScanKeys. Integrating it into ReScan
- * makes rescans of indices and
- * relations/general streams more uniform.
+ * ExecUpdateIndexScanKeys. Integrating it into ReScan makes
+ * rescans of indices and relations/general streams more uniform.
*
* ----------------------------------------------------------------
*/
{
EState *estate;
IndexScanState *indexstate;
+ ExprContext *econtext;
ScanDirection direction;
IndexScanDescPtr scanDescs;
ScanKey *scanKeys;
ScanKey skey;
int numIndices;
int i;
-
- Pointer *runtimeKeyInfo;
+ int **runtimeKeyInfo;
int *numScanKeys;
List *indxqual;
List *qual;
bool isNull;
bool isDone;
- indexstate = node->indxstate;
estate = node->scan.plan.state;
+ indexstate = node->indxstate;
+ econtext = indexstate->iss_RuntimeContext; /* context for runtime keys */
direction = estate->es_direction;
numIndices = indexstate->iss_NumIndices;
scanDescs = indexstate->iss_ScanDescs;
scanKeys = indexstate->iss_ScanKeys;
- runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
+ runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
numScanKeys = indexstate->iss_NumScanKeys;
indexstate->iss_IndexPtr = -1;
if (ScanDirectionIsBackward(node->indxorderdir))
indexstate->iss_IndexPtr = numIndices;
- /* If we are being passed an outer tuple, save it for runtime key calc */
- if (exprCtxt != NULL)
- node->scan.scanstate->cstate.cs_ExprContext->ecxt_outertuple =
- exprCtxt->ecxt_outertuple;
+ if (econtext)
+ {
+ /*
+ * If we are being passed an outer tuple,
+ * save it for runtime key calc
+ */
+ if (exprCtxt != NULL)
+ econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
+ /*
+ * Reset the runtime-key context so we don't leak memory as
+ * each outer tuple is scanned. Note this assumes that we
+ * will recalculate *all* runtime keys on each call.
+ */
+ ResetExprContext(econtext);
+ }
/* If this is re-scanning of PlanQual ... */
if (estate->es_evTuple != NULL &&
if (runtimeKeyInfo)
{
- run_keys = (int *) runtimeKeyInfo[i];
+ run_keys = runtimeKeyInfo[i];
for (j = 0; j < n_keys; j++)
{
* expression and evaluate it with respect to the current
* outer tuple. We then stick the result into the scan
* key.
+ *
+ * Note: the result of the eval could be a pass-by-ref
+ * value that's stored in the outer scan's tuple, not in
+ * econtext->ecxt_per_tuple_memory. We assume that the
+ * outer tuple will stay put throughout our scan. If this
+ * is wrong, we could copy the result into our context
+ * explicitly, but I think that's not necessary...
*/
if (run_keys[j] != NO_OP)
{
* pass in isDone but ignore it. We don't iterate in
* quals
*/
- scanvalue = (Datum)
- ExecEvalExpr(scanexpr,
- node->scan.scanstate->cstate.cs_ExprContext,
- &isNull, &isDone);
+ scanvalue = ExecEvalExprSwitchContext(scanexpr,
+ econtext,
+ &isNull,
+ &isDone);
scan_keys[j].sk_argument = scanvalue;
if (isNull)
scan_keys[j].sk_flags |= SK_ISNULL;
skey = scanKeys[i];
index_rescan(scan, direction, skey);
}
- /* ----------------
- * perhaps return something meaningful
- * ----------------
- */
- return;
}
/* ----------------------------------------------------------------
{
CommonScanState *scanstate;
IndexScanState *indexstate;
- Pointer *runtimeKeyInfo;
+ int **runtimeKeyInfo;
ScanKey *scanKeys;
List *indxqual;
int *numScanKeys;
scanstate = node->scan.scanstate;
indexstate = node->indxstate;
indxqual = node->indxqual;
- runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
+ runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
/* ----------------
* extract information from the node
* ----------------
*/
ExecFreeProjectionInfo(&scanstate->cstate);
+ ExecFreeExprContext(&scanstate->cstate);
+ if (indexstate->iss_RuntimeContext)
+ FreeExprContext(indexstate->iss_RuntimeContext);
/* ----------------
* close the heap and index relations
{
for (i = 0; i < numIndices; i++)
{
- List *qual;
- int n_keys;
-
- qual = nth(i, indxqual);
- n_keys = length(qual);
- if (n_keys > 0)
+ if (runtimeKeyInfo[i] != NULL)
pfree(runtimeKeyInfo[i]);
}
pfree(runtimeKeyInfo);
*/
ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
ExecClearTuple(scanstate->css_ScanTupleSlot);
-/* ExecClearTuple(scanstate->css_RawTupleSlot); */
}
/* ----------------------------------------------------------------
*
* old comments
* Creates the run-time state information for the node and
- * sets the relation id to contain relevant decriptors.
+ * sets the relation id to contain relevant descriptors.
*
* Parameters:
* node: IndexNode node produced by the planner.
int *numScanKeys;
RelationPtr relationDescs;
IndexScanDescPtr scanDescs;
- Pointer *runtimeKeyInfo;
+ int **runtimeKeyInfo;
bool have_runtime_keys;
List *rangeTable;
RangeTblEntry *rtentry;
Index relid;
Oid reloid;
-
Relation currentRelation;
HeapScanDesc currentScanDesc;
ScanDirection direction;
- int baseid;
-
- List *execParam = NULL;
+ List *execParam = NIL;
/* ----------------
* assign execution state to node
* --------------------------------
*/
scanstate = makeNode(CommonScanState);
-/*
- scanstate->ss_ProcOuterFlag = false;
- scanstate->ss_OldRelId = 0;
-*/
-
node->scan.scanstate = scanstate;
/* ----------------
- * assign node's base_id .. we don't use AssignNodeBaseid() because
- * the increment is done later on after we assign the index scan's
- * scanstate. see below.
- * ----------------
- */
- baseid = estate->es_BaseId;
-/* scanstate->csstate.cstate.bnode.base_id = baseid; */
- scanstate->cstate.cs_base_id = baseid;
-
- /* ----------------
- * create expression context for node
+ * Miscellaneous initialization
+ *
+ * + create expression context for node
* ----------------
*/
ExecAssignExprContext(estate, &scanstate->cstate);
*/
ExecInitResultTupleSlot(estate, &scanstate->cstate);
ExecInitScanTupleSlot(estate, scanstate);
-/* ExecInitRawTupleSlot(estate, scanstate); */
/* ----------------
* initialize projection info. result type comes from scan desc
indexstate->iss_ScanKeys = NULL;
indexstate->iss_NumScanKeys = NULL;
indexstate->iss_RuntimeKeyInfo = NULL;
+ indexstate->iss_RuntimeContext = NULL;
indexstate->iss_RelationDescs = NULL;
indexstate->iss_ScanDescs = NULL;
node->indxstate = indexstate;
- /* ----------------
- * assign base id to index scan state also
- * ----------------
- */
- indexstate->cstate.cs_base_id = baseid;
- baseid++;
- estate->es_BaseId = baseid;
-
/* ----------------
* get the index node information
* ----------------
scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
/* ----------------
- * initialize runtime key info.
+ * initialize space for runtime key info (may not be needed)
* ----------------
*/
have_runtime_keys = false;
- runtimeKeyInfo = (Pointer *)
- palloc(numIndices * sizeof(Pointer));
+ runtimeKeyInfo = (int **) palloc(numIndices * sizeof(int *));
/* ----------------
* build the index scan keys from the index qualification
qual = lfirst(indxqual);
indxqual = lnext(indxqual);
n_keys = length(qual);
- scan_keys = (n_keys <= 0) ? NULL :
+ scan_keys = (n_keys <= 0) ? (ScanKey) NULL :
(ScanKey) palloc(n_keys * sizeof(ScanKeyData));
- run_keys = (n_keys <= 0) ? NULL :
+ run_keys = (n_keys <= 0) ? (int *) NULL :
(int *) palloc(n_keys * sizeof(int));
CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
}
/* ----------------
- * store the key information into our array.
+ * store the key information into our arrays.
* ----------------
*/
numScanKeys[i] = n_keys;
scanKeys[i] = scan_keys;
- runtimeKeyInfo[i] = (Pointer) run_keys;
+ runtimeKeyInfo[i] = run_keys;
}
indexstate->iss_NumIndices = numIndices;
* (one for each index) to arrays of flags (one for each key)
* which indicate that the qual needs to be evaluated at runtime.
* -cim 10/24/89
+ *
+ * If we do have runtime keys, we need an ExprContext to evaluate them;
+ * the node's standard context won't do because we want to reset that
+ * context for every tuple. So, build another context just like the
+ * other one...
+ * -tgl 7/11/00
* ----------------
*/
if (have_runtime_keys)
- indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo;
+ {
+ ExprContext *stdecontext = scanstate->cstate.cs_ExprContext;
+
+ ExecAssignExprContext(estate, &scanstate->cstate);
+ indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo;
+ indexstate->iss_RuntimeContext = scanstate->cstate.cs_ExprContext;
+ scanstate->cstate.cs_ExprContext = stdecontext;
+ }
else
+ {
indexstate->iss_RuntimeKeyInfo = NULL;
+ indexstate->iss_RuntimeContext = NULL;
+ /* Get rid of the speculatively-allocated flag arrays, too */
+ for (i = 0; i < numIndices; i++)
+ {
+ if (runtimeKeyInfo[i] != NULL)
+ pfree(runtimeKeyInfo[i]);
+ }
+ pfree(runtimeKeyInfo);
+ }
/* ----------------
* get the range table and direction information
scanstate->css_currentRelation = currentRelation;
scanstate->css_currentScanDesc = currentScanDesc;
-
/* ----------------
* get the scan type from the relation descriptor.
* ----------------
ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
- /* ----------------
- * index scans don't have subtrees..
- * ----------------
- */
-/* scanstate->ss_ProcOuterFlag = false; */
-
/* ----------------
* open the index relations and initialize
* relation and scan descriptors.
indexstate->iss_RelationDescs = relationDescs;
indexstate->iss_ScanDescs = scanDescs;
- indexstate->cstate.cs_TupFromTlist = false;
-
/*
- * if there are some PARAM_EXEC in skankeys then force index rescan on
+ * if there are some PARAM_EXEC in scankeys then force index rescan on
* first scan.
*/
((Plan *) node)->chgParam = execParam;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.31 2000/06/18 22:44:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.32 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
node->matstate = matstate;
/* ----------------
- * Miscellanious initialization
- *
- * + assign node's base_id
- * + assign debugging hooks and
- * + assign result tuple slot
+ * Miscellaneous initialization
*
* Materialization nodes don't need ExprContexts because
- * they never call ExecQual or ExecTargetList.
+ * they never call ExecQual or ExecProject.
* ----------------
*/
- ExecAssignNodeBaseInfo(estate, &matstate->csstate.cstate, parent);
#define MATERIAL_NSLOTS 1
/* ----------------
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.35 2000/06/15 04:09:52 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.36 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static bool
MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
{
+ bool result;
+ MemoryContext oldContext;
List *clause;
List *eqclause;
- Datum const_value;
- bool isNull;
- bool isDone;
- /* ----------------
- * if we have no compare qualification, return nil
- * ----------------
+ /*
+ * Do expression eval in short-lived context.
*/
- if (compareQual == NIL)
- return false;
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
/* ----------------
* for each pair of clauses, test them until
- * our compare conditions are satisfied
+ * our compare conditions are satisfied.
+ * if we reach the end of the list, none of our key greater-than
+ * conditions were satisfied so we return false.
* ----------------
*/
+ result = false; /* assume 'false' result */
+
eqclause = eqQual;
foreach(clause, compareQual)
{
+ Datum const_value;
+ bool isNull;
+ bool isDone;
+
/* ----------------
* first test if our compare clause is satisfied.
- * if so then return true. ignore isDone, don't iterate in
- * quals.
+ * if so then return true.
+ *
+ * A NULL result is considered false.
+ * ignore isDone, don't iterate in quals.
* ----------------
*/
- const_value = (Datum)
- ExecEvalExpr((Node *) lfirst(clause), econtext, &isNull, &isDone);
+ const_value = ExecEvalExpr((Node *) lfirst(clause), econtext,
+ &isNull, &isDone);
- if (DatumGetInt32(const_value) != 0)
- return true;
+ if (DatumGetBool(const_value) && !isNull)
+ {
+ result = true;
+ break;
+ }
/* ----------------
* ok, the compare clause failed so we test if the keys
* are equal... if key1 != key2, we return false.
* otherwise key1 = key2 so we move on to the next pair of keys.
- *
- * ignore isDone, don't iterate in quals.
* ----------------
*/
const_value = ExecEvalExpr((Node *) lfirst(eqclause),
&isNull,
&isDone);
- if (DatumGetInt32(const_value) == 0)
- return false;
+ if (! DatumGetBool(const_value) || isNull)
+ break; /* return false */
+
eqclause = lnext(eqclause);
}
- /* ----------------
- * if we get here then it means none of our key greater-than
- * conditions were satisfied so we return false.
- * ----------------
- */
- return false;
+ MemoryContextSwitchTo(oldContext);
+
+ return result;
}
/* ----------------------------------------------------------------
List *qual;
bool qualResult;
bool compareResult;
-
Plan *innerPlan;
TupleTableSlot *innerTupleSlot;
-
Plan *outerPlan;
TupleTableSlot *outerTupleSlot;
-
ExprContext *econtext;
-
#ifdef ENABLE_OUTER_JOINS
-
/*
* These should be set from the expression context! - thomas
* 1999-02-20
*/
static bool isLeftJoin = true;
static bool isRightJoin = false;
-
#endif
/* ----------------
}
/* ----------------
- * ok, everything is setup.. let's go to work
+ * Reset per-tuple memory context to free any expression evaluation
+ * storage allocated in the previous tuple cycle.
+ * ----------------
+ */
+ ResetExprContext(econtext);
+
+ /* ----------------
+ * Check to see if we're still projecting out tuples from a previous
+ * join tuple (because there is a function-returning-set in the
+ * projection expressions). If so, try to project another one.
* ----------------
*/
if (mergestate->jstate.cs_TupFromTlist)
{
TupleTableSlot *result;
- ProjectionInfo *projInfo;
bool isDone;
- projInfo = mergestate->jstate.cs_ProjInfo;
- result = ExecProject(projInfo, &isDone);
+ result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone);
if (!isDone)
return result;
+ /* Done with that source tuple... */
+ mergestate->jstate.cs_TupFromTlist = false;
}
+
+ /* ----------------
+ * ok, everything is setup.. let's go to work
+ * ----------------
+ */
for (;;)
{
/* ----------------
case EXEC_MJ_JOINTEST:
MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
+ ResetExprContext(econtext);
+
qualResult = ExecQual((List *) mergeclauses, econtext, false);
MJ_DEBUG_QUAL(mergeclauses, qualResult);
MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
+ /*
+ * Check the qpqual to see if we actually want to return
+ * this join tuple. If not, can proceed with merge.
+ *
+ * (We don't bother with a ResetExprContext here, on the
+ * assumption that we just did one before checking the merge
+ * qual. One per tuple should be sufficient.)
+ */
qualResult = ExecQual((List *) qual, econtext, false);
MJ_DEBUG_QUAL(qual, qualResult);
innerTupleSlot = econtext->ecxt_innertuple;
econtext->ecxt_innertuple = mergestate->mj_MarkedTupleSlot;
+ ResetExprContext(econtext);
+
qualResult = ExecQual((List *) mergeclauses, econtext, false);
MJ_DEBUG_QUAL(mergeclauses, qualResult);
*/
ExecRestrPos(innerPlan);
-#if 0
- mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
-#endif
mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
-
}
else
{
* we update the marked tuple and go join them.
* ----------------
*/
+ ResetExprContext(econtext);
+
qualResult = ExecQual((List *) mergeclauses, econtext, false);
MJ_DEBUG_QUAL(mergeclauses, qualResult);
* we update the marked tuple and go join them.
* ----------------
*/
+ ResetExprContext(econtext);
+
qualResult = ExecQual((List *) mergeclauses, econtext, false);
MJ_DEBUG_QUAL(mergeclauses, qualResult);
/* ----------------
* Miscellaneous initialization
*
- * + assign node's base_id
- * + assign debugging hooks and
* + create expression context for node
* ----------------
*/
- ExecAssignNodeBaseInfo(estate, &mergestate->jstate, parent);
ExecAssignExprContext(estate, &mergestate->jstate);
#define MERGEJOIN_NSLOTS 2
* ----------------
*/
ExecFreeProjectionInfo(&mergestate->jstate);
+ ExecFreeExprContext(&mergestate->jstate);
/* ----------------
* shut down the subplans
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.16 2000/06/15 04:09:52 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.17 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*
* It scans the inner relation to join with current outer tuple.
*
- * If none is found, next tuple form the outer relation is retrieved
+ * If none is found, next tuple from the outer relation is retrieved
* and the inner relation is scanned from the beginning again to join
* with the outer tuple.
*
- * Nil is returned if all the remaining outer tuples are tried and
+ * NULL is returned if all the remaining outer tuples are tried and
* all fail to join with the inner tuples.
*
- * Nil is also returned if there is no tuple from inner realtion.
+ * NULL is also returned if there is no tuple from inner relation.
*
* Conditions:
* -- outerTuple contains current tuple from outer relation and
- * the right son(inner realtion) maintains "cursor" at the tuple
+ * the right son(inner relation) maintains "cursor" at the tuple
* returned previously.
* This is achieved by maintaining a scan position on the outer
* relation.
Plan *innerPlan;
Plan *outerPlan;
bool needNewOuterTuple;
-
TupleTableSlot *outerTupleSlot;
TupleTableSlot *innerTupleSlot;
-
List *qual;
ExprContext *econtext;
qual = node->join.qual;
outerPlan = outerPlan(&node->join);
innerPlan = innerPlan(&node->join);
-
- /* ----------------
- * initialize expression context
- * ----------------
- */
econtext = nlstate->jstate.cs_ExprContext;
/* ----------------
econtext->ecxt_outertuple = outerTupleSlot;
/* ----------------
- * Ok, everything is setup for the join so now loop until
- * we return a qualifying join tuple..
+ * Reset per-tuple memory context to free any expression evaluation
+ * storage allocated in the previous tuple cycle.
* ----------------
*/
+ ResetExprContext(econtext);
+ /* ----------------
+ * Check to see if we're still projecting out tuples from a previous
+ * join tuple (because there is a function-returning-set in the
+ * projection expressions). If so, try to project another one.
+ * ----------------
+ */
if (nlstate->jstate.cs_TupFromTlist)
{
TupleTableSlot *result;
result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
if (!isDone)
return result;
+ /* Done with that source tuple... */
+ nlstate->jstate.cs_TupFromTlist = false;
}
+ /* ----------------
+ * Ok, everything is setup for the join so now loop until
+ * we return a qualifying join tuple..
+ * ----------------
+ */
ENL1_printf("entering main loop");
+
for (;;)
{
/* ----------------
* and join it with the current outer tuple.
* ----------------
*/
- needNewOuterTuple = false;
-
- if (!TupIsNull(outerTupleSlot))
- ENL1_printf("have outer tuple, deal with it");
- else
- {
- ENL1_printf("outer tuple is nil, need new outer tuple");
- needNewOuterTuple = true;
- }
+ needNewOuterTuple = TupIsNull(outerTupleSlot);
/* ----------------
* if we have an outerTuple, try to get the next inner tuple.
}
/* ----------------
- * qualification failed so we have to try again..
+ * Tuple fails qual, so free per-tuple memory and try again.
* ----------------
*/
+ ResetExprContext(econtext);
+
ENL1_printf("qualification failed, looping");
}
}
* ----------------
*/
nlstate = makeNode(NestLoopState);
- nlstate->nl_PortalFlag = false;
node->nlstate = nlstate;
/* ----------------
- * Miscellanious initialization
+ * Miscellaneous initialization
*
- * + assign node's base_id
- * + assign debugging hooks and
* + create expression context for node
* ----------------
*/
- ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent);
ExecAssignExprContext(estate, &nlstate->jstate);
#define NESTLOOP_NSLOTS 1
* ----------------
*/
ExecFreeProjectionInfo(&nlstate->jstate);
+ ExecFreeExprContext(&nlstate->jstate);
/* ----------------
* close down subplans
if (outerPlan->chgParam == NULL)
ExecReScan(outerPlan, exprCtxt, (Plan *) node);
- /* let outerPlan to free its result typle ... */
+ /* let outerPlan to free its result tuple ... */
nlstate->jstate.cs_OuterTupleSlot = NULL;
nlstate->jstate.cs_TupFromTlist = false;
-
- return;
}
* nodeResult.c
* support for constant nodes needing special code.
*
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
* DESCRIPTION
*
- * Example: in constant queries where no relations are scanned,
- * the planner generates result nodes. Examples of such queries are:
+ * Result nodes are used in queries where no relations are scanned.
+ * Examples of such queries are:
*
* retrieve (x = 1)
* and
* append emp (name = "mike", salary = 15000)
*
- * Result nodes are also used to optimise queries
- * with tautological qualifications like:
+ * Result nodes are also used to optimise queries with constant
+ * qualifications (ie, quals that do not depend on the scanned data),
+ * such as:
*
* retrieve (emp.all) where 2 > 1
*
* /
* SeqScan (emp.all)
*
+ * At runtime, the Result node evaluates the constant qual once.
+ * If it's false, we can return an empty result set without running
+ * the controlled plan at all. If it's true, we run the controlled
+ * plan normally and pass back the results.
+ *
+ *
+ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.13 2000/01/26 05:56:23 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.14 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
-#include "postgres.h"
+#include "postgres.h"
#include "executor/executor.h"
#include "executor/nodeResult.h"
/* ----------------------------------------------------------------
* ExecResult(node)
*
- * returns the tuples from the outer plan which satisify the
+ * returns the tuples from the outer plan which satisfy the
* qualification clause. Since result nodes with right
* subtrees are never planned, we ignore the right subtree
* entirely (for now).. -cim 10/7/89
* ----------------
*/
resstate = node->resstate;
+ econtext = resstate->cstate.cs_ExprContext;
/* ----------------
- * get the expression context
+ * Reset per-tuple memory context to free any expression evaluation
+ * storage allocated in the previous tuple cycle.
* ----------------
*/
- econtext = resstate->cstate.cs_ExprContext;
+ ResetExprContext(econtext);
/* ----------------
- * check tautological qualifications like (2 > 1)
+ * check constant qualifications like (2 > 1), if not already done
* ----------------
*/
if (resstate->rs_checkqual)
}
}
+ /* ----------------
+ * Check to see if we're still projecting out tuples from a previous
+ * scan tuple (because there is a function-returning-set in the
+ * projection expressions). If so, try to project another one.
+ * ----------------
+ */
if (resstate->cstate.cs_TupFromTlist)
{
- ProjectionInfo *projInfo;
-
- projInfo = resstate->cstate.cs_ProjInfo;
- resultSlot = ExecProject(projInfo, &isDone);
+ resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone);
if (!isDone)
return resultSlot;
+ /* Done with that source tuple... */
+ resstate->cstate.cs_TupFromTlist = false;
}
/* ----------------
- * retrieve a tuple that satisfy the qual from the outer plan until
- * there are no more.
- *
- * if rs_done is 1 then it means that we were asked to return
- * a constant tuple and we alread did the last time ExecResult()
- * was called, so now we are through.
+ * if rs_done is true then it means that we were asked to return
+ * a constant tuple and we already did the last time ExecResult()
+ * was called, OR that we failed the constant qual check.
+ * Either way, now we are through.
* ----------------
*/
- outerPlan = outerPlan(node);
-
- while (!resstate->rs_done)
+ if (!resstate->rs_done)
{
+ outerPlan = outerPlan(node);
- /* ----------------
- * get next outer tuple if necessary.
- * ----------------
- */
if (outerPlan != NULL)
{
+ /* ----------------
+ * retrieve tuples from the outer plan until there are no more.
+ * ----------------
+ */
outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
if (TupIsNull(outerTupleSlot))
return NULL;
resstate->cstate.cs_OuterTupleSlot = outerTupleSlot;
+
+ /* ----------------
+ * XXX gross hack. use outer tuple as scan tuple for projection
+ * ----------------
+ */
+ econtext->ecxt_outertuple = outerTupleSlot;
+ econtext->ecxt_scantuple = outerTupleSlot;
}
else
{
-
/* ----------------
- * if we don't have an outer plan, then it's probably
- * the case that we are doing a retrieve or an append
- * with a constant target list, so we should only return
- * the constant tuple once or never if we fail the qual.
+ * if we don't have an outer plan, then we are just generating
+ * the results from a constant target list. Do it only once.
* ----------------
*/
- resstate->rs_done = 1;
+ resstate->rs_done = true;
}
/* ----------------
- * get the information to place into the expr context
- * ----------------
- */
- resstate = node->resstate;
-
- outerTupleSlot = resstate->cstate.cs_OuterTupleSlot;
-
- /* ----------------
- * fill in the information in the expression context
- * XXX gross hack. use outer tuple as scan tuple
- * ----------------
- */
- econtext->ecxt_outertuple = outerTupleSlot;
- econtext->ecxt_scantuple = outerTupleSlot;
-
- /* ----------------
- * form the result tuple and pass it back using ExecProject()
+ * form the result tuple using ExecProject(), and return it.
* ----------------
*/
projInfo = resstate->cstate.cs_ProjInfo;
node->resstate = resstate;
/* ----------------
- * Miscellanious initialization
+ * Miscellaneous initialization
*
- * + assign node's base_id
- * + assign debugging hooks and
* + create expression context for node
* ----------------
*/
- ExecAssignNodeBaseInfo(estate, &resstate->cstate, parent);
ExecAssignExprContext(estate, &resstate->cstate);
#define RESULT_NSLOTS 1
/* ----------------------------------------------------------------
* ExecEndResult
*
- * fees up storage allocated through C routines
+ * frees up storage allocated through C routines
* ----------------------------------------------------------------
*/
void
* is freed at end-transaction time. -cim 6/2/91
* ----------------
*/
- ExecFreeExprContext(&resstate->cstate); /* XXX - new for us - er1p */
- ExecFreeTypeInfo(&resstate->cstate); /* XXX - new for us - er1p */
ExecFreeProjectionInfo(&resstate->cstate);
+ ExecFreeExprContext(&resstate->cstate);
/* ----------------
* shut down subplans
if (((Plan *) node)->lefttree &&
((Plan *) node)->lefttree->chgParam == NULL)
ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.23 2000/06/15 04:09:52 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.24 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "parser/parsetree.h"
static Oid InitScanRelation(SeqScan *node, EState *estate,
- CommonScanState *scanstate, Plan *outerPlan);
-
+ CommonScanState *scanstate);
static TupleTableSlot *SeqNext(SeqScan *node);
/* ----------------------------------------------------------------
TupleTableSlot *
ExecSeqScan(SeqScan *node)
{
- TupleTableSlot *slot;
- Plan *outerPlan;
-
- S_printf("ExecSeqScan: scanning node: ");
- S_nodeDisplay(node);
-
/* ----------------
- * if there is an outer subplan, get a tuple from it
- * else, scan the relation
+ * use SeqNext as access method
* ----------------
*/
- if ((outerPlan = outerPlan((Plan *) node)) != NULL)
- slot = ExecProcNode(outerPlan, (Plan *) node);
- else
- slot = ExecScan(node, SeqNext);
-
- S1_printf("ExecSeqScan: returned tuple slot: %d\n", slot);
-
- return slot;
+ return ExecScan(node, (ExecScanAccessMtd) SeqNext);
}
/* ----------------------------------------------------------------
*/
static Oid
InitScanRelation(SeqScan *node, EState *estate,
- CommonScanState *scanstate, Plan *outerPlan)
+ CommonScanState *scanstate)
{
Index relid;
List *rangeTable;
HeapScanDesc currentScanDesc;
RelationInfo *resultRelationInfo;
- if (outerPlan == NULL)
- {
- /* ----------------
- * if the outer node is nil then we are doing a simple
- * sequential scan of a relation...
- *
- * get the relation object id from the relid'th entry
- * in the range table, open that relation and initialize
- * the scan state...
- * ----------------
- */
- relid = node->scanrelid;
- rangeTable = estate->es_range_table;
- rtentry = rt_fetch(relid, rangeTable);
- reloid = rtentry->relid;
- direction = estate->es_direction;
- resultRelationInfo = estate->es_result_relation_info;
-
- ExecOpenScanR(reloid, /* relation */
- 0, /* nkeys */
- NULL, /* scan key */
- 0, /* is index */
- direction,/* scan direction */
- estate->es_snapshot,
- ¤tRelation, /* return: rel desc */
- (Pointer *) ¤tScanDesc); /* return: scan desc */
-
- scanstate->css_currentRelation = currentRelation;
- scanstate->css_currentScanDesc = currentScanDesc;
-
- ExecAssignScanType(scanstate,
- RelationGetDescr(currentRelation));
- }
- else
- {
- /* ----------------
- * otherwise we are scanning tuples from the
- * outer subplan so we initialize the outer plan
- * and nullify
- * ----------------
- */
- ExecInitNode(outerPlan, estate, (Plan *) node);
-
- node->scanrelid = 0;
- scanstate->css_currentRelation = NULL;
- scanstate->css_currentScanDesc = NULL;
- ExecAssignScanType(scanstate, NULL);
- reloid = InvalidOid;
- }
-
/* ----------------
- * return the relation
+ * get the relation object id from the relid'th entry
+ * in the range table, open that relation and initialize
+ * the scan state...
* ----------------
*/
+ relid = node->scanrelid;
+ rangeTable = estate->es_range_table;
+ rtentry = rt_fetch(relid, rangeTable);
+ reloid = rtentry->relid;
+ direction = estate->es_direction;
+ resultRelationInfo = estate->es_result_relation_info;
+
+ ExecOpenScanR(reloid, /* relation */
+ 0, /* nkeys */
+ NULL, /* scan key */
+ 0, /* is index */
+ direction, /* scan direction */
+ estate->es_snapshot,
+ ¤tRelation, /* return: rel desc */
+ (Pointer *) ¤tScanDesc); /* return: scan desc */
+
+ scanstate->css_currentRelation = currentRelation;
+ scanstate->css_currentScanDesc = currentScanDesc;
+
+ ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
+
return reloid;
}
/* ----------------------------------------------------------------
* ExecInitSeqScan
- *
- * old comments
- * Creates the run-time state information for the seqscan node
- * and sets the relation id to contain relevant descriptors.
- *
- * If there is a outer subtree (sort), the outer subtree
- * is initialized and the relation id is set to the descriptors
- * returned by the subtree.
* ----------------------------------------------------------------
*/
bool
ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
{
CommonScanState *scanstate;
- Plan *outerPlan;
Oid reloid;
HeapScanDesc scandesc;
+ /* ----------------
+ * Once upon a time it was possible to have an outerPlan of a SeqScan,
+ * but not any more.
+ * ----------------
+ */
+ Assert(outerPlan((Plan *) node) == NULL);
+ Assert(innerPlan((Plan *) node) == NULL);
+
/* ----------------
* assign the node's execution state
* ----------------
node->scanstate = scanstate;
/* ----------------
- * Miscellanious initialization
+ * Miscellaneous initialization
*
- * + assign node's base_id
* + create expression context for node
* ----------------
*/
- ExecAssignNodeBaseInfo(estate, &scanstate->cstate, parent);
ExecAssignExprContext(estate, &scanstate->cstate);
#define SEQSCAN_NSLOTS 3
ExecInitScanTupleSlot(estate, scanstate);
/* ----------------
- * initialize scan relation or outer subplan
+ * initialize scan relation
* ----------------
*/
- outerPlan = outerPlan((Plan *) node);
-
- reloid = InitScanRelation(node, estate, scanstate, outerPlan);
+ reloid = InitScanRelation(node, estate, scanstate);
scandesc = scanstate->css_currentScanDesc;
scanstate->cstate.cs_TupFromTlist = false;
* ExecEndSeqScan
*
* frees any storage allocated through C routines.
- *| ...and also closes relations and/or shuts down outer subplan
- *| -cim 8/14/89
* ----------------------------------------------------------------
*/
void
ExecEndSeqScan(SeqScan *node)
{
CommonScanState *scanstate;
- Plan *outerPlan;
/* ----------------
* get information from node
* ----------------
*/
ExecFreeProjectionInfo(&scanstate->cstate);
+ ExecFreeExprContext(&scanstate->cstate);
/* ----------------
* close scan relation
*/
ExecCloseR((Plan *) node);
- /* ----------------
- * clean up outer subtree (does nothing if there is no outerPlan)
- * ----------------
- */
- outerPlan = outerPlan((Plan *) node);
- ExecEndNode(outerPlan, (Plan *) node);
-
/* ----------------
* clean out the tuple table
* ----------------
* Join Support
* ----------------------------------------------------------------
*/
+
/* ----------------------------------------------------------------
* ExecSeqReScan
*
{
CommonScanState *scanstate;
EState *estate;
- Plan *outerPlan;
Relation rel;
HeapScanDesc scan;
ScanDirection direction;
scanstate = node->scanstate;
estate = node->plan.state;
- if ((outerPlan = outerPlan((Plan *) node)) != NULL)
- {
- /* we are scanning a subplan */
- outerPlan = outerPlan((Plan *) node);
- ExecReScan(outerPlan, exprCtxt, parent);
- }
- else
-/* otherwise, we are scanning a relation */
+ /* If this is re-scanning of PlanQual ... */
+ if (estate->es_evTuple != NULL &&
+ estate->es_evTuple[node->scanrelid - 1] != NULL)
{
- /* If this is re-scanning of PlanQual ... */
- if (estate->es_evTuple != NULL &&
- estate->es_evTuple[node->scanrelid - 1] != NULL)
- {
- estate->es_evTupleNull[node->scanrelid - 1] = false;
- return;
- }
- rel = scanstate->css_currentRelation;
- scan = scanstate->css_currentScanDesc;
- direction = estate->es_direction;
- scan = ExecReScanR(rel, scan, direction, 0, NULL);
- scanstate->css_currentScanDesc = scan;
+ estate->es_evTupleNull[node->scanrelid - 1] = false;
+ return;
}
+ rel = scanstate->css_currentRelation;
+ scan = scanstate->css_currentScanDesc;
+ direction = estate->es_direction;
+ scan = ExecReScanR(rel, scan, direction, 0, NULL);
+ scanstate->css_currentScanDesc = scan;
}
/* ----------------------------------------------------------------
ExecSeqMarkPos(SeqScan *node)
{
CommonScanState *scanstate;
- Plan *outerPlan;
HeapScanDesc scan;
scanstate = node->scanstate;
-
- /* ----------------
- * if we are scanning a subplan then propagate
- * the ExecMarkPos() request to the subplan
- * ----------------
- */
- outerPlan = outerPlan((Plan *) node);
- if (outerPlan)
- {
- ExecMarkPos(outerPlan);
- return;
- }
-
- /* ----------------
- * otherwise we are scanning a relation so mark the
- * position using the access methods..
- *
- * ----------------
- */
scan = scanstate->css_currentScanDesc;
heap_markpos(scan);
-
- return;
}
/* ----------------------------------------------------------------
ExecSeqRestrPos(SeqScan *node)
{
CommonScanState *scanstate;
- Plan *outerPlan;
HeapScanDesc scan;
scanstate = node->scanstate;
-
- /* ----------------
- * if we are scanning a subplan then propagate
- * the ExecRestrPos() request to the subplan
- * ----------------
- */
- outerPlan = outerPlan((Plan *) node);
- if (outerPlan)
- {
- ExecRestrPos(outerPlan);
- return;
- }
-
- /* ----------------
- * otherwise we are scanning a relation so restore the
- * position using the access methods..
- * ----------------
- */
scan = scanstate->css_currentScanDesc;
heap_restrpos(scan);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.28 2000/07/09 04:17:53 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.29 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* ----------------
* Miscellaneous initialization
*
- * + assign node's base_id
- * + assign debugging hooks
- *
* Sort nodes don't initialize their ExprContexts because
- * they never call ExecQual or ExecTargetList.
+ * they never call ExecQual or ExecProject.
* ----------------
*/
- ExecAssignNodeBaseInfo(estate, &sortstate->csstate.cstate, parent);
#define SORT_NSLOTS 1
/* ----------------
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.25 2000/04/12 17:15:10 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.26 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
SubLink *sublink = node->sublink;
SubLinkType subLinkType = sublink->subLinkType;
bool useor = sublink->useor;
+ MemoryContext oldcontext;
TupleTableSlot *slot;
Datum result;
+ bool isDone;
bool found = false; /* TRUE if got at least one subplan tuple */
List *lst;
+ /*
+ * We are probably in a short-lived expression-evaluation context.
+ * Switch to longer-lived per-query context.
+ */
+ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
if (node->setParam != NIL)
elog(ERROR, "ExecSubPlan: can't set parent params from subquery");
{
foreach(lst, node->parParam)
{
- ParamExecData *prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
+ ParamExecData *prm;
+ prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
Assert(pvar != NIL);
- prm->value = ExecEvalExpr((Node *) lfirst(pvar),
- econtext,
- &(prm->isnull), NULL);
+ prm->value = ExecEvalExprSwitchContext((Node *) lfirst(pvar),
+ econtext,
+ &(prm->isnull),
+ &isDone);
+ if (!isDone)
+ elog(ERROR, "ExecSubPlan: set values not supported for params");
pvar = lnext(pvar);
}
plan->chgParam = nconc(plan->chgParam, listCopy(node->parParam));
* return NULL. Assuming we get a tuple, we just return its first
* column (there can be only one non-junk column in this case).
*/
- result = (Datum) (subLinkType == ALL_SUBLINK ? true : false);
+ result = BoolGetDatum(subLinkType == ALL_SUBLINK);
*isNull = false;
for (slot = ExecProcNode(plan, plan);
{
HeapTuple tup = slot->val;
TupleDesc tdesc = slot->ttc_tupleDescriptor;
- Datum rowresult = (Datum) (useor ? false : true);
+ Datum rowresult = BoolGetDatum(! useor);
bool rownull = false;
int col = 1;
if (subLinkType == EXISTS_SUBLINK)
- return (Datum) true;
+ {
+ found = true;
+ result = BoolGetDatum(true);
+ break;
+ }
if (subLinkType == EXPR_SUBLINK)
{
/*
* Now we can eval the combining operator for this column.
*/
- expresult = ExecEvalExpr((Node *) expr, econtext, &expnull,
- (bool *) NULL);
+ expresult = ExecEvalExprSwitchContext((Node *) expr, econtext,
+ &expnull, &isDone);
+ if (!isDone)
+ elog(ERROR, "ExecSubPlan: set values not supported for combining operators");
/*
* Combine the result into the row result as appropriate.
/* combine within row per OR semantics */
if (expnull)
rownull = true;
- else if (DatumGetInt32(expresult) != 0)
+ else if (DatumGetBool(expresult))
{
- rowresult = (Datum) true;
+ rowresult = BoolGetDatum(true);
rownull = false;
break; /* needn't look at any more columns */
}
/* combine within row per AND semantics */
if (expnull)
rownull = true;
- else if (DatumGetInt32(expresult) == 0)
+ else if (! DatumGetBool(expresult))
{
- rowresult = (Datum) false;
+ rowresult = BoolGetDatum(false);
rownull = false;
break; /* needn't look at any more columns */
}
/* combine across rows per OR semantics */
if (rownull)
*isNull = true;
- else if (DatumGetInt32(rowresult) != 0)
+ else if (DatumGetBool(rowresult))
{
- result = (Datum) true;
+ result = BoolGetDatum(true);
*isNull = false;
break; /* needn't look at any more rows */
}
/* combine across rows per AND semantics */
if (rownull)
*isNull = true;
- else if (DatumGetInt32(rowresult) == 0)
+ else if (! DatumGetBool(rowresult))
{
- result = (Datum) false;
+ result = BoolGetDatum(false);
*isNull = false;
break; /* needn't look at any more rows */
}
*/
if (subLinkType == EXPR_SUBLINK || subLinkType == MULTIEXPR_SUBLINK)
{
- result = (Datum) false;
+ result = (Datum) 0;
*isNull = true;
}
}
+ MemoryContextSwitchTo(oldcontext);
+
return result;
}
ExecCreateTupleTable(ExecCountSlotsNode(node->plan) + 10);
sp_estate->es_snapshot = estate->es_snapshot;
- node->shutdown = false;
+ node->needShutdown = false;
node->curTuple = NULL;
if (!ExecInitNode(node->plan, sp_estate, NULL))
return false;
- node->shutdown = true; /* now we need to shutdown the subplan */
+ node->needShutdown = true; /* now we need to shutdown the subplan */
/*
* If this plan is un-correlated or undirect correlated one and want
* ----------------------------------------------------------------
*/
void
-ExecSetParamPlan(SubPlan *node)
+ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
{
Plan *plan = node->plan;
SubLink *sublink = node->sublink;
+ MemoryContext oldcontext;
TupleTableSlot *slot;
List *lst;
bool found = false;
+ /*
+ * We are probably in a short-lived expression-evaluation context.
+ * Switch to longer-lived per-query context.
+ */
+ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
if (sublink->subLinkType == ANY_SUBLINK ||
sublink->subLinkType == ALL_SUBLINK)
elog(ERROR, "ExecSetParamPlan: ANY/ALL subselect unsupported");
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
prm->execPlan = NULL;
- prm->value = (Datum) true;
+ prm->value = BoolGetDatum(true);
prm->isnull = false;
found = true;
break;
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
prm->execPlan = NULL;
- prm->value = (Datum) false;
+ prm->value = BoolGetDatum(false);
prm->isnull = false;
}
else
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
prm->execPlan = NULL;
- prm->value = (Datum) NULL;
+ prm->value = (Datum) 0;
prm->isnull = true;
}
}
}
+ MemoryContextSwitchTo(oldcontext);
+
if (plan->extParam == NULL) /* un-correlated ... */
{
ExecEndNode(plan, plan);
- node->shutdown = false;
+ node->needShutdown = false;
}
}
void
ExecEndSubPlan(SubPlan *node)
{
- if (node->shutdown)
+ if (node->needShutdown)
{
ExecEndNode(node->plan, node->plan);
- node->shutdown = false;
+ node->needShutdown = false;
}
if (node->curTuple)
{
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.9 2000/06/15 04:09:52 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.10 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List *lst;
ItemPointer itemptr;
bool isNull;
+ bool isDone;
int numTids = 0;
foreach(lst, evalList)
{
- itemptr = (ItemPointer) ExecEvalExpr(lfirst(lst), econtext,
- &isNull, (bool *) 0);
+ itemptr = (ItemPointer)
+ DatumGetPointer(ExecEvalExprSwitchContext(lfirst(lst),
+ econtext,
+ &isNull,
+ &isDone));
if (itemptr && ItemPointerIsValid(itemptr))
{
tidList[numTids] = itemptr;
* use TidNext as access method
* ----------------
*/
- return ExecScan(&node->scan, TidNext);
+ return ExecScan(&node->scan, (ExecScanAccessMtd) TidNext);
}
/* ----------------------------------------------------------------
* ----------------
*/
ExecFreeProjectionInfo(&scanstate->cstate);
+ ExecFreeExprContext(&scanstate->cstate);
/* ----------------
* close the heap and tid relations
*/
ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
ExecClearTuple(scanstate->css_ScanTupleSlot);
-/* ExecClearTuple(scanstate->css_RawTupleSlot); */
}
/* ----------------------------------------------------------------
RangeTblEntry *rtentry;
Oid relid;
Oid reloid;
-
Relation currentRelation;
- int baseid;
-
- List *execParam = NULL;
+ List *execParam = NIL;
/* ----------------
* assign execution state to node
* --------------------------------
*/
scanstate = makeNode(CommonScanState);
-/*
- scanstate->ss_ProcOuterFlag = false;
- scanstate->ss_OldRelId = 0;
-*/
-
node->scan.scanstate = scanstate;
/* ----------------
- * assign node's base_id .. we don't use AssignNodeBaseid() because
- * the increment is done later on after we assign the tid scan's
- * scanstate. see below.
- * ----------------
- */
- baseid = estate->es_BaseId;
-/* scanstate->csstate.cstate.bnode.base_id = baseid; */
- scanstate->cstate.cs_base_id = baseid;
-
- /* ----------------
- * create expression context for node
+ * Miscellaneous initialization
+ *
+ * + create expression context for node
* ----------------
*/
ExecAssignExprContext(estate, &scanstate->cstate);
*/
ExecInitResultTupleSlot(estate, &scanstate->cstate);
ExecInitScanTupleSlot(estate, scanstate);
-/* ExecInitRawTupleSlot(estate, scanstate); */
/* ----------------
* initialize projection info. result type comes from scan desc
tidstate = makeNode(TidScanState);
node->tidstate = tidstate;
- /* ----------------
- * assign base id to tid scan state also
- * ----------------
- */
- tidstate->cstate.cs_base_id = baseid;
- baseid++;
- estate->es_BaseId = baseid;
-
/* ----------------
* get the tid node information
* ----------------
ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
- /* ----------------
- * tid scans don't have subtrees..
- * ----------------
- */
-/* scanstate->ss_ProcOuterFlag = false; */
-
- tidstate->cstate.cs_TupFromTlist = false;
-
/*
* if there are some PARAM_EXEC in skankeys then force tid rescan on
* first scan.
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.29 2000/05/30 00:49:45 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.30 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (!execTuplesMatch(slot->val, uniquestate->priorTuple,
tupDesc,
node->numCols, node->uniqColIdx,
- uniquestate->eqfunctions))
+ uniquestate->eqfunctions,
+ uniquestate->tempContext))
break;
}
/* ----------------
* We have a new tuple different from the previous saved tuple (if any).
- * Save it and return it. Note that we make two copies of the tuple:
- * one to keep for our own future comparisons, and one to return to the
- * caller. We need to copy the tuple returned by the subplan to avoid
- * holding buffer refcounts, and we need our own copy because the caller
- * may alter the resultTupleSlot (eg via ExecRemoveJunk).
+ * Save it and return it. We must copy it because the source subplan
+ * won't guarantee that this source tuple is still accessible after
+ * fetching the next source tuple.
+ *
+ * Note that we manage the copy ourselves. We can't rely on the result
+ * tuple slot to maintain the tuple reference because our caller may
+ * replace the slot contents with a different tuple (see junk filter
+ * handling in execMain.c). We assume that the caller will no longer
+ * be interested in the current tuple after he next calls us.
* ----------------
*/
if (uniquestate->priorTuple != NULL)
heap_freetuple(uniquestate->priorTuple);
uniquestate->priorTuple = heap_copytuple(slot->val);
- ExecStoreTuple(heap_copytuple(slot->val),
+ ExecStoreTuple(uniquestate->priorTuple,
resultTupleSlot,
InvalidBuffer,
- true);
+ false); /* tuple does not belong to slot */
return resultTupleSlot;
}
/* ----------------
* Miscellaneous initialization
*
- * + assign node's base_id
- * + assign debugging hooks and
- *
* Unique nodes have no ExprContext initialization because
- * they never call ExecQual or ExecTargetList.
+ * they never call ExecQual or ExecProject. But they do need a
+ * per-tuple memory context anyway for calling execTuplesMatch.
* ----------------
*/
- ExecAssignNodeBaseInfo(estate, &uniquestate->cstate, parent);
+ uniquestate->tempContext =
+ AllocSetContextCreate(CurrentMemoryContext,
+ "Unique",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
#define UNIQUE_NSLOTS 1
/* ------------
ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
+ MemoryContextDelete(uniquestate->tempContext);
+
/* clean up tuple table */
ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot);
if (uniquestate->priorTuple != NULL)
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.115 2000/06/29 07:35:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.116 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Node_Copy(from, newnode, sublink);
/* do not copy execution state */
- newnode->shutdown = false;
+ newnode->needShutdown = false;
newnode->curTuple = NULL;
return newnode;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.67 2000/06/29 07:35:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.68 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
if (a->constisnull)
return true;
- return (datumIsEqual(a->constvalue, b->constvalue,
- a->consttype, a->constbyval, a->constlen));
+ return datumIsEqual(a->constvalue, b->constvalue,
+ a->constbyval, a->constlen);
}
static bool
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.120 2000/06/18 22:44:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.121 2000/07/12 02:37:06 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
static void
_outDatum(StringInfo str, Datum value, Oid type)
{
- char *s;
- Size length,
- typeLength;
bool byValue;
+ int typeLength;
+ Size length;
+ char *s;
int i;
/*
*/
byValue = get_typbyval(type);
typeLength = get_typlen(type);
- length = datumGetSize(value, type, byValue, typeLength);
+ length = datumGetSize(value, byValue, typeLength);
if (byValue)
{
s = (char *) (&value);
- appendStringInfo(str, " %d [ ", length);
+ appendStringInfo(str, " %u [ ", (unsigned int) length);
for (i = 0; i < (int) sizeof(Datum); i++)
appendStringInfo(str, "%d ", (int) (s[i]));
appendStringInfo(str, "] ");
appendStringInfo(str, " 0 [ ] ");
else
{
-
- /*
- * length is unsigned - very bad to do < comparison to -1
- * without casting it to int first!! -mer 8 Jan 1991
- */
- if (((int) length) <= -1)
- length = VARSIZE(s);
- appendStringInfo(str, " %d [ ", length);
+ appendStringInfo(str, " %u [ ", (unsigned int) length);
for (i = 0; i < (int) length; i++)
appendStringInfo(str, "%d ", (int) (s[i]));
appendStringInfo(str, "] ");
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.91 2000/06/18 22:44:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.92 2000/07/12 02:37:06 tgl Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
_getPlan((Plan *) local_node);
token = lsptok(NULL, &length); /* eat :hashkey */
- local_node->hashkey = (Var *) nodeRead(true);
+ local_node->hashkey = nodeRead(true);
return local_node;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.93 2000/06/18 22:44:07 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.94 2000/07/12 02:37:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Plan *righttree);
static HashJoin *make_hashjoin(List *tlist, List *qpqual,
List *hashclauses, Plan *lefttree, Plan *righttree);
-static Hash *make_hash(List *tlist, Var *hashkey, Plan *lefttree);
+static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree);
static MergeJoin *make_mergejoin(List *tlist, List *qpqual,
List *mergeclauses, Plan *righttree, Plan *lefttree);
static void copy_path_costsize(Plan *dest, Path *src);
List *hashclauses;
HashJoin *join_node;
Hash *hash_node;
- Var *innerhashkey;
+ Node *innerhashkey;
/*
* NOTE: there will always be exactly one hashclause in the list
(Index) 0));
/* Now the righthand op of the sole hashclause is the inner hash key. */
- innerhashkey = get_rightop(lfirst(hashclauses));
+ innerhashkey = (Node *) get_rightop(lfirst(hashclauses));
/*
* Build the hash node and hash join node.
}
static Hash *
-make_hash(List *tlist, Var *hashkey, Plan *lefttree)
+make_hash(List *tlist, Node *hashkey, Plan *lefttree)
{
Hash *node = makeNode(Hash);
Plan *plan = &node->plan;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.38 2000/06/18 22:44:09 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.39 2000/07/12 02:37:09 tgl Exp $
*
*-------------------------------------------------------------------------
*/
break;
case T_Hash:
- finalize_primnode((Node *) ((Hash *) plan)->hashkey,
+ finalize_primnode(((Hash *) plan)->hashkey,
&results);
break;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.68 2000/05/30 00:49:49 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.69 2000/07/12 02:37:11 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
#include "optimizer/var.h"
#include "parser/parse_type.h"
#include "parser/parsetree.h"
+#include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
HeapTuple func_tuple;
Form_pg_proc funcform;
Type resultType;
+ bool resultTypByVal;
+ int resultTypLen;
Expr *newexpr;
+ ExprContext *econtext;
Datum const_val;
bool has_nonconst_input = false;
bool has_null_input = false;
newexpr->oper = expr->oper;
newexpr->args = args;
+ /* Get info needed about result datatype */
+ resultType = typeidType(result_typeid);
+ resultTypByVal = typeByVal(resultType);
+ resultTypLen = typeLen(resultType);
+
/*
- * It is OK to pass econtext = NULL because none of the ExecEvalExpr()
+ * It is OK to pass a dummy econtext because none of the ExecEvalExpr()
* code used in this situation will use econtext. That might seem
* fortuitous, but it's not so unreasonable --- a constant expression
* does not depend on context, by definition, n'est ce pas?
*/
- const_val = ExecEvalExpr((Node *) newexpr, NULL,
- &const_is_null, &isDone);
+ econtext = MakeExprContext(NULL, CurrentMemoryContext);
+
+ const_val = ExecEvalExprSwitchContext((Node *) newexpr, econtext,
+ &const_is_null, &isDone);
Assert(isDone); /* if this isn't set, we blew it... */
+
+ /* Must copy result out of sub-context used by expression eval */
+ const_val = datumCopy(const_val, resultTypByVal, resultTypLen);
+
+ FreeExprContext(econtext);
pfree(newexpr);
/*
* Make the constant result node.
*/
- resultType = typeidType(result_typeid);
- return (Expr *) makeConst(result_typeid, typeLen(resultType),
+ return (Expr *) makeConst(result_typeid, resultTypLen,
const_val, const_is_null,
- typeByVal(resultType),
- false, false);
+ resultTypByVal, false, false);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.35 2000/06/28 03:32:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.36 2000/07/12 02:37:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
state->es_param_list_info = NULL;
state->es_param_exec_vals = NULL;
- state->es_BaseId = 0;
state->es_tupleTable = NULL;
state->es_junkFilter = NULL;
/*-------------------------------------------------------------------------
*
* datum.c
+ * POSTGRES Datum (abstract data type) manipulation routines.
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/datum.c,v 1.17 2000/01/26 05:57:13 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/datum.c,v 1.18 2000/07/12 02:37:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*
* A) if a type is "byVal" then all the information is stored in the
* Datum itself (i.e. no pointers involved!). In this case the
- * length of the type is always greater than zero and less than
+ * length of the type is always greater than zero and not more than
* "sizeof(Datum)"
* B) if a type is not "byVal" and it has a fixed length, then
* the "Datum" always contain a pointer to a stream of bytes.
* This varlena structure has information about the actual length of this
* particular instance of the type and about its value.
*
+ * Note that we do not treat "toasted" datums specially; therefore what
+ * will be copied or compared is the compressed data or toast reference.
*/
+
#include "postgres.h"
+
#include "utils/datum.h"
/*-------------------------------------------------------------------------
* datumGetSize
*
* Find the "real" size of a datum, given the datum value,
- * its type, whether it is a "by value", and its length.
+ * whether it is a "by value", and its length.
*
* To cut a long story short, usually the real size is equal to the
* type length, with the exception of variable length types which have
*-------------------------------------------------------------------------
*/
Size
-datumGetSize(Datum value, Oid type, bool byVal, Size len)
+datumGetSize(Datum value, bool typByVal, int typLen)
{
+ Size size;
- struct varlena *s;
- Size size = 0;
-
- if (byVal)
+ if (typByVal)
{
- if (len <= sizeof(Datum))
- size = len;
- else
- {
- elog(ERROR,
- "datumGetSize: Error: type=%ld, byVaL with len=%d",
- (long) type, len);
- }
+ /* Pass-by-value types are always fixed-length */
+ Assert(typLen > 0 && typLen <= sizeof(Datum));
+ size = (Size) typLen;
}
else
- { /* not byValue */
- if (len == -1)
+ {
+ if (typLen == -1)
{
+ /* Assume it is a varlena datatype */
+ struct varlena *s = (struct varlena *) DatumGetPointer(value);
- /*
- * variable length type Look at the varlena struct for its
- * real length...
- */
- s = (struct varlena *) DatumGetPointer(value);
if (!PointerIsValid(s))
- {
- elog(ERROR,
- "datumGetSize: Invalid Datum Pointer");
- }
+ elog(ERROR, "datumGetSize: Invalid Datum Pointer");
size = (Size) VARSIZE(s);
}
else
{
-
- /*
- * fixed length type
- */
- size = len;
+ /* Fixed-length pass-by-ref type */
+ size = (Size) typLen;
}
}
*
* make a copy of a datum
*
- * If the type of the datum is not passed by value (i.e. "byVal=false")
- * then we assume that the datum contains a pointer and we copy all the
- * bytes pointed by this pointer
+ * If the datatype is pass-by-reference, memory is obtained with palloc().
*-------------------------------------------------------------------------
*/
Datum
-datumCopy(Datum value, Oid type, bool byVal, Size len)
+datumCopy(Datum value, bool typByVal, int typLen)
{
-
- Size realSize;
Datum res;
- char *s;
-
- if (byVal)
+ if (typByVal)
res = value;
else
{
- if (value == 0)
- return (Datum) NULL;
- realSize = datumGetSize(value, type, byVal, len);
+ Size realSize;
+ char *s;
+
+ if (DatumGetPointer(value) == NULL)
+ return PointerGetDatum(NULL);
+
+ realSize = datumGetSize(value, typByVal, typLen);
- /*
- * the value is a pointer. Allocate enough space and copy the
- * pointed data.
- */
s = (char *) palloc(realSize);
- if (s == NULL)
- elog(ERROR, "datumCopy: out of memory\n");
- memmove(s, DatumGetPointer(value), realSize);
- res = (Datum) s;
+ memcpy(s, DatumGetPointer(value), realSize);
+ res = PointerGetDatum(s);
}
return res;
}
*/
#ifdef NOT_USED
void
-datumFree(Datum value, Oid type, bool byVal, Size len)
+datumFree(Datum value, bool typByVal, int typLen)
{
-
- Size realSize;
- Pointer s;
-
- realSize = datumGetSize(value, type, byVal, len);
-
- if (!byVal)
+ if (!typByVal)
{
+ Pointer s = DatumGetPointer(value);
- /*
- * free the space palloced by "datumCopy()"
- */
- s = DatumGetPointer(value);
pfree(s);
}
}
* This routine will return false if there are 2 different
* representations of the same value (something along the lines
* of say the representation of zero in one's complement arithmetic).
- *
+ * Also, it will probably not give the answer you want if either
+ * datum has been "toasted".
*-------------------------------------------------------------------------
*/
bool
-datumIsEqual(Datum value1, Datum value2, Oid type, bool byVal, Size len)
+datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
{
- Size size1,
- size2;
- char *s1,
- *s2;
+ bool res;
- if (byVal)
+ if (typByVal)
{
-
/*
* just compare the two datums. NOTE: just comparing "len" bytes
* will not do the work, because we do not know how these bytes
* are aligned inside the "Datum".
*/
- if (value1 == value2)
- return true;
- else
- return false;
+ res = (value1 == value2);
}
else
{
+ Size size1,
+ size2;
+ char *s1,
+ *s2;
/*
- * byVal = false Compare the bytes pointed by the pointers stored
- * in the datums.
+ * Compare the bytes pointed by the pointers stored in the datums.
*/
- size1 = datumGetSize(value1, type, byVal, len);
- size2 = datumGetSize(value2, type, byVal, len);
+ size1 = datumGetSize(value1, typByVal, typLen);
+ size2 = datumGetSize(value2, typByVal, typLen);
if (size1 != size2)
return false;
s1 = (char *) DatumGetPointer(value1);
s2 = (char *) DatumGetPointer(value2);
- if (!memcmp(s1, s2, size1))
- return true;
- else
- return false;
+ res = (memcmp(s1, s2, size1) == 0);
}
+ return res;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.63 2000/07/06 05:48:11 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.64 2000/07/12 02:37:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* texteq - returns true iff arguments are equal
* textne - returns true iff arguments are not equal
+ *
+ * Note: btree indexes need these routines not to leak memory; therefore,
+ * be careful to free working copies of toasted datums. Most places don't
+ * need to be so careful.
*/
Datum
texteq(PG_FUNCTION_ARGS)
{
text *arg1 = PG_GETARG_TEXT_P(0);
text *arg2 = PG_GETARG_TEXT_P(1);
- int len;
- char *a1p,
- *a2p;
+ bool result;
if (VARSIZE(arg1) != VARSIZE(arg2))
- PG_RETURN_BOOL(false);
+ result = false;
+ else
+ {
+ int len;
+ char *a1p,
+ *a2p;
- len = VARSIZE(arg1) - VARHDRSZ;
+ len = VARSIZE(arg1) - VARHDRSZ;
- a1p = VARDATA(arg1);
- a2p = VARDATA(arg2);
+ a1p = VARDATA(arg1);
+ a2p = VARDATA(arg2);
- PG_RETURN_BOOL(memcmp(a1p, a2p, len) == 0);
+ result = (memcmp(a1p, a2p, len) == 0);
+ }
+
+ PG_FREE_IF_COPY(arg1, 0);
+ PG_FREE_IF_COPY(arg2, 1);
+
+ PG_RETURN_BOOL(result);
}
Datum
{
text *arg1 = PG_GETARG_TEXT_P(0);
text *arg2 = PG_GETARG_TEXT_P(1);
- int len;
- char *a1p,
- *a2p;
+ bool result;
if (VARSIZE(arg1) != VARSIZE(arg2))
- PG_RETURN_BOOL(true);
+ result = true;
+ else
+ {
+ int len;
+ char *a1p,
+ *a2p;
- len = VARSIZE(arg1) - VARHDRSZ;
+ len = VARSIZE(arg1) - VARHDRSZ;
- a1p = VARDATA(arg1);
- a2p = VARDATA(arg2);
+ a1p = VARDATA(arg1);
+ a2p = VARDATA(arg2);
+
+ result = (memcmp(a1p, a2p, len) != 0);
+ }
- PG_RETURN_BOOL(memcmp(a1p, a2p, len) != 0);
+ PG_FREE_IF_COPY(arg1, 0);
+ PG_FREE_IF_COPY(arg2, 1);
+
+ PG_RETURN_BOOL(result);
}
/* varstr_cmp()
/*
* Comparison functions for text strings.
+ *
+ * Note: btree indexes need these routines not to leak memory; therefore,
+ * be careful to free working copies of toasted datums. Most places don't
+ * need to be so careful.
*/
Datum
{
text *arg1 = PG_GETARG_TEXT_P(0);
text *arg2 = PG_GETARG_TEXT_P(1);
+ bool result;
- PG_RETURN_BOOL(text_cmp(arg1, arg2) < 0);
+ result = (text_cmp(arg1, arg2) < 0);
+
+ PG_FREE_IF_COPY(arg1, 0);
+ PG_FREE_IF_COPY(arg2, 1);
+
+ PG_RETURN_BOOL(result);
}
Datum
{
text *arg1 = PG_GETARG_TEXT_P(0);
text *arg2 = PG_GETARG_TEXT_P(1);
+ bool result;
+
+ result = (text_cmp(arg1, arg2) <= 0);
+
+ PG_FREE_IF_COPY(arg1, 0);
+ PG_FREE_IF_COPY(arg2, 1);
- PG_RETURN_BOOL(text_cmp(arg1, arg2) <= 0);
+ PG_RETURN_BOOL(result);
}
Datum
{
text *arg1 = PG_GETARG_TEXT_P(0);
text *arg2 = PG_GETARG_TEXT_P(1);
+ bool result;
- PG_RETURN_BOOL(text_cmp(arg1, arg2) > 0);
+ result = (text_cmp(arg1, arg2) > 0);
+
+ PG_FREE_IF_COPY(arg1, 0);
+ PG_FREE_IF_COPY(arg2, 1);
+
+ PG_RETURN_BOOL(result);
}
Datum
{
text *arg1 = PG_GETARG_TEXT_P(0);
text *arg2 = PG_GETARG_TEXT_P(1);
+ bool result;
+
+ result = (text_cmp(arg1, arg2) >= 0);
+
+ PG_FREE_IF_COPY(arg1, 0);
+ PG_FREE_IF_COPY(arg2, 1);
- PG_RETURN_BOOL(text_cmp(arg1, arg2) >= 0);
+ PG_RETURN_BOOL(result);
}
Datum
text *arg1 = PG_GETARG_TEXT_P(0);
text *arg2 = PG_GETARG_TEXT_P(1);
text *result;
- text *temp;
- temp = ((text_cmp(arg1, arg2) > 0) ? arg1 : arg2);
-
- /* Make a copy --- temporary hack until nodeAgg.c is smarter */
-
- result = (text *) palloc(VARSIZE(temp));
- memcpy((char *) result, (char *) temp, VARSIZE(temp));
+ result = ((text_cmp(arg1, arg2) > 0) ? arg1 : arg2);
PG_RETURN_TEXT_P(result);
}
text *arg1 = PG_GETARG_TEXT_P(0);
text *arg2 = PG_GETARG_TEXT_P(1);
text *result;
- text *temp;
-
- temp = ((text_cmp(arg1, arg2) < 0) ? arg1 : arg2);
-
- /* Make a copy --- temporary hack until nodeAgg.c is smarter */
- result = (text *) palloc(VARSIZE(temp));
- memcpy((char *) result, (char *) temp, VARSIZE(temp));
+ result = ((text_cmp(arg1, arg2) < 0) ? arg1 : arg2);
PG_RETURN_TEXT_P(result);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.33 2000/07/05 23:11:39 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.34 2000/07/12 02:37:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
retval = (FunctionCachePtr) palloc(sizeof(FunctionCache));
MemSet(retval, 0, sizeof(FunctionCache));
+ retval->fcacheCxt = CurrentMemoryContext;
/* ----------------
* get the procedure tuple corresponding to the given functionOid
void
setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext)
{
- Func *fnode;
- Oper *onode;
+ MemoryContext oldcontext;
FunctionCachePtr fcache;
+ /* Switch to a context long-lived enough for the fcache entry */
+ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+
fcache = init_fcache(foid, argList, econtext);
if (IsA(node, Oper))
{
- onode = (Oper *) node;
+ Oper *onode = (Oper *) node;
onode->op_fcache = fcache;
}
else if (IsA(node, Func))
{
- fnode = (Func *) node;
+ Func *fnode = (Func *) node;
fnode->func_fcache = fcache;
}
else
elog(ERROR, "init_fcache: node must be Oper or Func!");
+
+ MemoryContextSwitchTo(oldcontext);
}
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.29 2000/07/11 14:30:28 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.30 2000/07/12 02:37:23 tgl Exp $
*
* NOTE:
* This is a new (Feb. 05, 1999) implementation of the allocation set
if (block == set->keeper)
{
/* Reset the block, but don't return it to malloc */
- block->next = NULL;
- block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
+ char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ;
+
#ifdef CLOBBER_FREED_MEMORY
/* Wipe freed memory for debugging purposes */
- memset(block->freeptr, 0x7F,
- ((char *) block->endptr) - ((char *) block->freeptr));
+ memset(datastart, 0x7F, ((char *) block->freeptr) - datastart);
#endif
+ block->freeptr = datastart;
+ block->next = NULL;
}
else
{
/* Normal case, release the block */
#ifdef CLOBBER_FREED_MEMORY
/* Wipe freed memory for debugging purposes */
- memset(block, 0x7F, ((char *) block->endptr) - ((char *) block));
+ memset(block, 0x7F, ((char *) block->freeptr) - ((char *) block));
#endif
free(block);
}
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: index.h,v 1.27 2000/07/04 06:11:54 tgl Exp $
+ * $Id: index.h,v 1.28 2000/07/12 02:37:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern bool SetReindexProcessing(bool processing);
extern bool IsReindexProcessing(void);
-extern void FillDummyExprContext(ExprContext *econtext, TupleTableSlot *slot,
- TupleDesc tupdesc, Buffer buffer);
-
extern void index_build(Relation heapRelation, Relation indexRelation,
int numberOfAttributes, AttrNumber *attributeNumber,
FuncIndexInfo *funcInfo, PredInfo *predInfo,
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: executor.h,v 1.45 2000/06/18 22:44:28 tgl Exp $
+ * $Id: executor.h,v 1.46 2000/07/12 02:37:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* prototypes from functions in execQual.c
*/
-extern bool execConstByVal;
-extern int execConstLen;
-
-extern Datum ExecExtractResult(TupleTableSlot *slot, AttrNumber attnum,
- bool *isNull);
extern Datum ExecEvalParam(Param *expression, ExprContext *econtext,
bool *isNull);
-
extern char *GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno,
bool *isNull);
-extern char *GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull);
-extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext, bool *isNull,
- bool *isDone);
+extern char *GetAttributeByName(TupleTableSlot *slot, char *attname,
+ bool *isNull);
+extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext,
+ bool *isNull, bool *isDone);
+extern Datum ExecEvalExprSwitchContext(Node *expression, ExprContext *econtext,
+ bool *isNull, bool *isDone);
extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
extern int ExecTargetListLength(List *targetlist);
extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, bool *isDone);
/*
* prototypes from functions in execScan.c
*/
-extern TupleTableSlot *ExecScan(Scan *node, TupleTableSlot *(*accessMtd) ());
+typedef TupleTableSlot *(*ExecScanAccessMtd) (Scan *node);
+
+extern TupleTableSlot *ExecScan(Scan *node, ExecScanAccessMtd accessMtd);
/*
* prototypes from functions in execTuples.c
* prototypes from functions in execUtils.c
*/
extern void ResetTupleCount(void);
-extern void ExecAssignNodeBaseInfo(EState *estate, CommonState *basenode,
- Plan *parent);
extern void ExecAssignExprContext(EState *estate, CommonState *commonstate);
extern void ExecAssignResultType(CommonState *commonstate,
TupleDesc tupDesc);
extern void ExecAssignProjectionInfo(Plan *node, CommonState *commonstate);
extern void ExecFreeProjectionInfo(CommonState *commonstate);
extern void ExecFreeExprContext(CommonState *commonstate);
-extern void ExecFreeTypeInfo(CommonState *commonstate);
extern TupleDesc ExecGetScanType(CommonScanState *csstate);
extern void ExecAssignScanType(CommonScanState *csstate,
TupleDesc tupDesc);
CommonScanState *csstate);
extern Form_pg_attribute ExecGetTypeInfo(Relation relDesc);
+extern ExprContext *MakeExprContext(TupleTableSlot *slot,
+ MemoryContext queryContext);
+extern void FreeExprContext(ExprContext *econtext);
+
+#define ResetExprContext(econtext) \
+ MemoryContextReset((econtext)->ecxt_per_tuple_memory)
+
extern void ExecOpenIndices(RelationInfo *resultRelationInfo);
extern void ExecCloseIndices(RelationInfo *resultRelationInfo);
extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: hashjoin.h,v 1.17 2000/06/28 03:33:05 tgl Exp $
+ * $Id: hashjoin.h,v 1.18 2000/07/12 02:37:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* hash-join hash table structures
*
* Each active hashjoin has a HashJoinTable control block which is
- * palloc'd in the executor's context. All other storage needed for
- * the hashjoin is kept in private memory contexts, two for each hashjoin.
+ * palloc'd in the executor's per-query context. All other storage needed
+ * for the hashjoin is kept in private memory contexts, two for each hashjoin.
* This makes it easy and fast to release the storage when we don't need it
* anymore.
*
long *innerBatchSize; /* count of tuples in each inner batch
* file */
+ /*
+ * Info about the datatype being hashed. We assume that the inner
+ * and outer sides of the hash are the same type, or at least
+ * binary-compatible types.
+ */
+ bool typByVal;
+ int typLen;
+
/*
* During 1st scan of inner relation, we get tuples from executor. If
* nbatch > 0 then tuples that don't belong in first nbuckets logical
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: nodeGroup.h,v 1.16 2000/04/12 17:16:33 momjian Exp $
+ * $Id: nodeGroup.h,v 1.17 2000/07/12 02:37:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
TupleDesc tupdesc,
int numCols,
AttrNumber *matchColIdx,
- FmgrInfo *eqfunctions);
+ FmgrInfo *eqfunctions,
+ MemoryContext evalContext);
extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc,
int numCols,
AttrNumber *matchColIdx);
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: nodeHash.h,v 1.16 2000/04/18 05:43:00 tgl Exp $
+ * $Id: nodeHash.h,v 1.17 2000/07/12 02:37:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void ExecEndHash(Hash *node);
extern HashJoinTable ExecHashTableCreate(Hash *node);
extern void ExecHashTableDestroy(HashJoinTable hashtable);
-extern void ExecHashTableInsert(HashJoinTable hashtable, ExprContext *econtext,
- Var *hashkey);
-extern int ExecHashGetBucket(HashJoinTable hashtable, ExprContext *econtext,
- Var *hashkey);
+extern void ExecHashTableInsert(HashJoinTable hashtable,
+ ExprContext *econtext,
+ Node *hashkey);
+extern int ExecHashGetBucket(HashJoinTable hashtable,
+ ExprContext *econtext,
+ Node *hashkey);
extern HeapTuple ExecScanHashBucket(HashJoinState *hjstate, List *hjclauses,
ExprContext *econtext);
extern void ExecHashTableReset(HashJoinTable hashtable, long ntuples);
bool *isNull);
extern bool ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent);
extern void ExecReScanSetParamPlan(SubPlan *node, Plan *parent);
-extern void ExecSetParamPlan(SubPlan *node);
+extern void ExecSetParamPlan(SubPlan *node, ExprContext *econtext);
extern void ExecEndSubPlan(SubPlan *node);
#endif /* NODESUBPLAN_H */
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: fmgr.h,v 1.7 2000/07/06 05:48:17 tgl Exp $
+ * $Id: fmgr.h,v 1.8 2000/07/12 02:37:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define PG_DETOAST_DATUM_COPY(datum) \
pg_detoast_datum_copy((struct varlena *) DatumGetPointer(datum))
+/*
+ * Support for cleaning up detoasted copies of inputs. This must only
+ * be used for pass-by-ref datatypes, and normally would only be used
+ * for toastable types. If the given pointer is different from the
+ * original argument, assume it's a palloc'd detoasted copy, and pfree it.
+ * NOTE: most functions on toastable types do not have to worry about this,
+ * but we currently require that support functions for indexes not leak
+ * memory.
+ */
+#define PG_FREE_IF_COPY(ptr,n) \
+ do { \
+ if ((Pointer) (ptr) != PG_GETARG_POINTER(n)) \
+ pfree(ptr); \
+ } while (0)
+
/* Macros for fetching arguments of standard types */
#define PG_GETARG_DATUM(n) (fcinfo->arg[n])
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: execnodes.h,v 1.42 2000/06/18 22:44:29 tgl Exp $
+ * $Id: execnodes.h,v 1.43 2000/07/12 02:37:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* to an attribute in the current inner tuple then we need to know
* what the current inner tuple is and so we look at the expression
* context.
+ *
+ * There are two memory contexts associated with an ExprContext:
+ * * ecxt_per_query_memory is a relatively long-lived context (such as
+ * TransactionCommandContext); typically it's the same context the
+ * ExprContext node itself is allocated in. This context can be
+ * used for purposes such as storing operator/function fcache nodes.
+ * * ecxt_per_tuple_memory is a short-term context for expression results.
+ * As the name suggests, it will typically be reset once per tuple,
+ * before we begin to evaluate expressions for that tuple. Each
+ * ExprContext normally has its very own per-tuple memory context.
+ * CurrentMemoryContext should be set to ecxt_per_tuple_memory before
+ * calling ExecEvalExpr() --- see ExecEvalExprSwitchContext().
* ----------------
*/
typedef struct ExprContext
{
NodeTag type;
+ /* Tuples that Var nodes in expression may refer to */
TupleTableSlot *ecxt_scantuple;
TupleTableSlot *ecxt_innertuple;
TupleTableSlot *ecxt_outertuple;
- Relation ecxt_relation;
- Index ecxt_relid;
- ParamListInfo ecxt_param_list_info;
- ParamExecData *ecxt_param_exec_vals; /* this is for subselects */
- List *ecxt_range_table;
+ /* Memory contexts for expression evaluation --- see notes above */
+ MemoryContext ecxt_per_query_memory;
+ MemoryContext ecxt_per_tuple_memory;
+ /* Values to substitute for Param nodes in expression */
+ ParamExecData *ecxt_param_exec_vals; /* for PARAM_EXEC params */
+ ParamListInfo ecxt_param_list_info; /* for other param types */
+ /* Values to substitute for Aggref nodes in expression */
Datum *ecxt_aggvalues; /* precomputed values for Aggref nodes */
bool *ecxt_aggnulls; /* null flags for Aggref nodes */
+ /* Range table that Vars in expression refer to --- seldom needed */
+ List *ecxt_range_table;
} ExprContext;
/* ----------------
* ProjectionInfo node information
*
- * This is all the information needed to preform projections
+ * This is all the information needed to perform projections
* on a tuple. Nodes which need to do projections create one
- * of these. In theory, when a node wants to preform a projection
+ * of these. In theory, when a node wants to perform a projection
* it should just update this information as necessary and then
* call ExecProject(). -cim 6/3/91
*
/* ----------------
* JunkFilter
*
- * this class is used to store information regarding junk attributes.
+ * This class is used to store information regarding junk attributes.
* A junk attribute is an attribute in a tuple that is needed only for
* storing intermediate information in the executor, and does not belong
- * in the tuple proper. For example, when we do a delete or replace
- * query, the planner adds an entry to the targetlist so that the tuples
- * returned to ExecutePlan() contain an extra attribute: the t_ctid of
- * the tuple to be deleted/replaced. This is needed for amdelete() and
- * amreplace(). In doing a delete this does not make much of a
- * difference, but in doing a replace we have to make sure we disgard
- * all the junk in a tuple before calling amreplace(). Otherwise the
- * inserted tuple will not have the correct schema. This solves a
- * problem with hash-join and merge-sort replace plans. -cim 10/10/90
+ * in emitted tuples. For example, when we do an UPDATE query,
+ * the planner adds a "junk" entry to the targetlist so that the tuples
+ * returned to ExecutePlan() contain an extra attribute: the ctid of
+ * the tuple to be updated. This is needed to do the update, but we
+ * don't want the ctid to be part of the stored new tuple! So, we
+ * apply a "junk filter" to remove the junk attributes and form the
+ * real output tuple.
*
* targetList: the original target list (including junk attributes).
* length: the length of 'targetList'.
* param_list_info information needed to transform
* Param nodes into Const nodes
*
- * BaseId during InitPlan(), each node is
- * given a number. this is the next
- * number to be assigned.
- *
* tupleTable this is a pointer to an array
* of pointers to tuples used by
* the executor at any given moment.
* junkFilter contains information used to
* extract junk attributes from a tuple.
* (see JunkFilter above)
- *
- * refcount local buffer refcounts used in
- * an ExecMain cycle. this is introduced
- * to avoid ExecStart's unpinning each
- * other's buffers when called recursively
* ----------------
*/
typedef struct EState
Relation es_into_relation_descriptor;
ParamListInfo es_param_list_info;
ParamExecData *es_param_exec_vals; /* this is for subselects */
- int es_BaseId;
TupleTable es_tupleTable;
JunkFilter *es_junkFilter;
uint32 es_processed; /* # of tuples processed */
* ----------------------------------------------------------------
*/
-/* BaseNode removed -- base_id moved into CommonState - jolly */
-
/* ----------------
* CommonState information
*
- *| this is a bogus class used to hold slots so other
- *| nodes can inherit them...
+ * Superclass for all executor node-state object types.
*
* OuterTupleSlot pointer to slot containing current "outer" tuple
* ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
+ * ExprContext node's expression-evaluation context
* ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
- *
+ * TupFromTlist state flag used by some node types (why kept here?)
* ----------------
*/
typedef struct CommonState
{
NodeTag type; /* its first field is NodeTag */
- int cs_base_id;
TupleTableSlot *cs_OuterTupleSlot;
TupleTableSlot *cs_ResultTupleSlot;
ExprContext *cs_ExprContext;
*
* done flag which tells us to quit when we
* have already returned a constant tuple.
- *
- * CommonState information
- *
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
* ----------------
*/
typedef struct ResultState
* rtentries range table for the current plan
* result_relation_info_list array of each subplan's result relation info
* junkFilter_list array of each subplan's junk filter
- *
- * CommonState information
- *
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
* ----------------
*/
typedef struct AppendState
/* ----------------
* CommonScanState information
*
- * CommonScanState is a class like CommonState, but is used more
- * by the nodes like SeqScan and Sort which want to
- * keep track of an underlying relation.
+ * CommonScanState extends CommonState for node types that represent
+ * scans of an underlying relation. It can also be used for nodes
+ * that scan the output of an underlying plan node --- in that case,
+ * only ScanTupleSlot is actually useful, and it refers to the tuple
+ * retrieved from the subplan.
*
- * currentRelation relation being scanned
- * currentScanDesc current scan descriptor for scan
+ * currentRelation relation being scanned (NULL if none)
+ * currentScanDesc current scan descriptor for scan (NULL if none)
* ScanTupleSlot pointer to slot in tuple table holding scan tuple
- *
- * CommonState information
- *
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
* ----------------
*/
typedef struct CommonScanState
TupleTableSlot *css_ScanTupleSlot;
} CommonScanState;
+/*
+ * SeqScan uses a bare CommonScanState as its state item, since it needs
+ * no additional fields.
+ */
+
/* ----------------
* IndexScanState information
*
- *| index scans don't use CommonScanState because
- *| the underlying AM abstractions for heap scans and
- *| index scans are too different.. It would be nice
- *| if the current abstraction was more useful but ... -cim 10/15/89
+ * Note that an IndexScan node *also* has a CommonScanState state item.
+ * IndexScanState stores the info needed specifically for indexing.
+ * There's probably no good reason why this is a separate node type
+ * rather than an extension of CommonScanState.
*
- * IndexPtr current index in use
* NumIndices number of indices in this scan
+ * IndexPtr current index in use
* ScanKeys Skey structures to scan index rels
* NumScanKeys array of no of keys in each Skey struct
* RuntimeKeyInfo array of array of flags for Skeys evaled at runtime
+ * RuntimeContext expr context for evaling runtime Skeys
* RelationDescs ptr to array of relation descriptors
* ScanDescs ptr to array of scan descriptors
- *
- * CommonState information
- *
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
* ----------------
*/
typedef struct IndexScanState
{
- CommonState cstate; /* its first field is NodeTag */
+ NodeTag type;
int iss_NumIndices;
int iss_IndexPtr;
int iss_MarkIndexPtr;
ScanKey *iss_ScanKeys;
int *iss_NumScanKeys;
- Pointer iss_RuntimeKeyInfo;
+ int **iss_RuntimeKeyInfo;
+ ExprContext *iss_RuntimeContext;
RelationPtr iss_RelationDescs;
IndexScanDescPtr iss_ScanDescs;
HeapTupleData iss_htup;
/* ----------------
* TidScanState information
*
- *| tid scans don't use CommonScanState because
- *| the underlying AM abstractions for heap scans and
- *| tid scans are too different.. It would be nice
- *| if the current abstraction was more useful but ... -cim 10/15/89
+ * Note that a TidScan node *also* has a CommonScanState state item.
+ * There's probably no good reason why this is a separate node type
+ * rather than an extension of CommonScanState.
*
- * TidPtr current tid in use
* NumTids number of tids in this scan
- * tidList evaluated item pointers
- *
- * CommonState information
- *
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
+ * TidPtr current tid in use
+ * TidList evaluated item pointers
* ----------------
*/
typedef struct TidScanState
{
- CommonState cstate; /* its first field is NodeTag */
+ NodeTag type;
int tss_NumTids;
int tss_TidPtr;
int tss_MarkTidPtr;
/* ----------------
* JoinState information
*
- * CommonState information
- *
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
+ * Superclass for state items of join nodes.
+ * Currently this is the same as CommonState.
* ----------------
*/
typedef CommonState JoinState;
/* ----------------
* NestLoopState information
- *
- * PortalFlag Set to enable portals to work.
- *
- * JoinState information
- *
- * CommonState information
- *
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
* ----------------
*/
typedef struct NestLoopState
{
JoinState jstate; /* its first field is NodeTag */
- bool nl_PortalFlag;
} NestLoopState;
/* ----------------
* InnerSkipQual outerKey1 > innerKey1 ...
* JoinState current "state" of join. see executor.h
* MarkedTupleSlot pointer to slot in tuple table for marked tuple
- *
- * JoinState information
- *
- * CommonState information
- *
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
* ----------------
*/
typedef struct MergeJoinState
* hj_InnerHashKey the inner hash key in the hashjoin condition
* hj_OuterTupleSlot tuple slot for outer tuples
* hj_HashTupleSlot tuple slot for hashed tuples
- *
- * JoinState information
- *
- * CommonState information
- *
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
* ----------------
*/
typedef struct HashJoinState
HashJoinTable hj_HashTable;
int hj_CurBucketNo;
HashJoinTuple hj_CurTuple;
- Var *hj_InnerHashKey;
+ Node *hj_InnerHashKey;
TupleTableSlot *hj_OuterTupleSlot;
TupleTableSlot *hj_HashTupleSlot;
} HashJoinState;
* materialize nodes are used to materialize the results
* of a subplan into a temporary file.
*
- * tuplestorestate private state of tuplestore.c
- *
- * CommonScanState information
- *
- * currentRelation relation descriptor of sorted relation
- * currentScanDesc current scan descriptor for scan
- * ScanTupleSlot pointer to slot in tuple table holding scan tuple
- *
- * CommonState information
+ * csstate.css_ScanTupleSlot refers to output of underlying plan.
*
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
+ * tuplestorestate private state of tuplestore.c
* ----------------
*/
typedef struct MaterialState
/* ---------------------
* AggregateState information
*
+ * csstate.css_ScanTupleSlot refers to output of underlying plan.
+ *
* Note: the associated ExprContext contains ecxt_aggvalues and ecxt_aggnulls
* arrays, which hold the computed agg values for the current input group
* during evaluation of an Agg node's output tuple(s).
* -------------------------
*/
-typedef struct AggStatePerAggData *AggStatePerAgg; /* private in nodeAgg.c */
+typedef struct AggStatePerAggData *AggStatePerAgg; /* private in nodeAgg.c */
typedef struct AggState
{
List *aggs; /* all Aggref nodes in targetlist & quals */
int numaggs; /* length of list (could be zero!) */
AggStatePerAgg peragg; /* per-Aggref working state */
+ MemoryContext tup_cxt; /* context for per-output-tuple expressions */
+ MemoryContext agg_cxt[2]; /* pair of expression eval memory contexts */
+ int which_cxt; /* 0 or 1, indicates current agg_cxt */
bool agg_done; /* indicates completion of Agg scan */
} AggState;
/* ---------------------
* GroupState information
- *
* -------------------------
*/
typedef struct GroupState
* sort_Done indicates whether sort has been performed yet
* sort_Keys scan key structures describing the sort keys
* tuplesortstate private state of tuplesort.c
- *
- * CommonScanState information
- *
- * currentRelation relation descriptor of sorted relation
- * currentScanDesc current scan descriptor for scan
- * ScanTupleSlot pointer to slot in tuple table holding scan tuple
- *
- * CommonState information
- *
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
* ----------------
*/
typedef struct SortState
* with the previously fetched tuple stored in priorTuple.
* If the two are identical in all interesting fields, then
* we just fetch another tuple from the sort and try again.
- *
- * CommonState information
- *
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
* ----------------
*/
typedef struct UniqueState
CommonState cstate; /* its first field is NodeTag */
FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */
HeapTuple priorTuple; /* most recently returned tuple, or NULL */
+ MemoryContext tempContext; /* short-term context for comparisons */
} UniqueState;
* HashState information
*
* hashtable hash table for the hashjoin
- *
- * CommonState information
- *
- * OuterTupleSlot pointer to slot containing current "outer" tuple
- * ResultTupleSlot pointer to slot in tuple table for projected tuple
- * ExprContext node's current expression context
- * ProjInfo info this node uses to form tuple projections
- * NumScanAttributes size of ScanAttributes array
- * ScanAttributes attribute numbers of interest in this tuple
* ----------------
*/
typedef struct HashState
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: plannodes.h,v 1.40 2000/06/18 22:44:31 tgl Exp $
+ * $Id: plannodes.h,v 1.41 2000/07/12 02:37:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
typedef struct Hash
{
Plan plan;
- Var *hashkey;
+ Node *hashkey;
HashState *hashstate;
} Hash;
* Remaining fields are working state for executor; not used in
* planning
*/
- bool shutdown; /* TRUE = need to shutdown plan */
+ bool needShutdown; /* TRUE = need to shutdown subplan */
HeapTuple curTuple; /* copy of most recent tuple from subplan */
} SubPlan;
/*-------------------------------------------------------------------------
*
* datum.h
- * POSTGRES abstract data type datum representation definitions.
+ * POSTGRES Datum (abstract data type) manipulation routines.
*
+ * These routines are driven by the 'typbyval' and 'typlen' information,
+ * which must previously have been obtained by the caller for the datatype
+ * of the Datum. (We do it this way because in most situations the caller
+ * can look up the info just once and use it for many per-datum operations.)
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: datum.h,v 1.10 2000/01/26 05:58:37 momjian Exp $
+ * $Id: datum.h,v 1.11 2000/07/12 02:37:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef DATUM_H
#define DATUM_H
-
-/*--------------------------------------------------------
- * SOME NOT VERY PORTABLE ROUTINES ???
- *--------------------------------------------------------
- *
- * In the implementation of the next routines we assume the following:
- *
- * A) if a type is "byVal" then all the information is stored in the
- * Datum itself (i.e. no pointers involved!). In this case the
- * length of the type is always greater than zero and less than
- * "sizeof(Datum)"
- * B) if a type is not "byVal" and it has a fixed length, then
- * the "Datum" always contain a pointer to a stream of bytes.
- * The number of significant bytes are always equal to the length of thr
- * type.
- * C) if a type is not "byVal" and is of variable length (i.e. it has
- * length == -1) then "Datum" always points to a "struct varlena".
- * This varlena structure has information about the actual length of this
- * particular instance of the type and about its value.
- */
-
-/*---------------
- * datumGetSize
- * find the "real" length of a datum
+/*
+ * datumGetSize - find the "real" length of a datum
*/
-extern Size datumGetSize(Datum value, Oid type, bool byVal, Size len);
+extern Size datumGetSize(Datum value, bool typByVal, int typLen);
-/*---------------
- * datumCopy
- * make a copy of a datum.
+/*
+ * datumCopy - make a copy of a datum.
+ *
+ * If the datatype is pass-by-reference, memory is obtained with palloc().
*/
-extern Datum datumCopy(Datum value, Oid type, bool byVal, Size len);
+extern Datum datumCopy(Datum value, bool typByVal, int typLen);
-/*---------------
- * datumFree
- * free space that *might* have been palloced by "datumCopy"
+/*
+ * datumFree - free a datum previously allocated by datumCopy, if any.
+ *
+ * Does nothing if datatype is pass-by-value.
*/
-extern void datumFree(Datum value, Oid type, bool byVal, Size len);
+extern void datumFree(Datum value, bool typByVal, int typLen);
-/*---------------
+/*
* datumIsEqual
- * return true if thwo datums are equal, false otherwise.
+ * return true if two datums of the same type are equal, false otherwise.
+ *
* XXX : See comments in the code for restrictions!
*/
-extern bool datumIsEqual(Datum value1, Datum value2, Oid type,
- bool byVal, Size len);
+extern bool datumIsEqual(Datum value1, Datum value2,
+ bool typByVal, int typLen);
#endif /* DATUM_H */
/*-------------------------------------------------------------------------
*
* fcache.h
+ * Declarations for function cache records.
*
- *
+ * The first time any Oper or Func node is evaluated, we compute a cache
+ * record for the function being invoked, and save a pointer to the cache
+ * record in the Oper or Func node. This saves repeated lookup of info
+ * about the function.
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: fcache.h,v 1.11 2000/05/28 17:56:20 tgl Exp $
+ * $Id: fcache.h,v 1.12 2000/07/12 02:37:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* expr whose argument is func returning a
* set ugh! */
+ /* If additional info is added to an existing fcache, be sure to
+ * allocate it in the fcacheCxt.
+ */
+ MemoryContext fcacheCxt; /* context the fcache lives in */
+
int nargs; /* actual number of arguments */
Oid *argOidVect; /* oids of all the argument types */
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.24 2000/07/05 23:11:58 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.25 2000/07/12 02:37:39 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
#include "plpgsql.h"
#include "pl.tab.h"
+#include "access/heapam.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/trigger.h"
#include "executor/spi.h"
#include "executor/spi_priv.h"
-#include "commands/trigger.h"
-#include "utils/builtins.h"
#include "fmgr.h"
-#include "access/heapam.h"
-
#include "tcop/tcopprot.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/syscache.h"
-#include "catalog/pg_proc.h"
-#include "catalog/pg_type.h"
static PLpgSQL_function *error_info_func = NULL;
* Create a simple expression context to hold the arguments
* ----------
*/
- econtext = makeNode(ExprContext);
+ econtext = MakeExprContext(NULL, TransactionCommandContext);
paramLI = (ParamListInfo) palloc((expr->nparams + 1) *
sizeof(ParamListInfoData));
econtext->ecxt_param_list_info = paramLI;
* ----------
*/
SPI_push();
- retval = ExecEvalExpr(expr->plan_simple_expr,
- econtext,
- isNull,
- &isdone);
+ retval = ExecEvalExprSwitchContext(expr->plan_simple_expr,
+ econtext,
+ isNull,
+ &isdone);
SPI_pop();
+ /*
+ * Copy the result out of the expression-evaluation memory context,
+ * so that we can free the expression context.
+ */
+ retval = datumCopy(retval, get_typbyval(*rettype), get_typlen(*rettype));
+
+ FreeExprContext(econtext);
+
/* ----------
* That's it.
* ----------