First stage of reclaiming memory in executor by resetting short-term
memory contexts. Currently, only leaks in expressions executed as quals or projections are handled. Clean up some old dead cruft in executor while at it --- unused fields in state nodes, that sort of thing.
This commit is contained in:
parent
46fb9c29e2
commit
badce86a2c
|
@ -6,7 +6,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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);
|
tupleTable = ExecCreateTupleTable(1);
|
||||||
slot = ExecAllocTableSlot(tupleTable);
|
slot = ExecAllocTableSlot(tupleTable);
|
||||||
econtext = makeNode(ExprContext);
|
ExecSetSlotDescriptor(slot, hd);
|
||||||
FillDummyExprContext(econtext, slot, hd, InvalidBuffer);
|
econtext = MakeExprContext(slot, TransactionCommandContext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
/* shut the compiler up */
|
|
||||||
{
|
{
|
||||||
tupleTable = NULL;
|
tupleTable = NULL;
|
||||||
slot = NULL;
|
slot = NULL;
|
||||||
|
@ -161,13 +160,13 @@ gistbuild(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
nh++;
|
nh++;
|
||||||
|
|
||||||
|
#ifndef OMIT_PARTIAL_INDEX
|
||||||
/*
|
/*
|
||||||
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
||||||
* this tuple if it was already in the existing partial index
|
* this tuple if it was already in the existing partial index
|
||||||
*/
|
*/
|
||||||
if (oldPred != NULL)
|
if (oldPred != NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
/* SetSlotContents(slot, htup); */
|
/* SetSlotContents(slot, htup); */
|
||||||
slot->val = htup;
|
slot->val = htup;
|
||||||
if (ExecQual((List *) oldPred, econtext, false))
|
if (ExecQual((List *) oldPred, econtext, false))
|
||||||
|
@ -175,7 +174,6 @@ gistbuild(PG_FUNCTION_ARGS)
|
||||||
ni++;
|
ni++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -184,13 +182,12 @@ gistbuild(PG_FUNCTION_ARGS)
|
||||||
*/
|
*/
|
||||||
if (pred != NULL)
|
if (pred != NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
/* SetSlotContents(slot, htup); */
|
/* SetSlotContents(slot, htup); */
|
||||||
slot->val = htup;
|
slot->val = htup;
|
||||||
if (!ExecQual((List *) pred, econtext, false))
|
if (!ExecQual((List *) pred, econtext, false))
|
||||||
continue;
|
continue;
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
|
|
||||||
ni++;
|
ni++;
|
||||||
|
|
||||||
|
@ -262,13 +259,13 @@ gistbuild(PG_FUNCTION_ARGS)
|
||||||
/* okay, all heap tuples are indexed */
|
/* okay, all heap tuples are indexed */
|
||||||
heap_endscan(scan);
|
heap_endscan(scan);
|
||||||
|
|
||||||
|
#ifndef OMIT_PARTIAL_INDEX
|
||||||
if (pred != NULL || oldPred != NULL)
|
if (pred != NULL || oldPred != NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
ExecDropTupleTable(tupleTable, true);
|
ExecDropTupleTable(tupleTable, true);
|
||||||
pfree(econtext);
|
FreeExprContext(econtext);
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since we just counted the tuples in the heap, we update its stats
|
* Since we just counted the tuples in the heap, we update its stats
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* This file contains only the public interface routines.
|
* This file contains only the public interface routines.
|
||||||
|
@ -102,15 +102,14 @@ hashbuild(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
tupleTable = ExecCreateTupleTable(1);
|
tupleTable = ExecCreateTupleTable(1);
|
||||||
slot = ExecAllocTableSlot(tupleTable);
|
slot = ExecAllocTableSlot(tupleTable);
|
||||||
econtext = makeNode(ExprContext);
|
ExecSetSlotDescriptor(slot, htupdesc);
|
||||||
FillDummyExprContext(econtext, slot, htupdesc, InvalidBuffer);
|
econtext = MakeExprContext(slot, TransactionCommandContext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
/* quiet the compiler */
|
|
||||||
{
|
{
|
||||||
|
tupleTable = NULL;
|
||||||
|
slot = NULL;
|
||||||
econtext = NULL;
|
econtext = NULL;
|
||||||
tupleTable = 0;
|
|
||||||
slot = 0;
|
|
||||||
}
|
}
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
|
|
||||||
|
@ -122,9 +121,9 @@ hashbuild(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
while (HeapTupleIsValid(htup = heap_getnext(hscan, 0)))
|
while (HeapTupleIsValid(htup = heap_getnext(hscan, 0)))
|
||||||
{
|
{
|
||||||
|
|
||||||
nhtups++;
|
nhtups++;
|
||||||
|
|
||||||
|
#ifndef OMIT_PARTIAL_INDEX
|
||||||
/*
|
/*
|
||||||
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
||||||
* this tuple if it was already in the existing partial index
|
* this tuple if it was already in the existing partial index
|
||||||
|
@ -132,14 +131,12 @@ hashbuild(PG_FUNCTION_ARGS)
|
||||||
if (oldPred != NULL)
|
if (oldPred != NULL)
|
||||||
{
|
{
|
||||||
/* SetSlotContents(slot, htup); */
|
/* SetSlotContents(slot, htup); */
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
slot->val = htup;
|
slot->val = htup;
|
||||||
if (ExecQual((List *) oldPred, econtext, false))
|
if (ExecQual((List *) oldPred, econtext, false))
|
||||||
{
|
{
|
||||||
nitups++;
|
nitups++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -148,13 +145,12 @@ hashbuild(PG_FUNCTION_ARGS)
|
||||||
*/
|
*/
|
||||||
if (pred != NULL)
|
if (pred != NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
/* SetSlotContents(slot, htup); */
|
/* SetSlotContents(slot, htup); */
|
||||||
slot->val = htup;
|
slot->val = htup;
|
||||||
if (!ExecQual((List *) pred, econtext, false))
|
if (!ExecQual((List *) pred, econtext, false))
|
||||||
continue;
|
continue;
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
|
|
||||||
nitups++;
|
nitups++;
|
||||||
|
|
||||||
|
@ -221,13 +217,13 @@ hashbuild(PG_FUNCTION_ARGS)
|
||||||
/* okay, all heap tuples are indexed */
|
/* okay, all heap tuples are indexed */
|
||||||
heap_endscan(hscan);
|
heap_endscan(hscan);
|
||||||
|
|
||||||
|
#ifndef OMIT_PARTIAL_INDEX
|
||||||
if (pred != NULL || oldPred != NULL)
|
if (pred != NULL || oldPred != NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
ExecDropTupleTable(tupleTable, true);
|
ExecDropTupleTable(tupleTable, true);
|
||||||
pfree(econtext);
|
FreeExprContext(econtext);
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since we just counted the tuples in the heap, we update its stats
|
* Since we just counted the tuples in the heap, we update its stats
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
*
|
*
|
||||||
|
@ -27,6 +27,10 @@
|
||||||
* that work on 32-bit or wider datatypes can't just return "a - b".
|
* that work on 32-bit or wider datatypes can't just return "a - b".
|
||||||
* That could overflow and give the wrong answer.
|
* 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);
|
} 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
|
#endif
|
||||||
|
|
||||||
if (res != 0 || VARSIZE(a) == VARSIZE(b))
|
/* Avoid leaking memory when handed toasted input. */
|
||||||
PG_RETURN_INT32(res);
|
PG_FREE_IF_COPY(a, 0);
|
||||||
|
PG_FREE_IF_COPY(b, 1);
|
||||||
|
|
||||||
/*
|
PG_RETURN_INT32(res);
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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);
|
tupleTable = ExecCreateTupleTable(1);
|
||||||
slot = ExecAllocTableSlot(tupleTable);
|
slot = ExecAllocTableSlot(tupleTable);
|
||||||
econtext = makeNode(ExprContext);
|
ExecSetSlotDescriptor(slot, htupdesc);
|
||||||
FillDummyExprContext(econtext, slot, htupdesc, InvalidBuffer);
|
econtext = MakeExprContext(slot, TransactionCommandContext);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* we never want to use sort/build if we are extending an existing
|
* we never want to use sort/build if we are extending an existing
|
||||||
|
@ -151,14 +151,13 @@ btbuild(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
nhtups++;
|
nhtups++;
|
||||||
|
|
||||||
|
#ifndef OMIT_PARTIAL_INDEX
|
||||||
/*
|
/*
|
||||||
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
||||||
* this tuple if it was already in the existing partial index
|
* this tuple if it was already in the existing partial index
|
||||||
*/
|
*/
|
||||||
if (oldPred != NULL)
|
if (oldPred != NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
|
|
||||||
/* SetSlotContents(slot, htup); */
|
/* SetSlotContents(slot, htup); */
|
||||||
slot->val = htup;
|
slot->val = htup;
|
||||||
if (ExecQual((List *) oldPred, econtext, false))
|
if (ExecQual((List *) oldPred, econtext, false))
|
||||||
|
@ -166,7 +165,6 @@ btbuild(PG_FUNCTION_ARGS)
|
||||||
nitups++;
|
nitups++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -175,13 +173,12 @@ btbuild(PG_FUNCTION_ARGS)
|
||||||
*/
|
*/
|
||||||
if (pred != NULL)
|
if (pred != NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
/* SetSlotContents(slot, htup); */
|
/* SetSlotContents(slot, htup); */
|
||||||
slot->val = htup;
|
slot->val = htup;
|
||||||
if (!ExecQual((List *) pred, econtext, false))
|
if (!ExecQual((List *) pred, econtext, false))
|
||||||
continue;
|
continue;
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
|
|
||||||
nitups++;
|
nitups++;
|
||||||
|
|
||||||
|
@ -260,13 +257,13 @@ btbuild(PG_FUNCTION_ARGS)
|
||||||
/* okay, all heap tuples are indexed */
|
/* okay, all heap tuples are indexed */
|
||||||
heap_endscan(hscan);
|
heap_endscan(hscan);
|
||||||
|
|
||||||
|
#ifndef OMIT_PARTIAL_INDEX
|
||||||
if (pred != NULL || oldPred != NULL)
|
if (pred != NULL || oldPred != NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
ExecDropTupleTable(tupleTable, true);
|
ExecDropTupleTable(tupleTable, true);
|
||||||
pfree(econtext);
|
FreeExprContext(econtext);
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if we are doing bottom-up btree build, finish the build by (1)
|
* if we are doing bottom-up btree build, finish the build by (1)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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);
|
tupleTable = ExecCreateTupleTable(1);
|
||||||
slot = ExecAllocTableSlot(tupleTable);
|
slot = ExecAllocTableSlot(tupleTable);
|
||||||
econtext = makeNode(ExprContext);
|
ExecSetSlotDescriptor(slot, hd);
|
||||||
FillDummyExprContext(econtext, slot, hd, InvalidBuffer);
|
econtext = MakeExprContext(slot, TransactionCommandContext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
econtext = NULL;
|
|
||||||
tupleTable = NULL;
|
tupleTable = NULL;
|
||||||
slot = NULL;
|
slot = NULL;
|
||||||
|
econtext = NULL;
|
||||||
}
|
}
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
|
|
||||||
|
@ -156,13 +156,13 @@ rtbuild(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
nh++;
|
nh++;
|
||||||
|
|
||||||
|
#ifndef OMIT_PARTIAL_INDEX
|
||||||
/*
|
/*
|
||||||
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
||||||
* this tuple if it was already in the existing partial index
|
* this tuple if it was already in the existing partial index
|
||||||
*/
|
*/
|
||||||
if (oldPred != NULL)
|
if (oldPred != NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
/* SetSlotContents(slot, htup); */
|
/* SetSlotContents(slot, htup); */
|
||||||
slot->val = htup;
|
slot->val = htup;
|
||||||
if (ExecQual((List *) oldPred, econtext, false))
|
if (ExecQual((List *) oldPred, econtext, false))
|
||||||
|
@ -170,7 +170,6 @@ rtbuild(PG_FUNCTION_ARGS)
|
||||||
ni++;
|
ni++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -179,13 +178,12 @@ rtbuild(PG_FUNCTION_ARGS)
|
||||||
*/
|
*/
|
||||||
if (pred != NULL)
|
if (pred != NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
/* SetSlotContents(slot, htup); */
|
/* SetSlotContents(slot, htup); */
|
||||||
slot->val = htup;
|
slot->val = htup;
|
||||||
if (!ExecQual((List *) pred, econtext, false))
|
if (!ExecQual((List *) pred, econtext, false))
|
||||||
continue;
|
continue;
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
|
|
||||||
ni++;
|
ni++;
|
||||||
|
|
||||||
|
@ -239,13 +237,13 @@ rtbuild(PG_FUNCTION_ARGS)
|
||||||
/* okay, all heap tuples are indexed */
|
/* okay, all heap tuples are indexed */
|
||||||
heap_endscan(scan);
|
heap_endscan(scan);
|
||||||
|
|
||||||
|
#ifndef OMIT_PARTIAL_INDEX
|
||||||
if (pred != NULL || oldPred != NULL)
|
if (pred != NULL || oldPred != NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
ExecDropTupleTable(tupleTable, true);
|
ExecDropTupleTable(tupleTable, true);
|
||||||
pfree(econtext);
|
FreeExprContext(econtext);
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since we just counted the tuples in the heap, we update its stats
|
* Since we just counted the tuples in the heap, we update its stats
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* 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
|
* DefaultBuild
|
||||||
*
|
*
|
||||||
|
@ -1777,14 +1749,14 @@ DefaultBuild(Relation heapRelation,
|
||||||
{
|
{
|
||||||
tupleTable = ExecCreateTupleTable(1);
|
tupleTable = ExecCreateTupleTable(1);
|
||||||
slot = ExecAllocTableSlot(tupleTable);
|
slot = ExecAllocTableSlot(tupleTable);
|
||||||
econtext = makeNode(ExprContext);
|
ExecSetSlotDescriptor(slot, heapDescriptor);
|
||||||
FillDummyExprContext(econtext, slot, heapDescriptor, InvalidBuffer);
|
econtext = MakeExprContext(slot, TransactionCommandContext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
econtext = NULL;
|
tupleTable = NULL;
|
||||||
tupleTable = 0;
|
|
||||||
slot = NULL;
|
slot = NULL;
|
||||||
|
econtext = NULL;
|
||||||
}
|
}
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
|
|
||||||
|
@ -1812,7 +1784,6 @@ DefaultBuild(Relation heapRelation,
|
||||||
reltuples++;
|
reltuples++;
|
||||||
|
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
#ifndef OMIT_PARTIAL_INDEX
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
* If oldPred != NULL, this is an EXTEND INDEX command, so skip
|
||||||
* this tuple if it was already in the existing partial index
|
* 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 */
|
/* parameter was 'false', almost certainly wrong --- tgl 9/21/99 */
|
||||||
ExecDropTupleTable(tupleTable, true);
|
ExecDropTupleTable(tupleTable, true);
|
||||||
|
FreeExprContext(econtext);
|
||||||
}
|
}
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
char *predString;
|
||||||
Node **indexPred = NULL;
|
Node **indexPred = NULL;
|
||||||
TupleDesc rtupdesc;
|
TupleDesc rtupdesc;
|
||||||
ExprContext *econtext = NULL;
|
|
||||||
EState *estate = makeNode(EState); /* for ExecConstraints() */
|
EState *estate = makeNode(EState); /* for ExecConstraints() */
|
||||||
|
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
#ifndef OMIT_PARTIAL_INDEX
|
||||||
|
ExprContext *econtext = NULL;
|
||||||
TupleTable tupleTable;
|
TupleTable tupleTable;
|
||||||
TupleTableSlot *slot = NULL;
|
TupleTableSlot *slot = NULL;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
int natts;
|
int natts;
|
||||||
AttrNumber *attnumP;
|
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));
|
finfo = (FuncIndexInfo *) palloc(n_indices * sizeof(FuncIndexInfo));
|
||||||
finfoP = (FuncIndexInfo **) palloc(n_indices * sizeof(FuncIndexInfo *));
|
finfoP = (FuncIndexInfo **) palloc(n_indices * sizeof(FuncIndexInfo *));
|
||||||
indexPred = (Node **) palloc(n_indices * sizeof(Node *));
|
indexPred = (Node **) palloc(n_indices * sizeof(Node *));
|
||||||
econtext = NULL;
|
|
||||||
for (i = 0; i < n_indices; i++)
|
for (i = 0; i < n_indices; i++)
|
||||||
{
|
{
|
||||||
itupdescArr[i] = RelationGetDescr(index_rels[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)));
|
PointerGetDatum(&pgIndexP[i]->indpred)));
|
||||||
indexPred[i] = stringToNode(predString);
|
indexPred[i] = stringToNode(predString);
|
||||||
pfree(predString);
|
pfree(predString);
|
||||||
|
#ifndef OMIT_PARTIAL_INDEX
|
||||||
/* make dummy ExprContext for use by ExecQual */
|
/* make dummy ExprContext for use by ExecQual */
|
||||||
if (econtext == NULL)
|
if (econtext == NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
tupleTable = ExecCreateTupleTable(1);
|
tupleTable = ExecCreateTupleTable(1);
|
||||||
slot = ExecAllocTableSlot(tupleTable);
|
slot = ExecAllocTableSlot(tupleTable);
|
||||||
econtext = makeNode(ExprContext);
|
|
||||||
econtext->ecxt_scantuple = slot;
|
|
||||||
rtupdesc = RelationGetDescr(rel);
|
rtupdesc = RelationGetDescr(rel);
|
||||||
slot->ttc_tupleDescriptor = rtupdesc;
|
ExecSetSlotDescriptor(slot, rtupdesc);
|
||||||
|
econtext = MakeExprContext(slot,
|
||||||
/*
|
TransactionCommandContext);
|
||||||
* 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 */
|
|
||||||
}
|
}
|
||||||
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
indexPred[i] = NULL;
|
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++)
|
for (i = 0; i < n_indices; i++)
|
||||||
{
|
{
|
||||||
|
#ifndef OMIT_PARTIAL_INDEX
|
||||||
if (indexPred[i] != NULL)
|
if (indexPred[i] != NULL)
|
||||||
{
|
{
|
||||||
#ifndef OMIT_PARTIAL_INDEX
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* if tuple doesn't satisfy predicate, don't
|
* if tuple doesn't satisfy predicate, don't
|
||||||
* update index
|
* update index
|
||||||
|
@ -939,8 +917,8 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null
|
||||||
/* SetSlotContents(slot, tuple); */
|
/* SetSlotContents(slot, tuple); */
|
||||||
if (!ExecQual((List *) indexPred[i], econtext, false))
|
if (!ExecQual((List *) indexPred[i], econtext, false))
|
||||||
continue;
|
continue;
|
||||||
#endif /* OMIT_PARTIAL_INDEX */
|
|
||||||
}
|
}
|
||||||
|
#endif /* OMIT_PARTIAL_INDEX */
|
||||||
FormIndexDatum(indexNatts[i],
|
FormIndexDatum(indexNatts[i],
|
||||||
(AttrNumber *) &(pgIndexP[i]->indkey[0]),
|
(AttrNumber *) &(pgIndexP[i]->indkey[0]),
|
||||||
tuple,
|
tuple,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* ExecCloseR
|
||||||
*
|
*
|
||||||
* closes the relation and scan descriptor for a scan or sort
|
* closes the relation and scan descriptor for a scan node.
|
||||||
* node. Also closes index relations and scans for index scans.
|
* Also closes index relations and scans for index scans.
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
|
@ -197,20 +197,12 @@ ExecCloseR(Plan *node)
|
||||||
state = ((IndexScan *) node)->scan.scanstate;
|
state = ((IndexScan *) node)->scan.scanstate;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_Sort:
|
|
||||||
state = &(((Sort *) node)->sortstate->csstate);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_Agg:
|
|
||||||
state = &(((Agg *) node)->aggstate->csstate);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_TidScan:
|
case T_TidScan:
|
||||||
state = ((TidScan *) node)->scan.scanstate;
|
state = ((TidScan *) node)->scan.scanstate;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
elog(DEBUG, "ExecCloseR: not a scan or sort node!");
|
elog(DEBUG, "ExecCloseR: not a scan node!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,13 +229,12 @@ ExecCloseR(Plan *node)
|
||||||
if (IsA(node, IndexScan))
|
if (IsA(node, IndexScan))
|
||||||
{
|
{
|
||||||
IndexScan *iscan = (IndexScan *) node;
|
IndexScan *iscan = (IndexScan *) node;
|
||||||
IndexScanState *indexstate;
|
IndexScanState *indexstate = iscan->indxstate;
|
||||||
int numIndices;
|
int numIndices;
|
||||||
RelationPtr indexRelationDescs;
|
RelationPtr indexRelationDescs;
|
||||||
IndexScanDescPtr indexScanDescs;
|
IndexScanDescPtr indexScanDescs;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
indexstate = iscan->indxstate;
|
|
||||||
numIndices = indexstate->iss_NumIndices;
|
numIndices = indexstate->iss_NumIndices;
|
||||||
indexRelationDescs = indexstate->iss_RelationDescs;
|
indexRelationDescs = indexstate->iss_RelationDescs;
|
||||||
indexScanDescs = indexstate->iss_ScanDescs;
|
indexScanDescs = indexstate->iss_ScanDescs;
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* This routine must be called at the beginning of any execution of any
|
||||||
* query plan
|
* 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.
|
* 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
|
TupleDesc
|
||||||
|
@ -103,7 +106,8 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate)
|
||||||
{
|
{
|
||||||
estate->es_param_exec_vals = (ParamExecData *)
|
estate->es_param_exec_vals = (ParamExecData *)
|
||||||
palloc(queryDesc->plantree->nParamExec * sizeof(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
|
* EXEC_RETONE: return one tuple but don't 'retrieve' it
|
||||||
* used in postquel function processing
|
* used in postquel function processing
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
TupleTableSlot *
|
TupleTableSlot *
|
||||||
|
@ -687,13 +690,6 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
|
||||||
*/
|
*/
|
||||||
estate->es_range_table = rangeTable;
|
estate->es_range_table = rangeTable;
|
||||||
|
|
||||||
/*
|
|
||||||
* initialize the BaseId counter so node base_id's are assigned
|
|
||||||
* correctly. Someday baseid's will have to be stored someplace other
|
|
||||||
* than estate because they should be unique per query planned.
|
|
||||||
*/
|
|
||||||
estate->es_BaseId = 1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* initialize result relation stuff
|
* initialize 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
|
* initialize the private state information for all the nodes in the
|
||||||
* query tree. This opens files, allocates storage and leaves us
|
* query tree. This opens files, allocates storage and leaves us
|
||||||
* ready to start processing tuples..
|
* ready to start processing tuples.
|
||||||
*/
|
*/
|
||||||
ExecInitNode(plan, estate, NULL);
|
ExecInitNode(plan, estate, NULL);
|
||||||
|
|
||||||
|
@ -1589,7 +1585,7 @@ ExecAttrDefault(Relation rel, HeapTuple tuple)
|
||||||
{
|
{
|
||||||
int ndef = rel->rd_att->constr->num_defval;
|
int ndef = rel->rd_att->constr->num_defval;
|
||||||
AttrDefault *attrdef = rel->rd_att->constr->defval;
|
AttrDefault *attrdef = rel->rd_att->constr->defval;
|
||||||
ExprContext *econtext = makeNode(ExprContext);
|
ExprContext *econtext = MakeExprContext(NULL, CurrentMemoryContext);
|
||||||
HeapTuple newtuple;
|
HeapTuple newtuple;
|
||||||
Node *expr;
|
Node *expr;
|
||||||
bool isnull;
|
bool isnull;
|
||||||
|
@ -1600,23 +1596,13 @@ ExecAttrDefault(Relation rel, HeapTuple tuple)
|
||||||
char *repl = NULL;
|
char *repl = NULL;
|
||||||
int i;
|
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++)
|
for (i = 0; i < ndef; i++)
|
||||||
{
|
{
|
||||||
if (!heap_attisnull(tuple, attrdef[i].adnum))
|
if (!heap_attisnull(tuple, attrdef[i].adnum))
|
||||||
continue;
|
continue;
|
||||||
expr = (Node *) stringToNode(attrdef[i].adbin);
|
expr = (Node *) stringToNode(attrdef[i].adbin);
|
||||||
|
|
||||||
val = ExecEvalExpr(expr, econtext, &isnull, &isdone);
|
val = ExecEvalExprSwitchContext(expr, econtext, &isnull, &isdone);
|
||||||
|
|
||||||
pfree(expr);
|
|
||||||
|
|
||||||
if (isnull)
|
if (isnull)
|
||||||
continue;
|
continue;
|
||||||
|
@ -1635,20 +1621,24 @@ ExecAttrDefault(Relation rel, HeapTuple tuple)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pfree(econtext);
|
|
||||||
|
|
||||||
if (repl == NULL)
|
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);
|
FreeMemoryContext(econtext);
|
||||||
heap_freetuple(tuple);
|
|
||||||
pfree(replNull);
|
|
||||||
pfree(replValue);
|
|
||||||
|
|
||||||
return newtuple;
|
return newtuple;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1658,9 +1648,10 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
|
||||||
{
|
{
|
||||||
int ncheck = rel->rd_att->constr->num_check;
|
int ncheck = rel->rd_att->constr->num_check;
|
||||||
ConstrCheck *check = rel->rd_att->constr->check;
|
ConstrCheck *check = rel->rd_att->constr->check;
|
||||||
ExprContext *econtext = makeNode(ExprContext);
|
|
||||||
TupleTableSlot *slot = makeNode(TupleTableSlot);
|
TupleTableSlot *slot = makeNode(TupleTableSlot);
|
||||||
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
||||||
|
ExprContext *econtext = MakeExprContext(slot,
|
||||||
|
TransactionCommandContext);
|
||||||
List *rtlist;
|
List *rtlist;
|
||||||
List *qual;
|
List *qual;
|
||||||
int i;
|
int i;
|
||||||
|
@ -1677,17 +1668,21 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
|
||||||
rte->relid = RelationGetRelid(rel);
|
rte->relid = RelationGetRelid(rel);
|
||||||
/* inh, inFromCl, inJoinSet, skipAcl won't be used, leave them zero */
|
/* inh, inFromCl, inJoinSet, skipAcl won't be used, leave them zero */
|
||||||
rtlist = lcons(rte, NIL);
|
rtlist = lcons(rte, NIL);
|
||||||
econtext->ecxt_scantuple = slot; /* scan tuple slot */
|
econtext->ecxt_range_table = rtlist; /* phony range table */
|
||||||
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 */
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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)
|
if (estate->es_result_relation_constraints == NULL)
|
||||||
{
|
{
|
||||||
|
MemoryContext oldContext;
|
||||||
|
|
||||||
|
oldContext = MemoryContextSwitchTo(TransactionCommandContext);
|
||||||
|
|
||||||
estate->es_result_relation_constraints =
|
estate->es_result_relation_constraints =
|
||||||
(List **) palloc(ncheck * sizeof(List *));
|
(List **) palloc(ncheck * sizeof(List *));
|
||||||
|
|
||||||
|
@ -1696,6 +1691,8 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
|
||||||
qual = (List *) stringToNode(check[i].ccbin);
|
qual = (List *) stringToNode(check[i].ccbin);
|
||||||
estate->es_result_relation_constraints[i] = qual;
|
estate->es_result_relation_constraints[i] = qual;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ncheck; i++)
|
for (i = 0; i < ncheck; i++)
|
||||||
|
@ -1714,16 +1711,15 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
|
||||||
pfree(slot);
|
pfree(slot);
|
||||||
pfree(rte);
|
pfree(rte);
|
||||||
pfree(rtlist);
|
pfree(rtlist);
|
||||||
pfree(econtext);
|
|
||||||
|
FreeExprContext(econtext);
|
||||||
|
|
||||||
return (char *) NULL;
|
return (char *) NULL;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate)
|
ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate)
|
||||||
{
|
{
|
||||||
|
|
||||||
Assert(rel->rd_att->constr);
|
Assert(rel->rd_att->constr);
|
||||||
|
|
||||||
if (rel->rd_att->constr->has_not_null)
|
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++)
|
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",
|
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;
|
char *failed;
|
||||||
|
|
||||||
if ((failed = ExecRelCheck(rel, tuple, estate)) != NULL)
|
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 *
|
TupleTableSlot *
|
||||||
|
|
|
@ -8,15 +8,16 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* INTERFACE ROUTINES
|
||||||
* ExecEvalExpr - evaluate an expression and return a datum
|
* ExecEvalExpr - evaluate an expression and return a datum
|
||||||
|
* ExecEvalExprSwitchContext - same, but switch into eval memory context
|
||||||
* ExecQual - return true/false if qualification is satisfied
|
* 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
|
* NOTES
|
||||||
* ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster
|
* ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster
|
||||||
|
@ -24,7 +25,7 @@
|
||||||
* implemented recursively. Eliminating the recursion is bound to
|
* implemented recursively. Eliminating the recursion is bound to
|
||||||
* improve the speed of the executor.
|
* 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
|
* trying to speed it up, the execution plan should be pre-processed
|
||||||
* to facilitate attribute sharing between nodes wherever possible,
|
* to facilitate attribute sharing between nodes wherever possible,
|
||||||
* instead of doing needless copying. -cim 5/31/91
|
* instead of doing needless copying. -cim 5/31/91
|
||||||
|
@ -44,31 +45,19 @@
|
||||||
#include "utils/fcache2.h"
|
#include "utils/fcache2.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/* static function decls */
|
||||||
* 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 Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull);
|
static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull);
|
||||||
static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
|
static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
|
||||||
bool *isNull, bool *isDone);
|
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,
|
static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
|
||||||
bool *isNull, bool *isDone);
|
bool *isNull, bool *isDone);
|
||||||
static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
|
static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
|
||||||
List *argList, FunctionCallInfo fcinfo,
|
List *argList, FunctionCallInfo fcinfo,
|
||||||
bool *argIsDone);
|
bool *argIsDone);
|
||||||
static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
|
static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
|
||||||
static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
|
static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
|
||||||
bool *isNull);
|
|
||||||
static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
|
static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
|
||||||
static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
|
static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
|
||||||
static Datum ExecMakeFunctionResult(Node *node, List *arguments,
|
static Datum ExecMakeFunctionResult(Node *node, List *arguments,
|
||||||
|
@ -100,10 +89,11 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
|
||||||
|
|
||||||
if (arrayRef->refexpr != NULL)
|
if (arrayRef->refexpr != NULL)
|
||||||
{
|
{
|
||||||
array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr,
|
array_scanner = (ArrayType *)
|
||||||
econtext,
|
DatumGetPointer(ExecEvalExpr(arrayRef->refexpr,
|
||||||
isNull,
|
econtext,
|
||||||
isDone);
|
isNull,
|
||||||
|
isDone));
|
||||||
/* If refexpr yields NULL, result is always NULL, for now anyway */
|
/* If refexpr yields NULL, result is always NULL, for now anyway */
|
||||||
if (*isNull)
|
if (*isNull)
|
||||||
return (Datum) NULL;
|
return (Datum) NULL;
|
||||||
|
@ -128,10 +118,10 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
|
||||||
elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
|
elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
|
||||||
MAXDIM);
|
MAXDIM);
|
||||||
|
|
||||||
upper.indx[i++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
|
upper.indx[i++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
|
||||||
econtext,
|
econtext,
|
||||||
isNull,
|
isNull,
|
||||||
&dummy);
|
&dummy));
|
||||||
/* If any index expr yields NULL, result is NULL */
|
/* If any index expr yields NULL, result is NULL */
|
||||||
if (*isNull)
|
if (*isNull)
|
||||||
return (Datum) NULL;
|
return (Datum) NULL;
|
||||||
|
@ -145,10 +135,10 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
|
||||||
elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
|
elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
|
||||||
MAXDIM);
|
MAXDIM);
|
||||||
|
|
||||||
lower.indx[j++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
|
lower.indx[j++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
|
||||||
econtext,
|
econtext,
|
||||||
isNull,
|
isNull,
|
||||||
&dummy);
|
&dummy));
|
||||||
/* If any index expr yields NULL, result is NULL */
|
/* If any index expr yields NULL, result is NULL */
|
||||||
if (*isNull)
|
if (*isNull)
|
||||||
return (Datum) NULL;
|
return (Datum) NULL;
|
||||||
|
@ -171,9 +161,6 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
|
||||||
if (*isNull)
|
if (*isNull)
|
||||||
return (Datum) NULL;
|
return (Datum) NULL;
|
||||||
|
|
||||||
execConstByVal = arrayRef->refelembyval;
|
|
||||||
execConstLen = arrayRef->refelemlength;
|
|
||||||
|
|
||||||
if (array_scanner == NULL)
|
if (array_scanner == NULL)
|
||||||
return sourceData; /* XXX do something else? */
|
return sourceData; /* XXX do something else? */
|
||||||
|
|
||||||
|
@ -199,9 +186,6 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
|
||||||
isNull));
|
isNull));
|
||||||
}
|
}
|
||||||
|
|
||||||
execConstByVal = arrayRef->refelembyval;
|
|
||||||
execConstLen = arrayRef->refelemlength;
|
|
||||||
|
|
||||||
if (lIndex == NULL)
|
if (lIndex == NULL)
|
||||||
return array_ref(array_scanner, i,
|
return array_ref(array_scanner, i,
|
||||||
upper.indx,
|
upper.indx,
|
||||||
|
@ -325,7 +309,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
|
||||||
ExecSetSlotDescriptor(tempSlot, td);
|
ExecSetSlotDescriptor(tempSlot, td);
|
||||||
|
|
||||||
ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
|
ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
|
||||||
return (Datum) tempSlot;
|
return PointerGetDatum(tempSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = heap_getattr(heapTuple, /* tuple containing attribute */
|
result = heap_getattr(heapTuple, /* tuple containing attribute */
|
||||||
|
@ -338,7 +322,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
|
||||||
* return null if att is null
|
* return null if att is null
|
||||||
*/
|
*/
|
||||||
if (*isNull)
|
if (*isNull)
|
||||||
return (Datum) NULL;
|
return (Datum) 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get length and type information.. ??? what should we do about
|
* 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;
|
byval = tuple_type->attrs[attnum - 1]->attbyval ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
execConstByVal = byval;
|
|
||||||
execConstLen = len;
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,7 +378,6 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
|
||||||
Datum
|
Datum
|
||||||
ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
|
ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
|
||||||
{
|
{
|
||||||
|
|
||||||
char *thisParameterName;
|
char *thisParameterName;
|
||||||
int thisParameterKind = expression->paramkind;
|
int thisParameterKind = expression->paramkind;
|
||||||
AttrNumber thisParameterId = expression->paramid;
|
AttrNumber thisParameterId = expression->paramid;
|
||||||
|
@ -406,11 +386,15 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
|
||||||
|
|
||||||
if (thisParameterKind == PARAM_EXEC)
|
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)
|
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;
|
*isNull = prm->isnull;
|
||||||
return prm->value;
|
return prm->value;
|
||||||
}
|
}
|
||||||
|
@ -493,7 +477,7 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
|
||||||
if (paramList->isnull)
|
if (paramList->isnull)
|
||||||
{
|
{
|
||||||
*isNull = true;
|
*isNull = true;
|
||||||
return (Datum) NULL;
|
return (Datum) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expression->param_tlist != NIL)
|
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
|
* named attribute out of the tuple from the arg slot. User defined
|
||||||
* C functions which take a tuple as an argument are expected
|
* C functions which take a tuple as an argument are expected
|
||||||
* to use this. Ex: overpaid(EMP) might call GetAttributeByNum().
|
* 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 *
|
char *
|
||||||
GetAttributeByNum(TupleTableSlot *slot,
|
GetAttributeByNum(TupleTableSlot *slot,
|
||||||
AttrNumber attrno,
|
AttrNumber attrno,
|
||||||
|
@ -559,18 +547,6 @@ GetAttributeByNum(TupleTableSlot *slot,
|
||||||
return (char *) retval;
|
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 *
|
char *
|
||||||
GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
|
GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
|
||||||
{
|
{
|
||||||
|
@ -617,15 +593,6 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
|
||||||
return (char *) retval;
|
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
|
static void
|
||||||
ExecEvalFuncArgs(FunctionCachePtr fcache,
|
ExecEvalFuncArgs(FunctionCachePtr fcache,
|
||||||
|
@ -732,9 +699,8 @@ ExecMakeFunctionResult(Node *node,
|
||||||
if (fcache->hasSetArg && argDone)
|
if (fcache->hasSetArg && argDone)
|
||||||
{
|
{
|
||||||
/* can only get here if input is an empty set. */
|
/* can only get here if input is an empty set. */
|
||||||
if (isDone)
|
|
||||||
*isDone = true;
|
|
||||||
*isNull = true;
|
*isNull = true;
|
||||||
|
*isDone = true;
|
||||||
return (Datum) 0;
|
return (Datum) 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -817,8 +783,8 @@ ExecMakeFunctionResult(Node *node,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = (Datum) 0;
|
result = (Datum) 0;
|
||||||
*isDone = true;
|
|
||||||
*isNull = true;
|
*isNull = true;
|
||||||
|
*isDone = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*isDone)
|
if (!*isDone)
|
||||||
|
@ -872,8 +838,8 @@ ExecMakeFunctionResult(Node *node,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* A non-SQL function cannot return a set, at present. */
|
/* 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,
|
* If function is strict, and there are any NULL arguments,
|
||||||
* skip calling the function and return NULL.
|
* skip calling the function and return NULL.
|
||||||
|
@ -904,15 +870,7 @@ ExecMakeFunctionResult(Node *node,
|
||||||
* ExecEvalFunc
|
* ExecEvalFunc
|
||||||
*
|
*
|
||||||
* Evaluate the functional result of a list of arguments by calling the
|
* Evaluate the functional result of a list of arguments by calling the
|
||||||
* function manager. Note that in the case of operator expressions, the
|
* function manager.
|
||||||
* 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.
|
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -929,8 +887,6 @@ ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
|
||||||
bool isDone;
|
bool isDone;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* an opclause is a list (op args). (I think)
|
|
||||||
*
|
|
||||||
* we extract the oid of the function associated with the op and then
|
* we extract the oid of the function associated with the op and then
|
||||||
* pass the work onto ExecMakeFunctionResult which evaluates the
|
* pass the work onto ExecMakeFunctionResult which evaluates the
|
||||||
* arguments and returns the result of calling the function on 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.
|
* call ExecMakeFunctionResult() with a dummy isDone that we ignore.
|
||||||
* We don't have operator whose arguments are sets.
|
* 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;
|
FunctionCachePtr fcache;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* an funcclause is a list (func args). (I think)
|
|
||||||
*
|
|
||||||
* we extract the oid of the function associated with the func node and
|
* we extract the oid of the function associated with the func node and
|
||||||
* then pass the work onto ExecMakeFunctionResult which evaluates the
|
* then pass the work onto ExecMakeFunctionResult which evaluates the
|
||||||
* arguments and returns the result of calling the function on the
|
* arguments and returns the result of calling the function on the
|
||||||
|
@ -996,7 +951,8 @@ ExecEvalFunc(Expr *funcClause,
|
||||||
fcache = func->func_fcache;
|
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'
|
* evaluation of 'not' is simple.. expr is false, then return 'true'
|
||||||
* and vice versa.
|
* and vice versa.
|
||||||
*/
|
*/
|
||||||
if (DatumGetInt32(expr_value) == 0)
|
return BoolGetDatum(! DatumGetBool(expr_value));
|
||||||
return (Datum) true;
|
|
||||||
|
|
||||||
return (Datum) false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
@ -1094,13 +1047,13 @@ ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
|
||||||
*/
|
*/
|
||||||
if (*isNull)
|
if (*isNull)
|
||||||
AnyNull = true; /* remember we got a null */
|
AnyNull = true; /* remember we got a null */
|
||||||
else if (DatumGetInt32(clause_value) != 0)
|
else if (DatumGetBool(clause_value))
|
||||||
return clause_value;
|
return clause_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AnyNull is true if at least one clause evaluated to NULL */
|
/* AnyNull is true if at least one clause evaluated to NULL */
|
||||||
*isNull = AnyNull;
|
*isNull = AnyNull;
|
||||||
return (Datum) false;
|
return BoolGetDatum(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
@ -1144,13 +1097,13 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
|
||||||
*/
|
*/
|
||||||
if (*isNull)
|
if (*isNull)
|
||||||
AnyNull = true; /* remember we got a null */
|
AnyNull = true; /* remember we got a null */
|
||||||
else if (DatumGetInt32(clause_value) == 0)
|
else if (! DatumGetBool(clause_value))
|
||||||
return clause_value;
|
return clause_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AnyNull is true if at least one clause evaluated to NULL */
|
/* AnyNull is true if at least one clause evaluated to NULL */
|
||||||
*isNull = AnyNull;
|
*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
|
* case statement is satisfied. A NULL result from the test is
|
||||||
* not considered true.
|
* not considered true.
|
||||||
*/
|
*/
|
||||||
if (DatumGetInt32(clause_value) != 0 && !*isNull)
|
if (DatumGetBool(clause_value) && !*isNull)
|
||||||
{
|
{
|
||||||
return ExecEvalExpr(wclause->result,
|
return ExecEvalExpr(wclause->result,
|
||||||
econtext,
|
econtext,
|
||||||
|
@ -1221,19 +1174,15 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
|
||||||
*
|
*
|
||||||
* Recursively evaluate a targetlist or qualification expression.
|
* 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.
|
* 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
|
Datum
|
||||||
|
@ -1244,25 +1193,21 @@ ExecEvalExpr(Node *expression,
|
||||||
{
|
{
|
||||||
Datum retDatum;
|
Datum retDatum;
|
||||||
|
|
||||||
|
/* Set default values for result flags: non-null, not a set result */
|
||||||
*isNull = false;
|
*isNull = false;
|
||||||
|
*isDone = true;
|
||||||
|
|
||||||
/*
|
/* Is this still necessary? Doubtful... */
|
||||||
* Some callers don't care about is done and only want 1 result. They
|
if (expression == NULL)
|
||||||
* indicate this by passing NULL
|
{
|
||||||
*/
|
*isNull = true;
|
||||||
if (isDone)
|
return (Datum) 0;
|
||||||
*isDone = true;
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* here we dispatch the work to the appropriate type of function given
|
* here we dispatch the work to the appropriate type of function given
|
||||||
* the type of our expression.
|
* the type of our expression.
|
||||||
*/
|
*/
|
||||||
if (expression == NULL)
|
|
||||||
{
|
|
||||||
*isNull = true;
|
|
||||||
return (Datum) true;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (nodeTag(expression))
|
switch (nodeTag(expression))
|
||||||
{
|
{
|
||||||
case T_Var:
|
case T_Var:
|
||||||
|
@ -1350,8 +1295,27 @@ ExecEvalExpr(Node *expression,
|
||||||
} /* ExecEvalExpr() */
|
} /* 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
|
bool
|
||||||
ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
|
ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
|
||||||
{
|
{
|
||||||
|
bool result;
|
||||||
|
MemoryContext oldContext;
|
||||||
List *qlist;
|
List *qlist;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1397,6 +1363,11 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
|
||||||
|
|
||||||
IncrProcessed();
|
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
|
* Evaluate the qual conditions one at a time. If we find a FALSE
|
||||||
* result, we can stop evaluating and return FALSE --- the AND result
|
* 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
|
* is NULL (one or more NULL subresult, with all the rest TRUE) and
|
||||||
* the caller has specified resultForNull = TRUE.
|
* the caller has specified resultForNull = TRUE.
|
||||||
*/
|
*/
|
||||||
|
result = true;
|
||||||
|
|
||||||
foreach(qlist, qual)
|
foreach(qlist, qual)
|
||||||
{
|
{
|
||||||
|
@ -1417,14 +1389,6 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
|
||||||
bool isNull;
|
bool isNull;
|
||||||
bool isDone;
|
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
|
* pass isDone, but ignore it. We don't iterate over multiple
|
||||||
* returns in the qualifications.
|
* returns in the qualifications.
|
||||||
|
@ -1434,16 +1398,24 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
|
||||||
if (isNull)
|
if (isNull)
|
||||||
{
|
{
|
||||||
if (resultForNull == false)
|
if (resultForNull == false)
|
||||||
return false; /* treat NULL as FALSE */
|
{
|
||||||
|
result = false; /* treat NULL as FALSE */
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (DatumGetInt32(expr_value) == 0)
|
if (! DatumGetBool(expr_value))
|
||||||
return false; /* definitely FALSE */
|
{
|
||||||
|
result = false; /* definitely FALSE */
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
MemoryContextSwitchTo(oldContext);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -1481,6 +1453,7 @@ ExecTargetList(List *targetlist,
|
||||||
ExprContext *econtext,
|
ExprContext *econtext,
|
||||||
bool *isDone)
|
bool *isDone)
|
||||||
{
|
{
|
||||||
|
MemoryContext oldContext;
|
||||||
char nulls_array[64];
|
char nulls_array[64];
|
||||||
bool fjNullArray[64];
|
bool fjNullArray[64];
|
||||||
bool itemIsDoneArray[64];
|
bool itemIsDoneArray[64];
|
||||||
|
@ -1506,6 +1479,11 @@ ExecTargetList(List *targetlist,
|
||||||
EV_nodeDisplay(targetlist);
|
EV_nodeDisplay(targetlist);
|
||||||
EV_printf("\n");
|
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
|
* There used to be some klugy and demonstrably broken code here that
|
||||||
* special-cased the situation where targetlist == NIL. Now we just
|
* special-cased the situation where targetlist == NIL. Now we just
|
||||||
|
@ -1563,10 +1541,10 @@ ExecTargetList(List *targetlist,
|
||||||
resdom = tle->resdom;
|
resdom = tle->resdom;
|
||||||
resind = resdom->resno - 1;
|
resind = resdom->resno - 1;
|
||||||
|
|
||||||
constvalue = (Datum) ExecEvalExpr(expr,
|
constvalue = ExecEvalExpr(expr,
|
||||||
econtext,
|
econtext,
|
||||||
&isNull,
|
&isNull,
|
||||||
&itemIsDone[resind]);
|
&itemIsDone[resind]);
|
||||||
|
|
||||||
values[resind] = constvalue;
|
values[resind] = constvalue;
|
||||||
|
|
||||||
|
@ -1597,7 +1575,10 @@ ExecTargetList(List *targetlist,
|
||||||
|
|
||||||
/* this is probably wrong: */
|
/* this is probably wrong: */
|
||||||
if (*isDone)
|
if (*isDone)
|
||||||
return (HeapTuple) NULL;
|
{
|
||||||
|
newTuple = NULL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get the result from the inner node
|
* get the result from the inner node
|
||||||
|
@ -1681,10 +1662,10 @@ ExecTargetList(List *targetlist,
|
||||||
|
|
||||||
if (IsA(expr, Iter) &&itemIsDone[resind])
|
if (IsA(expr, Iter) &&itemIsDone[resind])
|
||||||
{
|
{
|
||||||
constvalue = (Datum) ExecEvalExpr(expr,
|
constvalue = ExecEvalExpr(expr,
|
||||||
econtext,
|
econtext,
|
||||||
&isNull,
|
&isNull,
|
||||||
&itemIsDone[resind]);
|
&itemIsDone[resind]);
|
||||||
if (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);
|
newTuple = (HeapTuple) heap_formtuple(targettype, values, null_head);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
@ -1726,6 +1709,9 @@ exit:
|
||||||
pfree(itemIsDone);
|
pfree(itemIsDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* make sure we are in the right context if we did "goto exit" */
|
||||||
|
MemoryContextSwitchTo(oldContext);
|
||||||
|
|
||||||
return newTuple;
|
return newTuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* returns the next qualifying tuple in the direction specified
|
||||||
* in the global variable ExecDirection.
|
* in the global variable ExecDirection.
|
||||||
* The access method returns the next tuple and execScan() is
|
* 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:
|
* Conditions:
|
||||||
* -- the "cursor" maintained by the AMI is positioned at the tuple
|
* -- the "cursor" maintained by the AMI is positioned at the tuple
|
||||||
|
@ -39,59 +39,50 @@
|
||||||
* Initial States:
|
* Initial States:
|
||||||
* -- the relation indicated is opened for scanning so that the
|
* -- the relation indicated is opened for scanning so that the
|
||||||
* "cursor" is positioned before the first qualifying tuple.
|
* "cursor" is positioned before the first qualifying tuple.
|
||||||
*
|
|
||||||
* May need to put startmmgr and endmmgr in here.
|
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
TupleTableSlot *
|
TupleTableSlot *
|
||||||
ExecScan(Scan *node,
|
ExecScan(Scan *node,
|
||||||
TupleTableSlot *(*accessMtd) ()) /* function returning a
|
ExecScanAccessMtd accessMtd) /* function returning a tuple */
|
||||||
* tuple */
|
|
||||||
{
|
{
|
||||||
CommonScanState *scanstate;
|
CommonScanState *scanstate;
|
||||||
EState *estate;
|
EState *estate;
|
||||||
List *qual;
|
List *qual;
|
||||||
bool isDone;
|
bool isDone;
|
||||||
|
|
||||||
TupleTableSlot *slot;
|
|
||||||
TupleTableSlot *resultSlot;
|
TupleTableSlot *resultSlot;
|
||||||
HeapTuple newTuple;
|
|
||||||
|
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
ProjectionInfo *projInfo;
|
ProjectionInfo *projInfo;
|
||||||
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* initialize misc variables
|
* Fetch data from node
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
newTuple = NULL;
|
|
||||||
slot = NULL;
|
|
||||||
|
|
||||||
estate = node->plan.state;
|
estate = node->plan.state;
|
||||||
scanstate = node->scanstate;
|
scanstate = node->scanstate;
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* get the expression context
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
econtext = scanstate->cstate.cs_ExprContext;
|
econtext = scanstate->cstate.cs_ExprContext;
|
||||||
|
qual = node->plan.qual;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* initialize fields in ExprContext which don't change
|
* Reset per-tuple memory context to free any expression evaluation
|
||||||
* in the course of the scan..
|
* storage allocated in the previous tuple cycle.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
qual = node->plan.qual;
|
ResetExprContext(econtext);
|
||||||
econtext->ecxt_relation = scanstate->css_currentRelation;
|
|
||||||
econtext->ecxt_relid = node->scanrelid;
|
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* 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)
|
if (scanstate->cstate.cs_TupFromTlist)
|
||||||
{
|
{
|
||||||
projInfo = scanstate->cstate.cs_ProjInfo;
|
projInfo = scanstate->cstate.cs_ProjInfo;
|
||||||
resultSlot = ExecProject(projInfo, &isDone);
|
resultSlot = ExecProject(projInfo, &isDone);
|
||||||
if (!isDone)
|
if (!isDone)
|
||||||
return resultSlot;
|
return resultSlot;
|
||||||
|
/* Done with that source tuple... */
|
||||||
|
scanstate->cstate.cs_TupFromTlist = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -100,27 +91,23 @@ ExecScan(Scan *node,
|
||||||
*/
|
*/
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
slot = (TupleTableSlot *) (*accessMtd) (node);
|
TupleTableSlot *slot;
|
||||||
|
|
||||||
|
slot = (*accessMtd) (node);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* if the slot returned by the accessMtd contains
|
* if the slot returned by the accessMtd contains
|
||||||
* NULL, then it means there is nothing more to scan
|
* NULL, then it means there is nothing more to scan
|
||||||
* so we just return the empty slot...
|
* so we just return an empty slot, being careful to use
|
||||||
*
|
* the projection result slot so it has correct tupleDesc.
|
||||||
* ... 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
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
if (TupIsNull(slot))
|
if (TupIsNull(slot))
|
||||||
{
|
{
|
||||||
scanstate->cstate.cs_TupFromTlist = false;
|
return ExecStoreTuple(NULL,
|
||||||
resultSlot = scanstate->cstate.cs_ProjInfo->pi_slot;
|
scanstate->cstate.cs_ProjInfo->pi_slot,
|
||||||
return (TupleTableSlot *)
|
InvalidBuffer,
|
||||||
ExecStoreTuple(NULL,
|
true);
|
||||||
resultSlot,
|
|
||||||
InvalidBuffer,
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -131,22 +118,28 @@ ExecScan(Scan *node,
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* check that the current tuple satisfies the qual-clause
|
* 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.
|
* leave the loop.
|
||||||
* ----------------
|
*
|
||||||
*/
|
* check for non-nil qual here to avoid a function call to
|
||||||
|
|
||||||
/*
|
|
||||||
* add a check for non-nil qual here to avoid a function call to
|
|
||||||
* ExecQual() when the qual is nil ... saves only a few cycles,
|
* ExecQual() when the qual is nil ... saves only a few cycles,
|
||||||
* but they add up ...
|
* but they add up ...
|
||||||
|
* ----------------
|
||||||
*/
|
*/
|
||||||
if (!qual || ExecQual(qual, econtext, false))
|
if (!qual || ExecQual(qual, econtext, false))
|
||||||
break;
|
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.
|
* slot and return it.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* by the access methods into the scan tuple slot.
|
||||||
*
|
*
|
||||||
* - ExecSeqScan() calls ExecStoreTuple() to take the result
|
* - ExecSeqScan() calls ExecStoreTuple() to take the result
|
||||||
* tuple from ExecTargetList() and place it into the result tuple
|
* tuple from ExecProject() and place it into the result tuple slot.
|
||||||
* slot.
|
|
||||||
*
|
*
|
||||||
* - ExecutePlan() calls ExecRetrieve() which gets the tuple out of
|
* - ExecutePlan() calls ExecRetrieve() which gets the tuple out of
|
||||||
* the slot passed to it by calling ExecFetchTuple(). this tuple
|
* the slot passed to it by calling ExecFetchTuple(). this tuple
|
||||||
|
@ -182,8 +181,8 @@ ExecCreateTupleTable(int initialSize) /* initial number of slots in
|
||||||
/* --------------------------------
|
/* --------------------------------
|
||||||
* ExecDropTupleTable
|
* ExecDropTupleTable
|
||||||
*
|
*
|
||||||
* This pfrees the storage assigned to the tuple table and
|
* This frees the storage used by the tuple table itself
|
||||||
* optionally pfrees the contents of the table also.
|
* and optionally frees the contents of the table also.
|
||||||
* It is expected that this routine be called by EndPlan().
|
* It is expected that this routine be called by EndPlan().
|
||||||
* --------------------------------
|
* --------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -239,7 +238,6 @@ ExecDropTupleTable(TupleTable table, /* tuple table */
|
||||||
*/
|
*/
|
||||||
pfree(array);
|
pfree(array);
|
||||||
pfree(table);
|
pfree(table);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -252,13 +250,12 @@ ExecDropTupleTable(TupleTable table, /* tuple table */
|
||||||
*
|
*
|
||||||
* This routine is used to reserve slots in the table for
|
* This routine is used to reserve slots in the table for
|
||||||
* use by the various plan nodes. It is expected to be
|
* 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
|
* once per slot needed by the node. Not all nodes need
|
||||||
* slots (some just pass tuples around).
|
* slots (some just pass tuples around).
|
||||||
* --------------------------------
|
* --------------------------------
|
||||||
*/
|
*/
|
||||||
TupleTableSlot * /* return: the slot allocated in the tuple
|
TupleTableSlot *
|
||||||
* table */
|
|
||||||
ExecAllocTableSlot(TupleTable table)
|
ExecAllocTableSlot(TupleTable table)
|
||||||
{
|
{
|
||||||
int slotnum; /* new slot number */
|
int slotnum; /* new slot number */
|
||||||
|
@ -283,22 +280,12 @@ ExecAllocTableSlot(TupleTable table)
|
||||||
* pointers into _freed_ memory. This leads to bad ends. We
|
* pointers into _freed_ memory. This leads to bad ends. We
|
||||||
* now count the number of slots we will need and create all the
|
* 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
|
* 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)
|
if (table->next >= table->size)
|
||||||
{
|
elog(ERROR, "Plan requires more slots than are available"
|
||||||
|
"\n\tsend mail to your local executor guru to fix this");
|
||||||
/*
|
|
||||||
* 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* at this point, space in the table is guaranteed so we
|
* 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->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.
|
* Drop the pin on the referenced buffer, if there is one.
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* execUtils.c
|
* execUtils.c
|
||||||
* miscellanious executor utility routines
|
* miscellaneous executor utility routines
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* INTERFACE ROUTINES
|
||||||
* ExecAssignNodeBaseInfo \
|
* ExecAssignExprContext Common code for plan node init routines.
|
||||||
* ExecAssignDebugHooks > preforms misc work done in all the
|
|
||||||
* ExecAssignExprContext / init node routines.
|
|
||||||
*
|
*
|
||||||
* ExecGetTypeInfo | old execCStructs interface
|
* ExecGetTypeInfo | old execCStructs interface
|
||||||
* ExecMakeTypeInfo | code from the version 1
|
* ExecMakeTypeInfo | code from the version 1
|
||||||
|
@ -53,6 +51,7 @@
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
|
#include "utils/memutils.h"
|
||||||
#include "utils/relcache.h"
|
#include "utils/relcache.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
@ -137,59 +136,106 @@ DisplayTupleCount(FILE *statfp)
|
||||||
#endif
|
#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
|
* 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
|
* ExecAssignExprContext
|
||||||
*
|
*
|
||||||
* This initializes the ExprContext field. It is only necessary
|
* This initializes the ExprContext field. It is only necessary
|
||||||
* to do this for nodes which use ExecQual or ExecTargetList
|
* to do this for nodes which use ExecQual or ExecProject
|
||||||
* because those routines depend on econtext. Other nodes which
|
* because those routines depend on econtext. Other nodes that
|
||||||
* dont have to evaluate expressions don't need to do this.
|
* 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
|
void
|
||||||
ExecAssignExprContext(EState *estate, CommonState *commonstate)
|
ExecAssignExprContext(EState *estate, CommonState *commonstate)
|
||||||
{
|
{
|
||||||
ExprContext *econtext;
|
ExprContext *econtext = makeNode(ExprContext);
|
||||||
|
|
||||||
econtext = makeNode(ExprContext);
|
econtext->ecxt_scantuple = NULL;
|
||||||
econtext->ecxt_scantuple = NULL; /* scan tuple slot */
|
econtext->ecxt_innertuple = NULL;
|
||||||
econtext->ecxt_innertuple = NULL; /* inner tuple slot */
|
econtext->ecxt_outertuple = NULL;
|
||||||
econtext->ecxt_outertuple = NULL; /* outer tuple slot */
|
econtext->ecxt_per_query_memory = CurrentMemoryContext;
|
||||||
econtext->ecxt_relation = NULL; /* relation */
|
/*
|
||||||
econtext->ecxt_relid = 0; /* relid */
|
* Create working memory for expression evaluation in this context.
|
||||||
econtext->ecxt_param_list_info = estate->es_param_list_info;
|
*/
|
||||||
|
econtext->ecxt_per_tuple_memory =
|
||||||
|
AllocSetContextCreate(CurrentMemoryContext,
|
||||||
|
"PlanExprContext",
|
||||||
|
ALLOCSET_DEFAULT_MINSIZE,
|
||||||
|
ALLOCSET_DEFAULT_INITSIZE,
|
||||||
|
ALLOCSET_DEFAULT_MAXSIZE);
|
||||||
econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
|
econtext->ecxt_param_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;
|
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
|
* Result slot tuple type and ProjectionInfo support
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
|
@ -390,6 +436,7 @@ ExecFreeExprContext(CommonState *commonstate)
|
||||||
* clean up memory used.
|
* clean up memory used.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
|
MemoryContextDelete(econtext->ecxt_per_tuple_memory);
|
||||||
pfree(econtext);
|
pfree(econtext);
|
||||||
commonstate->cs_ExprContext = NULL;
|
commonstate->cs_ExprContext = NULL;
|
||||||
}
|
}
|
||||||
|
@ -398,6 +445,7 @@ ExecFreeExprContext(CommonState *commonstate)
|
||||||
* ExecFreeTypeInfo
|
* ExecFreeTypeInfo
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
|
#ifdef NOT_USED
|
||||||
void
|
void
|
||||||
ExecFreeTypeInfo(CommonState *commonstate)
|
ExecFreeTypeInfo(CommonState *commonstate)
|
||||||
{
|
{
|
||||||
|
@ -414,6 +462,7 @@ ExecFreeTypeInfo(CommonState *commonstate)
|
||||||
FreeTupleDesc(tupDesc);
|
FreeTupleDesc(tupDesc);
|
||||||
commonstate->cs_ResultTupleSlot->ttc_tupleDescriptor = NULL;
|
commonstate->cs_ResultTupleSlot->ttc_tupleDescriptor = NULL;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* the following scan type support functions are for
|
* the following scan type support functions are for
|
||||||
|
@ -974,8 +1023,8 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
||||||
if (predicate != NULL)
|
if (predicate != NULL)
|
||||||
{
|
{
|
||||||
if (econtext == NULL)
|
if (econtext == NULL)
|
||||||
econtext = makeNode(ExprContext);
|
econtext = MakeExprContext(slot,
|
||||||
econtext->ecxt_scantuple = slot;
|
TransactionCommandContext);
|
||||||
|
|
||||||
/* Skip this index-update if the predicate isn't satisfied */
|
/* Skip this index-update if the predicate isn't satisfied */
|
||||||
if (!ExecQual((List *) predicate, econtext, false))
|
if (!ExecQual((List *) predicate, econtext, false))
|
||||||
|
@ -1023,7 +1072,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
||||||
pfree(result);
|
pfree(result);
|
||||||
}
|
}
|
||||||
if (econtext != NULL)
|
if (econtext != NULL)
|
||||||
pfree(econtext);
|
FreeExprContext(econtext);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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,
|
HeapTuple tup,
|
||||||
bool *isnullP)
|
bool *isnullP)
|
||||||
{
|
{
|
||||||
Datum val,
|
Datum val;
|
||||||
valueP;
|
|
||||||
Var *attrVar = (Var *) tlist->expr;
|
Var *attrVar = (Var *) tlist->expr;
|
||||||
AttrNumber attrno = attrVar->varattno;
|
AttrNumber attrno = attrVar->varattno;
|
||||||
|
|
||||||
val = heap_getattr(tup, attrno, TD, isnullP);
|
val = heap_getattr(tup, attrno, TD, isnullP);
|
||||||
if (*isnullP)
|
|
||||||
return (Datum) NULL;
|
|
||||||
|
|
||||||
valueP = datumCopy(val,
|
if (*isnullP)
|
||||||
TD->attrs[attrno - 1]->atttypid,
|
return (Datum) 0;
|
||||||
TD->attrs[attrno - 1]->attbyval,
|
|
||||||
(Size) TD->attrs[attrno - 1]->attlen);
|
return datumCopy(val,
|
||||||
return valueP;
|
TD->attrs[attrno - 1]->attbyval,
|
||||||
|
TD->attrs[attrno - 1]->attlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static execution_state *
|
static execution_state *
|
||||||
|
@ -351,10 +349,18 @@ postquel_function(FunctionCallInfo fcinfo,
|
||||||
List *func_tlist,
|
List *func_tlist,
|
||||||
bool *isDone)
|
bool *isDone)
|
||||||
{
|
{
|
||||||
|
MemoryContext oldcontext;
|
||||||
execution_state *es;
|
execution_state *es;
|
||||||
Datum result = 0;
|
Datum result = 0;
|
||||||
CommandId savedId;
|
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
|
* Before we start do anything we must save CurrentScanCommandId to
|
||||||
* restore it before return to upper Executor. Also, we have to set
|
* 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.
|
* Let caller know we're finished.
|
||||||
*/
|
*/
|
||||||
*isDone = true;
|
*isDone = true;
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
return (fcache->oneResult) ? result : (Datum) NULL;
|
return (fcache->oneResult) ? result : (Datum) NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,5 +433,8 @@ postquel_function(FunctionCallInfo fcinfo,
|
||||||
Assert(LAST_POSTQUEL_COMMAND(es));
|
Assert(LAST_POSTQUEL_COMMAND(es));
|
||||||
|
|
||||||
*isDone = false;
|
*isDone = false;
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "parser/parse_type.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
#include "utils/tuplesort.h"
|
#include "utils/tuplesort.h"
|
||||||
|
#include "utils/datum.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AggStatePerAggData - per-aggregate working state for the Agg scan
|
* AggStatePerAggData - per-aggregate working state for the Agg scan
|
||||||
|
@ -101,13 +102,15 @@ typedef struct AggStatePerAggData
|
||||||
initValue2IsNull;
|
initValue2IsNull;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need the len and byval info for the agg's input and transition
|
* We need the len and byval info for the agg's input, result, and
|
||||||
* data types in order to know how to copy/delete values.
|
* transition data types in order to know how to copy/delete values.
|
||||||
*/
|
*/
|
||||||
int inputtypeLen,
|
int inputtypeLen,
|
||||||
|
resulttypeLen,
|
||||||
transtype1Len,
|
transtype1Len,
|
||||||
transtype2Len;
|
transtype2Len;
|
||||||
bool inputtypeByVal,
|
bool inputtypeByVal,
|
||||||
|
resulttypeByVal,
|
||||||
transtype1ByVal,
|
transtype1ByVal,
|
||||||
transtype2ByVal;
|
transtype2ByVal;
|
||||||
|
|
||||||
|
@ -143,13 +146,16 @@ typedef struct AggStatePerAggData
|
||||||
static void initialize_aggregate(AggStatePerAgg peraggstate);
|
static void initialize_aggregate(AggStatePerAgg peraggstate);
|
||||||
static void advance_transition_functions(AggStatePerAgg peraggstate,
|
static void advance_transition_functions(AggStatePerAgg peraggstate,
|
||||||
Datum newVal, bool isNull);
|
Datum newVal, bool isNull);
|
||||||
|
static void process_sorted_aggregate(AggState *aggstate,
|
||||||
|
AggStatePerAgg peraggstate);
|
||||||
static void finalize_aggregate(AggStatePerAgg peraggstate,
|
static void finalize_aggregate(AggStatePerAgg peraggstate,
|
||||||
Datum *resultVal, bool *resultIsNull);
|
Datum *resultVal, bool *resultIsNull);
|
||||||
static Datum copyDatum(Datum val, int typLen, bool typByVal);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize one aggregate for a new set of input values.
|
* Initialize one aggregate for a new set of input values.
|
||||||
|
*
|
||||||
|
* When called, CurrentMemoryContext should be the per-query context.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
initialize_aggregate(AggStatePerAgg peraggstate)
|
initialize_aggregate(AggStatePerAgg peraggstate)
|
||||||
|
@ -177,23 +183,14 @@ initialize_aggregate(AggStatePerAgg peraggstate)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (Re)set value1 and value2 to their initial values.
|
* (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->value1 = peraggstate->initValue1;
|
||||||
!peraggstate->initValue1IsNull)
|
|
||||||
peraggstate->value1 = copyDatum(peraggstate->initValue1,
|
|
||||||
peraggstate->transtype1Len,
|
|
||||||
peraggstate->transtype1ByVal);
|
|
||||||
else
|
|
||||||
peraggstate->value1 = (Datum) NULL;
|
|
||||||
peraggstate->value1IsNull = peraggstate->initValue1IsNull;
|
peraggstate->value1IsNull = peraggstate->initValue1IsNull;
|
||||||
|
peraggstate->value2 = peraggstate->initValue2;
|
||||||
if (OidIsValid(peraggstate->xfn2_oid) &&
|
|
||||||
!peraggstate->initValue2IsNull)
|
|
||||||
peraggstate->value2 = copyDatum(peraggstate->initValue2,
|
|
||||||
peraggstate->transtype2Len,
|
|
||||||
peraggstate->transtype2ByVal);
|
|
||||||
else
|
|
||||||
peraggstate->value2 = (Datum) NULL;
|
|
||||||
peraggstate->value2IsNull = peraggstate->initValue2IsNull;
|
peraggstate->value2IsNull = peraggstate->initValue2IsNull;
|
||||||
|
|
||||||
/* ------------------------------------------
|
/* ------------------------------------------
|
||||||
|
@ -211,6 +208,9 @@ initialize_aggregate(AggStatePerAgg peraggstate)
|
||||||
/*
|
/*
|
||||||
* Given a new input value, advance the transition functions of an aggregate.
|
* 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
|
* Note: if the agg does not have usenulls set, null inputs will be filtered
|
||||||
* out before reaching here.
|
* out before reaching here.
|
||||||
*/
|
*/
|
||||||
|
@ -237,12 +237,13 @@ advance_transition_functions(AggStatePerAgg peraggstate,
|
||||||
* XXX We assume, without having checked, that the agg's input
|
* XXX We assume, without having checked, that the agg's input
|
||||||
* type is binary-compatible with its transtype1!
|
* 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.
|
* will be freed on the next iteration of the scan.
|
||||||
*/
|
*/
|
||||||
peraggstate->value1 = copyDatum(newVal,
|
peraggstate->value1 = datumCopy(newVal,
|
||||||
peraggstate->transtype1Len,
|
peraggstate->transtype1ByVal,
|
||||||
peraggstate->transtype1ByVal);
|
peraggstate->transtype1Len);
|
||||||
peraggstate->value1IsNull = false;
|
peraggstate->value1IsNull = false;
|
||||||
peraggstate->noInitValue = false;
|
peraggstate->noInitValue = false;
|
||||||
}
|
}
|
||||||
|
@ -264,8 +265,18 @@ advance_transition_functions(AggStatePerAgg peraggstate,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
newVal = FunctionCallInvoke(&fcinfo);
|
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->value1 = newVal;
|
||||||
peraggstate->value1IsNull = fcinfo.isnull;
|
peraggstate->value1IsNull = fcinfo.isnull;
|
||||||
}
|
}
|
||||||
|
@ -287,70 +298,116 @@ advance_transition_functions(AggStatePerAgg peraggstate,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
newVal = FunctionCallInvoke(&fcinfo);
|
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->value2 = newVal;
|
||||||
peraggstate->value2IsNull = fcinfo.isnull;
|
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
|
static void
|
||||||
finalize_aggregate(AggStatePerAgg peraggstate,
|
process_sorted_aggregate(AggState *aggstate,
|
||||||
Datum *resultVal, bool *resultIsNull)
|
AggStatePerAgg peraggstate)
|
||||||
{
|
{
|
||||||
Aggref *aggref = peraggstate->aggref;
|
Datum oldVal = (Datum) 0;
|
||||||
FunctionCallInfoData fcinfo;
|
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
|
* Note: if input type is pass-by-ref, the datums returned by the sort
|
||||||
* input values into the sort object. Complete the sort, then run the
|
* are freshly palloc'd in the per-query context, so we must be careful
|
||||||
* transition functions on the non-duplicate values. Note that
|
* to pfree them when they are no longer needed.
|
||||||
* DISTINCT always suppresses nulls, per SQL spec, regardless of
|
|
||||||
* usenulls.
|
|
||||||
*/
|
*/
|
||||||
if (aggref->aggdistinct)
|
|
||||||
{
|
|
||||||
Datum oldVal = (Datum) 0;
|
|
||||||
bool haveOldVal = false;
|
|
||||||
Datum newVal;
|
|
||||||
bool isNull;
|
|
||||||
|
|
||||||
tuplesort_performsort(peraggstate->sortstate);
|
while (tuplesort_getdatum(peraggstate->sortstate, true,
|
||||||
while (tuplesort_getdatum(peraggstate->sortstate, true,
|
&newVal, &isNull))
|
||||||
&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);
|
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)
|
if (haveOldVal && !peraggstate->inputtypeByVal)
|
||||||
pfree(DatumGetPointer(oldVal));
|
pfree(DatumGetPointer(oldVal));
|
||||||
oldVal = newVal;
|
oldVal = newVal;
|
||||||
haveOldVal = true;
|
haveOldVal = true;
|
||||||
}
|
}
|
||||||
if (haveOldVal && !peraggstate->inputtypeByVal)
|
|
||||||
pfree(DatumGetPointer(oldVal));
|
MemoryContextSwitchTo(oldContext);
|
||||||
tuplesort_end(peraggstate->sortstate);
|
|
||||||
peraggstate->sortstate = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
* transition value if there is no finalfn.
|
||||||
*
|
*
|
||||||
* XXX For now, only apply finalfn if we got at least one non-null input
|
* 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 */
|
/* Return value1 */
|
||||||
*resultVal = peraggstate->value1;
|
*resultVal = peraggstate->value1;
|
||||||
*resultIsNull = peraggstate->value1IsNull;
|
*resultIsNull = peraggstate->value1IsNull;
|
||||||
/* prevent pfree below */
|
|
||||||
peraggstate->value1IsNull = true;
|
|
||||||
}
|
}
|
||||||
else if (OidIsValid(peraggstate->xfn2_oid))
|
else if (OidIsValid(peraggstate->xfn2_oid))
|
||||||
{
|
{
|
||||||
/* Return value2 */
|
/* Return value2 */
|
||||||
*resultVal = peraggstate->value2;
|
*resultVal = peraggstate->value2;
|
||||||
*resultIsNull = peraggstate->value2IsNull;
|
*resultIsNull = peraggstate->value2IsNull;
|
||||||
/* prevent pfree below */
|
|
||||||
peraggstate->value2IsNull = true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
elog(ERROR, "ExecAgg: no valid transition functions??");
|
elog(ERROR, "ExecAgg: no valid transition functions??");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Release any per-group working storage, unless we're passing it back
|
* If result is pass-by-ref, make sure it is in the right context.
|
||||||
* as the result of the aggregate.
|
|
||||||
*/
|
*/
|
||||||
if (OidIsValid(peraggstate->xfn1_oid) &&
|
if (!peraggstate->resulttypeByVal && ! *resultIsNull &&
|
||||||
!peraggstate->value1IsNull &&
|
! MemoryContextContains(CurrentMemoryContext,
|
||||||
!peraggstate->transtype1ByVal)
|
DatumGetPointer(*resultVal)))
|
||||||
pfree(DatumGetPointer(peraggstate->value1));
|
*resultVal = datumCopy(*resultVal,
|
||||||
|
peraggstate->resulttypeByVal,
|
||||||
if (OidIsValid(peraggstate->xfn2_oid) &&
|
peraggstate->resulttypeLen);
|
||||||
!peraggstate->value2IsNull &&
|
|
||||||
!peraggstate->transtype2ByVal)
|
|
||||||
pfree(DatumGetPointer(peraggstate->value2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ---------------------------------------
|
/* ---------------------------------------
|
||||||
*
|
*
|
||||||
* ExecAgg -
|
* ExecAgg -
|
||||||
|
@ -461,6 +510,7 @@ ExecAgg(Agg *node)
|
||||||
Datum *aggvalues;
|
Datum *aggvalues;
|
||||||
bool *aggnulls;
|
bool *aggnulls;
|
||||||
AggStatePerAgg peragg;
|
AggStatePerAgg peragg;
|
||||||
|
MemoryContext oldContext;
|
||||||
TupleTableSlot *resultSlot;
|
TupleTableSlot *resultSlot;
|
||||||
HeapTuple inputTuple;
|
HeapTuple inputTuple;
|
||||||
int aggno;
|
int aggno;
|
||||||
|
@ -481,14 +531,18 @@ ExecAgg(Agg *node)
|
||||||
peragg = aggstate->peragg;
|
peragg = aggstate->peragg;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We loop retrieving groups until we find one matching
|
* We loop retrieving groups until we find one matching node->plan.qual
|
||||||
* node->plan.qual
|
|
||||||
*/
|
*/
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (aggstate->agg_done)
|
if (aggstate->agg_done)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear the per-output-tuple context for each group
|
||||||
|
*/
|
||||||
|
MemoryContextReset(aggstate->tup_cxt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize working state for a new input tuple group
|
* Initialize working state for a new input tuple group
|
||||||
*/
|
*/
|
||||||
|
@ -514,6 +568,17 @@ ExecAgg(Agg *node)
|
||||||
break;
|
break;
|
||||||
econtext->ecxt_scantuple = outerslot;
|
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++)
|
for (aggno = 0; aggno < aggstate->numaggs; aggno++)
|
||||||
{
|
{
|
||||||
AggStatePerAgg peraggstate = &peragg[aggno];
|
AggStatePerAgg peraggstate = &peragg[aggno];
|
||||||
|
@ -527,13 +592,26 @@ ExecAgg(Agg *node)
|
||||||
continue; /* ignore this tuple for this agg */
|
continue; /* ignore this tuple for this agg */
|
||||||
|
|
||||||
if (aggref->aggdistinct)
|
if (aggref->aggdistinct)
|
||||||
|
{
|
||||||
|
/* putdatum has to be called in per-query context */
|
||||||
|
MemoryContextSwitchTo(oldContext);
|
||||||
tuplesort_putdatum(peraggstate->sortstate,
|
tuplesort_putdatum(peraggstate->sortstate,
|
||||||
newVal, isNull);
|
newVal, isNull);
|
||||||
|
MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
advance_transition_functions(peraggstate,
|
advance_transition_functions(peraggstate,
|
||||||
newVal, isNull);
|
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.
|
* Keep a copy of the first input tuple for the projection.
|
||||||
* (We only need one since only the GROUP BY columns in it can
|
* (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
|
* 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++)
|
for (aggno = 0; aggno < aggstate->numaggs; aggno++)
|
||||||
{
|
{
|
||||||
AggStatePerAgg peraggstate = &peragg[aggno];
|
AggStatePerAgg peraggstate = &peragg[aggno];
|
||||||
|
|
||||||
finalize_aggregate(peraggstate,
|
if (! peraggstate->aggref->aggdistinct)
|
||||||
&aggvalues[aggno], &aggnulls[aggno]);
|
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
|
* If inputtuple==NULL (ie, the outerPlan didn't return
|
||||||
* anything), create a dummy all-nulls input tuple for use by
|
* 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
|
* because ordinarily the projected output tuple's targetlist
|
||||||
* cannot contain any direct (non-aggregated) references to
|
* cannot contain any direct (non-aggregated) references to
|
||||||
* input columns, so the dummy tuple will not be referenced.
|
* 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
|
* 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,
|
ExecStoreTuple(inputTuple,
|
||||||
aggstate->csstate.css_ScanTupleSlot,
|
aggstate->csstate.css_ScanTupleSlot,
|
||||||
|
@ -627,6 +730,11 @@ ExecAgg(Agg *node)
|
||||||
true);
|
true);
|
||||||
econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
|
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
|
* Form a projection tuple using the aggregate results and the
|
||||||
* representative input tuple. Store it in the result tuple slot.
|
* 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);
|
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
|
#define AGG_NSLOTS 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -769,16 +899,20 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
|
||||||
/* Fill in the peraggstate data */
|
/* Fill in the peraggstate data */
|
||||||
peraggstate->aggref = aggref;
|
peraggstate->aggref = aggref;
|
||||||
|
|
||||||
aggTuple = SearchSysCacheTuple(AGGNAME,
|
aggTuple = SearchSysCacheTupleCopy(AGGNAME,
|
||||||
PointerGetDatum(aggname),
|
PointerGetDatum(aggname),
|
||||||
ObjectIdGetDatum(aggref->basetype),
|
ObjectIdGetDatum(aggref->basetype),
|
||||||
0, 0);
|
0, 0);
|
||||||
if (!HeapTupleIsValid(aggTuple))
|
if (!HeapTupleIsValid(aggTuple))
|
||||||
elog(ERROR, "ExecAgg: cache lookup failed for aggregate %s(%s)",
|
elog(ERROR, "ExecAgg: cache lookup failed for aggregate %s(%s)",
|
||||||
aggname,
|
aggname,
|
||||||
typeidTypeName(aggref->basetype));
|
typeidTypeName(aggref->basetype));
|
||||||
aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
|
aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
|
||||||
|
|
||||||
|
typeInfo = typeidType(aggform->aggfinaltype);
|
||||||
|
peraggstate->resulttypeLen = typeLen(typeInfo);
|
||||||
|
peraggstate->resulttypeByVal = typeByVal(typeInfo);
|
||||||
|
|
||||||
peraggstate->initValue1 =
|
peraggstate->initValue1 =
|
||||||
AggNameGetInitVal(aggname,
|
AggNameGetInitVal(aggname,
|
||||||
aggform->aggbasetype,
|
aggform->aggbasetype,
|
||||||
|
@ -846,6 +980,8 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
|
||||||
peraggstate->sortOperator = any_ordering_op(inputType);
|
peraggstate->sortOperator = any_ordering_op(inputType);
|
||||||
peraggstate->sortstate = NULL;
|
peraggstate->sortstate = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
heap_freetuple(aggTuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -866,6 +1002,17 @@ ExecEndAgg(Agg *node)
|
||||||
Plan *outerPlan;
|
Plan *outerPlan;
|
||||||
|
|
||||||
ExecFreeProjectionInfo(&aggstate->csstate.cstate);
|
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);
|
outerPlan = outerPlan(node);
|
||||||
ExecEndNode(outerPlan, (Plan *) node);
|
ExecEndNode(outerPlan, (Plan *) node);
|
||||||
|
@ -890,28 +1037,4 @@ ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
*/
|
*/
|
||||||
if (((Plan *) node)->lefttree->chgParam == NULL)
|
if (((Plan *) node)->lefttree->chgParam == NULL)
|
||||||
ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
node->appendstate = appendstate;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Miscellanious initialization
|
* Miscellaneous initialization
|
||||||
*
|
|
||||||
* + assign node's base_id
|
|
||||||
* + assign debugging hooks
|
|
||||||
*
|
*
|
||||||
* Append plans don't have expression contexts because they
|
* 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
|
#define APPEND_NSLOTS 1
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -380,7 +376,7 @@ ExecCountSlotsAppend(Append *node)
|
||||||
*
|
*
|
||||||
* Handles the iteration over the multiple scans.
|
* 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 *
|
TupleTableSlot *
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
* locate group boundaries.
|
* locate group boundaries.
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
EState *estate;
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
|
|
||||||
HeapTuple outerTuple = NULL;
|
HeapTuple outerTuple = NULL;
|
||||||
HeapTuple firsttuple;
|
HeapTuple firsttuple;
|
||||||
TupleTableSlot *outerslot;
|
TupleTableSlot *outerslot;
|
||||||
ProjectionInfo *projInfo;
|
ProjectionInfo *projInfo;
|
||||||
TupleTableSlot *resultSlot;
|
TupleTableSlot *resultSlot;
|
||||||
|
|
||||||
bool isDone;
|
bool isDone;
|
||||||
|
|
||||||
/* ---------------------
|
/* ---------------------
|
||||||
|
@ -84,14 +82,16 @@ ExecGroupEveryTuple(Group *node)
|
||||||
grpstate = node->grpstate;
|
grpstate = node->grpstate;
|
||||||
if (grpstate->grp_done)
|
if (grpstate->grp_done)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
estate = node->plan.state;
|
estate = node->plan.state;
|
||||||
|
|
||||||
econtext = grpstate->csstate.cstate.cs_ExprContext;
|
econtext = grpstate->csstate.cstate.cs_ExprContext;
|
||||||
|
|
||||||
tupdesc = ExecGetScanType(&grpstate->csstate);
|
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)
|
if (grpstate->grp_useFirstTuple)
|
||||||
{
|
{
|
||||||
grpstate->grp_useFirstTuple = FALSE;
|
grpstate->grp_useFirstTuple = FALSE;
|
||||||
|
@ -130,7 +130,8 @@ ExecGroupEveryTuple(Group *node)
|
||||||
if (!execTuplesMatch(firsttuple, outerTuple,
|
if (!execTuplesMatch(firsttuple, outerTuple,
|
||||||
tupdesc,
|
tupdesc,
|
||||||
node->numCols, node->grpColIdx,
|
node->numCols, node->grpColIdx,
|
||||||
grpstate->eqfunctions))
|
grpstate->eqfunctions,
|
||||||
|
econtext->ecxt_per_tuple_memory))
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -179,13 +180,11 @@ ExecGroupOneTuple(Group *node)
|
||||||
EState *estate;
|
EState *estate;
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
|
|
||||||
HeapTuple outerTuple = NULL;
|
HeapTuple outerTuple = NULL;
|
||||||
HeapTuple firsttuple;
|
HeapTuple firsttuple;
|
||||||
TupleTableSlot *outerslot;
|
TupleTableSlot *outerslot;
|
||||||
ProjectionInfo *projInfo;
|
ProjectionInfo *projInfo;
|
||||||
TupleTableSlot *resultSlot;
|
TupleTableSlot *resultSlot;
|
||||||
|
|
||||||
bool isDone;
|
bool isDone;
|
||||||
|
|
||||||
/* ---------------------
|
/* ---------------------
|
||||||
|
@ -195,13 +194,15 @@ ExecGroupOneTuple(Group *node)
|
||||||
grpstate = node->grpstate;
|
grpstate = node->grpstate;
|
||||||
if (grpstate->grp_done)
|
if (grpstate->grp_done)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
estate = node->plan.state;
|
estate = node->plan.state;
|
||||||
|
|
||||||
econtext = node->grpstate->csstate.cstate.cs_ExprContext;
|
econtext = node->grpstate->csstate.cstate.cs_ExprContext;
|
||||||
|
|
||||||
tupdesc = ExecGetScanType(&grpstate->csstate);
|
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;
|
firsttuple = grpstate->grp_firstTuple;
|
||||||
if (firsttuple == NULL)
|
if (firsttuple == NULL)
|
||||||
{
|
{
|
||||||
|
@ -237,7 +238,8 @@ ExecGroupOneTuple(Group *node)
|
||||||
if (!execTuplesMatch(firsttuple, outerTuple,
|
if (!execTuplesMatch(firsttuple, outerTuple,
|
||||||
tupdesc,
|
tupdesc,
|
||||||
node->numCols, node->grpColIdx,
|
node->numCols, node->grpColIdx,
|
||||||
grpstate->eqfunctions))
|
grpstate->eqfunctions,
|
||||||
|
econtext->ecxt_per_tuple_memory))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,10 +298,8 @@ ExecInitGroup(Group *node, EState *estate, Plan *parent)
|
||||||
grpstate->grp_firstTuple = NULL;
|
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);
|
ExecAssignExprContext(estate, &grpstate->csstate.cstate);
|
||||||
|
|
||||||
#define GROUP_NSLOTS 2
|
#define GROUP_NSLOTS 2
|
||||||
|
@ -360,6 +360,7 @@ ExecEndGroup(Group *node)
|
||||||
grpstate = node->grpstate;
|
grpstate = node->grpstate;
|
||||||
|
|
||||||
ExecFreeProjectionInfo(&grpstate->csstate.cstate);
|
ExecFreeProjectionInfo(&grpstate->csstate.cstate);
|
||||||
|
ExecFreeExprContext(&grpstate->csstate.cstate);
|
||||||
|
|
||||||
outerPlan = outerPlan(node);
|
outerPlan = outerPlan(node);
|
||||||
ExecEndNode(outerPlan, (Plan *) node);
|
ExecEndNode(outerPlan, (Plan *) node);
|
||||||
|
@ -406,6 +407,9 @@ ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
* numCols: the number of attributes to be examined
|
* numCols: the number of attributes to be examined
|
||||||
* matchColIdx: array of attribute column numbers
|
* matchColIdx: array of attribute column numbers
|
||||||
* eqFunctions: array of fmgr lookup info for the equality functions to use
|
* 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
|
bool
|
||||||
execTuplesMatch(HeapTuple tuple1,
|
execTuplesMatch(HeapTuple tuple1,
|
||||||
|
@ -413,16 +417,25 @@ execTuplesMatch(HeapTuple tuple1,
|
||||||
TupleDesc tupdesc,
|
TupleDesc tupdesc,
|
||||||
int numCols,
|
int numCols,
|
||||||
AttrNumber *matchColIdx,
|
AttrNumber *matchColIdx,
|
||||||
FmgrInfo *eqfunctions)
|
FmgrInfo *eqfunctions,
|
||||||
|
MemoryContext evalContext)
|
||||||
{
|
{
|
||||||
|
MemoryContext oldContext;
|
||||||
|
bool result;
|
||||||
int i;
|
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
|
* 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,
|
* can report a non-match as soon as we find unequal fields. So,
|
||||||
* start comparing at the last field (least significant sort key).
|
* start comparing at the last field (least significant sort key).
|
||||||
* That's the most likely to be different...
|
* That's the most likely to be different...
|
||||||
*/
|
*/
|
||||||
|
result = true;
|
||||||
|
|
||||||
for (i = numCols; --i >= 0;)
|
for (i = numCols; --i >= 0;)
|
||||||
{
|
{
|
||||||
AttrNumber att = matchColIdx[i];
|
AttrNumber att = matchColIdx[i];
|
||||||
|
@ -442,7 +455,10 @@ execTuplesMatch(HeapTuple tuple1,
|
||||||
&isNull2);
|
&isNull2);
|
||||||
|
|
||||||
if (isNull1 != 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)
|
if (isNull1)
|
||||||
continue; /* both are null, treat as equal */
|
continue; /* both are null, treat as equal */
|
||||||
|
@ -451,10 +467,15 @@ execTuplesMatch(HeapTuple tuple1,
|
||||||
|
|
||||||
if (! DatumGetBool(FunctionCall2(&eqfunctions[i],
|
if (! DatumGetBool(FunctionCall2(&eqfunctions[i],
|
||||||
attr1, attr2)))
|
attr1, attr2)))
|
||||||
return FALSE;
|
{
|
||||||
|
result = false; /* they aren't equal */
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
MemoryContextSwitchTo(oldContext);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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/nodeHash.h"
|
||||||
#include "executor/nodeHashjoin.h"
|
#include "executor/nodeHashjoin.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
#include "parser/parse_expr.h"
|
||||||
|
#include "parser/parse_type.h"
|
||||||
|
|
||||||
static int hashFunc(Datum key, int len, bool byVal);
|
static int hashFunc(Datum key, int len, bool byVal);
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ ExecHash(Hash *node)
|
||||||
EState *estate;
|
EState *estate;
|
||||||
HashState *hashstate;
|
HashState *hashstate;
|
||||||
Plan *outerNode;
|
Plan *outerNode;
|
||||||
Var *hashkey;
|
Node *hashkey;
|
||||||
HashJoinTable hashtable;
|
HashJoinTable hashtable;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
|
@ -139,12 +140,9 @@ ExecInitHash(Hash *node, EState *estate, Plan *parent)
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Miscellaneous initialization
|
* Miscellaneous initialization
|
||||||
*
|
*
|
||||||
* + assign node's base_id
|
|
||||||
* + assign debugging hooks and
|
|
||||||
* + create expression context for node
|
* + create expression context for node
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecAssignNodeBaseInfo(estate, &hashstate->cstate, parent);
|
|
||||||
ExecAssignExprContext(estate, &hashstate->cstate);
|
ExecAssignExprContext(estate, &hashstate->cstate);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -204,6 +202,7 @@ ExecEndHash(Hash *node)
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecFreeProjectionInfo(&hashstate->cstate);
|
ExecFreeProjectionInfo(&hashstate->cstate);
|
||||||
|
ExecFreeExprContext(&hashstate->cstate);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* shut down the subplan
|
* shut down the subplan
|
||||||
|
@ -236,6 +235,7 @@ ExecHashTableCreate(Hash *node)
|
||||||
int totalbuckets;
|
int totalbuckets;
|
||||||
int bucketsize;
|
int bucketsize;
|
||||||
int i;
|
int i;
|
||||||
|
Type typeInfo;
|
||||||
MemoryContext oldcxt;
|
MemoryContext oldcxt;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -346,6 +346,14 @@ ExecHashTableCreate(Hash *node)
|
||||||
hashtable->innerBatchSize = NULL;
|
hashtable->innerBatchSize = NULL;
|
||||||
hashtable->outerBatchSize = 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
|
* Create temporary memory contexts in which to keep the hashtable
|
||||||
* working storage. See notes in executor/hashjoin.h.
|
* working storage. See notes in executor/hashjoin.h.
|
||||||
|
@ -448,7 +456,7 @@ ExecHashTableDestroy(HashJoinTable hashtable)
|
||||||
void
|
void
|
||||||
ExecHashTableInsert(HashJoinTable hashtable,
|
ExecHashTableInsert(HashJoinTable hashtable,
|
||||||
ExprContext *econtext,
|
ExprContext *econtext,
|
||||||
Var *hashkey)
|
Node *hashkey)
|
||||||
{
|
{
|
||||||
int bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
|
int bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
|
||||||
TupleTableSlot *slot = econtext->ecxt_innertuple;
|
TupleTableSlot *slot = econtext->ecxt_innertuple;
|
||||||
|
@ -508,43 +516,44 @@ ExecHashTableInsert(HashJoinTable hashtable,
|
||||||
int
|
int
|
||||||
ExecHashGetBucket(HashJoinTable hashtable,
|
ExecHashGetBucket(HashJoinTable hashtable,
|
||||||
ExprContext *econtext,
|
ExprContext *econtext,
|
||||||
Var *hashkey)
|
Node *hashkey)
|
||||||
{
|
{
|
||||||
int bucketno;
|
int bucketno;
|
||||||
Datum keyval;
|
Datum keyval;
|
||||||
bool isNull;
|
bool isNull;
|
||||||
|
bool isDone;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Get the join attribute value of the tuple
|
* Get the join attribute value of the tuple
|
||||||
*
|
*
|
||||||
* ...It's quick hack - use ExecEvalExpr instead of ExecEvalVar:
|
* We reset the eval context each time to avoid any possibility
|
||||||
* hashkey may be T_ArrayRef, not just T_Var. - vadim 04/22/97
|
* of memory leaks in the hash function.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
keyval = ExecEvalExpr((Node *) hashkey, econtext, &isNull, NULL);
|
ResetExprContext(econtext);
|
||||||
|
|
||||||
/*
|
keyval = ExecEvalExprSwitchContext(hashkey, econtext,
|
||||||
* keyval could be null, so we better point it to something valid
|
&isNull, &isDone);
|
||||||
* before trying to run hashFunc on it. --djm 8/17/96
|
|
||||||
*/
|
|
||||||
if (isNull)
|
|
||||||
{
|
|
||||||
execConstByVal = 0;
|
|
||||||
execConstLen = 0;
|
|
||||||
keyval = (Datum) "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------
|
/* ------------------
|
||||||
* compute the hash function
|
* 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
|
#ifdef HJDEBUG
|
||||||
if (bucketno >= hashtable->nbuckets)
|
if (bucketno >= hashtable->nbuckets)
|
||||||
printf("hash(%d) = %d SAVED\n", keyval, bucketno);
|
printf("hash(%ld) = %d SAVED\n", (long) keyval, bucketno);
|
||||||
else
|
else
|
||||||
printf("hash(%d) = %d\n", keyval, bucketno);
|
printf("hash(%ld) = %d\n", (long) keyval, bucketno);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return bucketno;
|
return bucketno;
|
||||||
|
@ -585,6 +594,9 @@ ExecScanHashBucket(HashJoinState *hjstate,
|
||||||
false); /* do not pfree this tuple */
|
false); /* do not pfree this tuple */
|
||||||
econtext->ecxt_innertuple = inntuple;
|
econtext->ecxt_innertuple = inntuple;
|
||||||
|
|
||||||
|
/* reset temp memory each time to avoid leaks from qual expression */
|
||||||
|
ResetExprContext(econtext);
|
||||||
|
|
||||||
if (ExecQual(hjclauses, econtext, false))
|
if (ExecQual(hjclauses, econtext, false))
|
||||||
{
|
{
|
||||||
hjstate->hj_CurTuple = hashTuple;
|
hjstate->hj_CurTuple = hashTuple;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
List *qual;
|
||||||
ScanDirection dir;
|
ScanDirection dir;
|
||||||
TupleTableSlot *inntuple;
|
TupleTableSlot *inntuple;
|
||||||
Var *outerVar;
|
Node *outerVar;
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
HashJoinTable hashtable;
|
HashJoinTable hashtable;
|
||||||
HeapTuple curtuple;
|
HeapTuple curtuple;
|
||||||
TupleTableSlot *outerTupleSlot;
|
TupleTableSlot *outerTupleSlot;
|
||||||
TupleTableSlot *innerTupleSlot;
|
TupleTableSlot *innerTupleSlot;
|
||||||
Var *innerhashkey;
|
|
||||||
int i;
|
int i;
|
||||||
bool hashPhaseDone;
|
bool hashPhaseDone;
|
||||||
|
|
||||||
|
@ -73,7 +72,6 @@ ExecHashJoin(HashJoin *node)
|
||||||
hashNode = (Hash *) innerPlan(node);
|
hashNode = (Hash *) innerPlan(node);
|
||||||
outerNode = outerPlan(node);
|
outerNode = outerPlan(node);
|
||||||
hashPhaseDone = node->hashdone;
|
hashPhaseDone = node->hashdone;
|
||||||
|
|
||||||
dir = estate->es_direction;
|
dir = estate->es_direction;
|
||||||
|
|
||||||
/* -----------------
|
/* -----------------
|
||||||
|
@ -81,13 +79,21 @@ ExecHashJoin(HashJoin *node)
|
||||||
* -----------------
|
* -----------------
|
||||||
*/
|
*/
|
||||||
hashtable = hjstate->hj_HashTable;
|
hashtable = hjstate->hj_HashTable;
|
||||||
|
|
||||||
/* --------------------
|
|
||||||
* initialize expression context
|
|
||||||
* --------------------
|
|
||||||
*/
|
|
||||||
econtext = hjstate->jstate.cs_ExprContext;
|
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)
|
if (hjstate->jstate.cs_TupFromTlist)
|
||||||
{
|
{
|
||||||
TupleTableSlot *result;
|
TupleTableSlot *result;
|
||||||
|
@ -96,6 +102,8 @@ ExecHashJoin(HashJoin *node)
|
||||||
result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
|
result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
|
||||||
if (!isDone)
|
if (!isDone)
|
||||||
return result;
|
return result;
|
||||||
|
/* Done with that source tuple... */
|
||||||
|
hjstate->jstate.cs_TupFromTlist = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -112,8 +120,7 @@ ExecHashJoin(HashJoin *node)
|
||||||
*/
|
*/
|
||||||
hashtable = ExecHashTableCreate(hashNode);
|
hashtable = ExecHashTableCreate(hashNode);
|
||||||
hjstate->hj_HashTable = hashtable;
|
hjstate->hj_HashTable = hashtable;
|
||||||
innerhashkey = hashNode->hashkey;
|
hjstate->hj_InnerHashKey = hashNode->hashkey;
|
||||||
hjstate->hj_InnerHashKey = innerhashkey;
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* execute the Hash node, to build the hash table
|
* execute the Hash node, to build the hash table
|
||||||
|
@ -139,7 +146,7 @@ ExecHashJoin(HashJoin *node)
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
|
outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
|
||||||
outerVar = get_leftop(clause);
|
outerVar = (Node *) get_leftop(clause);
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
@ -220,6 +227,10 @@ ExecHashJoin(HashJoin *node)
|
||||||
InvalidBuffer,
|
InvalidBuffer,
|
||||||
false); /* don't pfree this tuple */
|
false); /* don't pfree this tuple */
|
||||||
econtext->ecxt_innertuple = inntuple;
|
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
|
* if we pass the qual, then save state for next call and
|
||||||
* have ExecProject form the projection, store it
|
* have ExecProject form the projection, store it
|
||||||
|
@ -279,12 +290,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Miscellaneous initialization
|
* Miscellaneous initialization
|
||||||
*
|
*
|
||||||
* + assign node's base_id
|
|
||||||
* + assign debugging hooks and
|
|
||||||
* + create expression context for node
|
* + create expression context for node
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent);
|
|
||||||
ExecAssignExprContext(estate, &hjstate->jstate);
|
ExecAssignExprContext(estate, &hjstate->jstate);
|
||||||
|
|
||||||
#define HASHJOIN_NSLOTS 2
|
#define HASHJOIN_NSLOTS 2
|
||||||
|
@ -343,10 +351,10 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
|
||||||
hjstate->hj_HashTable = (HashJoinTable) NULL;
|
hjstate->hj_HashTable = (HashJoinTable) NULL;
|
||||||
hjstate->hj_CurBucketNo = 0;
|
hjstate->hj_CurBucketNo = 0;
|
||||||
hjstate->hj_CurTuple = (HashJoinTuple) NULL;
|
hjstate->hj_CurTuple = (HashJoinTuple) NULL;
|
||||||
hjstate->hj_InnerHashKey = (Var *) NULL;
|
hjstate->hj_InnerHashKey = (Node *) NULL;
|
||||||
|
|
||||||
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
|
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
|
||||||
hjstate->jstate.cs_TupFromTlist = (bool) false;
|
hjstate->jstate.cs_TupFromTlist = false;
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -396,6 +404,7 @@ ExecEndHashJoin(HashJoin *node)
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecFreeProjectionInfo(&hjstate->jstate);
|
ExecFreeProjectionInfo(&hjstate->jstate);
|
||||||
|
ExecFreeExprContext(&hjstate->jstate);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* clean up subtrees
|
* clean up subtrees
|
||||||
|
@ -510,7 +519,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate)
|
||||||
BufFile *innerFile;
|
BufFile *innerFile;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
Var *innerhashkey;
|
Node *innerhashkey;
|
||||||
|
|
||||||
if (newbatch > 1)
|
if (newbatch > 1)
|
||||||
{
|
{
|
||||||
|
@ -651,10 +660,10 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
|
|
||||||
hjstate->hj_CurBucketNo = 0;
|
hjstate->hj_CurBucketNo = 0;
|
||||||
hjstate->hj_CurTuple = (HashJoinTuple) NULL;
|
hjstate->hj_CurTuple = (HashJoinTuple) NULL;
|
||||||
hjstate->hj_InnerHashKey = (Var *) NULL;
|
hjstate->hj_InnerHashKey = (Node *) NULL;
|
||||||
|
|
||||||
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) 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
|
* if chgParam of subnodes is not null then plans will be re-scanned
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
EState *estate;
|
||||||
CommonScanState *scanstate;
|
CommonScanState *scanstate;
|
||||||
IndexScanState *indexstate;
|
IndexScanState *indexstate;
|
||||||
|
ExprContext *econtext;
|
||||||
ScanDirection direction;
|
ScanDirection direction;
|
||||||
Snapshot snapshot;
|
Snapshot snapshot;
|
||||||
IndexScanDescPtr scanDescs;
|
IndexScanDescPtr scanDescs;
|
||||||
|
@ -89,7 +90,6 @@ IndexNext(IndexScan *node)
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
Buffer buffer = InvalidBuffer;
|
Buffer buffer = InvalidBuffer;
|
||||||
int numIndices;
|
int numIndices;
|
||||||
|
|
||||||
bool bBackward;
|
bool bBackward;
|
||||||
int indexNumber;
|
int indexNumber;
|
||||||
|
|
||||||
|
@ -112,6 +112,7 @@ IndexNext(IndexScan *node)
|
||||||
scanDescs = indexstate->iss_ScanDescs;
|
scanDescs = indexstate->iss_ScanDescs;
|
||||||
heapRelation = scanstate->css_currentRelation;
|
heapRelation = scanstate->css_currentRelation;
|
||||||
numIndices = indexstate->iss_NumIndices;
|
numIndices = indexstate->iss_NumIndices;
|
||||||
|
econtext = scanstate->cstate.cs_ExprContext;
|
||||||
slot = scanstate->css_ScanTupleSlot;
|
slot = scanstate->css_ScanTupleSlot;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -133,14 +134,15 @@ IndexNext(IndexScan *node)
|
||||||
slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
|
slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
|
||||||
slot->ttc_shouldFree = false;
|
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? */
|
/* Does the tuple meet any of the OR'd indxqual conditions? */
|
||||||
|
|
||||||
|
ResetExprContext(econtext);
|
||||||
|
|
||||||
foreach(qual, node->indxqualorig)
|
foreach(qual, node->indxqualorig)
|
||||||
{
|
{
|
||||||
if (ExecQual((List *) lfirst(qual),
|
if (ExecQual((List *) lfirst(qual), econtext, false))
|
||||||
scanstate->cstate.cs_ExprContext,
|
|
||||||
false))
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (qual == NIL) /* would not be returned by indices */
|
if (qual == NIL) /* would not be returned by indices */
|
||||||
|
@ -219,14 +221,13 @@ IndexNext(IndexScan *node)
|
||||||
* and checking for failure with all previous
|
* and checking for failure with all previous
|
||||||
* qualifications.
|
* qualifications.
|
||||||
*/
|
*/
|
||||||
scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
|
econtext->ecxt_scantuple = slot;
|
||||||
|
ResetExprContext(econtext);
|
||||||
qual = node->indxqualorig;
|
qual = node->indxqualorig;
|
||||||
for (prev_index = 0; prev_index < indexstate->iss_IndexPtr;
|
for (prev_index = 0; prev_index < indexstate->iss_IndexPtr;
|
||||||
prev_index++)
|
prev_index++)
|
||||||
{
|
{
|
||||||
if (ExecQual((List *) lfirst(qual),
|
if (ExecQual((List *) lfirst(qual), econtext, false))
|
||||||
scanstate->cstate.cs_ExprContext,
|
|
||||||
false))
|
|
||||||
{
|
{
|
||||||
prev_matches = true;
|
prev_matches = true;
|
||||||
break;
|
break;
|
||||||
|
@ -234,7 +235,7 @@ IndexNext(IndexScan *node)
|
||||||
qual = lnext(qual);
|
qual = lnext(qual);
|
||||||
}
|
}
|
||||||
if (!prev_matches)
|
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 */
|
/* Duplicate tuple, so drop it and loop back for another */
|
||||||
ExecClearTuple(slot);
|
ExecClearTuple(slot);
|
||||||
}
|
}
|
||||||
|
@ -284,7 +285,7 @@ ExecIndexScan(IndexScan *node)
|
||||||
* use IndexNext as access method
|
* 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
|
* Recalculates the value of the scan keys whose value depends on
|
||||||
* information known at runtime and rescans the indexed relation.
|
* information known at runtime and rescans the indexed relation.
|
||||||
* Updating the scan key was formerly done separately in
|
* Updating the scan key was formerly done separately in
|
||||||
* ExecUpdateIndexScanKeys. Integrating it into ReScan
|
* ExecUpdateIndexScanKeys. Integrating it into ReScan makes
|
||||||
* makes rescans of indices and
|
* rescans of indices and relations/general streams more uniform.
|
||||||
* relations/general streams more uniform.
|
|
||||||
*
|
*
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -304,6 +304,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
{
|
{
|
||||||
EState *estate;
|
EState *estate;
|
||||||
IndexScanState *indexstate;
|
IndexScanState *indexstate;
|
||||||
|
ExprContext *econtext;
|
||||||
ScanDirection direction;
|
ScanDirection direction;
|
||||||
IndexScanDescPtr scanDescs;
|
IndexScanDescPtr scanDescs;
|
||||||
ScanKey *scanKeys;
|
ScanKey *scanKeys;
|
||||||
|
@ -311,8 +312,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
ScanKey skey;
|
ScanKey skey;
|
||||||
int numIndices;
|
int numIndices;
|
||||||
int i;
|
int i;
|
||||||
|
int **runtimeKeyInfo;
|
||||||
Pointer *runtimeKeyInfo;
|
|
||||||
int *numScanKeys;
|
int *numScanKeys;
|
||||||
List *indxqual;
|
List *indxqual;
|
||||||
List *qual;
|
List *qual;
|
||||||
|
@ -326,22 +326,34 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
bool isNull;
|
bool isNull;
|
||||||
bool isDone;
|
bool isDone;
|
||||||
|
|
||||||
indexstate = node->indxstate;
|
|
||||||
estate = node->scan.plan.state;
|
estate = node->scan.plan.state;
|
||||||
|
indexstate = node->indxstate;
|
||||||
|
econtext = indexstate->iss_RuntimeContext; /* context for runtime keys */
|
||||||
direction = estate->es_direction;
|
direction = estate->es_direction;
|
||||||
numIndices = indexstate->iss_NumIndices;
|
numIndices = indexstate->iss_NumIndices;
|
||||||
scanDescs = indexstate->iss_ScanDescs;
|
scanDescs = indexstate->iss_ScanDescs;
|
||||||
scanKeys = indexstate->iss_ScanKeys;
|
scanKeys = indexstate->iss_ScanKeys;
|
||||||
runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
|
runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
|
||||||
numScanKeys = indexstate->iss_NumScanKeys;
|
numScanKeys = indexstate->iss_NumScanKeys;
|
||||||
indexstate->iss_IndexPtr = -1;
|
indexstate->iss_IndexPtr = -1;
|
||||||
if (ScanDirectionIsBackward(node->indxorderdir))
|
if (ScanDirectionIsBackward(node->indxorderdir))
|
||||||
indexstate->iss_IndexPtr = numIndices;
|
indexstate->iss_IndexPtr = numIndices;
|
||||||
|
|
||||||
/* If we are being passed an outer tuple, save it for runtime key calc */
|
if (econtext)
|
||||||
if (exprCtxt != NULL)
|
{
|
||||||
node->scan.scanstate->cstate.cs_ExprContext->ecxt_outertuple =
|
/*
|
||||||
exprCtxt->ecxt_outertuple;
|
* 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 this is re-scanning of PlanQual ... */
|
||||||
if (estate->es_evTuple != NULL &&
|
if (estate->es_evTuple != NULL &&
|
||||||
|
@ -364,7 +376,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
|
|
||||||
if (runtimeKeyInfo)
|
if (runtimeKeyInfo)
|
||||||
{
|
{
|
||||||
run_keys = (int *) runtimeKeyInfo[i];
|
run_keys = runtimeKeyInfo[i];
|
||||||
for (j = 0; j < n_keys; j++)
|
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
|
* expression and evaluate it with respect to the current
|
||||||
* outer tuple. We then stick the result into the scan
|
* outer tuple. We then stick the result into the scan
|
||||||
* key.
|
* 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)
|
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
|
* pass in isDone but ignore it. We don't iterate in
|
||||||
* quals
|
* quals
|
||||||
*/
|
*/
|
||||||
scanvalue = (Datum)
|
scanvalue = ExecEvalExprSwitchContext(scanexpr,
|
||||||
ExecEvalExpr(scanexpr,
|
econtext,
|
||||||
node->scan.scanstate->cstate.cs_ExprContext,
|
&isNull,
|
||||||
&isNull, &isDone);
|
&isDone);
|
||||||
scan_keys[j].sk_argument = scanvalue;
|
scan_keys[j].sk_argument = scanvalue;
|
||||||
if (isNull)
|
if (isNull)
|
||||||
scan_keys[j].sk_flags |= SK_ISNULL;
|
scan_keys[j].sk_flags |= SK_ISNULL;
|
||||||
|
@ -401,11 +420,6 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
skey = scanKeys[i];
|
skey = scanKeys[i];
|
||||||
index_rescan(scan, direction, skey);
|
index_rescan(scan, direction, skey);
|
||||||
}
|
}
|
||||||
/* ----------------
|
|
||||||
* perhaps return something meaningful
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
@ -421,7 +435,7 @@ ExecEndIndexScan(IndexScan *node)
|
||||||
{
|
{
|
||||||
CommonScanState *scanstate;
|
CommonScanState *scanstate;
|
||||||
IndexScanState *indexstate;
|
IndexScanState *indexstate;
|
||||||
Pointer *runtimeKeyInfo;
|
int **runtimeKeyInfo;
|
||||||
ScanKey *scanKeys;
|
ScanKey *scanKeys;
|
||||||
List *indxqual;
|
List *indxqual;
|
||||||
int *numScanKeys;
|
int *numScanKeys;
|
||||||
|
@ -431,7 +445,7 @@ ExecEndIndexScan(IndexScan *node)
|
||||||
scanstate = node->scan.scanstate;
|
scanstate = node->scan.scanstate;
|
||||||
indexstate = node->indxstate;
|
indexstate = node->indxstate;
|
||||||
indxqual = node->indxqual;
|
indxqual = node->indxqual;
|
||||||
runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
|
runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* extract information from the node
|
* extract information from the node
|
||||||
|
@ -451,6 +465,9 @@ ExecEndIndexScan(IndexScan *node)
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecFreeProjectionInfo(&scanstate->cstate);
|
ExecFreeProjectionInfo(&scanstate->cstate);
|
||||||
|
ExecFreeExprContext(&scanstate->cstate);
|
||||||
|
if (indexstate->iss_RuntimeContext)
|
||||||
|
FreeExprContext(indexstate->iss_RuntimeContext);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* close the heap and index relations
|
* close the heap and index relations
|
||||||
|
@ -474,12 +491,7 @@ ExecEndIndexScan(IndexScan *node)
|
||||||
{
|
{
|
||||||
for (i = 0; i < numIndices; i++)
|
for (i = 0; i < numIndices; i++)
|
||||||
{
|
{
|
||||||
List *qual;
|
if (runtimeKeyInfo[i] != NULL)
|
||||||
int n_keys;
|
|
||||||
|
|
||||||
qual = nth(i, indxqual);
|
|
||||||
n_keys = length(qual);
|
|
||||||
if (n_keys > 0)
|
|
||||||
pfree(runtimeKeyInfo[i]);
|
pfree(runtimeKeyInfo[i]);
|
||||||
}
|
}
|
||||||
pfree(runtimeKeyInfo);
|
pfree(runtimeKeyInfo);
|
||||||
|
@ -491,7 +503,6 @@ ExecEndIndexScan(IndexScan *node)
|
||||||
*/
|
*/
|
||||||
ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
|
ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
|
||||||
ExecClearTuple(scanstate->css_ScanTupleSlot);
|
ExecClearTuple(scanstate->css_ScanTupleSlot);
|
||||||
/* ExecClearTuple(scanstate->css_RawTupleSlot); */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
@ -562,7 +573,7 @@ ExecIndexRestrPos(IndexScan *node)
|
||||||
*
|
*
|
||||||
* old comments
|
* old comments
|
||||||
* Creates the run-time state information for the node and
|
* 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:
|
* Parameters:
|
||||||
* node: IndexNode node produced by the planner.
|
* node: IndexNode node produced by the planner.
|
||||||
|
@ -583,19 +594,16 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
||||||
int *numScanKeys;
|
int *numScanKeys;
|
||||||
RelationPtr relationDescs;
|
RelationPtr relationDescs;
|
||||||
IndexScanDescPtr scanDescs;
|
IndexScanDescPtr scanDescs;
|
||||||
Pointer *runtimeKeyInfo;
|
int **runtimeKeyInfo;
|
||||||
bool have_runtime_keys;
|
bool have_runtime_keys;
|
||||||
List *rangeTable;
|
List *rangeTable;
|
||||||
RangeTblEntry *rtentry;
|
RangeTblEntry *rtentry;
|
||||||
Index relid;
|
Index relid;
|
||||||
Oid reloid;
|
Oid reloid;
|
||||||
|
|
||||||
Relation currentRelation;
|
Relation currentRelation;
|
||||||
HeapScanDesc currentScanDesc;
|
HeapScanDesc currentScanDesc;
|
||||||
ScanDirection direction;
|
ScanDirection direction;
|
||||||
int baseid;
|
List *execParam = NIL;
|
||||||
|
|
||||||
List *execParam = NULL;
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* assign execution state to node
|
* assign execution state to node
|
||||||
|
@ -610,25 +618,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
||||||
* --------------------------------
|
* --------------------------------
|
||||||
*/
|
*/
|
||||||
scanstate = makeNode(CommonScanState);
|
scanstate = makeNode(CommonScanState);
|
||||||
/*
|
|
||||||
scanstate->ss_ProcOuterFlag = false;
|
|
||||||
scanstate->ss_OldRelId = 0;
|
|
||||||
*/
|
|
||||||
|
|
||||||
node->scan.scanstate = scanstate;
|
node->scan.scanstate = scanstate;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* assign node's base_id .. we don't use AssignNodeBaseid() because
|
* Miscellaneous initialization
|
||||||
* the increment is done later on after we assign the index scan's
|
*
|
||||||
* scanstate. see below.
|
* + create expression context for node
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
baseid = estate->es_BaseId;
|
|
||||||
/* scanstate->csstate.cstate.bnode.base_id = baseid; */
|
|
||||||
scanstate->cstate.cs_base_id = baseid;
|
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* create expression context for node
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecAssignExprContext(estate, &scanstate->cstate);
|
ExecAssignExprContext(estate, &scanstate->cstate);
|
||||||
|
@ -640,7 +635,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
||||||
*/
|
*/
|
||||||
ExecInitResultTupleSlot(estate, &scanstate->cstate);
|
ExecInitResultTupleSlot(estate, &scanstate->cstate);
|
||||||
ExecInitScanTupleSlot(estate, scanstate);
|
ExecInitScanTupleSlot(estate, scanstate);
|
||||||
/* ExecInitRawTupleSlot(estate, scanstate); */
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* initialize projection info. result type comes from scan desc
|
* 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_ScanKeys = NULL;
|
||||||
indexstate->iss_NumScanKeys = NULL;
|
indexstate->iss_NumScanKeys = NULL;
|
||||||
indexstate->iss_RuntimeKeyInfo = NULL;
|
indexstate->iss_RuntimeKeyInfo = NULL;
|
||||||
|
indexstate->iss_RuntimeContext = NULL;
|
||||||
indexstate->iss_RelationDescs = NULL;
|
indexstate->iss_RelationDescs = NULL;
|
||||||
indexstate->iss_ScanDescs = NULL;
|
indexstate->iss_ScanDescs = NULL;
|
||||||
|
|
||||||
node->indxstate = indexstate;
|
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
|
* get the index node information
|
||||||
* ----------------
|
* ----------------
|
||||||
|
@ -696,12 +683,11 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
||||||
scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
|
scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* initialize runtime key info.
|
* initialize space for runtime key info (may not be needed)
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
have_runtime_keys = false;
|
have_runtime_keys = false;
|
||||||
runtimeKeyInfo = (Pointer *)
|
runtimeKeyInfo = (int **) palloc(numIndices * sizeof(int *));
|
||||||
palloc(numIndices * sizeof(Pointer));
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* build the index scan keys from the index qualification
|
* build the index scan keys from the index qualification
|
||||||
|
@ -719,9 +705,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
||||||
qual = lfirst(indxqual);
|
qual = lfirst(indxqual);
|
||||||
indxqual = lnext(indxqual);
|
indxqual = lnext(indxqual);
|
||||||
n_keys = length(qual);
|
n_keys = length(qual);
|
||||||
scan_keys = (n_keys <= 0) ? NULL :
|
scan_keys = (n_keys <= 0) ? (ScanKey) NULL :
|
||||||
(ScanKey) palloc(n_keys * sizeof(ScanKeyData));
|
(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));
|
(int *) palloc(n_keys * sizeof(int));
|
||||||
|
|
||||||
CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
|
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;
|
numScanKeys[i] = n_keys;
|
||||||
scanKeys[i] = scan_keys;
|
scanKeys[i] = scan_keys;
|
||||||
runtimeKeyInfo[i] = (Pointer) run_keys;
|
runtimeKeyInfo[i] = run_keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
indexstate->iss_NumIndices = numIndices;
|
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)
|
* (one for each index) to arrays of flags (one for each key)
|
||||||
* which indicate that the qual needs to be evaluated at runtime.
|
* which indicate that the qual needs to be evaluated at runtime.
|
||||||
* -cim 10/24/89
|
* -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)
|
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
|
else
|
||||||
|
{
|
||||||
indexstate->iss_RuntimeKeyInfo = NULL;
|
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
|
* get the range table and direction information
|
||||||
|
@ -1026,7 +1035,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
||||||
scanstate->css_currentRelation = currentRelation;
|
scanstate->css_currentRelation = currentRelation;
|
||||||
scanstate->css_currentScanDesc = currentScanDesc;
|
scanstate->css_currentScanDesc = currentScanDesc;
|
||||||
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* get the scan type from the relation descriptor.
|
* get the scan type from the relation descriptor.
|
||||||
* ----------------
|
* ----------------
|
||||||
|
@ -1034,12 +1042,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
||||||
ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
|
ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
|
||||||
ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
|
ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* index scans don't have subtrees..
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
/* scanstate->ss_ProcOuterFlag = false; */
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* open the index relations and initialize
|
* open the index relations and initialize
|
||||||
* relation and scan descriptors.
|
* relation and scan descriptors.
|
||||||
|
@ -1066,10 +1068,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
||||||
indexstate->iss_RelationDescs = relationDescs;
|
indexstate->iss_RelationDescs = relationDescs;
|
||||||
indexstate->iss_ScanDescs = scanDescs;
|
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.
|
* first scan.
|
||||||
*/
|
*/
|
||||||
((Plan *) node)->chgParam = execParam;
|
((Plan *) node)->chgParam = execParam;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
node->matstate = matstate;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Miscellanious initialization
|
* Miscellaneous initialization
|
||||||
*
|
|
||||||
* + assign node's base_id
|
|
||||||
* + assign debugging hooks and
|
|
||||||
* + assign result tuple slot
|
|
||||||
*
|
*
|
||||||
* Materialization nodes don't need ExprContexts because
|
* 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
|
#define MATERIAL_NSLOTS 1
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
static bool
|
||||||
MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
|
MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
|
||||||
{
|
{
|
||||||
|
bool result;
|
||||||
|
MemoryContext oldContext;
|
||||||
List *clause;
|
List *clause;
|
||||||
List *eqclause;
|
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)
|
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
||||||
return false;
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* for each pair of clauses, test them until
|
* 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;
|
eqclause = eqQual;
|
||||||
foreach(clause, compareQual)
|
foreach(clause, compareQual)
|
||||||
{
|
{
|
||||||
|
Datum const_value;
|
||||||
|
bool isNull;
|
||||||
|
bool isDone;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* first test if our compare clause is satisfied.
|
* first test if our compare clause is satisfied.
|
||||||
* if so then return true. ignore isDone, don't iterate in
|
* if so then return true.
|
||||||
* quals.
|
*
|
||||||
|
* A NULL result is considered false.
|
||||||
|
* ignore isDone, don't iterate in quals.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
const_value = (Datum)
|
const_value = ExecEvalExpr((Node *) lfirst(clause), econtext,
|
||||||
ExecEvalExpr((Node *) lfirst(clause), econtext, &isNull, &isDone);
|
&isNull, &isDone);
|
||||||
|
|
||||||
if (DatumGetInt32(const_value) != 0)
|
if (DatumGetBool(const_value) && !isNull)
|
||||||
return true;
|
{
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* ok, the compare clause failed so we test if the keys
|
* ok, the compare clause failed so we test if the keys
|
||||||
* are equal... if key1 != key2, we return false.
|
* are equal... if key1 != key2, we return false.
|
||||||
* otherwise key1 = key2 so we move on to the next pair of keys.
|
* 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),
|
const_value = ExecEvalExpr((Node *) lfirst(eqclause),
|
||||||
|
@ -248,17 +256,15 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
|
||||||
&isNull,
|
&isNull,
|
||||||
&isDone);
|
&isDone);
|
||||||
|
|
||||||
if (DatumGetInt32(const_value) == 0)
|
if (! DatumGetBool(const_value) || isNull)
|
||||||
return false;
|
break; /* return false */
|
||||||
|
|
||||||
eqclause = lnext(eqclause);
|
eqclause = lnext(eqclause);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
MemoryContextSwitchTo(oldContext);
|
||||||
* if we get here then it means none of our key greater-than
|
|
||||||
* conditions were satisfied so we return false.
|
return result;
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
@ -403,24 +409,18 @@ ExecMergeJoin(MergeJoin *node)
|
||||||
List *qual;
|
List *qual;
|
||||||
bool qualResult;
|
bool qualResult;
|
||||||
bool compareResult;
|
bool compareResult;
|
||||||
|
|
||||||
Plan *innerPlan;
|
Plan *innerPlan;
|
||||||
TupleTableSlot *innerTupleSlot;
|
TupleTableSlot *innerTupleSlot;
|
||||||
|
|
||||||
Plan *outerPlan;
|
Plan *outerPlan;
|
||||||
TupleTableSlot *outerTupleSlot;
|
TupleTableSlot *outerTupleSlot;
|
||||||
|
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
|
|
||||||
#ifdef ENABLE_OUTER_JOINS
|
#ifdef ENABLE_OUTER_JOINS
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These should be set from the expression context! - thomas
|
* These should be set from the expression context! - thomas
|
||||||
* 1999-02-20
|
* 1999-02-20
|
||||||
*/
|
*/
|
||||||
static bool isLeftJoin = true;
|
static bool isLeftJoin = true;
|
||||||
static bool isRightJoin = false;
|
static bool isRightJoin = false;
|
||||||
|
|
||||||
#endif
|
#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)
|
if (mergestate->jstate.cs_TupFromTlist)
|
||||||
{
|
{
|
||||||
TupleTableSlot *result;
|
TupleTableSlot *result;
|
||||||
ProjectionInfo *projInfo;
|
|
||||||
bool isDone;
|
bool isDone;
|
||||||
|
|
||||||
projInfo = mergestate->jstate.cs_ProjInfo;
|
result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone);
|
||||||
result = ExecProject(projInfo, &isDone);
|
|
||||||
if (!isDone)
|
if (!isDone)
|
||||||
return result;
|
return result;
|
||||||
|
/* Done with that source tuple... */
|
||||||
|
mergestate->jstate.cs_TupFromTlist = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* ok, everything is setup.. let's go to work
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -547,6 +561,8 @@ ExecMergeJoin(MergeJoin *node)
|
||||||
case EXEC_MJ_JOINTEST:
|
case EXEC_MJ_JOINTEST:
|
||||||
MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
|
MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
|
||||||
|
|
||||||
|
ResetExprContext(econtext);
|
||||||
|
|
||||||
qualResult = ExecQual((List *) mergeclauses, econtext, false);
|
qualResult = ExecQual((List *) mergeclauses, econtext, false);
|
||||||
MJ_DEBUG_QUAL(mergeclauses, qualResult);
|
MJ_DEBUG_QUAL(mergeclauses, qualResult);
|
||||||
|
|
||||||
|
@ -565,6 +581,14 @@ ExecMergeJoin(MergeJoin *node)
|
||||||
MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
|
MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
|
||||||
mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
|
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);
|
qualResult = ExecQual((List *) qual, econtext, false);
|
||||||
MJ_DEBUG_QUAL(qual, qualResult);
|
MJ_DEBUG_QUAL(qual, qualResult);
|
||||||
|
|
||||||
|
@ -693,6 +717,8 @@ ExecMergeJoin(MergeJoin *node)
|
||||||
innerTupleSlot = econtext->ecxt_innertuple;
|
innerTupleSlot = econtext->ecxt_innertuple;
|
||||||
econtext->ecxt_innertuple = mergestate->mj_MarkedTupleSlot;
|
econtext->ecxt_innertuple = mergestate->mj_MarkedTupleSlot;
|
||||||
|
|
||||||
|
ResetExprContext(econtext);
|
||||||
|
|
||||||
qualResult = ExecQual((List *) mergeclauses, econtext, false);
|
qualResult = ExecQual((List *) mergeclauses, econtext, false);
|
||||||
MJ_DEBUG_QUAL(mergeclauses, qualResult);
|
MJ_DEBUG_QUAL(mergeclauses, qualResult);
|
||||||
|
|
||||||
|
@ -709,11 +735,7 @@ ExecMergeJoin(MergeJoin *node)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ExecRestrPos(innerPlan);
|
ExecRestrPos(innerPlan);
|
||||||
#if 0
|
|
||||||
mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
|
|
||||||
#endif
|
|
||||||
mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
|
mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -777,6 +799,8 @@ ExecMergeJoin(MergeJoin *node)
|
||||||
* we update the marked tuple and go join them.
|
* we update the marked tuple and go join them.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
|
ResetExprContext(econtext);
|
||||||
|
|
||||||
qualResult = ExecQual((List *) mergeclauses, econtext, false);
|
qualResult = ExecQual((List *) mergeclauses, econtext, false);
|
||||||
MJ_DEBUG_QUAL(mergeclauses, qualResult);
|
MJ_DEBUG_QUAL(mergeclauses, qualResult);
|
||||||
|
|
||||||
|
@ -886,6 +910,8 @@ ExecMergeJoin(MergeJoin *node)
|
||||||
* we update the marked tuple and go join them.
|
* we update the marked tuple and go join them.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
|
ResetExprContext(econtext);
|
||||||
|
|
||||||
qualResult = ExecQual((List *) mergeclauses, econtext, false);
|
qualResult = ExecQual((List *) mergeclauses, econtext, false);
|
||||||
MJ_DEBUG_QUAL(mergeclauses, qualResult);
|
MJ_DEBUG_QUAL(mergeclauses, qualResult);
|
||||||
|
|
||||||
|
@ -1142,12 +1168,9 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Miscellaneous initialization
|
* Miscellaneous initialization
|
||||||
*
|
*
|
||||||
* + assign node's base_id
|
|
||||||
* + assign debugging hooks and
|
|
||||||
* + create expression context for node
|
* + create expression context for node
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecAssignNodeBaseInfo(estate, &mergestate->jstate, parent);
|
|
||||||
ExecAssignExprContext(estate, &mergestate->jstate);
|
ExecAssignExprContext(estate, &mergestate->jstate);
|
||||||
|
|
||||||
#define MERGEJOIN_NSLOTS 2
|
#define MERGEJOIN_NSLOTS 2
|
||||||
|
@ -1251,6 +1274,7 @@ ExecEndMergeJoin(MergeJoin *node)
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecFreeProjectionInfo(&mergestate->jstate);
|
ExecFreeProjectionInfo(&mergestate->jstate);
|
||||||
|
ExecFreeExprContext(&mergestate->jstate);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* shut down the subplans
|
* shut down the subplans
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* 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
|
* and the inner relation is scanned from the beginning again to join
|
||||||
* with the outer tuple.
|
* 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.
|
* 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:
|
* Conditions:
|
||||||
* -- outerTuple contains current tuple from outer relation and
|
* -- 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.
|
* returned previously.
|
||||||
* This is achieved by maintaining a scan position on the outer
|
* This is achieved by maintaining a scan position on the outer
|
||||||
* relation.
|
* relation.
|
||||||
|
@ -60,10 +60,8 @@ ExecNestLoop(NestLoop *node, Plan *parent)
|
||||||
Plan *innerPlan;
|
Plan *innerPlan;
|
||||||
Plan *outerPlan;
|
Plan *outerPlan;
|
||||||
bool needNewOuterTuple;
|
bool needNewOuterTuple;
|
||||||
|
|
||||||
TupleTableSlot *outerTupleSlot;
|
TupleTableSlot *outerTupleSlot;
|
||||||
TupleTableSlot *innerTupleSlot;
|
TupleTableSlot *innerTupleSlot;
|
||||||
|
|
||||||
List *qual;
|
List *qual;
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
|
|
||||||
|
@ -77,11 +75,6 @@ ExecNestLoop(NestLoop *node, Plan *parent)
|
||||||
qual = node->join.qual;
|
qual = node->join.qual;
|
||||||
outerPlan = outerPlan(&node->join);
|
outerPlan = outerPlan(&node->join);
|
||||||
innerPlan = innerPlan(&node->join);
|
innerPlan = innerPlan(&node->join);
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* initialize expression context
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
econtext = nlstate->jstate.cs_ExprContext;
|
econtext = nlstate->jstate.cs_ExprContext;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -92,11 +85,18 @@ ExecNestLoop(NestLoop *node, Plan *parent)
|
||||||
econtext->ecxt_outertuple = outerTupleSlot;
|
econtext->ecxt_outertuple = outerTupleSlot;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Ok, everything is setup for the join so now loop until
|
* Reset per-tuple memory context to free any expression evaluation
|
||||||
* we return a qualifying join tuple..
|
* 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)
|
if (nlstate->jstate.cs_TupFromTlist)
|
||||||
{
|
{
|
||||||
TupleTableSlot *result;
|
TupleTableSlot *result;
|
||||||
|
@ -105,9 +105,17 @@ ExecNestLoop(NestLoop *node, Plan *parent)
|
||||||
result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
|
result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
|
||||||
if (!isDone)
|
if (!isDone)
|
||||||
return result;
|
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");
|
ENL1_printf("entering main loop");
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -115,15 +123,7 @@ ExecNestLoop(NestLoop *node, Plan *parent)
|
||||||
* and join it with the current outer tuple.
|
* and join it with the current outer tuple.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
needNewOuterTuple = false;
|
needNewOuterTuple = TupIsNull(outerTupleSlot);
|
||||||
|
|
||||||
if (!TupIsNull(outerTupleSlot))
|
|
||||||
ENL1_printf("have outer tuple, deal with it");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ENL1_printf("outer tuple is nil, need new outer tuple");
|
|
||||||
needNewOuterTuple = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* if we have an outerTuple, try to get the next inner tuple.
|
* 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");
|
ENL1_printf("qualification failed, looping");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,18 +265,14 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
nlstate = makeNode(NestLoopState);
|
nlstate = makeNode(NestLoopState);
|
||||||
nlstate->nl_PortalFlag = false;
|
|
||||||
node->nlstate = nlstate;
|
node->nlstate = nlstate;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Miscellanious initialization
|
* Miscellaneous initialization
|
||||||
*
|
*
|
||||||
* + assign node's base_id
|
|
||||||
* + assign debugging hooks and
|
|
||||||
* + create expression context for node
|
* + create expression context for node
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent);
|
|
||||||
ExecAssignExprContext(estate, &nlstate->jstate);
|
ExecAssignExprContext(estate, &nlstate->jstate);
|
||||||
|
|
||||||
#define NESTLOOP_NSLOTS 1
|
#define NESTLOOP_NSLOTS 1
|
||||||
|
@ -348,6 +346,7 @@ ExecEndNestLoop(NestLoop *node)
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecFreeProjectionInfo(&nlstate->jstate);
|
ExecFreeProjectionInfo(&nlstate->jstate);
|
||||||
|
ExecFreeExprContext(&nlstate->jstate);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* close down subplans
|
* close down subplans
|
||||||
|
@ -386,9 +385,7 @@ ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
if (outerPlan->chgParam == NULL)
|
if (outerPlan->chgParam == NULL)
|
||||||
ExecReScan(outerPlan, exprCtxt, (Plan *) node);
|
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_OuterTupleSlot = NULL;
|
||||||
nlstate->jstate.cs_TupFromTlist = false;
|
nlstate->jstate.cs_TupFromTlist = false;
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,21 +3,18 @@
|
||||||
* nodeResult.c
|
* nodeResult.c
|
||||||
* support for constant nodes needing special code.
|
* 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
|
* DESCRIPTION
|
||||||
*
|
*
|
||||||
* Example: in constant queries where no relations are scanned,
|
* Result nodes are used in queries where no relations are scanned.
|
||||||
* the planner generates result nodes. Examples of such queries are:
|
* Examples of such queries are:
|
||||||
*
|
*
|
||||||
* retrieve (x = 1)
|
* retrieve (x = 1)
|
||||||
* and
|
* and
|
||||||
* append emp (name = "mike", salary = 15000)
|
* append emp (name = "mike", salary = 15000)
|
||||||
*
|
*
|
||||||
* Result nodes are also used to optimise queries
|
* Result nodes are also used to optimise queries with constant
|
||||||
* with tautological qualifications like:
|
* qualifications (ie, quals that do not depend on the scanned data),
|
||||||
|
* such as:
|
||||||
*
|
*
|
||||||
* retrieve (emp.all) where 2 > 1
|
* retrieve (emp.all) where 2 > 1
|
||||||
*
|
*
|
||||||
|
@ -27,13 +24,22 @@
|
||||||
* /
|
* /
|
||||||
* SeqScan (emp.all)
|
* 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
|
* 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/executor.h"
|
||||||
#include "executor/nodeResult.h"
|
#include "executor/nodeResult.h"
|
||||||
|
@ -41,7 +47,7 @@
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* ExecResult(node)
|
* 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
|
* qualification clause. Since result nodes with right
|
||||||
* subtrees are never planned, we ignore the right subtree
|
* subtrees are never planned, we ignore the right subtree
|
||||||
* entirely (for now).. -cim 10/7/89
|
* entirely (for now).. -cim 10/7/89
|
||||||
|
@ -67,15 +73,17 @@ ExecResult(Result *node)
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
resstate = node->resstate;
|
resstate = node->resstate;
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* get the expression context
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
econtext = resstate->cstate.cs_ExprContext;
|
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)
|
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)
|
if (resstate->cstate.cs_TupFromTlist)
|
||||||
{
|
{
|
||||||
ProjectionInfo *projInfo;
|
resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone);
|
||||||
|
|
||||||
projInfo = resstate->cstate.cs_ProjInfo;
|
|
||||||
resultSlot = ExecProject(projInfo, &isDone);
|
|
||||||
if (!isDone)
|
if (!isDone)
|
||||||
return resultSlot;
|
return resultSlot;
|
||||||
|
/* Done with that source tuple... */
|
||||||
|
resstate->cstate.cs_TupFromTlist = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* retrieve a tuple that satisfy the qual from the outer plan until
|
* if rs_done is true then it means that we were asked to return
|
||||||
* there are no more.
|
* a constant tuple and we already did the last time ExecResult()
|
||||||
*
|
* was called, OR that we failed the constant qual check.
|
||||||
* if rs_done is 1 then it means that we were asked to return
|
* Either way, now we are through.
|
||||||
* a constant tuple and we alread did the last time ExecResult()
|
|
||||||
* was called, so now we are through.
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
outerPlan = outerPlan(node);
|
if (!resstate->rs_done)
|
||||||
|
|
||||||
while (!resstate->rs_done)
|
|
||||||
{
|
{
|
||||||
|
outerPlan = outerPlan(node);
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* get next outer tuple if necessary.
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
if (outerPlan != NULL)
|
if (outerPlan != NULL)
|
||||||
{
|
{
|
||||||
|
/* ----------------
|
||||||
|
* retrieve tuples from the outer plan until there are no more.
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
|
outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
|
||||||
|
|
||||||
if (TupIsNull(outerTupleSlot))
|
if (TupIsNull(outerTupleSlot))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
resstate->cstate.cs_OuterTupleSlot = outerTupleSlot;
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* if we don't have an outer plan, then it's probably
|
* if we don't have an outer plan, then we are just generating
|
||||||
* the case that we are doing a retrieve or an append
|
* the results from a constant target list. Do it only once.
|
||||||
* with a constant target list, so we should only return
|
|
||||||
* the constant tuple once or never if we fail the qual.
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
resstate->rs_done = 1;
|
resstate->rs_done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* get the information to place into the expr context
|
* form the result tuple using ExecProject(), and return it.
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
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()
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
projInfo = resstate->cstate.cs_ProjInfo;
|
projInfo = resstate->cstate.cs_ProjInfo;
|
||||||
|
@ -200,14 +198,11 @@ ExecInitResult(Result *node, EState *estate, Plan *parent)
|
||||||
node->resstate = resstate;
|
node->resstate = resstate;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Miscellanious initialization
|
* Miscellaneous initialization
|
||||||
*
|
*
|
||||||
* + assign node's base_id
|
|
||||||
* + assign debugging hooks and
|
|
||||||
* + create expression context for node
|
* + create expression context for node
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecAssignNodeBaseInfo(estate, &resstate->cstate, parent);
|
|
||||||
ExecAssignExprContext(estate, &resstate->cstate);
|
ExecAssignExprContext(estate, &resstate->cstate);
|
||||||
|
|
||||||
#define RESULT_NSLOTS 1
|
#define RESULT_NSLOTS 1
|
||||||
|
@ -247,7 +242,7 @@ ExecCountSlotsResult(Result *node)
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* ExecEndResult
|
* ExecEndResult
|
||||||
*
|
*
|
||||||
* fees up storage allocated through C routines
|
* frees up storage allocated through C routines
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
|
@ -266,9 +261,8 @@ ExecEndResult(Result *node)
|
||||||
* is freed at end-transaction time. -cim 6/2/91
|
* 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);
|
ExecFreeProjectionInfo(&resstate->cstate);
|
||||||
|
ExecFreeExprContext(&resstate->cstate);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* shut down subplans
|
* shut down subplans
|
||||||
|
@ -301,5 +295,4 @@ ExecReScanResult(Result *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
if (((Plan *) node)->lefttree &&
|
if (((Plan *) node)->lefttree &&
|
||||||
((Plan *) node)->lefttree->chgParam == NULL)
|
((Plan *) node)->lefttree->chgParam == NULL)
|
||||||
ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
|
ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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"
|
#include "parser/parsetree.h"
|
||||||
|
|
||||||
static Oid InitScanRelation(SeqScan *node, EState *estate,
|
static Oid InitScanRelation(SeqScan *node, EState *estate,
|
||||||
CommonScanState *scanstate, Plan *outerPlan);
|
CommonScanState *scanstate);
|
||||||
|
|
||||||
static TupleTableSlot *SeqNext(SeqScan *node);
|
static TupleTableSlot *SeqNext(SeqScan *node);
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
@ -132,25 +131,11 @@ SeqNext(SeqScan *node)
|
||||||
TupleTableSlot *
|
TupleTableSlot *
|
||||||
ExecSeqScan(SeqScan *node)
|
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
|
* use SeqNext as access method
|
||||||
* else, scan the relation
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
if ((outerPlan = outerPlan((Plan *) node)) != NULL)
|
return ExecScan(node, (ExecScanAccessMtd) SeqNext);
|
||||||
slot = ExecProcNode(outerPlan, (Plan *) node);
|
|
||||||
else
|
|
||||||
slot = ExecScan(node, SeqNext);
|
|
||||||
|
|
||||||
S1_printf("ExecSeqScan: returned tuple slot: %d\n", slot);
|
|
||||||
|
|
||||||
return slot;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
@ -162,7 +147,7 @@ ExecSeqScan(SeqScan *node)
|
||||||
*/
|
*/
|
||||||
static Oid
|
static Oid
|
||||||
InitScanRelation(SeqScan *node, EState *estate,
|
InitScanRelation(SeqScan *node, EState *estate,
|
||||||
CommonScanState *scanstate, Plan *outerPlan)
|
CommonScanState *scanstate)
|
||||||
{
|
{
|
||||||
Index relid;
|
Index relid;
|
||||||
List *rangeTable;
|
List *rangeTable;
|
||||||
|
@ -173,84 +158,56 @@ InitScanRelation(SeqScan *node, EState *estate,
|
||||||
HeapScanDesc currentScanDesc;
|
HeapScanDesc currentScanDesc;
|
||||||
RelationInfo *resultRelationInfo;
|
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;
|
return reloid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* ExecInitSeqScan
|
* 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
|
bool
|
||||||
ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
|
ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
|
||||||
{
|
{
|
||||||
CommonScanState *scanstate;
|
CommonScanState *scanstate;
|
||||||
Plan *outerPlan;
|
|
||||||
Oid reloid;
|
Oid reloid;
|
||||||
HeapScanDesc scandesc;
|
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
|
* assign the node's execution state
|
||||||
* ----------------
|
* ----------------
|
||||||
|
@ -265,13 +222,11 @@ ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
|
||||||
node->scanstate = scanstate;
|
node->scanstate = scanstate;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Miscellanious initialization
|
* Miscellaneous initialization
|
||||||
*
|
*
|
||||||
* + assign node's base_id
|
|
||||||
* + create expression context for node
|
* + create expression context for node
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecAssignNodeBaseInfo(estate, &scanstate->cstate, parent);
|
|
||||||
ExecAssignExprContext(estate, &scanstate->cstate);
|
ExecAssignExprContext(estate, &scanstate->cstate);
|
||||||
|
|
||||||
#define SEQSCAN_NSLOTS 3
|
#define SEQSCAN_NSLOTS 3
|
||||||
|
@ -283,12 +238,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
|
||||||
ExecInitScanTupleSlot(estate, scanstate);
|
ExecInitScanTupleSlot(estate, scanstate);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* initialize scan relation or outer subplan
|
* initialize scan relation
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
outerPlan = outerPlan((Plan *) node);
|
reloid = InitScanRelation(node, estate, scanstate);
|
||||||
|
|
||||||
reloid = InitScanRelation(node, estate, scanstate, outerPlan);
|
|
||||||
|
|
||||||
scandesc = scanstate->css_currentScanDesc;
|
scandesc = scanstate->css_currentScanDesc;
|
||||||
scanstate->cstate.cs_TupFromTlist = false;
|
scanstate->cstate.cs_TupFromTlist = false;
|
||||||
|
@ -315,15 +268,12 @@ ExecCountSlotsSeqScan(SeqScan *node)
|
||||||
* ExecEndSeqScan
|
* ExecEndSeqScan
|
||||||
*
|
*
|
||||||
* frees any storage allocated through C routines.
|
* frees any storage allocated through C routines.
|
||||||
*| ...and also closes relations and/or shuts down outer subplan
|
|
||||||
*| -cim 8/14/89
|
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExecEndSeqScan(SeqScan *node)
|
ExecEndSeqScan(SeqScan *node)
|
||||||
{
|
{
|
||||||
CommonScanState *scanstate;
|
CommonScanState *scanstate;
|
||||||
Plan *outerPlan;
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* get information from node
|
* get information from node
|
||||||
|
@ -341,6 +291,7 @@ ExecEndSeqScan(SeqScan *node)
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecFreeProjectionInfo(&scanstate->cstate);
|
ExecFreeProjectionInfo(&scanstate->cstate);
|
||||||
|
ExecFreeExprContext(&scanstate->cstate);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* close scan relation
|
* close scan relation
|
||||||
|
@ -348,13 +299,6 @@ ExecEndSeqScan(SeqScan *node)
|
||||||
*/
|
*/
|
||||||
ExecCloseR((Plan *) 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
|
* clean out the tuple table
|
||||||
* ----------------
|
* ----------------
|
||||||
|
@ -367,6 +311,7 @@ ExecEndSeqScan(SeqScan *node)
|
||||||
* Join Support
|
* Join Support
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* ExecSeqReScan
|
* ExecSeqReScan
|
||||||
*
|
*
|
||||||
|
@ -378,7 +323,6 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
{
|
{
|
||||||
CommonScanState *scanstate;
|
CommonScanState *scanstate;
|
||||||
EState *estate;
|
EState *estate;
|
||||||
Plan *outerPlan;
|
|
||||||
Relation rel;
|
Relation rel;
|
||||||
HeapScanDesc scan;
|
HeapScanDesc scan;
|
||||||
ScanDirection direction;
|
ScanDirection direction;
|
||||||
|
@ -386,28 +330,18 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent)
|
||||||
scanstate = node->scanstate;
|
scanstate = node->scanstate;
|
||||||
estate = node->plan.state;
|
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 */
|
estate->es_evTupleNull[node->scanrelid - 1] = false;
|
||||||
outerPlan = outerPlan((Plan *) node);
|
return;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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)
|
ExecSeqMarkPos(SeqScan *node)
|
||||||
{
|
{
|
||||||
CommonScanState *scanstate;
|
CommonScanState *scanstate;
|
||||||
Plan *outerPlan;
|
|
||||||
HeapScanDesc scan;
|
HeapScanDesc scan;
|
||||||
|
|
||||||
scanstate = node->scanstate;
|
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;
|
scan = scanstate->css_currentScanDesc;
|
||||||
heap_markpos(scan);
|
heap_markpos(scan);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
@ -459,28 +371,9 @@ void
|
||||||
ExecSeqRestrPos(SeqScan *node)
|
ExecSeqRestrPos(SeqScan *node)
|
||||||
{
|
{
|
||||||
CommonScanState *scanstate;
|
CommonScanState *scanstate;
|
||||||
Plan *outerPlan;
|
|
||||||
HeapScanDesc scan;
|
HeapScanDesc scan;
|
||||||
|
|
||||||
scanstate = node->scanstate;
|
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;
|
scan = scanstate->css_currentScanDesc;
|
||||||
heap_restrpos(scan);
|
heap_restrpos(scan);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* Miscellaneous initialization
|
||||||
*
|
*
|
||||||
* + assign node's base_id
|
|
||||||
* + assign debugging hooks
|
|
||||||
*
|
|
||||||
* Sort nodes don't initialize their ExprContexts because
|
* 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
|
#define SORT_NSLOTS 1
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
SubLink *sublink = node->sublink;
|
||||||
SubLinkType subLinkType = sublink->subLinkType;
|
SubLinkType subLinkType = sublink->subLinkType;
|
||||||
bool useor = sublink->useor;
|
bool useor = sublink->useor;
|
||||||
|
MemoryContext oldcontext;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
Datum result;
|
Datum result;
|
||||||
|
bool isDone;
|
||||||
bool found = false; /* TRUE if got at least one subplan tuple */
|
bool found = false; /* TRUE if got at least one subplan tuple */
|
||||||
List *lst;
|
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)
|
if (node->setParam != NIL)
|
||||||
elog(ERROR, "ExecSubPlan: can't set parent params from subquery");
|
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)
|
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);
|
Assert(pvar != NIL);
|
||||||
prm->value = ExecEvalExpr((Node *) lfirst(pvar),
|
prm->value = ExecEvalExprSwitchContext((Node *) lfirst(pvar),
|
||||||
econtext,
|
econtext,
|
||||||
&(prm->isnull), NULL);
|
&(prm->isnull),
|
||||||
|
&isDone);
|
||||||
|
if (!isDone)
|
||||||
|
elog(ERROR, "ExecSubPlan: set values not supported for params");
|
||||||
pvar = lnext(pvar);
|
pvar = lnext(pvar);
|
||||||
}
|
}
|
||||||
plan->chgParam = nconc(plan->chgParam, listCopy(node->parParam));
|
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
|
* return NULL. Assuming we get a tuple, we just return its first
|
||||||
* column (there can be only one non-junk column in this case).
|
* 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;
|
*isNull = false;
|
||||||
|
|
||||||
for (slot = ExecProcNode(plan, plan);
|
for (slot = ExecProcNode(plan, plan);
|
||||||
|
@ -93,12 +105,16 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
|
||||||
{
|
{
|
||||||
HeapTuple tup = slot->val;
|
HeapTuple tup = slot->val;
|
||||||
TupleDesc tdesc = slot->ttc_tupleDescriptor;
|
TupleDesc tdesc = slot->ttc_tupleDescriptor;
|
||||||
Datum rowresult = (Datum) (useor ? false : true);
|
Datum rowresult = BoolGetDatum(! useor);
|
||||||
bool rownull = false;
|
bool rownull = false;
|
||||||
int col = 1;
|
int col = 1;
|
||||||
|
|
||||||
if (subLinkType == EXISTS_SUBLINK)
|
if (subLinkType == EXISTS_SUBLINK)
|
||||||
return (Datum) true;
|
{
|
||||||
|
found = true;
|
||||||
|
result = BoolGetDatum(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (subLinkType == EXPR_SUBLINK)
|
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.
|
* Now we can eval the combining operator for this column.
|
||||||
*/
|
*/
|
||||||
expresult = ExecEvalExpr((Node *) expr, econtext, &expnull,
|
expresult = ExecEvalExprSwitchContext((Node *) expr, econtext,
|
||||||
(bool *) NULL);
|
&expnull, &isDone);
|
||||||
|
if (!isDone)
|
||||||
|
elog(ERROR, "ExecSubPlan: set values not supported for combining operators");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Combine the result into the row result as appropriate.
|
* 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 */
|
/* combine within row per OR semantics */
|
||||||
if (expnull)
|
if (expnull)
|
||||||
rownull = true;
|
rownull = true;
|
||||||
else if (DatumGetInt32(expresult) != 0)
|
else if (DatumGetBool(expresult))
|
||||||
{
|
{
|
||||||
rowresult = (Datum) true;
|
rowresult = BoolGetDatum(true);
|
||||||
rownull = false;
|
rownull = false;
|
||||||
break; /* needn't look at any more columns */
|
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 */
|
/* combine within row per AND semantics */
|
||||||
if (expnull)
|
if (expnull)
|
||||||
rownull = true;
|
rownull = true;
|
||||||
else if (DatumGetInt32(expresult) == 0)
|
else if (! DatumGetBool(expresult))
|
||||||
{
|
{
|
||||||
rowresult = (Datum) false;
|
rowresult = BoolGetDatum(false);
|
||||||
rownull = false;
|
rownull = false;
|
||||||
break; /* needn't look at any more columns */
|
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 */
|
/* combine across rows per OR semantics */
|
||||||
if (rownull)
|
if (rownull)
|
||||||
*isNull = true;
|
*isNull = true;
|
||||||
else if (DatumGetInt32(rowresult) != 0)
|
else if (DatumGetBool(rowresult))
|
||||||
{
|
{
|
||||||
result = (Datum) true;
|
result = BoolGetDatum(true);
|
||||||
*isNull = false;
|
*isNull = false;
|
||||||
break; /* needn't look at any more rows */
|
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 */
|
/* combine across rows per AND semantics */
|
||||||
if (rownull)
|
if (rownull)
|
||||||
*isNull = true;
|
*isNull = true;
|
||||||
else if (DatumGetInt32(rowresult) == 0)
|
else if (! DatumGetBool(rowresult))
|
||||||
{
|
{
|
||||||
result = (Datum) false;
|
result = BoolGetDatum(false);
|
||||||
*isNull = false;
|
*isNull = false;
|
||||||
break; /* needn't look at any more rows */
|
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)
|
if (subLinkType == EXPR_SUBLINK || subLinkType == MULTIEXPR_SUBLINK)
|
||||||
{
|
{
|
||||||
result = (Datum) false;
|
result = (Datum) 0;
|
||||||
*isNull = true;
|
*isNull = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,13 +297,13 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
|
||||||
ExecCreateTupleTable(ExecCountSlotsNode(node->plan) + 10);
|
ExecCreateTupleTable(ExecCountSlotsNode(node->plan) + 10);
|
||||||
sp_estate->es_snapshot = estate->es_snapshot;
|
sp_estate->es_snapshot = estate->es_snapshot;
|
||||||
|
|
||||||
node->shutdown = false;
|
node->needShutdown = false;
|
||||||
node->curTuple = NULL;
|
node->curTuple = NULL;
|
||||||
|
|
||||||
if (!ExecInitNode(node->plan, sp_estate, NULL))
|
if (!ExecInitNode(node->plan, sp_estate, NULL))
|
||||||
return false;
|
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
|
* If this plan is un-correlated or undirect correlated one and want
|
||||||
|
@ -317,14 +337,21 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExecSetParamPlan(SubPlan *node)
|
ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
|
||||||
{
|
{
|
||||||
Plan *plan = node->plan;
|
Plan *plan = node->plan;
|
||||||
SubLink *sublink = node->sublink;
|
SubLink *sublink = node->sublink;
|
||||||
|
MemoryContext oldcontext;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
List *lst;
|
List *lst;
|
||||||
bool found = false;
|
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 ||
|
if (sublink->subLinkType == ANY_SUBLINK ||
|
||||||
sublink->subLinkType == ALL_SUBLINK)
|
sublink->subLinkType == ALL_SUBLINK)
|
||||||
elog(ERROR, "ExecSetParamPlan: ANY/ALL subselect unsupported");
|
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)]);
|
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
|
||||||
|
|
||||||
prm->execPlan = NULL;
|
prm->execPlan = NULL;
|
||||||
prm->value = (Datum) true;
|
prm->value = BoolGetDatum(true);
|
||||||
prm->isnull = false;
|
prm->isnull = false;
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
|
@ -386,7 +413,7 @@ ExecSetParamPlan(SubPlan *node)
|
||||||
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
|
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
|
||||||
|
|
||||||
prm->execPlan = NULL;
|
prm->execPlan = NULL;
|
||||||
prm->value = (Datum) false;
|
prm->value = BoolGetDatum(false);
|
||||||
prm->isnull = false;
|
prm->isnull = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -396,16 +423,18 @@ ExecSetParamPlan(SubPlan *node)
|
||||||
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
|
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
|
||||||
|
|
||||||
prm->execPlan = NULL;
|
prm->execPlan = NULL;
|
||||||
prm->value = (Datum) NULL;
|
prm->value = (Datum) 0;
|
||||||
prm->isnull = true;
|
prm->isnull = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
if (plan->extParam == NULL) /* un-correlated ... */
|
if (plan->extParam == NULL) /* un-correlated ... */
|
||||||
{
|
{
|
||||||
ExecEndNode(plan, plan);
|
ExecEndNode(plan, plan);
|
||||||
node->shutdown = false;
|
node->needShutdown = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,10 +445,10 @@ ExecSetParamPlan(SubPlan *node)
|
||||||
void
|
void
|
||||||
ExecEndSubPlan(SubPlan *node)
|
ExecEndSubPlan(SubPlan *node)
|
||||||
{
|
{
|
||||||
if (node->shutdown)
|
if (node->needShutdown)
|
||||||
{
|
{
|
||||||
ExecEndNode(node->plan, node->plan);
|
ExecEndNode(node->plan, node->plan);
|
||||||
node->shutdown = false;
|
node->needShutdown = false;
|
||||||
}
|
}
|
||||||
if (node->curTuple)
|
if (node->curTuple)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
List *lst;
|
||||||
ItemPointer itemptr;
|
ItemPointer itemptr;
|
||||||
bool isNull;
|
bool isNull;
|
||||||
|
bool isDone;
|
||||||
int numTids = 0;
|
int numTids = 0;
|
||||||
|
|
||||||
foreach(lst, evalList)
|
foreach(lst, evalList)
|
||||||
{
|
{
|
||||||
itemptr = (ItemPointer) ExecEvalExpr(lfirst(lst), econtext,
|
itemptr = (ItemPointer)
|
||||||
&isNull, (bool *) 0);
|
DatumGetPointer(ExecEvalExprSwitchContext(lfirst(lst),
|
||||||
|
econtext,
|
||||||
|
&isNull,
|
||||||
|
&isDone));
|
||||||
if (itemptr && ItemPointerIsValid(itemptr))
|
if (itemptr && ItemPointerIsValid(itemptr))
|
||||||
{
|
{
|
||||||
tidList[numTids] = itemptr;
|
tidList[numTids] = itemptr;
|
||||||
|
@ -243,7 +247,7 @@ ExecTidScan(TidScan *node)
|
||||||
* use TidNext as access method
|
* 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);
|
ExecFreeProjectionInfo(&scanstate->cstate);
|
||||||
|
ExecFreeExprContext(&scanstate->cstate);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* close the heap and tid relations
|
* close the heap and tid relations
|
||||||
|
@ -332,7 +337,6 @@ ExecEndTidScan(TidScan *node)
|
||||||
*/
|
*/
|
||||||
ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
|
ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
|
||||||
ExecClearTuple(scanstate->css_ScanTupleSlot);
|
ExecClearTuple(scanstate->css_ScanTupleSlot);
|
||||||
/* ExecClearTuple(scanstate->css_RawTupleSlot); */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
@ -394,11 +398,8 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
|
||||||
RangeTblEntry *rtentry;
|
RangeTblEntry *rtentry;
|
||||||
Oid relid;
|
Oid relid;
|
||||||
Oid reloid;
|
Oid reloid;
|
||||||
|
|
||||||
Relation currentRelation;
|
Relation currentRelation;
|
||||||
int baseid;
|
List *execParam = NIL;
|
||||||
|
|
||||||
List *execParam = NULL;
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* assign execution state to node
|
* assign execution state to node
|
||||||
|
@ -413,25 +414,12 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
|
||||||
* --------------------------------
|
* --------------------------------
|
||||||
*/
|
*/
|
||||||
scanstate = makeNode(CommonScanState);
|
scanstate = makeNode(CommonScanState);
|
||||||
/*
|
|
||||||
scanstate->ss_ProcOuterFlag = false;
|
|
||||||
scanstate->ss_OldRelId = 0;
|
|
||||||
*/
|
|
||||||
|
|
||||||
node->scan.scanstate = scanstate;
|
node->scan.scanstate = scanstate;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* assign node's base_id .. we don't use AssignNodeBaseid() because
|
* Miscellaneous initialization
|
||||||
* the increment is done later on after we assign the tid scan's
|
*
|
||||||
* scanstate. see below.
|
* + create expression context for node
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
baseid = estate->es_BaseId;
|
|
||||||
/* scanstate->csstate.cstate.bnode.base_id = baseid; */
|
|
||||||
scanstate->cstate.cs_base_id = baseid;
|
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* create expression context for node
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecAssignExprContext(estate, &scanstate->cstate);
|
ExecAssignExprContext(estate, &scanstate->cstate);
|
||||||
|
@ -443,7 +431,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
|
||||||
*/
|
*/
|
||||||
ExecInitResultTupleSlot(estate, &scanstate->cstate);
|
ExecInitResultTupleSlot(estate, &scanstate->cstate);
|
||||||
ExecInitScanTupleSlot(estate, scanstate);
|
ExecInitScanTupleSlot(estate, scanstate);
|
||||||
/* ExecInitRawTupleSlot(estate, scanstate); */
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* initialize projection info. result type comes from scan desc
|
* initialize projection info. result type comes from scan desc
|
||||||
|
@ -461,14 +448,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
|
||||||
tidstate = makeNode(TidScanState);
|
tidstate = makeNode(TidScanState);
|
||||||
node->tidstate = tidstate;
|
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
|
* get the tid node information
|
||||||
* ----------------
|
* ----------------
|
||||||
|
@ -514,14 +493,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
|
||||||
ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
|
ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
|
||||||
ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
|
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
|
* if there are some PARAM_EXEC in skankeys then force tid rescan on
|
||||||
* first scan.
|
* first scan.
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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,
|
if (!execTuplesMatch(slot->val, uniquestate->priorTuple,
|
||||||
tupDesc,
|
tupDesc,
|
||||||
node->numCols, node->uniqColIdx,
|
node->numCols, node->uniqColIdx,
|
||||||
uniquestate->eqfunctions))
|
uniquestate->eqfunctions,
|
||||||
|
uniquestate->tempContext))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* We have a new tuple different from the previous saved tuple (if any).
|
* 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:
|
* Save it and return it. We must copy it because the source subplan
|
||||||
* one to keep for our own future comparisons, and one to return to the
|
* won't guarantee that this source tuple is still accessible after
|
||||||
* caller. We need to copy the tuple returned by the subplan to avoid
|
* fetching the next source tuple.
|
||||||
* holding buffer refcounts, and we need our own copy because the caller
|
*
|
||||||
* may alter the resultTupleSlot (eg via ExecRemoveJunk).
|
* 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)
|
if (uniquestate->priorTuple != NULL)
|
||||||
heap_freetuple(uniquestate->priorTuple);
|
heap_freetuple(uniquestate->priorTuple);
|
||||||
uniquestate->priorTuple = heap_copytuple(slot->val);
|
uniquestate->priorTuple = heap_copytuple(slot->val);
|
||||||
|
|
||||||
ExecStoreTuple(heap_copytuple(slot->val),
|
ExecStoreTuple(uniquestate->priorTuple,
|
||||||
resultTupleSlot,
|
resultTupleSlot,
|
||||||
InvalidBuffer,
|
InvalidBuffer,
|
||||||
true);
|
false); /* tuple does not belong to slot */
|
||||||
|
|
||||||
return resultTupleSlot;
|
return resultTupleSlot;
|
||||||
}
|
}
|
||||||
|
@ -143,14 +148,17 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* Miscellaneous initialization
|
* Miscellaneous initialization
|
||||||
*
|
*
|
||||||
* + assign node's base_id
|
|
||||||
* + assign debugging hooks and
|
|
||||||
*
|
|
||||||
* Unique nodes have no ExprContext initialization because
|
* 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
|
#define UNIQUE_NSLOTS 1
|
||||||
/* ------------
|
/* ------------
|
||||||
|
@ -207,6 +215,8 @@ ExecEndUnique(Unique *node)
|
||||||
|
|
||||||
ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
|
ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
|
||||||
|
|
||||||
|
MemoryContextDelete(uniquestate->tempContext);
|
||||||
|
|
||||||
/* clean up tuple table */
|
/* clean up tuple table */
|
||||||
ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot);
|
ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot);
|
||||||
if (uniquestate->priorTuple != NULL)
|
if (uniquestate->priorTuple != NULL)
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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);
|
Node_Copy(from, newnode, sublink);
|
||||||
|
|
||||||
/* do not copy execution state */
|
/* do not copy execution state */
|
||||||
newnode->shutdown = false;
|
newnode->needShutdown = false;
|
||||||
newnode->curTuple = NULL;
|
newnode->curTuple = NULL;
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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)
|
if (a->constisnull)
|
||||||
return true;
|
return true;
|
||||||
return (datumIsEqual(a->constvalue, b->constvalue,
|
return datumIsEqual(a->constvalue, b->constvalue,
|
||||||
a->consttype, a->constbyval, a->constlen));
|
a->constbyval, a->constlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* NOTES
|
||||||
* Every (plan) node in POSTGRES has an associated "out" routine which
|
* Every (plan) node in POSTGRES has an associated "out" routine which
|
||||||
|
@ -1155,10 +1155,10 @@ _outJoinInfo(StringInfo str, JoinInfo *node)
|
||||||
static void
|
static void
|
||||||
_outDatum(StringInfo str, Datum value, Oid type)
|
_outDatum(StringInfo str, Datum value, Oid type)
|
||||||
{
|
{
|
||||||
char *s;
|
|
||||||
Size length,
|
|
||||||
typeLength;
|
|
||||||
bool byValue;
|
bool byValue;
|
||||||
|
int typeLength;
|
||||||
|
Size length;
|
||||||
|
char *s;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1167,12 +1167,12 @@ _outDatum(StringInfo str, Datum value, Oid type)
|
||||||
*/
|
*/
|
||||||
byValue = get_typbyval(type);
|
byValue = get_typbyval(type);
|
||||||
typeLength = get_typlen(type);
|
typeLength = get_typlen(type);
|
||||||
length = datumGetSize(value, type, byValue, typeLength);
|
length = datumGetSize(value, byValue, typeLength);
|
||||||
|
|
||||||
if (byValue)
|
if (byValue)
|
||||||
{
|
{
|
||||||
s = (char *) (&value);
|
s = (char *) (&value);
|
||||||
appendStringInfo(str, " %d [ ", length);
|
appendStringInfo(str, " %u [ ", (unsigned int) length);
|
||||||
for (i = 0; i < (int) sizeof(Datum); i++)
|
for (i = 0; i < (int) sizeof(Datum); i++)
|
||||||
appendStringInfo(str, "%d ", (int) (s[i]));
|
appendStringInfo(str, "%d ", (int) (s[i]));
|
||||||
appendStringInfo(str, "] ");
|
appendStringInfo(str, "] ");
|
||||||
|
@ -1184,14 +1184,7 @@ _outDatum(StringInfo str, Datum value, Oid type)
|
||||||
appendStringInfo(str, " 0 [ ] ");
|
appendStringInfo(str, " 0 [ ] ");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
appendStringInfo(str, " %u [ ", (unsigned int) length);
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
for (i = 0; i < (int) length; i++)
|
for (i = 0; i < (int) length; i++)
|
||||||
appendStringInfo(str, "%d ", (int) (s[i]));
|
appendStringInfo(str, "%d ", (int) (s[i]));
|
||||||
appendStringInfo(str, "] ");
|
appendStringInfo(str, "] ");
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* Most of the read functions for plan nodes are tested. (In fact, they
|
* Most of the read functions for plan nodes are tested. (In fact, they
|
||||||
|
@ -608,7 +608,7 @@ _readHash()
|
||||||
_getPlan((Plan *) local_node);
|
_getPlan((Plan *) local_node);
|
||||||
|
|
||||||
token = lsptok(NULL, &length); /* eat :hashkey */
|
token = lsptok(NULL, &length); /* eat :hashkey */
|
||||||
local_node->hashkey = (Var *) nodeRead(true);
|
local_node->hashkey = nodeRead(true);
|
||||||
|
|
||||||
return local_node;
|
return local_node;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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);
|
Plan *righttree);
|
||||||
static HashJoin *make_hashjoin(List *tlist, List *qpqual,
|
static HashJoin *make_hashjoin(List *tlist, List *qpqual,
|
||||||
List *hashclauses, Plan *lefttree, Plan *righttree);
|
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,
|
static MergeJoin *make_mergejoin(List *tlist, List *qpqual,
|
||||||
List *mergeclauses, Plan *righttree, Plan *lefttree);
|
List *mergeclauses, Plan *righttree, Plan *lefttree);
|
||||||
static void copy_path_costsize(Plan *dest, Path *src);
|
static void copy_path_costsize(Plan *dest, Path *src);
|
||||||
|
@ -664,7 +664,7 @@ create_hashjoin_node(HashPath *best_path,
|
||||||
List *hashclauses;
|
List *hashclauses;
|
||||||
HashJoin *join_node;
|
HashJoin *join_node;
|
||||||
Hash *hash_node;
|
Hash *hash_node;
|
||||||
Var *innerhashkey;
|
Node *innerhashkey;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE: there will always be exactly one hashclause in the list
|
* NOTE: there will always be exactly one hashclause in the list
|
||||||
|
@ -694,7 +694,7 @@ create_hashjoin_node(HashPath *best_path,
|
||||||
(Index) 0));
|
(Index) 0));
|
||||||
|
|
||||||
/* Now the righthand op of the sole hashclause is the inner hash key. */
|
/* 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.
|
* Build the hash node and hash join node.
|
||||||
|
@ -1103,7 +1103,7 @@ make_hashjoin(List *tlist,
|
||||||
}
|
}
|
||||||
|
|
||||||
static Hash *
|
static Hash *
|
||||||
make_hash(List *tlist, Var *hashkey, Plan *lefttree)
|
make_hash(List *tlist, Node *hashkey, Plan *lefttree)
|
||||||
{
|
{
|
||||||
Hash *node = makeNode(Hash);
|
Hash *node = makeNode(Hash);
|
||||||
Plan *plan = &node->plan;
|
Plan *plan = &node->plan;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
break;
|
||||||
|
|
||||||
case T_Hash:
|
case T_Hash:
|
||||||
finalize_primnode((Node *) ((Hash *) plan)->hashkey,
|
finalize_primnode(((Hash *) plan)->hashkey,
|
||||||
&results);
|
&results);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
|
@ -30,6 +30,7 @@
|
||||||
#include "optimizer/var.h"
|
#include "optimizer/var.h"
|
||||||
#include "parser/parse_type.h"
|
#include "parser/parse_type.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
|
#include "utils/datum.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
@ -1317,7 +1318,10 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||||
HeapTuple func_tuple;
|
HeapTuple func_tuple;
|
||||||
Form_pg_proc funcform;
|
Form_pg_proc funcform;
|
||||||
Type resultType;
|
Type resultType;
|
||||||
|
bool resultTypByVal;
|
||||||
|
int resultTypLen;
|
||||||
Expr *newexpr;
|
Expr *newexpr;
|
||||||
|
ExprContext *econtext;
|
||||||
Datum const_val;
|
Datum const_val;
|
||||||
bool has_nonconst_input = false;
|
bool has_nonconst_input = false;
|
||||||
bool has_null_input = false;
|
bool has_null_input = false;
|
||||||
|
@ -1424,25 +1428,35 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||||
newexpr->oper = expr->oper;
|
newexpr->oper = expr->oper;
|
||||||
newexpr->args = args;
|
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
|
* code used in this situation will use econtext. That might seem
|
||||||
* fortuitous, but it's not so unreasonable --- a constant expression
|
* fortuitous, but it's not so unreasonable --- a constant expression
|
||||||
* does not depend on context, by definition, n'est ce pas?
|
* does not depend on context, by definition, n'est ce pas?
|
||||||
*/
|
*/
|
||||||
const_val = ExecEvalExpr((Node *) newexpr, NULL,
|
econtext = MakeExprContext(NULL, CurrentMemoryContext);
|
||||||
&const_is_null, &isDone);
|
|
||||||
|
const_val = ExecEvalExprSwitchContext((Node *) newexpr, econtext,
|
||||||
|
&const_is_null, &isDone);
|
||||||
Assert(isDone); /* if this isn't set, we blew it... */
|
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);
|
pfree(newexpr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make the constant result node.
|
* Make the constant result node.
|
||||||
*/
|
*/
|
||||||
resultType = typeidType(result_typeid);
|
return (Expr *) makeConst(result_typeid, resultTypLen,
|
||||||
return (Expr *) makeConst(result_typeid, typeLen(resultType),
|
|
||||||
const_val, const_is_null,
|
const_val, const_is_null,
|
||||||
typeByVal(resultType),
|
resultTypByVal, false, false);
|
||||||
false, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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_list_info = NULL;
|
||||||
state->es_param_exec_vals = NULL;
|
state->es_param_exec_vals = NULL;
|
||||||
|
|
||||||
state->es_BaseId = 0;
|
|
||||||
state->es_tupleTable = NULL;
|
state->es_tupleTable = NULL;
|
||||||
|
|
||||||
state->es_junkFilter = NULL;
|
state->es_junkFilter = NULL;
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* datum.c
|
* datum.c
|
||||||
|
* POSTGRES Datum (abstract data type) manipulation routines.
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* 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
|
* 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)"
|
* "sizeof(Datum)"
|
||||||
* B) if a type is not "byVal" and it has a fixed length, then
|
* 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 "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
|
* This varlena structure has information about the actual length of this
|
||||||
* particular instance of the type and about its value.
|
* 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 "postgres.h"
|
||||||
|
|
||||||
#include "utils/datum.h"
|
#include "utils/datum.h"
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
* datumGetSize
|
* datumGetSize
|
||||||
*
|
*
|
||||||
* Find the "real" size of a datum, given the datum value,
|
* 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
|
* 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
|
* type length, with the exception of variable length types which have
|
||||||
|
@ -45,47 +50,31 @@
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
Size
|
Size
|
||||||
datumGetSize(Datum value, Oid type, bool byVal, Size len)
|
datumGetSize(Datum value, bool typByVal, int typLen)
|
||||||
{
|
{
|
||||||
|
Size size;
|
||||||
|
|
||||||
struct varlena *s;
|
if (typByVal)
|
||||||
Size size = 0;
|
|
||||||
|
|
||||||
if (byVal)
|
|
||||||
{
|
{
|
||||||
if (len <= sizeof(Datum))
|
/* Pass-by-value types are always fixed-length */
|
||||||
size = len;
|
Assert(typLen > 0 && typLen <= sizeof(Datum));
|
||||||
else
|
size = (Size) typLen;
|
||||||
{
|
|
||||||
elog(ERROR,
|
|
||||||
"datumGetSize: Error: type=%ld, byVaL with len=%d",
|
|
||||||
(long) type, len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
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))
|
if (!PointerIsValid(s))
|
||||||
{
|
elog(ERROR, "datumGetSize: Invalid Datum Pointer");
|
||||||
elog(ERROR,
|
|
||||||
"datumGetSize: Invalid Datum Pointer");
|
|
||||||
}
|
|
||||||
size = (Size) VARSIZE(s);
|
size = (Size) VARSIZE(s);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/* Fixed-length pass-by-ref type */
|
||||||
/*
|
size = (Size) typLen;
|
||||||
* fixed length type
|
|
||||||
*/
|
|
||||||
size = len;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,37 +86,29 @@ datumGetSize(Datum value, Oid type, bool byVal, Size len)
|
||||||
*
|
*
|
||||||
* make a copy of a datum
|
* make a copy of a datum
|
||||||
*
|
*
|
||||||
* If the type of the datum is not passed by value (i.e. "byVal=false")
|
* If the datatype is pass-by-reference, memory is obtained with palloc().
|
||||||
* then we assume that the datum contains a pointer and we copy all the
|
|
||||||
* bytes pointed by this pointer
|
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
datumCopy(Datum value, Oid type, bool byVal, Size len)
|
datumCopy(Datum value, bool typByVal, int typLen)
|
||||||
{
|
{
|
||||||
|
|
||||||
Size realSize;
|
|
||||||
Datum res;
|
Datum res;
|
||||||
char *s;
|
|
||||||
|
|
||||||
|
if (typByVal)
|
||||||
if (byVal)
|
|
||||||
res = value;
|
res = value;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (value == 0)
|
Size realSize;
|
||||||
return (Datum) NULL;
|
char *s;
|
||||||
realSize = datumGetSize(value, type, byVal, len);
|
|
||||||
|
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);
|
s = (char *) palloc(realSize);
|
||||||
if (s == NULL)
|
memcpy(s, DatumGetPointer(value), realSize);
|
||||||
elog(ERROR, "datumCopy: out of memory\n");
|
res = PointerGetDatum(s);
|
||||||
memmove(s, DatumGetPointer(value), realSize);
|
|
||||||
res = (Datum) s;
|
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -143,21 +124,12 @@ datumCopy(Datum value, Oid type, bool byVal, Size len)
|
||||||
*/
|
*/
|
||||||
#ifdef NOT_USED
|
#ifdef NOT_USED
|
||||||
void
|
void
|
||||||
datumFree(Datum value, Oid type, bool byVal, Size len)
|
datumFree(Datum value, bool typByVal, int typLen)
|
||||||
{
|
{
|
||||||
|
if (!typByVal)
|
||||||
Size realSize;
|
|
||||||
Pointer s;
|
|
||||||
|
|
||||||
realSize = datumGetSize(value, type, byVal, len);
|
|
||||||
|
|
||||||
if (!byVal)
|
|
||||||
{
|
{
|
||||||
|
Pointer s = DatumGetPointer(value);
|
||||||
|
|
||||||
/*
|
|
||||||
* free the space palloced by "datumCopy()"
|
|
||||||
*/
|
|
||||||
s = DatumGetPointer(value);
|
|
||||||
pfree(s);
|
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
|
* This routine will return false if there are 2 different
|
||||||
* representations of the same value (something along the lines
|
* representations of the same value (something along the lines
|
||||||
* of say the representation of zero in one's complement arithmetic).
|
* 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
|
bool
|
||||||
datumIsEqual(Datum value1, Datum value2, Oid type, bool byVal, Size len)
|
datumIsEqual(Datum value1, Datum value2, bool typByVal, int typLen)
|
||||||
{
|
{
|
||||||
Size size1,
|
bool res;
|
||||||
size2;
|
|
||||||
char *s1,
|
|
||||||
*s2;
|
|
||||||
|
|
||||||
if (byVal)
|
if (typByVal)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* just compare the two datums. NOTE: just comparing "len" bytes
|
* just compare the two datums. NOTE: just comparing "len" bytes
|
||||||
* will not do the work, because we do not know how these bytes
|
* will not do the work, because we do not know how these bytes
|
||||||
* are aligned inside the "Datum".
|
* are aligned inside the "Datum".
|
||||||
*/
|
*/
|
||||||
if (value1 == value2)
|
res = (value1 == value2);
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Size size1,
|
||||||
|
size2;
|
||||||
|
char *s1,
|
||||||
|
*s2;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* byVal = false Compare the bytes pointed by the pointers stored
|
* Compare the bytes pointed by the pointers stored in the datums.
|
||||||
* in the datums.
|
|
||||||
*/
|
*/
|
||||||
size1 = datumGetSize(value1, type, byVal, len);
|
size1 = datumGetSize(value1, typByVal, typLen);
|
||||||
size2 = datumGetSize(value2, type, byVal, len);
|
size2 = datumGetSize(value2, typByVal, typLen);
|
||||||
if (size1 != size2)
|
if (size1 != size2)
|
||||||
return false;
|
return false;
|
||||||
s1 = (char *) DatumGetPointer(value1);
|
s1 = (char *) DatumGetPointer(value1);
|
||||||
s2 = (char *) DatumGetPointer(value2);
|
s2 = (char *) DatumGetPointer(value2);
|
||||||
if (!memcmp(s1, s2, size1))
|
res = (memcmp(s1, s2, size1) == 0);
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* texteq - returns true iff arguments are equal
|
||||||
* textne - returns true iff arguments are not 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
|
Datum
|
||||||
texteq(PG_FUNCTION_ARGS)
|
texteq(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *arg1 = PG_GETARG_TEXT_P(0);
|
text *arg1 = PG_GETARG_TEXT_P(0);
|
||||||
text *arg2 = PG_GETARG_TEXT_P(1);
|
text *arg2 = PG_GETARG_TEXT_P(1);
|
||||||
int len;
|
bool result;
|
||||||
char *a1p,
|
|
||||||
*a2p;
|
|
||||||
|
|
||||||
if (VARSIZE(arg1) != VARSIZE(arg2))
|
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);
|
a1p = VARDATA(arg1);
|
||||||
a2p = VARDATA(arg2);
|
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
|
Datum
|
||||||
|
@ -462,19 +475,28 @@ textne(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *arg1 = PG_GETARG_TEXT_P(0);
|
text *arg1 = PG_GETARG_TEXT_P(0);
|
||||||
text *arg2 = PG_GETARG_TEXT_P(1);
|
text *arg2 = PG_GETARG_TEXT_P(1);
|
||||||
int len;
|
bool result;
|
||||||
char *a1p,
|
|
||||||
*a2p;
|
|
||||||
|
|
||||||
if (VARSIZE(arg1) != VARSIZE(arg2))
|
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);
|
a1p = VARDATA(arg1);
|
||||||
a2p = VARDATA(arg2);
|
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()
|
/* varstr_cmp()
|
||||||
|
@ -545,6 +567,10 @@ text_cmp(text *arg1, text *arg2)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Comparison functions for text strings.
|
* 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
|
Datum
|
||||||
|
@ -552,8 +578,14 @@ text_lt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *arg1 = PG_GETARG_TEXT_P(0);
|
text *arg1 = PG_GETARG_TEXT_P(0);
|
||||||
text *arg2 = PG_GETARG_TEXT_P(1);
|
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
|
Datum
|
||||||
|
@ -561,8 +593,14 @@ text_le(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *arg1 = PG_GETARG_TEXT_P(0);
|
text *arg1 = PG_GETARG_TEXT_P(0);
|
||||||
text *arg2 = PG_GETARG_TEXT_P(1);
|
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
|
Datum
|
||||||
|
@ -570,8 +608,14 @@ text_gt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *arg1 = PG_GETARG_TEXT_P(0);
|
text *arg1 = PG_GETARG_TEXT_P(0);
|
||||||
text *arg2 = PG_GETARG_TEXT_P(1);
|
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
|
Datum
|
||||||
|
@ -579,8 +623,14 @@ text_ge(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *arg1 = PG_GETARG_TEXT_P(0);
|
text *arg1 = PG_GETARG_TEXT_P(0);
|
||||||
text *arg2 = PG_GETARG_TEXT_P(1);
|
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
|
Datum
|
||||||
|
@ -589,14 +639,8 @@ text_larger(PG_FUNCTION_ARGS)
|
||||||
text *arg1 = PG_GETARG_TEXT_P(0);
|
text *arg1 = PG_GETARG_TEXT_P(0);
|
||||||
text *arg2 = PG_GETARG_TEXT_P(1);
|
text *arg2 = PG_GETARG_TEXT_P(1);
|
||||||
text *result;
|
text *result;
|
||||||
text *temp;
|
|
||||||
|
|
||||||
temp = ((text_cmp(arg1, arg2) > 0) ? arg1 : arg2);
|
result = ((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));
|
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(result);
|
PG_RETURN_TEXT_P(result);
|
||||||
}
|
}
|
||||||
|
@ -607,14 +651,8 @@ text_smaller(PG_FUNCTION_ARGS)
|
||||||
text *arg1 = PG_GETARG_TEXT_P(0);
|
text *arg1 = PG_GETARG_TEXT_P(0);
|
||||||
text *arg2 = PG_GETARG_TEXT_P(1);
|
text *arg2 = PG_GETARG_TEXT_P(1);
|
||||||
text *result;
|
text *result;
|
||||||
text *temp;
|
|
||||||
|
|
||||||
temp = ((text_cmp(arg1, arg2) < 0) ? arg1 : arg2);
|
result = ((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));
|
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(result);
|
PG_RETURN_TEXT_P(result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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));
|
retval = (FunctionCachePtr) palloc(sizeof(FunctionCache));
|
||||||
MemSet(retval, 0, sizeof(FunctionCache));
|
MemSet(retval, 0, sizeof(FunctionCache));
|
||||||
|
retval->fcacheCxt = CurrentMemoryContext;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* get the procedure tuple corresponding to the given functionOid
|
* get the procedure tuple corresponding to the given functionOid
|
||||||
|
@ -256,22 +257,26 @@ init_fcache(Oid foid,
|
||||||
void
|
void
|
||||||
setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext)
|
setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext)
|
||||||
{
|
{
|
||||||
Func *fnode;
|
MemoryContext oldcontext;
|
||||||
Oper *onode;
|
|
||||||
FunctionCachePtr fcache;
|
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);
|
fcache = init_fcache(foid, argList, econtext);
|
||||||
|
|
||||||
if (IsA(node, Oper))
|
if (IsA(node, Oper))
|
||||||
{
|
{
|
||||||
onode = (Oper *) node;
|
Oper *onode = (Oper *) node;
|
||||||
onode->op_fcache = fcache;
|
onode->op_fcache = fcache;
|
||||||
}
|
}
|
||||||
else if (IsA(node, Func))
|
else if (IsA(node, Func))
|
||||||
{
|
{
|
||||||
fnode = (Func *) node;
|
Func *fnode = (Func *) node;
|
||||||
fnode->func_fcache = fcache;
|
fnode->func_fcache = fcache;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
elog(ERROR, "init_fcache: node must be Oper or Func!");
|
elog(ERROR, "init_fcache: node must be Oper or Func!");
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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:
|
* NOTE:
|
||||||
* This is a new (Feb. 05, 1999) implementation of the allocation set
|
* This is a new (Feb. 05, 1999) implementation of the allocation set
|
||||||
|
@ -349,20 +349,21 @@ AllocSetReset(MemoryContext context)
|
||||||
if (block == set->keeper)
|
if (block == set->keeper)
|
||||||
{
|
{
|
||||||
/* Reset the block, but don't return it to malloc */
|
/* Reset the block, but don't return it to malloc */
|
||||||
block->next = NULL;
|
char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ;
|
||||||
block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ;
|
|
||||||
#ifdef CLOBBER_FREED_MEMORY
|
#ifdef CLOBBER_FREED_MEMORY
|
||||||
/* Wipe freed memory for debugging purposes */
|
/* Wipe freed memory for debugging purposes */
|
||||||
memset(block->freeptr, 0x7F,
|
memset(datastart, 0x7F, ((char *) block->freeptr) - datastart);
|
||||||
((char *) block->endptr) - ((char *) block->freeptr));
|
|
||||||
#endif
|
#endif
|
||||||
|
block->freeptr = datastart;
|
||||||
|
block->next = NULL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Normal case, release the block */
|
/* Normal case, release the block */
|
||||||
#ifdef CLOBBER_FREED_MEMORY
|
#ifdef CLOBBER_FREED_MEMORY
|
||||||
/* Wipe freed memory for debugging purposes */
|
/* Wipe freed memory for debugging purposes */
|
||||||
memset(block, 0x7F, ((char *) block->endptr) - ((char *) block));
|
memset(block, 0x7F, ((char *) block->freeptr) - ((char *) block));
|
||||||
#endif
|
#endif
|
||||||
free(block);
|
free(block);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 SetReindexProcessing(bool processing);
|
||||||
extern bool IsReindexProcessing(void);
|
extern bool IsReindexProcessing(void);
|
||||||
|
|
||||||
extern void FillDummyExprContext(ExprContext *econtext, TupleTableSlot *slot,
|
|
||||||
TupleDesc tupdesc, Buffer buffer);
|
|
||||||
|
|
||||||
extern void index_build(Relation heapRelation, Relation indexRelation,
|
extern void index_build(Relation heapRelation, Relation indexRelation,
|
||||||
int numberOfAttributes, AttrNumber *attributeNumber,
|
int numberOfAttributes, AttrNumber *attributeNumber,
|
||||||
FuncIndexInfo *funcInfo, PredInfo *predInfo,
|
FuncIndexInfo *funcInfo, PredInfo *predInfo,
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: executor.h,v 1.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
|
* 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,
|
extern Datum ExecEvalParam(Param *expression, ExprContext *econtext,
|
||||||
bool *isNull);
|
bool *isNull);
|
||||||
|
|
||||||
extern char *GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno,
|
extern char *GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno,
|
||||||
bool *isNull);
|
bool *isNull);
|
||||||
extern char *GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull);
|
extern char *GetAttributeByName(TupleTableSlot *slot, char *attname,
|
||||||
extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext, bool *isNull,
|
bool *isNull);
|
||||||
bool *isDone);
|
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 bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
|
||||||
extern int ExecTargetListLength(List *targetlist);
|
extern int ExecTargetListLength(List *targetlist);
|
||||||
extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, bool *isDone);
|
extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, bool *isDone);
|
||||||
|
@ -92,7 +89,9 @@ extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, bool *isDone);
|
||||||
/*
|
/*
|
||||||
* prototypes from functions in execScan.c
|
* prototypes from functions in execScan.c
|
||||||
*/
|
*/
|
||||||
extern TupleTableSlot *ExecScan(Scan *node, TupleTableSlot *(*accessMtd) ());
|
typedef TupleTableSlot *(*ExecScanAccessMtd) (Scan *node);
|
||||||
|
|
||||||
|
extern TupleTableSlot *ExecScan(Scan *node, ExecScanAccessMtd accessMtd);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prototypes from functions in execTuples.c
|
* prototypes from functions in execTuples.c
|
||||||
|
@ -121,8 +120,6 @@ extern void SetChangedParamList(Plan *node, List *newchg);
|
||||||
* prototypes from functions in execUtils.c
|
* prototypes from functions in execUtils.c
|
||||||
*/
|
*/
|
||||||
extern void ResetTupleCount(void);
|
extern void ResetTupleCount(void);
|
||||||
extern void ExecAssignNodeBaseInfo(EState *estate, CommonState *basenode,
|
|
||||||
Plan *parent);
|
|
||||||
extern void ExecAssignExprContext(EState *estate, CommonState *commonstate);
|
extern void ExecAssignExprContext(EState *estate, CommonState *commonstate);
|
||||||
extern void ExecAssignResultType(CommonState *commonstate,
|
extern void ExecAssignResultType(CommonState *commonstate,
|
||||||
TupleDesc tupDesc);
|
TupleDesc tupDesc);
|
||||||
|
@ -133,7 +130,6 @@ extern TupleDesc ExecGetResultType(CommonState *commonstate);
|
||||||
extern void ExecAssignProjectionInfo(Plan *node, CommonState *commonstate);
|
extern void ExecAssignProjectionInfo(Plan *node, CommonState *commonstate);
|
||||||
extern void ExecFreeProjectionInfo(CommonState *commonstate);
|
extern void ExecFreeProjectionInfo(CommonState *commonstate);
|
||||||
extern void ExecFreeExprContext(CommonState *commonstate);
|
extern void ExecFreeExprContext(CommonState *commonstate);
|
||||||
extern void ExecFreeTypeInfo(CommonState *commonstate);
|
|
||||||
extern TupleDesc ExecGetScanType(CommonScanState *csstate);
|
extern TupleDesc ExecGetScanType(CommonScanState *csstate);
|
||||||
extern void ExecAssignScanType(CommonScanState *csstate,
|
extern void ExecAssignScanType(CommonScanState *csstate,
|
||||||
TupleDesc tupDesc);
|
TupleDesc tupDesc);
|
||||||
|
@ -141,6 +137,13 @@ extern void ExecAssignScanTypeFromOuterPlan(Plan *node,
|
||||||
CommonScanState *csstate);
|
CommonScanState *csstate);
|
||||||
extern Form_pg_attribute ExecGetTypeInfo(Relation relDesc);
|
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 ExecOpenIndices(RelationInfo *resultRelationInfo);
|
||||||
extern void ExecCloseIndices(RelationInfo *resultRelationInfo);
|
extern void ExecCloseIndices(RelationInfo *resultRelationInfo);
|
||||||
extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
|
extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* hash-join hash table structures
|
||||||
*
|
*
|
||||||
* Each active hashjoin has a HashJoinTable control block which is
|
* Each active hashjoin has a HashJoinTable control block which is
|
||||||
* palloc'd in the executor's context. All other storage needed for
|
* palloc'd in the executor's per-query context. All other storage needed
|
||||||
* the hashjoin is kept in private memory contexts, two for each hashjoin.
|
* 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
|
* This makes it easy and fast to release the storage when we don't need it
|
||||||
* anymore.
|
* anymore.
|
||||||
*
|
*
|
||||||
|
@ -68,6 +68,14 @@ typedef struct HashTableData
|
||||||
long *innerBatchSize; /* count of tuples in each inner batch
|
long *innerBatchSize; /* count of tuples in each inner batch
|
||||||
* file */
|
* 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
|
* During 1st scan of inner relation, we get tuples from executor. If
|
||||||
* nbatch > 0 then tuples that don't belong in first nbuckets logical
|
* nbatch > 0 then tuples that don't belong in first nbuckets logical
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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,
|
TupleDesc tupdesc,
|
||||||
int numCols,
|
int numCols,
|
||||||
AttrNumber *matchColIdx,
|
AttrNumber *matchColIdx,
|
||||||
FmgrInfo *eqfunctions);
|
FmgrInfo *eqfunctions,
|
||||||
|
MemoryContext evalContext);
|
||||||
extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc,
|
extern FmgrInfo *execTuplesMatchPrepare(TupleDesc tupdesc,
|
||||||
int numCols,
|
int numCols,
|
||||||
AttrNumber *matchColIdx);
|
AttrNumber *matchColIdx);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 void ExecEndHash(Hash *node);
|
||||||
extern HashJoinTable ExecHashTableCreate(Hash *node);
|
extern HashJoinTable ExecHashTableCreate(Hash *node);
|
||||||
extern void ExecHashTableDestroy(HashJoinTable hashtable);
|
extern void ExecHashTableDestroy(HashJoinTable hashtable);
|
||||||
extern void ExecHashTableInsert(HashJoinTable hashtable, ExprContext *econtext,
|
extern void ExecHashTableInsert(HashJoinTable hashtable,
|
||||||
Var *hashkey);
|
ExprContext *econtext,
|
||||||
extern int ExecHashGetBucket(HashJoinTable hashtable, ExprContext *econtext,
|
Node *hashkey);
|
||||||
Var *hashkey);
|
extern int ExecHashGetBucket(HashJoinTable hashtable,
|
||||||
|
ExprContext *econtext,
|
||||||
|
Node *hashkey);
|
||||||
extern HeapTuple ExecScanHashBucket(HashJoinState *hjstate, List *hjclauses,
|
extern HeapTuple ExecScanHashBucket(HashJoinState *hjstate, List *hjclauses,
|
||||||
ExprContext *econtext);
|
ExprContext *econtext);
|
||||||
extern void ExecHashTableReset(HashJoinTable hashtable, long ntuples);
|
extern void ExecHashTableReset(HashJoinTable hashtable, long ntuples);
|
||||||
|
|
|
@ -13,7 +13,7 @@ extern Datum ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext,
|
||||||
bool *isNull);
|
bool *isNull);
|
||||||
extern bool ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent);
|
extern bool ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent);
|
||||||
extern void ExecReScanSetParamPlan(SubPlan *node, 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);
|
extern void ExecEndSubPlan(SubPlan *node);
|
||||||
|
|
||||||
#endif /* NODESUBPLAN_H */
|
#endif /* NODESUBPLAN_H */
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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) \
|
#define PG_DETOAST_DATUM_COPY(datum) \
|
||||||
pg_detoast_datum_copy((struct varlena *) DatumGetPointer(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 */
|
/* Macros for fetching arguments of standard types */
|
||||||
|
|
||||||
#define PG_GETARG_DATUM(n) (fcinfo->arg[n])
|
#define PG_GETARG_DATUM(n) (fcinfo->arg[n])
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: execnodes.h,v 1.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
|
* 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
|
* what the current inner tuple is and so we look at the expression
|
||||||
* context.
|
* 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
|
typedef struct ExprContext
|
||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
|
/* Tuples that Var nodes in expression may refer to */
|
||||||
TupleTableSlot *ecxt_scantuple;
|
TupleTableSlot *ecxt_scantuple;
|
||||||
TupleTableSlot *ecxt_innertuple;
|
TupleTableSlot *ecxt_innertuple;
|
||||||
TupleTableSlot *ecxt_outertuple;
|
TupleTableSlot *ecxt_outertuple;
|
||||||
Relation ecxt_relation;
|
/* Memory contexts for expression evaluation --- see notes above */
|
||||||
Index ecxt_relid;
|
MemoryContext ecxt_per_query_memory;
|
||||||
ParamListInfo ecxt_param_list_info;
|
MemoryContext ecxt_per_tuple_memory;
|
||||||
ParamExecData *ecxt_param_exec_vals; /* this is for subselects */
|
/* Values to substitute for Param nodes in expression */
|
||||||
List *ecxt_range_table;
|
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 */
|
Datum *ecxt_aggvalues; /* precomputed values for Aggref nodes */
|
||||||
bool *ecxt_aggnulls; /* null flags 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;
|
} ExprContext;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* ProjectionInfo node information
|
* 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
|
* 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
|
* it should just update this information as necessary and then
|
||||||
* call ExecProject(). -cim 6/3/91
|
* call ExecProject(). -cim 6/3/91
|
||||||
*
|
*
|
||||||
|
@ -122,18 +139,16 @@ typedef struct ProjectionInfo
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* JunkFilter
|
* 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
|
* A junk attribute is an attribute in a tuple that is needed only for
|
||||||
* storing intermediate information in the executor, and does not belong
|
* storing intermediate information in the executor, and does not belong
|
||||||
* in the tuple proper. For example, when we do a delete or replace
|
* in emitted tuples. For example, when we do an UPDATE query,
|
||||||
* query, the planner adds an entry to the targetlist so that the tuples
|
* the planner adds a "junk" entry to the targetlist so that the tuples
|
||||||
* returned to ExecutePlan() contain an extra attribute: the t_ctid of
|
* returned to ExecutePlan() contain an extra attribute: the ctid of
|
||||||
* the tuple to be deleted/replaced. This is needed for amdelete() and
|
* the tuple to be updated. This is needed to do the update, but we
|
||||||
* amreplace(). In doing a delete this does not make much of a
|
* don't want the ctid to be part of the stored new tuple! So, we
|
||||||
* difference, but in doing a replace we have to make sure we disgard
|
* apply a "junk filter" to remove the junk attributes and form the
|
||||||
* all the junk in a tuple before calling amreplace(). Otherwise the
|
* real output tuple.
|
||||||
* inserted tuple will not have the correct schema. This solves a
|
|
||||||
* problem with hash-join and merge-sort replace plans. -cim 10/10/90
|
|
||||||
*
|
*
|
||||||
* targetList: the original target list (including junk attributes).
|
* targetList: the original target list (including junk attributes).
|
||||||
* length: the length of 'targetList'.
|
* length: the length of 'targetList'.
|
||||||
|
@ -174,10 +189,6 @@ typedef struct JunkFilter
|
||||||
* param_list_info information needed to transform
|
* param_list_info information needed to transform
|
||||||
* Param nodes into Const nodes
|
* 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
|
* tupleTable this is a pointer to an array
|
||||||
* of pointers to tuples used by
|
* of pointers to tuples used by
|
||||||
* the executor at any given moment.
|
* the executor at any given moment.
|
||||||
|
@ -185,11 +196,6 @@ typedef struct JunkFilter
|
||||||
* junkFilter contains information used to
|
* junkFilter contains information used to
|
||||||
* extract junk attributes from a tuple.
|
* extract junk attributes from a tuple.
|
||||||
* (see JunkFilter above)
|
* (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
|
typedef struct EState
|
||||||
|
@ -203,7 +209,6 @@ typedef struct EState
|
||||||
Relation es_into_relation_descriptor;
|
Relation es_into_relation_descriptor;
|
||||||
ParamListInfo es_param_list_info;
|
ParamListInfo es_param_list_info;
|
||||||
ParamExecData *es_param_exec_vals; /* this is for subselects */
|
ParamExecData *es_param_exec_vals; /* this is for subselects */
|
||||||
int es_BaseId;
|
|
||||||
TupleTable es_tupleTable;
|
TupleTable es_tupleTable;
|
||||||
JunkFilter *es_junkFilter;
|
JunkFilter *es_junkFilter;
|
||||||
uint32 es_processed; /* # of tuples processed */
|
uint32 es_processed; /* # of tuples processed */
|
||||||
|
@ -249,27 +254,21 @@ typedef struct EState
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* BaseNode removed -- base_id moved into CommonState - jolly */
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* CommonState information
|
* CommonState information
|
||||||
*
|
*
|
||||||
*| this is a bogus class used to hold slots so other
|
* Superclass for all executor node-state object types.
|
||||||
*| nodes can inherit them...
|
|
||||||
*
|
*
|
||||||
* OuterTupleSlot pointer to slot containing current "outer" tuple
|
* OuterTupleSlot pointer to slot containing current "outer" tuple
|
||||||
* ResultTupleSlot pointer to slot in tuple table for projected 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
|
* ProjInfo info this node uses to form tuple projections
|
||||||
* NumScanAttributes size of ScanAttributes array
|
* TupFromTlist state flag used by some node types (why kept here?)
|
||||||
* ScanAttributes attribute numbers of interest in this tuple
|
|
||||||
*
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
typedef struct CommonState
|
typedef struct CommonState
|
||||||
{
|
{
|
||||||
NodeTag type; /* its first field is NodeTag */
|
NodeTag type; /* its first field is NodeTag */
|
||||||
int cs_base_id;
|
|
||||||
TupleTableSlot *cs_OuterTupleSlot;
|
TupleTableSlot *cs_OuterTupleSlot;
|
||||||
TupleTableSlot *cs_ResultTupleSlot;
|
TupleTableSlot *cs_ResultTupleSlot;
|
||||||
ExprContext *cs_ExprContext;
|
ExprContext *cs_ExprContext;
|
||||||
|
@ -288,15 +287,6 @@ typedef struct CommonState
|
||||||
*
|
*
|
||||||
* done flag which tells us to quit when we
|
* done flag which tells us to quit when we
|
||||||
* have already returned a constant tuple.
|
* 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
|
typedef struct ResultState
|
||||||
|
@ -319,15 +309,6 @@ typedef struct ResultState
|
||||||
* rtentries range table for the current plan
|
* rtentries range table for the current plan
|
||||||
* result_relation_info_list array of each subplan's result relation info
|
* result_relation_info_list array of each subplan's result relation info
|
||||||
* junkFilter_list array of each subplan's junk filter
|
* 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
|
typedef struct AppendState
|
||||||
|
@ -349,22 +330,15 @@ typedef struct AppendState
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* CommonScanState information
|
* CommonScanState information
|
||||||
*
|
*
|
||||||
* CommonScanState is a class like CommonState, but is used more
|
* CommonScanState extends CommonState for node types that represent
|
||||||
* by the nodes like SeqScan and Sort which want to
|
* scans of an underlying relation. It can also be used for nodes
|
||||||
* keep track of an underlying relation.
|
* 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
|
* currentRelation relation being scanned (NULL if none)
|
||||||
* currentScanDesc current scan descriptor for scan
|
* currentScanDesc current scan descriptor for scan (NULL if none)
|
||||||
* ScanTupleSlot pointer to slot in tuple table holding scan tuple
|
* 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
|
typedef struct CommonScanState
|
||||||
|
@ -375,41 +349,39 @@ typedef struct CommonScanState
|
||||||
TupleTableSlot *css_ScanTupleSlot;
|
TupleTableSlot *css_ScanTupleSlot;
|
||||||
} CommonScanState;
|
} CommonScanState;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SeqScan uses a bare CommonScanState as its state item, since it needs
|
||||||
|
* no additional fields.
|
||||||
|
*/
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* IndexScanState information
|
* IndexScanState information
|
||||||
*
|
*
|
||||||
*| index scans don't use CommonScanState because
|
* Note that an IndexScan node *also* has a CommonScanState state item.
|
||||||
*| the underlying AM abstractions for heap scans and
|
* IndexScanState stores the info needed specifically for indexing.
|
||||||
*| index scans are too different.. It would be nice
|
* There's probably no good reason why this is a separate node type
|
||||||
*| if the current abstraction was more useful but ... -cim 10/15/89
|
* rather than an extension of CommonScanState.
|
||||||
*
|
*
|
||||||
* IndexPtr current index in use
|
|
||||||
* NumIndices number of indices in this scan
|
* NumIndices number of indices in this scan
|
||||||
|
* IndexPtr current index in use
|
||||||
* ScanKeys Skey structures to scan index rels
|
* ScanKeys Skey structures to scan index rels
|
||||||
* NumScanKeys array of no of keys in each Skey struct
|
* NumScanKeys array of no of keys in each Skey struct
|
||||||
* RuntimeKeyInfo array of array of flags for Skeys evaled at runtime
|
* 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
|
* RelationDescs ptr to array of relation descriptors
|
||||||
* ScanDescs ptr to array of scan 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
|
typedef struct IndexScanState
|
||||||
{
|
{
|
||||||
CommonState cstate; /* its first field is NodeTag */
|
NodeTag type;
|
||||||
int iss_NumIndices;
|
int iss_NumIndices;
|
||||||
int iss_IndexPtr;
|
int iss_IndexPtr;
|
||||||
int iss_MarkIndexPtr;
|
int iss_MarkIndexPtr;
|
||||||
ScanKey *iss_ScanKeys;
|
ScanKey *iss_ScanKeys;
|
||||||
int *iss_NumScanKeys;
|
int *iss_NumScanKeys;
|
||||||
Pointer iss_RuntimeKeyInfo;
|
int **iss_RuntimeKeyInfo;
|
||||||
|
ExprContext *iss_RuntimeContext;
|
||||||
RelationPtr iss_RelationDescs;
|
RelationPtr iss_RelationDescs;
|
||||||
IndexScanDescPtr iss_ScanDescs;
|
IndexScanDescPtr iss_ScanDescs;
|
||||||
HeapTupleData iss_htup;
|
HeapTupleData iss_htup;
|
||||||
|
@ -418,28 +390,18 @@ typedef struct IndexScanState
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* TidScanState information
|
* TidScanState information
|
||||||
*
|
*
|
||||||
*| tid scans don't use CommonScanState because
|
* Note that a TidScan node *also* has a CommonScanState state item.
|
||||||
*| the underlying AM abstractions for heap scans and
|
* There's probably no good reason why this is a separate node type
|
||||||
*| tid scans are too different.. It would be nice
|
* rather than an extension of CommonScanState.
|
||||||
*| if the current abstraction was more useful but ... -cim 10/15/89
|
|
||||||
*
|
*
|
||||||
* TidPtr current tid in use
|
|
||||||
* NumTids number of tids in this scan
|
* NumTids number of tids in this scan
|
||||||
* tidList evaluated item pointers
|
* TidPtr current tid in use
|
||||||
*
|
* 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
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
typedef struct TidScanState
|
typedef struct TidScanState
|
||||||
{
|
{
|
||||||
CommonState cstate; /* its first field is NodeTag */
|
NodeTag type;
|
||||||
int tss_NumTids;
|
int tss_NumTids;
|
||||||
int tss_TidPtr;
|
int tss_TidPtr;
|
||||||
int tss_MarkTidPtr;
|
int tss_MarkTidPtr;
|
||||||
|
@ -455,39 +417,19 @@ typedef struct TidScanState
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* JoinState information
|
* JoinState information
|
||||||
*
|
*
|
||||||
* CommonState information
|
* Superclass for state items of join nodes.
|
||||||
*
|
* Currently this is the same as CommonState.
|
||||||
* 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 CommonState JoinState;
|
typedef CommonState JoinState;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* NestLoopState information
|
* 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
|
typedef struct NestLoopState
|
||||||
{
|
{
|
||||||
JoinState jstate; /* its first field is NodeTag */
|
JoinState jstate; /* its first field is NodeTag */
|
||||||
bool nl_PortalFlag;
|
|
||||||
} NestLoopState;
|
} NestLoopState;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -497,17 +439,6 @@ typedef struct NestLoopState
|
||||||
* InnerSkipQual outerKey1 > innerKey1 ...
|
* InnerSkipQual outerKey1 > innerKey1 ...
|
||||||
* JoinState current "state" of join. see executor.h
|
* JoinState current "state" of join. see executor.h
|
||||||
* MarkedTupleSlot pointer to slot in tuple table for marked tuple
|
* 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
|
typedef struct MergeJoinState
|
||||||
|
@ -531,17 +462,6 @@ typedef struct MergeJoinState
|
||||||
* hj_InnerHashKey the inner hash key in the hashjoin condition
|
* hj_InnerHashKey the inner hash key in the hashjoin condition
|
||||||
* hj_OuterTupleSlot tuple slot for outer tuples
|
* hj_OuterTupleSlot tuple slot for outer tuples
|
||||||
* hj_HashTupleSlot tuple slot for hashed 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
|
typedef struct HashJoinState
|
||||||
|
@ -550,7 +470,7 @@ typedef struct HashJoinState
|
||||||
HashJoinTable hj_HashTable;
|
HashJoinTable hj_HashTable;
|
||||||
int hj_CurBucketNo;
|
int hj_CurBucketNo;
|
||||||
HashJoinTuple hj_CurTuple;
|
HashJoinTuple hj_CurTuple;
|
||||||
Var *hj_InnerHashKey;
|
Node *hj_InnerHashKey;
|
||||||
TupleTableSlot *hj_OuterTupleSlot;
|
TupleTableSlot *hj_OuterTupleSlot;
|
||||||
TupleTableSlot *hj_HashTupleSlot;
|
TupleTableSlot *hj_HashTupleSlot;
|
||||||
} HashJoinState;
|
} HashJoinState;
|
||||||
|
@ -567,22 +487,9 @@ typedef struct HashJoinState
|
||||||
* materialize nodes are used to materialize the results
|
* materialize nodes are used to materialize the results
|
||||||
* of a subplan into a temporary file.
|
* of a subplan into a temporary file.
|
||||||
*
|
*
|
||||||
|
* csstate.css_ScanTupleSlot refers to output of underlying plan.
|
||||||
|
*
|
||||||
* tuplestorestate private state of tuplestore.c
|
* 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
|
typedef struct MaterialState
|
||||||
|
@ -594,12 +501,14 @@ typedef struct MaterialState
|
||||||
/* ---------------------
|
/* ---------------------
|
||||||
* AggregateState information
|
* AggregateState information
|
||||||
*
|
*
|
||||||
|
* csstate.css_ScanTupleSlot refers to output of underlying plan.
|
||||||
|
*
|
||||||
* Note: the associated ExprContext contains ecxt_aggvalues and ecxt_aggnulls
|
* Note: the associated ExprContext contains ecxt_aggvalues and ecxt_aggnulls
|
||||||
* arrays, which hold the computed agg values for the current input group
|
* arrays, which hold the computed agg values for the current input group
|
||||||
* during evaluation of an Agg node's output tuple(s).
|
* 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
|
typedef struct AggState
|
||||||
{
|
{
|
||||||
|
@ -607,12 +516,14 @@ typedef struct AggState
|
||||||
List *aggs; /* all Aggref nodes in targetlist & quals */
|
List *aggs; /* all Aggref nodes in targetlist & quals */
|
||||||
int numaggs; /* length of list (could be zero!) */
|
int numaggs; /* length of list (could be zero!) */
|
||||||
AggStatePerAgg peragg; /* per-Aggref working state */
|
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 */
|
bool agg_done; /* indicates completion of Agg scan */
|
||||||
} AggState;
|
} AggState;
|
||||||
|
|
||||||
/* ---------------------
|
/* ---------------------
|
||||||
* GroupState information
|
* GroupState information
|
||||||
*
|
|
||||||
* -------------------------
|
* -------------------------
|
||||||
*/
|
*/
|
||||||
typedef struct GroupState
|
typedef struct GroupState
|
||||||
|
@ -630,21 +541,6 @@ typedef struct GroupState
|
||||||
* sort_Done indicates whether sort has been performed yet
|
* sort_Done indicates whether sort has been performed yet
|
||||||
* sort_Keys scan key structures describing the sort keys
|
* sort_Keys scan key structures describing the sort keys
|
||||||
* tuplesortstate private state of tuplesort.c
|
* 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
|
typedef struct SortState
|
||||||
|
@ -664,15 +560,6 @@ typedef struct SortState
|
||||||
* with the previously fetched tuple stored in priorTuple.
|
* with the previously fetched tuple stored in priorTuple.
|
||||||
* If the two are identical in all interesting fields, then
|
* If the two are identical in all interesting fields, then
|
||||||
* we just fetch another tuple from the sort and try again.
|
* 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
|
typedef struct UniqueState
|
||||||
|
@ -680,6 +567,7 @@ typedef struct UniqueState
|
||||||
CommonState cstate; /* its first field is NodeTag */
|
CommonState cstate; /* its first field is NodeTag */
|
||||||
FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */
|
FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */
|
||||||
HeapTuple priorTuple; /* most recently returned tuple, or NULL */
|
HeapTuple priorTuple; /* most recently returned tuple, or NULL */
|
||||||
|
MemoryContext tempContext; /* short-term context for comparisons */
|
||||||
} UniqueState;
|
} UniqueState;
|
||||||
|
|
||||||
|
|
||||||
|
@ -687,15 +575,6 @@ typedef struct UniqueState
|
||||||
* HashState information
|
* HashState information
|
||||||
*
|
*
|
||||||
* hashtable hash table for the hashjoin
|
* 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
|
typedef struct HashState
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
typedef struct Hash
|
||||||
{
|
{
|
||||||
Plan plan;
|
Plan plan;
|
||||||
Var *hashkey;
|
Node *hashkey;
|
||||||
HashState *hashstate;
|
HashState *hashstate;
|
||||||
} Hash;
|
} Hash;
|
||||||
|
|
||||||
|
@ -370,7 +370,7 @@ typedef struct SubPlan
|
||||||
* Remaining fields are working state for executor; not used in
|
* Remaining fields are working state for executor; not used in
|
||||||
* planning
|
* planning
|
||||||
*/
|
*/
|
||||||
bool shutdown; /* TRUE = need to shutdown plan */
|
bool needShutdown; /* TRUE = need to shutdown subplan */
|
||||||
HeapTuple curTuple; /* copy of most recent tuple from subplan */
|
HeapTuple curTuple; /* copy of most recent tuple from subplan */
|
||||||
} SubPlan;
|
} SubPlan;
|
||||||
|
|
||||||
|
|
|
@ -1,64 +1,49 @@
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* datum.h
|
* 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) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
#ifndef DATUM_H
|
||||||
#define 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
|
* Does nothing if datatype is pass-by-value.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
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
|
* 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!
|
* XXX : See comments in the code for restrictions!
|
||||||
*/
|
*/
|
||||||
extern bool datumIsEqual(Datum value1, Datum value2, Oid type,
|
extern bool datumIsEqual(Datum value1, Datum value2,
|
||||||
bool byVal, Size len);
|
bool typByVal, int typLen);
|
||||||
|
|
||||||
#endif /* DATUM_H */
|
#endif /* DATUM_H */
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* fcache.h
|
* fcache.h
|
||||||
|
* Declarations for function cache records.
|
||||||
*
|
*
|
||||||
*
|
* The first time any Oper or Func node is evaluated, we compute a cache
|
||||||
|
* record for the function being invoked, and save a pointer to the cache
|
||||||
|
* record in the Oper or Func node. This saves repeated lookup of info
|
||||||
|
* about the function.
|
||||||
*
|
*
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* expr whose argument is func returning a
|
||||||
* set ugh! */
|
* 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 */
|
int nargs; /* actual number of arguments */
|
||||||
Oid *argOidVect; /* oids of all the argument types */
|
Oid *argOidVect; /* oids of all the argument types */
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
|
@ -47,17 +47,17 @@
|
||||||
#include "plpgsql.h"
|
#include "plpgsql.h"
|
||||||
#include "pl.tab.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 "access/heapam.h"
|
||||||
|
|
||||||
#include "tcop/tcopprot.h"
|
|
||||||
#include "utils/syscache.h"
|
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "catalog/pg_type.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;
|
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
|
* Create a simple expression context to hold the arguments
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
econtext = makeNode(ExprContext);
|
econtext = MakeExprContext(NULL, TransactionCommandContext);
|
||||||
paramLI = (ParamListInfo) palloc((expr->nparams + 1) *
|
paramLI = (ParamListInfo) palloc((expr->nparams + 1) *
|
||||||
sizeof(ParamListInfoData));
|
sizeof(ParamListInfoData));
|
||||||
econtext->ecxt_param_list_info = paramLI;
|
econtext->ecxt_param_list_info = paramLI;
|
||||||
|
@ -2286,12 +2286,20 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate,
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
SPI_push();
|
SPI_push();
|
||||||
retval = ExecEvalExpr(expr->plan_simple_expr,
|
retval = ExecEvalExprSwitchContext(expr->plan_simple_expr,
|
||||||
econtext,
|
econtext,
|
||||||
isNull,
|
isNull,
|
||||||
&isdone);
|
&isdone);
|
||||||
SPI_pop();
|
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.
|
* That's it.
|
||||||
* ----------
|
* ----------
|
||||||
|
|
Loading…
Reference in New Issue