diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 794685da2e..28c547ffe6 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -6,7 +6,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -141,11 +141,10 @@ gistbuild(PG_FUNCTION_ARGS) { 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; @@ -161,13 +160,13 @@ gistbuild(PG_FUNCTION_ARGS) { 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)) @@ -175,7 +174,6 @@ gistbuild(PG_FUNCTION_ARGS) ni++; continue; } -#endif /* OMIT_PARTIAL_INDEX */ } /* @@ -184,13 +182,12 @@ gistbuild(PG_FUNCTION_ARGS) */ 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++; @@ -262,13 +259,13 @@ gistbuild(PG_FUNCTION_ARGS) /* 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 diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c index 9102b75f61..354d498572 100644 --- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -8,7 +8,7 @@ * * * 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. @@ -102,15 +102,14 @@ hashbuild(PG_FUNCTION_ARGS) { 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 */ @@ -122,9 +121,9 @@ hashbuild(PG_FUNCTION_ARGS) 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 @@ -132,14 +131,12 @@ hashbuild(PG_FUNCTION_ARGS) 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 */ } /* @@ -148,13 +145,12 @@ hashbuild(PG_FUNCTION_ARGS) */ 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++; @@ -221,13 +217,13 @@ hashbuild(PG_FUNCTION_ARGS) /* 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 diff --git a/src/backend/access/nbtree/nbtcompare.c b/src/backend/access/nbtree/nbtcompare.c index 73f52cb861..411564fb8d 100644 --- a/src/backend/access/nbtree/nbtcompare.c +++ b/src/backend/access/nbtree/nbtcompare.c @@ -8,7 +8,7 @@ * * * 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 * @@ -27,6 +27,10 @@ * 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. *------------------------------------------------------------------------- */ @@ -230,18 +234,23 @@ bttextcmp(PG_FUNCTION_ARGS) } 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); } diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index 59423ccb5f..3d8ea1a70a 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -12,7 +12,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -121,8 +121,8 @@ btbuild(PG_FUNCTION_ARGS) { 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 @@ -151,14 +151,13 @@ btbuild(PG_FUNCTION_ARGS) { 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)) @@ -166,7 +165,6 @@ btbuild(PG_FUNCTION_ARGS) nitups++; continue; } -#endif /* OMIT_PARTIAL_INDEX */ } /* @@ -175,13 +173,12 @@ btbuild(PG_FUNCTION_ARGS) */ 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++; @@ -260,13 +257,13 @@ btbuild(PG_FUNCTION_ARGS) /* 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) diff --git a/src/backend/access/rtree/rtree.c b/src/backend/access/rtree/rtree.c index 7e84d45638..badff1ee21 100644 --- a/src/backend/access/rtree/rtree.c +++ b/src/backend/access/rtree/rtree.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -136,14 +136,14 @@ rtbuild(PG_FUNCTION_ARGS) { 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 */ @@ -156,13 +156,13 @@ rtbuild(PG_FUNCTION_ARGS) { 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)) @@ -170,7 +170,6 @@ rtbuild(PG_FUNCTION_ARGS) ni++; continue; } -#endif /* OMIT_PARTIAL_INDEX */ } /* @@ -179,13 +178,12 @@ rtbuild(PG_FUNCTION_ARGS) */ 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++; @@ -239,13 +237,13 @@ rtbuild(PG_FUNCTION_ARGS) /* 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 diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index e1909e4404..41d747b0db 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * 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 @@ -1670,34 +1670,6 @@ UpdateStats(Oid relid, long reltuples, bool inplace) } -/* ------------------------- - * 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 * @@ -1777,14 +1749,14 @@ DefaultBuild(Relation heapRelation, { 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 */ @@ -1812,7 +1784,6 @@ DefaultBuild(Relation heapRelation, 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 @@ -1877,6 +1848,7 @@ DefaultBuild(Relation heapRelation, { /* parameter was 'false', almost certainly wrong --- tgl 9/21/99 */ ExecDropTupleTable(tupleTable, true); + FreeExprContext(econtext); } #endif /* OMIT_PARTIAL_INDEX */ diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 441941762e..355b218e64 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -7,7 +7,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -611,13 +611,11 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null 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; @@ -651,7 +649,6 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null 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]); @@ -680,36 +677,18 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null 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; @@ -927,10 +906,9 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *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 @@ -939,8 +917,8 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null /* 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, diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index ff3fa0b6ed..10478f60ad 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -6,7 +6,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -171,8 +171,8 @@ ExecBeginScan(Relation relation, /* ---------------------------------------------------------------- * 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 @@ -197,20 +197,12 @@ ExecCloseR(Plan *node) 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; } @@ -237,13 +229,12 @@ ExecCloseR(Plan *node) 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; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 6f161d95c0..3125bf175e 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -27,7 +27,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -86,9 +86,12 @@ static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation, * 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 @@ -103,7 +106,8 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate) { 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)); } /* @@ -151,7 +155,6 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate) * EXEC_RETONE: return one tuple but don't 'retrieve' it * used in postquel function processing * - * * ---------------------------------------------------------------- */ TupleTableSlot * @@ -687,13 +690,6 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) */ 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 */ @@ -793,7 +789,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) /* * 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); @@ -1589,7 +1585,7 @@ ExecAttrDefault(Relation rel, HeapTuple tuple) { 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; @@ -1600,23 +1596,13 @@ ExecAttrDefault(Relation rel, HeapTuple tuple) 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; @@ -1635,20 +1621,24 @@ ExecAttrDefault(Relation rel, HeapTuple tuple) } - 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 @@ -1658,9 +1648,10 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate) { 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; @@ -1677,17 +1668,21 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate) 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 *)); @@ -1696,6 +1691,8 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate) qual = (List *) stringToNode(check[i].ccbin); estate->es_result_relation_constraints[i] = qual; } + + MemoryContextSwitchTo(oldContext); } for (i = 0; i < ncheck; i++) @@ -1714,16 +1711,15 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate) pfree(slot); pfree(rte); pfree(rtlist); - pfree(econtext); + + 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) @@ -1732,9 +1728,10 @@ ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate) 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)); } } @@ -1743,10 +1740,9 @@ ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate) 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 * diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 33bfa88734..fd9d761ffc 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,15 +8,16 @@ * * * 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 @@ -24,7 +25,7 @@ * 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 @@ -44,31 +45,19 @@ #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, @@ -100,10 +89,11 @@ ExecEvalArrayRef(ArrayRef *arrayRef, 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; @@ -128,10 +118,10 @@ ExecEvalArrayRef(ArrayRef *arrayRef, 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; @@ -145,10 +135,10 @@ ExecEvalArrayRef(ArrayRef *arrayRef, 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; @@ -171,9 +161,6 @@ ExecEvalArrayRef(ArrayRef *arrayRef, if (*isNull) return (Datum) NULL; - execConstByVal = arrayRef->refelembyval; - execConstLen = arrayRef->refelemlength; - if (array_scanner == NULL) return sourceData; /* XXX do something else? */ @@ -199,9 +186,6 @@ ExecEvalArrayRef(ArrayRef *arrayRef, isNull)); } - execConstByVal = arrayRef->refelembyval; - execConstLen = arrayRef->refelemlength; - if (lIndex == NULL) return array_ref(array_scanner, i, upper.indx, @@ -325,7 +309,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull) ExecSetSlotDescriptor(tempSlot, td); ExecStoreTuple(tup, tempSlot, InvalidBuffer, true); - return (Datum) tempSlot; + return PointerGetDatum(tempSlot); } result = heap_getattr(heapTuple, /* tuple containing attribute */ @@ -338,7 +322,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull) * return null if att is null */ if (*isNull) - return (Datum) NULL; + return (Datum) 0; /* * get length and type information.. ??? what should we do about @@ -364,9 +348,6 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull) byval = tuple_type->attrs[attnum - 1]->attbyval ? true : false; } - execConstByVal = byval; - execConstLen = len; - return result; } @@ -397,7 +378,6 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull) Datum ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull) { - char *thisParameterName; int thisParameterKind = expression->paramkind; AttrNumber thisParameterId = expression->paramid; @@ -406,11 +386,15 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull) 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; } @@ -493,7 +477,7 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull) if (paramList->isnull) { *isNull = true; - return (Datum) NULL; + return (Datum) 0; } if (expression->param_tlist != NIL) @@ -526,8 +510,12 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull) * 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, @@ -559,18 +547,6 @@ GetAttributeByNum(TupleTableSlot *slot, 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) { @@ -617,15 +593,6 @@ 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, @@ -732,9 +699,8 @@ ExecMakeFunctionResult(Node *node, 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; } } @@ -817,8 +783,8 @@ ExecMakeFunctionResult(Node *node, else { result = (Datum) 0; - *isDone = true; *isNull = true; + *isDone = true; } if (!*isDone) @@ -872,8 +838,8 @@ ExecMakeFunctionResult(Node *node, 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. @@ -904,15 +870,7 @@ ExecMakeFunctionResult(Node *node, * 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. * ---------------------------------------------------------------- */ @@ -929,8 +887,6 @@ ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull) 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 @@ -954,7 +910,8 @@ ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull) * 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); } /* ---------------------------------------------------------------- @@ -973,8 +930,6 @@ ExecEvalFunc(Expr *funcClause, 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 @@ -996,7 +951,8 @@ ExecEvalFunc(Expr *funcClause, fcache = func->func_fcache; } - return ExecMakeFunctionResult((Node *) func, argList, econtext, isNull, isDone); + return ExecMakeFunctionResult((Node *) func, argList, econtext, + isNull, isDone); } /* ---------------------------------------------------------------- @@ -1041,10 +997,7 @@ ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull) * 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)); } /* ---------------------------------------------------------------- @@ -1094,13 +1047,13 @@ ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull) */ 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); } /* ---------------------------------------------------------------- @@ -1144,13 +1097,13 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull) */ 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); } /* ---------------------------------------------------------------- @@ -1195,7 +1148,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull) * 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, @@ -1221,19 +1174,15 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull) * * Recursively evaluate a targetlist or qualification expression. * - * This routine is an inner loop routine and should be as fast + * 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(). + * + * This routine is an inner loop routine and must be as fast * as possible. - * - * 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. * ---------------------------------------------------------------- */ Datum @@ -1244,25 +1193,21 @@ ExecEvalExpr(Node *expression, { 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; + /* Is this still necessary? Doubtful... */ + if (expression == NULL) + { + *isNull = true; + return (Datum) 0; + } /* * here we dispatch the work to the appropriate type of function given * the type of our expression. */ - if (expression == NULL) - { - *isNull = true; - return (Datum) true; - } - switch (nodeTag(expression)) { case T_Var: @@ -1350,8 +1295,27 @@ ExecEvalExpr(Node *expression, } /* 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 * ---------------------------------------------------------------- */ @@ -1386,6 +1350,8 @@ ExecEvalExpr(Node *expression, bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull) { + bool result; + MemoryContext oldContext; List *qlist; /* @@ -1397,6 +1363,11 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull) 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 @@ -1409,6 +1380,7 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull) * is NULL (one or more NULL subresult, with all the rest TRUE) and * the caller has specified resultForNull = TRUE. */ + result = true; foreach(qlist, qual) { @@ -1417,14 +1389,6 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull) 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. @@ -1434,16 +1398,24 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull) 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 @@ -1481,6 +1453,7 @@ ExecTargetList(List *targetlist, ExprContext *econtext, bool *isDone) { + MemoryContext oldContext; char nulls_array[64]; bool fjNullArray[64]; bool itemIsDoneArray[64]; @@ -1506,6 +1479,11 @@ ExecTargetList(List *targetlist, 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 @@ -1563,10 +1541,10 @@ ExecTargetList(List *targetlist, 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; @@ -1597,7 +1575,10 @@ ExecTargetList(List *targetlist, /* this is probably wrong: */ if (*isDone) - return (HeapTuple) NULL; + { + newTuple = NULL; + goto exit; + } /* * get the result from the inner node @@ -1681,10 +1662,10 @@ ExecTargetList(List *targetlist, if (IsA(expr, Iter) &&itemIsDone[resind]) { - constvalue = (Datum) ExecEvalExpr(expr, - econtext, - &isNull, - &itemIsDone[resind]); + constvalue = ExecEvalExpr(expr, + econtext, + &isNull, + &itemIsDone[resind]); if (itemIsDone[resind]) { @@ -1710,8 +1691,10 @@ ExecTargetList(List *targetlist, } /* - * 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: @@ -1726,6 +1709,9 @@ exit: pfree(itemIsDone); } + /* make sure we are in the right context if we did "goto exit" */ + MemoryContextSwitchTo(oldContext); + return newTuple; } diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index 98345b408a..40bbbd916f 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -12,7 +12,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -30,7 +30,7 @@ * 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 @@ -39,59 +39,50 @@ * 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; - - /* ---------------- - * get the expression context - * ---------------- - */ econtext = scanstate->cstate.cs_ExprContext; + qual = node->plan.qual; /* ---------------- - * initialize fields in ExprContext which don't change - * in the course of the scan.. + * Reset per-tuple memory context to free any expression evaluation + * storage allocated in the previous tuple cycle. * ---------------- */ - qual = node->plan.qual; - econtext->ecxt_relation = scanstate->css_currentRelation; - econtext->ecxt_relid = node->scanrelid; + ResetExprContext(econtext); + /* ---------------- + * 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 (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; } /* @@ -100,27 +91,23 @@ ExecScan(Scan *node, */ 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); } /* ---------------- @@ -131,22 +118,28 @@ ExecScan(Scan *node, /* ---------------- * 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. * ---------------- */ diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 578b8b8068..37b092fc20 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -15,7 +15,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -76,8 +76,7 @@ * 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 @@ -182,8 +181,8 @@ ExecCreateTupleTable(int initialSize) /* initial number of slots in /* -------------------------------- * 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(). * -------------------------------- */ @@ -239,7 +238,6 @@ ExecDropTupleTable(TupleTable table, /* tuple table */ */ pfree(array); pfree(table); - } @@ -252,13 +250,12 @@ ExecDropTupleTable(TupleTable table, /* tuple 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 */ @@ -283,22 +280,12 @@ ExecAllocTableSlot(TupleTable table) * 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 @@ -427,7 +414,7 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */ 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. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a023405a27..daa40dccf7 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -1,22 +1,20 @@ /*------------------------------------------------------------------------- * * 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 @@ -53,6 +51,7 @@ #include "miscadmin.h" #include "utils/builtins.h" #include "utils/fmgroids.h" +#include "utils/memutils.h" #include "utils/relcache.h" #include "utils/syscache.h" @@ -137,59 +136,106 @@ DisplayTupleCount(FILE *statfp) #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 - * - * 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. - * ---------------- - */ -void -ExecAssignNodeBaseInfo(EState *estate, CommonState *cstate, Plan *parent) -{ - int baseId; - - baseId = estate->es_BaseId; - cstate->cs_base_id = baseId; - estate->es_BaseId = baseId + 1; -} - /* ---------------- * ExecAssignExprContext * * 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. + * 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. + * + * Note: we assume CurrentMemoryContext is the correct per-query context. + * This should be true during plan node initialization. * ---------------- */ void ExecAssignExprContext(EState *estate, CommonState *commonstate) { - 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_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_range_table = estate->es_range_table; /* range table */ + 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; } +/* ---------------- + * MakeExprContext + * + * 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. + * ---------------- + */ +ExprContext * +MakeExprContext(TupleTableSlot *slot, + MemoryContext queryContext) +{ + ExprContext *econtext = makeNode(ExprContext); + + 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; +} + +/* + * 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); +} + /* ---------------------------------------------------------------- * Result slot tuple type and ProjectionInfo support * ---------------------------------------------------------------- @@ -390,6 +436,7 @@ ExecFreeExprContext(CommonState *commonstate) * clean up memory used. * ---------------- */ + MemoryContextDelete(econtext->ecxt_per_tuple_memory); pfree(econtext); commonstate->cs_ExprContext = NULL; } @@ -398,6 +445,7 @@ ExecFreeExprContext(CommonState *commonstate) * ExecFreeTypeInfo * ---------------- */ +#ifdef NOT_USED void ExecFreeTypeInfo(CommonState *commonstate) { @@ -414,6 +462,7 @@ ExecFreeTypeInfo(CommonState *commonstate) FreeTupleDesc(tupDesc); commonstate->cs_ResultTupleSlot->ttc_tupleDescriptor = NULL; } +#endif /* ---------------------------------------------------------------- * the following scan type support functions are for @@ -974,8 +1023,8 @@ ExecInsertIndexTuples(TupleTableSlot *slot, 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)) @@ -1023,7 +1072,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot, pfree(result); } if (econtext != NULL) - pfree(econtext); + FreeExprContext(econtext); } void diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 6d8d93a47f..a92811d034 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -57,20 +57,18 @@ ProjectAttribute(TupleDesc TD, 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; - valueP = datumCopy(val, - TD->attrs[attrno - 1]->atttypid, - TD->attrs[attrno - 1]->attbyval, - (Size) TD->attrs[attrno - 1]->attlen); - return valueP; + if (*isnullP) + return (Datum) 0; + + return datumCopy(val, + TD->attrs[attrno - 1]->attbyval, + TD->attrs[attrno - 1]->attlen); } static execution_state * @@ -351,10 +349,18 @@ postquel_function(FunctionCallInfo fcinfo, 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 @@ -416,6 +422,7 @@ postquel_function(FunctionCallInfo fcinfo, * Let caller know we're finished. */ *isDone = true; + MemoryContextSwitchTo(oldcontext); return (fcache->oneResult) ? result : (Datum) NULL; } @@ -426,5 +433,8 @@ postquel_function(FunctionCallInfo fcinfo, Assert(LAST_POSTQUEL_COMMAND(es)); *isDone = false; + + MemoryContextSwitchTo(oldcontext); + return result; } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 0289cf45bd..1ac5c3c9e2 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -32,7 +32,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -50,6 +50,7 @@ #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 @@ -101,13 +102,15 @@ typedef struct AggStatePerAggData 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; @@ -143,13 +146,16 @@ typedef struct AggStatePerAggData 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) @@ -177,23 +183,14 @@ 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; /* ------------------------------------------ @@ -211,6 +208,9 @@ initialize_aggregate(AggStatePerAgg peraggstate) /* * 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. */ @@ -237,12 +237,13 @@ advance_transition_functions(AggStatePerAgg peraggstate, * 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; } @@ -264,8 +265,18 @@ advance_transition_functions(AggStatePerAgg peraggstate, } 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; } @@ -287,70 +298,116 @@ advance_transition_functions(AggStatePerAgg peraggstate, } 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) - { - Datum oldVal = (Datum) 0; - bool haveOldVal = false; - Datum newVal; - bool isNull; - tuplesort_performsort(peraggstate->sortstate); - while (tuplesort_getdatum(peraggstate->sortstate, true, - &newVal, &isNull)) + 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 @@ -403,35 +460,27 @@ finalize_aggregate(AggStatePerAgg peraggstate, /* 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 - @@ -461,6 +510,7 @@ ExecAgg(Agg *node) Datum *aggvalues; bool *aggnulls; AggStatePerAgg peragg; + MemoryContext oldContext; TupleTableSlot *resultSlot; HeapTuple inputTuple; int aggno; @@ -481,14 +531,18 @@ ExecAgg(Agg *node) 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 */ @@ -514,6 +568,17 @@ ExecAgg(Agg *node) 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]; @@ -527,13 +592,26 @@ ExecAgg(Agg *node) 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 @@ -546,14 +624,38 @@ ExecAgg(Agg *node) /* * 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]; - finalize_aggregate(peraggstate, - &aggvalues[aggno], &aggnulls[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]; + + if (peraggstate->aggref->aggdistinct) + { + process_sorted_aggregate(aggstate, peraggstate); + oldContext = MemoryContextSwitchTo(aggstate->tup_cxt); + finalize_aggregate(peraggstate, + &aggvalues[aggno], &aggnulls[aggno]); + MemoryContextSwitchTo(oldContext); + } } /* @@ -584,7 +686,7 @@ ExecAgg(Agg *node) /* * 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. @@ -619,7 +721,8 @@ ExecAgg(Agg *node) /* * 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, @@ -627,6 +730,11 @@ ExecAgg(Agg *node) 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. @@ -701,11 +809,33 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent) } /* - * 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 /* @@ -769,16 +899,20 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent) /* 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, @@ -846,6 +980,8 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent) peraggstate->sortOperator = any_ordering_op(inputType); peraggstate->sortstate = NULL; } + + heap_freetuple(aggTuple); } return TRUE; @@ -866,6 +1002,17 @@ ExecEndAgg(Agg *node) 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); @@ -890,28 +1037,4 @@ ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent) */ 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); - } } diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 5e34e806e3..2b1eceb15d 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -219,16 +219,12 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent) 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 /* ---------------- @@ -380,7 +376,7 @@ ExecCountSlotsAppend(Append *node) * * 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 * diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index d1ae02616c..8a445b53d4 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -15,7 +15,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -68,13 +68,11 @@ ExecGroupEveryTuple(Group *node) EState *estate; ExprContext *econtext; TupleDesc tupdesc; - HeapTuple outerTuple = NULL; HeapTuple firsttuple; TupleTableSlot *outerslot; ProjectionInfo *projInfo; TupleTableSlot *resultSlot; - bool isDone; /* --------------------- @@ -84,14 +82,16 @@ ExecGroupEveryTuple(Group *node) 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; @@ -130,7 +130,8 @@ ExecGroupEveryTuple(Group *node) if (!execTuplesMatch(firsttuple, outerTuple, tupdesc, node->numCols, node->grpColIdx, - grpstate->eqfunctions)) + grpstate->eqfunctions, + econtext->ecxt_per_tuple_memory)) { /* @@ -179,13 +180,11 @@ ExecGroupOneTuple(Group *node) EState *estate; ExprContext *econtext; TupleDesc tupdesc; - HeapTuple outerTuple = NULL; HeapTuple firsttuple; TupleTableSlot *outerslot; ProjectionInfo *projInfo; TupleTableSlot *resultSlot; - bool isDone; /* --------------------- @@ -195,13 +194,15 @@ ExecGroupOneTuple(Group *node) 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) { @@ -237,7 +238,8 @@ ExecGroupOneTuple(Group *node) if (!execTuplesMatch(firsttuple, outerTuple, tupdesc, node->numCols, node->grpColIdx, - grpstate->eqfunctions)) + grpstate->eqfunctions, + econtext->ecxt_per_tuple_memory)) break; } @@ -296,10 +298,8 @@ ExecInitGroup(Group *node, EState *estate, Plan *parent) 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 @@ -360,6 +360,7 @@ ExecEndGroup(Group *node) grpstate = node->grpstate; ExecFreeProjectionInfo(&grpstate->csstate.cstate); + ExecFreeExprContext(&grpstate->csstate.cstate); outerPlan = outerPlan(node); ExecEndNode(outerPlan, (Plan *) node); @@ -406,6 +407,9 @@ ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent) * 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, @@ -413,16 +417,25 @@ 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]; @@ -442,7 +455,10 @@ execTuplesMatch(HeapTuple tuple1, &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 */ @@ -451,10 +467,15 @@ execTuplesMatch(HeapTuple tuple1, if (! DatumGetBool(FunctionCall2(&eqfunctions[i], attr1, attr2))) - return FALSE; + { + result = false; /* they aren't equal */ + break; + } } - return TRUE; + MemoryContextSwitchTo(oldContext); + + return result; } /* diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 34b0a269a5..9c2d293858 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -28,7 +28,8 @@ #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); @@ -45,7 +46,7 @@ ExecHash(Hash *node) EState *estate; HashState *hashstate; Plan *outerNode; - Var *hashkey; + Node *hashkey; HashJoinTable hashtable; TupleTableSlot *slot; ExprContext *econtext; @@ -139,12 +140,9 @@ ExecInitHash(Hash *node, EState *estate, Plan *parent) /* ---------------- * 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); /* ---------------- @@ -204,6 +202,7 @@ ExecEndHash(Hash *node) * ---------------- */ ExecFreeProjectionInfo(&hashstate->cstate); + ExecFreeExprContext(&hashstate->cstate); /* ---------------- * shut down the subplan @@ -236,6 +235,7 @@ ExecHashTableCreate(Hash *node) int totalbuckets; int bucketsize; int i; + Type typeInfo; MemoryContext oldcxt; /* ---------------- @@ -346,6 +346,14 @@ ExecHashTableCreate(Hash *node) 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. @@ -448,7 +456,7 @@ ExecHashTableDestroy(HashJoinTable hashtable) void ExecHashTableInsert(HashJoinTable hashtable, ExprContext *econtext, - Var *hashkey) + Node *hashkey) { int bucketno = ExecHashGetBucket(hashtable, econtext, hashkey); TupleTableSlot *slot = econtext->ecxt_innertuple; @@ -508,43 +516,44 @@ ExecHashTableInsert(HashJoinTable hashtable, 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; @@ -585,6 +594,9 @@ ExecScanHashBucket(HashJoinState *hjstate, 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; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index d1cb1fe242..e136c13154 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -51,13 +51,12 @@ ExecHashJoin(HashJoin *node) 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; @@ -73,7 +72,6 @@ ExecHashJoin(HashJoin *node) hashNode = (Hash *) innerPlan(node); outerNode = outerPlan(node); hashPhaseDone = node->hashdone; - dir = estate->es_direction; /* ----------------- @@ -81,13 +79,21 @@ ExecHashJoin(HashJoin *node) * ----------------- */ hashtable = hjstate->hj_HashTable; - - /* -------------------- - * initialize expression context - * -------------------- - */ econtext = hjstate->jstate.cs_ExprContext; + /* ---------------- + * 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 (hjstate->jstate.cs_TupFromTlist) { TupleTableSlot *result; @@ -96,6 +102,8 @@ ExecHashJoin(HashJoin *node) result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone); if (!isDone) return result; + /* Done with that source tuple... */ + hjstate->jstate.cs_TupFromTlist = false; } /* ---------------- @@ -112,8 +120,7 @@ ExecHashJoin(HashJoin *node) */ 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 @@ -139,7 +146,7 @@ ExecHashJoin(HashJoin *node) * ---------------- */ outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot; - outerVar = get_leftop(clause); + outerVar = (Node *) get_leftop(clause); for (;;) { @@ -220,6 +227,10 @@ ExecHashJoin(HashJoin *node) 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 @@ -279,12 +290,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent) /* ---------------- * 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 @@ -343,10 +351,10 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent) 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; } @@ -396,6 +404,7 @@ ExecEndHashJoin(HashJoin *node) * ---------------- */ ExecFreeProjectionInfo(&hjstate->jstate); + ExecFreeExprContext(&hjstate->jstate); /* ---------------- * clean up subtrees @@ -510,7 +519,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate) BufFile *innerFile; TupleTableSlot *slot; ExprContext *econtext; - Var *innerhashkey; + Node *innerhashkey; if (newbatch > 1) { @@ -651,10 +660,10 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent) 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 diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index be5617d5ef..0eb3b9f5ae 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -79,6 +79,7 @@ IndexNext(IndexScan *node) EState *estate; CommonScanState *scanstate; IndexScanState *indexstate; + ExprContext *econtext; ScanDirection direction; Snapshot snapshot; IndexScanDescPtr scanDescs; @@ -89,7 +90,6 @@ IndexNext(IndexScan *node) TupleTableSlot *slot; Buffer buffer = InvalidBuffer; int numIndices; - bool bBackward; int indexNumber; @@ -112,6 +112,7 @@ IndexNext(IndexScan *node) scanDescs = indexstate->iss_ScanDescs; heapRelation = scanstate->css_currentRelation; numIndices = indexstate->iss_NumIndices; + econtext = scanstate->cstate.cs_ExprContext; slot = scanstate->css_ScanTupleSlot; /* @@ -133,14 +134,15 @@ IndexNext(IndexScan *node) 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 */ @@ -219,14 +221,13 @@ IndexNext(IndexScan *node) * 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; @@ -234,7 +235,7 @@ IndexNext(IndexScan *node) 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); } @@ -284,7 +285,7 @@ ExecIndexScan(IndexScan *node) * use IndexNext as access method * ---------------- */ - return ExecScan(&node->scan, IndexNext); + return ExecScan(&node->scan, (ExecScanAccessMtd) IndexNext); } /* ---------------------------------------------------------------- @@ -293,9 +294,8 @@ ExecIndexScan(IndexScan *node) * 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. * * ---------------------------------------------------------------- */ @@ -304,6 +304,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent) { EState *estate; IndexScanState *indexstate; + ExprContext *econtext; ScanDirection direction; IndexScanDescPtr scanDescs; ScanKey *scanKeys; @@ -311,8 +312,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent) ScanKey skey; int numIndices; int i; - - Pointer *runtimeKeyInfo; + int **runtimeKeyInfo; int *numScanKeys; List *indxqual; List *qual; @@ -326,22 +326,34 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent) 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 && @@ -364,7 +376,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent) if (runtimeKeyInfo) { - run_keys = (int *) runtimeKeyInfo[i]; + run_keys = runtimeKeyInfo[i]; for (j = 0; j < n_keys; j++) { @@ -373,6 +385,13 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent) * 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) { @@ -385,10 +404,10 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent) * 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; @@ -401,11 +420,6 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent) skey = scanKeys[i]; index_rescan(scan, direction, skey); } - /* ---------------- - * perhaps return something meaningful - * ---------------- - */ - return; } /* ---------------------------------------------------------------- @@ -421,7 +435,7 @@ ExecEndIndexScan(IndexScan *node) { CommonScanState *scanstate; IndexScanState *indexstate; - Pointer *runtimeKeyInfo; + int **runtimeKeyInfo; ScanKey *scanKeys; List *indxqual; int *numScanKeys; @@ -431,7 +445,7 @@ ExecEndIndexScan(IndexScan *node) scanstate = node->scan.scanstate; indexstate = node->indxstate; indxqual = node->indxqual; - runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo; + runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo; /* ---------------- * extract information from the node @@ -451,6 +465,9 @@ ExecEndIndexScan(IndexScan *node) * ---------------- */ ExecFreeProjectionInfo(&scanstate->cstate); + ExecFreeExprContext(&scanstate->cstate); + if (indexstate->iss_RuntimeContext) + FreeExprContext(indexstate->iss_RuntimeContext); /* ---------------- * close the heap and index relations @@ -474,12 +491,7 @@ ExecEndIndexScan(IndexScan *node) { 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); @@ -491,7 +503,6 @@ ExecEndIndexScan(IndexScan *node) */ ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); ExecClearTuple(scanstate->css_ScanTupleSlot); -/* ExecClearTuple(scanstate->css_RawTupleSlot); */ } /* ---------------------------------------------------------------- @@ -562,7 +573,7 @@ ExecIndexRestrPos(IndexScan *node) * * 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. @@ -583,19 +594,16 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) 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 @@ -610,25 +618,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) * -------------------------------- */ 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); @@ -640,7 +635,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) */ ExecInitResultTupleSlot(estate, &scanstate->cstate); ExecInitScanTupleSlot(estate, scanstate); -/* ExecInitRawTupleSlot(estate, scanstate); */ /* ---------------- * initialize projection info. result type comes from scan desc @@ -661,19 +655,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) 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 * ---------------- @@ -696,12 +683,11 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) 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 @@ -719,9 +705,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) 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); @@ -966,12 +952,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) } /* ---------------- - * 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; @@ -988,12 +974,35 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) * (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 @@ -1026,7 +1035,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) scanstate->css_currentRelation = currentRelation; scanstate->css_currentScanDesc = currentScanDesc; - /* ---------------- * get the scan type from the relation descriptor. * ---------------- @@ -1034,12 +1042,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) 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. @@ -1066,10 +1068,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) 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; diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c index 1d5c904248..c0a94066be 100644 --- a/src/backend/executor/nodeMaterial.c +++ b/src/backend/executor/nodeMaterial.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -158,17 +158,12 @@ ExecInitMaterial(Material *node, EState *estate, Plan *parent) 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 /* ---------------- diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 0d522f0d51..a3f92b0690 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -202,45 +202,53 @@ MJFormSkipQual(List *qualList, char *replaceopname) 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), @@ -248,17 +256,15 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext) &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; } /* ---------------------------------------------------------------- @@ -403,24 +409,18 @@ ExecMergeJoin(MergeJoin *node) 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 /* ---------------- @@ -448,20 +448,34 @@ ExecMergeJoin(MergeJoin *node) } /* ---------------- - * 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 (;;) { /* ---------------- @@ -547,6 +561,8 @@ ExecMergeJoin(MergeJoin *node) case EXEC_MJ_JOINTEST: MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n"); + ResetExprContext(econtext); + qualResult = ExecQual((List *) mergeclauses, econtext, false); MJ_DEBUG_QUAL(mergeclauses, qualResult); @@ -565,6 +581,14 @@ ExecMergeJoin(MergeJoin *node) 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); @@ -693,6 +717,8 @@ ExecMergeJoin(MergeJoin *node) innerTupleSlot = econtext->ecxt_innertuple; econtext->ecxt_innertuple = mergestate->mj_MarkedTupleSlot; + ResetExprContext(econtext); + qualResult = ExecQual((List *) mergeclauses, econtext, false); MJ_DEBUG_QUAL(mergeclauses, qualResult); @@ -709,11 +735,7 @@ ExecMergeJoin(MergeJoin *node) */ ExecRestrPos(innerPlan); -#if 0 - mergestate->mj_JoinState = EXEC_MJ_JOINTEST; -#endif mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; - } else { @@ -777,6 +799,8 @@ ExecMergeJoin(MergeJoin *node) * we update the marked tuple and go join them. * ---------------- */ + ResetExprContext(econtext); + qualResult = ExecQual((List *) mergeclauses, econtext, false); MJ_DEBUG_QUAL(mergeclauses, qualResult); @@ -886,6 +910,8 @@ ExecMergeJoin(MergeJoin *node) * we update the marked tuple and go join them. * ---------------- */ + ResetExprContext(econtext); + qualResult = ExecQual((List *) mergeclauses, econtext, false); MJ_DEBUG_QUAL(mergeclauses, qualResult); @@ -1142,12 +1168,9 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent) /* ---------------- * 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 @@ -1251,6 +1274,7 @@ ExecEndMergeJoin(MergeJoin *node) * ---------------- */ ExecFreeProjectionInfo(&mergestate->jstate); + ExecFreeExprContext(&mergestate->jstate); /* ---------------- * shut down the subplans diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index ec71ad7ac7..0186e39436 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -32,18 +32,18 @@ * * 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. @@ -60,10 +60,8 @@ ExecNestLoop(NestLoop *node, Plan *parent) Plan *innerPlan; Plan *outerPlan; bool needNewOuterTuple; - TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *qual; ExprContext *econtext; @@ -77,11 +75,6 @@ ExecNestLoop(NestLoop *node, Plan *parent) qual = node->join.qual; outerPlan = outerPlan(&node->join); innerPlan = innerPlan(&node->join); - - /* ---------------- - * initialize expression context - * ---------------- - */ econtext = nlstate->jstate.cs_ExprContext; /* ---------------- @@ -92,11 +85,18 @@ ExecNestLoop(NestLoop *node, Plan *parent) 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; @@ -105,9 +105,17 @@ ExecNestLoop(NestLoop *node, Plan *parent) 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 (;;) { /* ---------------- @@ -115,15 +123,7 @@ ExecNestLoop(NestLoop *node, Plan *parent) * 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. @@ -229,9 +229,11 @@ ExecNestLoop(NestLoop *node, Plan *parent) } /* ---------------- - * 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"); } } @@ -263,18 +265,14 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent) * ---------------- */ 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 @@ -348,6 +346,7 @@ ExecEndNestLoop(NestLoop *node) * ---------------- */ ExecFreeProjectionInfo(&nlstate->jstate); + ExecFreeExprContext(&nlstate->jstate); /* ---------------- * close down subplans @@ -386,9 +385,7 @@ ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent) 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; } diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 5bf132520c..a1daaf52c4 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -3,21 +3,18 @@ * 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 * @@ -27,13 +24,22 @@ * / * 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" @@ -41,7 +47,7 @@ /* ---------------------------------------------------------------- * 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 @@ -67,15 +73,17 @@ ExecResult(Result *node) * ---------------- */ resstate = node->resstate; - - /* ---------------- - * get the expression context - * ---------------- - */ econtext = resstate->cstate.cs_ExprContext; /* ---------------- - * check tautological qualifications like (2 > 1) + * Reset per-tuple memory context to free any expression evaluation + * storage allocated in the previous tuple cycle. + * ---------------- + */ + ResetExprContext(econtext); + + /* ---------------- + * check constant qualifications like (2 > 1), if not already done * ---------------- */ if (resstate->rs_checkqual) @@ -92,74 +100,64 @@ ExecResult(Result *node) } } + /* ---------------- + * 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; @@ -200,14 +198,11 @@ ExecInitResult(Result *node, EState *estate, Plan *parent) 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 @@ -247,7 +242,7 @@ ExecCountSlotsResult(Result *node) /* ---------------------------------------------------------------- * ExecEndResult * - * fees up storage allocated through C routines + * frees up storage allocated through C routines * ---------------------------------------------------------------- */ void @@ -266,9 +261,8 @@ ExecEndResult(Result *node) * 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 @@ -301,5 +295,4 @@ ExecReScanResult(Result *node, ExprContext *exprCtxt, Plan *parent) if (((Plan *) node)->lefttree && ((Plan *) node)->lefttree->chgParam == NULL) ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); - } diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 24de618401..b953dcd369 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -31,8 +31,7 @@ #include "parser/parsetree.h" static Oid InitScanRelation(SeqScan *node, EState *estate, - CommonScanState *scanstate, Plan *outerPlan); - + CommonScanState *scanstate); static TupleTableSlot *SeqNext(SeqScan *node); /* ---------------------------------------------------------------- @@ -132,25 +131,11 @@ 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); } /* ---------------------------------------------------------------- @@ -162,7 +147,7 @@ ExecSeqScan(SeqScan *node) */ static Oid InitScanRelation(SeqScan *node, EState *estate, - CommonScanState *scanstate, Plan *outerPlan) + CommonScanState *scanstate) { Index relid; List *rangeTable; @@ -173,84 +158,56 @@ InitScanRelation(SeqScan *node, EState *estate, 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 * ---------------- @@ -265,13 +222,11 @@ ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent) 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 @@ -283,12 +238,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent) 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; @@ -315,15 +268,12 @@ ExecCountSlotsSeqScan(SeqScan *node) * 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 @@ -341,6 +291,7 @@ ExecEndSeqScan(SeqScan *node) * ---------------- */ ExecFreeProjectionInfo(&scanstate->cstate); + ExecFreeExprContext(&scanstate->cstate); /* ---------------- * close scan relation @@ -348,13 +299,6 @@ ExecEndSeqScan(SeqScan *node) */ 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 * ---------------- @@ -367,6 +311,7 @@ ExecEndSeqScan(SeqScan *node) * Join Support * ---------------------------------------------------------------- */ + /* ---------------------------------------------------------------- * ExecSeqReScan * @@ -378,7 +323,6 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent) { CommonScanState *scanstate; EState *estate; - Plan *outerPlan; Relation rel; HeapScanDesc scan; ScanDirection direction; @@ -386,28 +330,18 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent) scanstate = node->scanstate; estate = node->plan.state; - if ((outerPlan = outerPlan((Plan *) node)) != NULL) + /* If this is re-scanning of PlanQual ... */ + if (estate->es_evTuple != NULL && + estate->es_evTuple[node->scanrelid - 1] != 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) - { - 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; } /* ---------------------------------------------------------------- @@ -420,33 +354,11 @@ void 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; } /* ---------------------------------------------------------------- @@ -459,28 +371,9 @@ void 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); } diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c index f8b5571a57..6f2e1f7f47 100644 --- a/src/backend/executor/nodeSort.c +++ b/src/backend/executor/nodeSort.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -255,14 +255,10 @@ ExecInitSort(Sort *node, EState *estate, Plan *parent) /* ---------------- * 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 /* ---------------- diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 99b09f685a..3d331c714f 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -37,11 +37,19 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) 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"); @@ -52,12 +60,16 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) { 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)); @@ -84,7 +96,7 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) * 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); @@ -93,12 +105,16 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) { 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) { @@ -172,8 +188,10 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) /* * 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. @@ -188,9 +206,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) /* 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 */ } @@ -200,9 +218,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) /* 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 */ } @@ -215,9 +233,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) /* 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 */ } @@ -227,9 +245,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) /* 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 */ } @@ -252,11 +270,13 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) */ if (subLinkType == EXPR_SUBLINK || subLinkType == MULTIEXPR_SUBLINK) { - result = (Datum) false; + result = (Datum) 0; *isNull = true; } } + MemoryContextSwitchTo(oldcontext); + return result; } @@ -277,13 +297,13 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent) 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 @@ -317,14 +337,21 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent) * ---------------------------------------------------------------- */ 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"); @@ -345,7 +372,7 @@ ExecSetParamPlan(SubPlan *node) 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; @@ -386,7 +413,7 @@ ExecSetParamPlan(SubPlan *node) 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 @@ -396,16 +423,18 @@ ExecSetParamPlan(SubPlan *node) 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; } } @@ -416,10 +445,10 @@ ExecSetParamPlan(SubPlan *node) void ExecEndSubPlan(SubPlan *node) { - if (node->shutdown) + if (node->needShutdown) { ExecEndNode(node->plan, node->plan); - node->shutdown = false; + node->needShutdown = false; } if (node->curTuple) { diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 0978c1ec03..824ead5ec6 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -38,12 +38,16 @@ TidListCreate(List *evalList, ExprContext *econtext, ItemPointer *tidList) 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; @@ -243,7 +247,7 @@ ExecTidScan(TidScan *node) * use TidNext as access method * ---------------- */ - return ExecScan(&node->scan, TidNext); + return ExecScan(&node->scan, (ExecScanAccessMtd) TidNext); } /* ---------------------------------------------------------------- @@ -319,6 +323,7 @@ ExecEndTidScan(TidScan *node) * ---------------- */ ExecFreeProjectionInfo(&scanstate->cstate); + ExecFreeExprContext(&scanstate->cstate); /* ---------------- * close the heap and tid relations @@ -332,7 +337,6 @@ ExecEndTidScan(TidScan *node) */ ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); ExecClearTuple(scanstate->css_ScanTupleSlot); -/* ExecClearTuple(scanstate->css_RawTupleSlot); */ } /* ---------------------------------------------------------------- @@ -394,11 +398,8 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent) RangeTblEntry *rtentry; Oid relid; Oid reloid; - Relation currentRelation; - int baseid; - - List *execParam = NULL; + List *execParam = NIL; /* ---------------- * assign execution state to node @@ -413,25 +414,12 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent) * -------------------------------- */ 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); @@ -443,7 +431,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent) */ ExecInitResultTupleSlot(estate, &scanstate->cstate); ExecInitScanTupleSlot(estate, scanstate); -/* ExecInitRawTupleSlot(estate, scanstate); */ /* ---------------- * initialize projection info. result type comes from scan desc @@ -461,14 +448,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent) 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 * ---------------- @@ -514,14 +493,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent) 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. diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c index add569a2c2..29c790b2c7 100644 --- a/src/backend/executor/nodeUnique.c +++ b/src/backend/executor/nodeUnique.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -88,27 +88,32 @@ ExecUnique(Unique *node) 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; } @@ -143,14 +148,17 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent) /* ---------------- * 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 /* ------------ @@ -207,6 +215,8 @@ ExecEndUnique(Unique *node) ExecEndNode(outerPlan((Plan *) node), (Plan *) node); + MemoryContextDelete(uniquestate->tempContext); + /* clean up tuple table */ ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot); if (uniquestate->priorTuple != NULL) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 605fe70e6c..bc305382df 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -19,7 +19,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -578,7 +578,7 @@ _copySubPlan(SubPlan *from) Node_Copy(from, newnode, sublink); /* do not copy execution state */ - newnode->shutdown = false; + newnode->needShutdown = false; newnode->curTuple = NULL; return newnode; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 308f4d90e8..b881478618 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -24,7 +24,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -184,8 +184,8 @@ _equalConst(Const *a, Const *b) */ 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 diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b9830edc22..2999c4263b 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -6,7 +6,7 @@ * 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 @@ -1155,10 +1155,10 @@ _outJoinInfo(StringInfo str, JoinInfo *node) static void _outDatum(StringInfo str, Datum value, Oid type) { - char *s; - Size length, - typeLength; bool byValue; + int typeLength; + Size length; + char *s; int i; /* @@ -1167,12 +1167,12 @@ _outDatum(StringInfo str, Datum value, Oid type) */ 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, "] "); @@ -1184,14 +1184,7 @@ _outDatum(StringInfo str, Datum value, Oid type) 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, "] "); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index f872d952d0..4754cbc327 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * 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 @@ -608,7 +608,7 @@ _readHash() _getPlan((Plan *) local_node); token = lsptok(NULL, &length); /* eat :hashkey */ - local_node->hashkey = (Var *) nodeRead(true); + local_node->hashkey = nodeRead(true); return local_node; } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 4915133d0a..bf91b8d1a6 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -66,7 +66,7 @@ static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree, 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); @@ -664,7 +664,7 @@ create_hashjoin_node(HashPath *best_path, List *hashclauses; HashJoin *join_node; Hash *hash_node; - Var *innerhashkey; + Node *innerhashkey; /* * NOTE: there will always be exactly one hashclause in the list @@ -694,7 +694,7 @@ create_hashjoin_node(HashPath *best_path, (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. @@ -1103,7 +1103,7 @@ make_hashjoin(List *tlist, } 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; diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index cd624fb111..6d69171f1b 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -649,7 +649,7 @@ SS_finalize_plan(Plan *plan) break; case T_Hash: - finalize_primnode((Node *) ((Hash *) plan)->hashkey, + finalize_primnode(((Hash *) plan)->hashkey, &results); break; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index f1963d2296..d3a813fb86 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * 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 @@ -30,6 +30,7 @@ #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" @@ -1317,7 +1318,10 @@ simplify_op_or_func(Expr *expr, List *args) 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; @@ -1424,25 +1428,35 @@ simplify_op_or_func(Expr *expr, List *args) 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); } diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 9e52930e20..57818afb9e 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -72,7 +72,6 @@ CreateExecutorState(void) state->es_param_list_info = NULL; state->es_param_exec_vals = NULL; - state->es_BaseId = 0; state->es_tupleTable = NULL; state->es_junkFilter = NULL; diff --git a/src/backend/utils/adt/datum.c b/src/backend/utils/adt/datum.c index a86ec87067..7f590e06e4 100644 --- a/src/backend/utils/adt/datum.c +++ b/src/backend/utils/adt/datum.c @@ -1,13 +1,14 @@ /*------------------------------------------------------------------------- * * 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 $ * *------------------------------------------------------------------------- */ @@ -16,7 +17,7 @@ * * 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. @@ -27,15 +28,19 @@ * 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 @@ -45,47 +50,31 @@ *------------------------------------------------------------------------- */ 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; } } @@ -97,37 +86,29 @@ datumGetSize(Datum value, Oid type, bool byVal, Size len) * * 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; } @@ -143,21 +124,12 @@ datumCopy(Datum value, Oid type, bool byVal, Size len) */ #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); } } @@ -174,46 +146,41 @@ datumFree(Datum value, Oid type, bool byVal, Size len) * 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; } diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index 8247e16812..b5380a8c52 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -436,25 +436,38 @@ textpos(PG_FUNCTION_ARGS) /* * 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 @@ -462,19 +475,28 @@ textne(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(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); - 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); } /* varstr_cmp() @@ -545,6 +567,10 @@ text_cmp(text *arg1, text *arg2) /* * 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 @@ -552,8 +578,14 @@ text_lt(PG_FUNCTION_ARGS) { 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 @@ -561,8 +593,14 @@ text_le(PG_FUNCTION_ARGS) { 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 @@ -570,8 +608,14 @@ text_gt(PG_FUNCTION_ARGS) { 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 @@ -579,8 +623,14 @@ text_ge(PG_FUNCTION_ARGS) { 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 @@ -589,14 +639,8 @@ text_larger(PG_FUNCTION_ARGS) 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); } @@ -607,14 +651,8 @@ text_smaller(PG_FUNCTION_ARGS) 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); } diff --git a/src/backend/utils/cache/fcache.c b/src/backend/utils/cache/fcache.c index 7e9d18c7e2..ba34dfd03d 100644 --- a/src/backend/utils/cache/fcache.c +++ b/src/backend/utils/cache/fcache.c @@ -8,7 +8,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -75,6 +75,7 @@ init_fcache(Oid foid, retval = (FunctionCachePtr) palloc(sizeof(FunctionCache)); MemSet(retval, 0, sizeof(FunctionCache)); + retval->fcacheCxt = CurrentMemoryContext; /* ---------------- * get the procedure tuple corresponding to the given functionOid @@ -256,22 +257,26 @@ init_fcache(Oid foid, 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); } diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c index bf212ff7bb..6c6f58b022 100644 --- a/src/backend/utils/mmgr/aset.c +++ b/src/backend/utils/mmgr/aset.c @@ -11,7 +11,7 @@ * 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 @@ -349,20 +349,21 @@ AllocSetReset(MemoryContext context) 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); } diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index dd495517d3..66b4f601be 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -53,9 +53,6 @@ extern void setRelhasindexInplace(Oid relid, bool hasindex, bool immediate); 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, diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 703c907e12..b835b29044 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -72,19 +72,16 @@ extern void ExecEndNode(Plan *node, Plan *parent); /* * 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); @@ -92,7 +89,9 @@ 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 @@ -121,8 +120,6 @@ extern void SetChangedParamList(Plan *node, List *newchg); * 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); @@ -133,7 +130,6 @@ extern TupleDesc ExecGetResultType(CommonState *commonstate); 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); @@ -141,6 +137,13 @@ extern void ExecAssignScanTypeFromOuterPlan(Plan *node, 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, diff --git a/src/include/executor/hashjoin.h b/src/include/executor/hashjoin.h index 285bb314d3..8d4cb98469 100644 --- a/src/include/executor/hashjoin.h +++ b/src/include/executor/hashjoin.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -21,8 +21,8 @@ * 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. * @@ -68,6 +68,14 @@ typedef struct HashTableData 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 diff --git a/src/include/executor/nodeGroup.h b/src/include/executor/nodeGroup.h index 303516b167..71bd3b52ff 100644 --- a/src/include/executor/nodeGroup.h +++ b/src/include/executor/nodeGroup.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -27,7 +27,8 @@ extern bool execTuplesMatch(HeapTuple tuple1, TupleDesc tupdesc, int numCols, AttrNumber *matchColIdx, - FmgrInfo *eqfunctions); + FmgrInfo *eqfunctions, + MemoryContext evalContext); extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc, int numCols, AttrNumber *matchColIdx); diff --git a/src/include/executor/nodeHash.h b/src/include/executor/nodeHash.h index b61ced7cdc..5fabe7d253 100644 --- a/src/include/executor/nodeHash.h +++ b/src/include/executor/nodeHash.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -25,10 +25,12 @@ extern int ExecCountSlotsHash(Hash *node); 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); diff --git a/src/include/executor/nodeSubplan.h b/src/include/executor/nodeSubplan.h index a56ae216f5..f7c46c3ca9 100644 --- a/src/include/executor/nodeSubplan.h +++ b/src/include/executor/nodeSubplan.h @@ -13,7 +13,7 @@ extern Datum ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, 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 */ diff --git a/src/include/fmgr.h b/src/include/fmgr.h index b0aea5df09..03d13e3d4e 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -11,7 +11,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -119,6 +119,21 @@ extern struct varlena * pg_detoast_datum_copy(struct varlena * datum); #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]) diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index a74f16348d..2c21dba9c2 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -76,29 +76,46 @@ typedef struct RelationInfo * 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 * @@ -122,18 +139,16 @@ typedef struct ProjectionInfo /* ---------------- * 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'. @@ -174,10 +189,6 @@ typedef struct JunkFilter * 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. @@ -185,11 +196,6 @@ typedef struct JunkFilter * 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 @@ -203,7 +209,6 @@ 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 */ @@ -249,27 +254,21 @@ typedef struct EState * ---------------------------------------------------------------- */ -/* 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; @@ -288,15 +287,6 @@ typedef struct CommonState * * 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 @@ -319,15 +309,6 @@ 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 @@ -349,22 +330,15 @@ 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 @@ -375,41 +349,39 @@ 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; @@ -418,28 +390,18 @@ typedef struct IndexScanState /* ---------------- * 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; @@ -455,39 +417,19 @@ typedef struct TidScanState /* ---------------- * 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; /* ---------------- @@ -497,17 +439,6 @@ typedef struct 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 @@ -531,17 +462,6 @@ 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 @@ -550,7 +470,7 @@ 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; @@ -567,22 +487,9 @@ typedef struct HashJoinState * materialize nodes are used to materialize the results * of a subplan into a temporary file. * + * csstate.css_ScanTupleSlot refers to output of underlying plan. + * * 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 - * - * 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 MaterialState @@ -594,12 +501,14 @@ 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 { @@ -607,12 +516,14 @@ 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 @@ -630,21 +541,6 @@ 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 @@ -664,15 +560,6 @@ 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 @@ -680,6 +567,7 @@ 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; @@ -687,15 +575,6 @@ typedef struct 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 diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 37006e621f..e348d25b2b 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -318,7 +318,7 @@ typedef struct Unique typedef struct Hash { Plan plan; - Var *hashkey; + Node *hashkey; HashState *hashstate; } Hash; @@ -370,7 +370,7 @@ typedef struct SubPlan * 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; diff --git a/src/include/utils/datum.h b/src/include/utils/datum.h index 9079919fa0..199cf3109e 100644 --- a/src/include/utils/datum.h +++ b/src/include/utils/datum.h @@ -1,64 +1,49 @@ /*------------------------------------------------------------------------- * * 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 +/* + * datumGetSize - find the "real" length of a datum + */ +extern Size datumGetSize(Datum value, bool typByVal, int typLen); -/*-------------------------------------------------------- - * SOME NOT VERY PORTABLE ROUTINES ??? - *-------------------------------------------------------- +/* + * datumCopy - make a copy of a datum. * - * In the implementation of the next routines we assume the following: + * If the datatype is pass-by-reference, memory is obtained with palloc(). + */ +extern Datum datumCopy(Datum value, bool typByVal, int typLen); + +/* + * datumFree - free a datum previously allocated by datumCopy, if any. * - * 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. + * Does nothing if datatype is pass-by-value. */ +extern void datumFree(Datum value, bool typByVal, int typLen); -/*--------------- - * datumGetSize - * find the "real" length of a datum - */ -extern Size datumGetSize(Datum value, Oid type, bool byVal, Size len); - -/*--------------- - * datumCopy - * make a copy of a datum. - */ -extern Datum datumCopy(Datum value, Oid type, bool byVal, Size len); - -/*--------------- - * datumFree - * free space that *might* have been palloced by "datumCopy" - */ -extern void datumFree(Datum value, Oid type, bool byVal, Size len); - -/*--------------- +/* * 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 */ diff --git a/src/include/utils/fcache.h b/src/include/utils/fcache.h index db3a05baf4..59f35867a7 100644 --- a/src/include/utils/fcache.h +++ b/src/include/utils/fcache.h @@ -1,13 +1,17 @@ /*------------------------------------------------------------------------- * * 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 $ * *------------------------------------------------------------------------- */ @@ -31,6 +35,11 @@ typedef struct * 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 */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 5b8c6d773f..8268299f2f 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * 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. * @@ -47,17 +47,17 @@ #include "plpgsql.h" #include "pl.tab.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/syscache.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 "fmgr.h" +#include "tcop/tcopprot.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" static PLpgSQL_function *error_info_func = NULL; @@ -2205,7 +2205,7 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate, * 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; @@ -2286,12 +2286,20 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate, * ---------- */ 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. * ----------