summaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/Makefile36
-rw-r--r--src/backend/executor/README99
-rw-r--r--src/backend/executor/_deadcode/nodeTee.c499
-rw-r--r--src/backend/executor/execAmi.c258
-rw-r--r--src/backend/executor/execJunk.c431
-rw-r--r--src/backend/executor/execMain.c1941
-rw-r--r--src/backend/executor/execProcnode.c759
-rw-r--r--src/backend/executor/execQual.c1929
-rw-r--r--src/backend/executor/execScan.c147
-rw-r--r--src/backend/executor/execTuples.c795
-rw-r--r--src/backend/executor/execUtils.c793
-rw-r--r--src/backend/executor/functions.c611
-rw-r--r--src/backend/executor/instrument.c122
-rw-r--r--src/backend/executor/nodeAgg.c1053
-rw-r--r--src/backend/executor/nodeAppend.c406
-rw-r--r--src/backend/executor/nodeFunctionscan.c469
-rw-r--r--src/backend/executor/nodeGroup.c502
-rw-r--r--src/backend/executor/nodeHash.c736
-rw-r--r--src/backend/executor/nodeHashjoin.c713
-rw-r--r--src/backend/executor/nodeIndexscan.c1050
-rw-r--r--src/backend/executor/nodeLimit.c311
-rw-r--r--src/backend/executor/nodeMaterial.c303
-rw-r--r--src/backend/executor/nodeMergejoin.c1595
-rw-r--r--src/backend/executor/nodeNestloop.c418
-rw-r--r--src/backend/executor/nodeResult.c289
-rw-r--r--src/backend/executor/nodeSeqscan.c366
-rw-r--r--src/backend/executor/nodeSetOp.c334
-rw-r--r--src/backend/executor/nodeSort.c397
-rw-r--r--src/backend/executor/nodeSubplan.c491
-rw-r--r--src/backend/executor/nodeSubqueryscan.c262
-rw-r--r--src/backend/executor/nodeTidscan.c496
-rw-r--r--src/backend/executor/nodeUnique.c237
-rw-r--r--src/backend/executor/spi.c1424
33 files changed, 0 insertions, 20272 deletions
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
deleted file mode 100644
index b875259bc1a..00000000000
--- a/src/backend/executor/Makefile
+++ /dev/null
@@ -1,36 +0,0 @@
-#-------------------------------------------------------------------------
-#
-# Makefile--
-# Makefile for executor
-#
-# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.19 2002/05/12 23:43:02 tgl Exp $
-#
-#-------------------------------------------------------------------------
-
-subdir = src/backend/executor
-top_builddir = ../../..
-include $(top_builddir)/src/Makefile.global
-
-OBJS = execAmi.o execJunk.o execMain.o \
- execProcnode.o execQual.o execScan.o execTuples.o \
- execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o nodeHash.o \
- nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
- nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \
- nodeSetOp.o nodeSort.o nodeUnique.o nodeLimit.o nodeGroup.o \
- nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o spi.o
-
-all: SUBSYS.o
-
-SUBSYS.o: $(OBJS)
- $(LD) $(LDREL) $(LDOUT) SUBSYS.o $(OBJS)
-
-depend dep:
- $(CC) -MM $(CFLAGS) *.c >depend
-
-clean:
- rm -f SUBSYS.o $(OBJS)
-
-ifeq (depend,$(wildcard depend))
-include depend
-endif
diff --git a/src/backend/executor/README b/src/backend/executor/README
deleted file mode 100644
index 0a56c3fa6ae..00000000000
--- a/src/backend/executor/README
+++ /dev/null
@@ -1,99 +0,0 @@
-$Header: /cvsroot/pgsql/src/backend/executor/README,v 1.1 2001/05/15 00:35:50 tgl Exp $
-
-The Postgres Executor
----------------------
-
-The executor processes a tree of "plan nodes". The plan tree is essentially
-a demand-pull pipeline of tuple processing operations. Each node, when
-called, will produce the next tuple in its output sequence, or NULL if no
-more tuples are available. If the node is not a primitive relation-scanning
-node, it will have child node(s) that it calls in turn to obtain input
-tuples.
-
-Refinements on this basic model include:
-
-* Choice of scan direction (forwards or backwards). Caution: this is not
-currently well-supported. It works for primitive scan nodes, but not very
-well for joins, aggregates, etc.
-
-* Rescan command to reset a node and make it generate its output sequence
-over again.
-
-* Parameters that can alter a node's results. After adjusting a parameter,
-the rescan command must be applied to that node and all nodes above it.
-There is a moderately intelligent scheme to avoid rescanning nodes
-unnecessarily (for example, Sort does not rescan its input if no parameters
-of the input have changed, since it can just reread its stored sorted data).
-
-The plan tree concept implements SELECT directly: it is only necessary to
-deliver the top-level result tuples to the client, or insert them into
-another table in the case of INSERT ... SELECT. (INSERT ... VALUES is
-handled similarly, but the plan tree is just a Result node with no source
-tables.) For UPDATE, the plan tree selects the tuples that need to be
-updated (WHERE condition) and delivers a new calculated tuple value for each
-such tuple, plus a "junk" (hidden) tuple CTID identifying the target tuple.
-The executor's top level then uses this information to update the correct
-tuple. DELETE is similar to UPDATE except that only a CTID need be
-delivered by the plan tree.
-
-XXX a great deal more documentation needs to be written here...
-
-
-EvalPlanQual (READ COMMITTED update checking)
----------------------------------------------
-
-For simple SELECTs, the executor need only pay attention to tuples that are
-valid according to the snapshot seen by the current transaction (ie, they
-were inserted by a previously committed transaction, and not deleted by any
-previously committed transaction). However, for UPDATE and DELETE it is not
-cool to modify or delete a tuple that's been modified by an open or
-concurrently-committed transaction. If we are running in SERIALIZABLE
-isolation level then we just raise an error when this condition is seen to
-occur. In READ COMMITTED isolation level, we must work a lot harder.
-
-The basic idea in READ COMMITTED mode is to take the modified tuple
-committed by the concurrent transaction (after waiting for it to commit,
-if need be) and re-evaluate the query qualifications to see if it would
-still meet the quals. If so, we regenerate the updated tuple (if we are
-doing an UPDATE) from the modified tuple, and finally update/delete the
-modified tuple. SELECT FOR UPDATE behaves similarly, except that its action
-is just to mark the modified tuple for update by the current transaction.
-
-To implement this checking, we actually re-run the entire query from scratch
-for each modified tuple, but with the scan node that sourced the original
-tuple set to return only the modified tuple, not the original tuple or any
-of the rest of the relation. If this query returns a tuple, then the
-modified tuple passes the quals (and the query output is the suitably
-modified update tuple, if we're doing UPDATE). If no tuple is returned,
-then the modified tuple fails the quals, so we ignore it and continue the
-original query. (This is reasonably efficient for simple queries, but may
-be horribly slow for joins. A better design would be nice; one thought for
-future investigation is to treat the tuple substitution like a parameter,
-so that we can avoid rescanning unrelated nodes.)
-
-Note a fundamental bogosity of this approach: if the relation containing
-the original tuple is being used in a self-join, the other instance(s) of
-the relation will be treated as still containing the original tuple, whereas
-logical consistency would demand that the modified tuple appear in them too.
-But we'd have to actually substitute the modified tuple for the original,
-while still returning all the rest of the relation, to ensure consistent
-answers. Implementing this correctly is a task for future work.
-
-In UPDATE/DELETE, only the target relation needs to be handled this way,
-so only one special recheck query needs to execute at a time. In SELECT FOR
-UPDATE, there may be multiple relations flagged FOR UPDATE, so it's possible
-that while we are executing a recheck query for one modified tuple, we will
-hit another modified tuple in another relation. In this case we "stack up"
-recheck queries: a sub-recheck query is spawned in which both the first and
-second modified tuples will be returned as the only components of their
-relations. (In event of success, all these modified tuples will be marked
-for update.) Again, this isn't necessarily quite the right thing ... but in
-simple cases it works. Potentially, recheck queries could get nested to the
-depth of the number of FOR UPDATE relations in the query.
-
-It should be noted also that UPDATE/DELETE expect at most one tuple to
-result from the modified query, whereas in the FOR UPDATE case it's possible
-for multiple tuples to result (since we could be dealing with a join in
-which multiple tuples join to the modified tuple). We want FOR UPDATE to
-mark all relevant tuples, so we pass all tuples output by all the stacked
-recheck queries back to the executor toplevel for marking.
diff --git a/src/backend/executor/_deadcode/nodeTee.c b/src/backend/executor/_deadcode/nodeTee.c
deleted file mode 100644
index e02f8cb59f5..00000000000
--- a/src/backend/executor/_deadcode/nodeTee.c
+++ /dev/null
@@ -1,499 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeTee.c
- *
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * DESCRIPTION
- * This code provides support for a tee node, which allows
- * multiple parent in a megaplan.
- *
- * INTERFACE ROUTINES
- * ExecTee
- * ExecInitTee
- * ExecEndTee
- *
- * $Id: nodeTee.c,v 1.12 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-
-#include <sys/types.h>
-#include <sys/file.h>
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "catalog/catalog.h"
-#include "catalog/heap.h"
-#include "executor/executor.h"
-#include "executor/nodeTee.h"
-#include "optimizer/internal.h"
-#include "storage/bufmgr.h"
-#include "storage/smgr.h"
-#include "tcop/pquery.h"
-#include "utils/relcache.h"
-
-/* ------------------------------------------------------------------
- * ExecInitTee
- *
- * Create tee state
- *
- * ------------------------------------------------------------------
- */
-bool
-ExecInitTee(Tee * node, EState *currentEstate, Plan *parent)
-{
- TeeState *teeState;
- Plan *outerPlan;
- int len;
- Relation bufferRel;
- TupleDesc tupType;
- EState *estate;
-
- /*
- * it is possible that the Tee has already been initialized since it
- * can be reached by multiple parents. If it is already initialized,
- * simply return and do not initialize the children nodes again
- */
- if (node->plan.state)
- return TRUE;
-
- /*
- * assign the node's execution state
- */
-
- /*
- * make a new executor state, because we have a different
- * es_range_table
- */
-
-/* node->plan.state = estate;*/
-
- estate = CreateExecutorState();
- estate->es_direction = currentEstate->es_direction;
- estate->es_BaseId = currentEstate->es_BaseId;
- estate->es_BaseId = currentEstate->es_BaseId;
- estate->es_tupleTable = currentEstate->es_tupleTable;
- estate->es_refcount = currentEstate->es_refcount;
- estate->es_junkFilter = currentEstate->es_junkFilter;
- estate->es_snapshot = currentEstate->es_snapshot;
-
- /*
- * use the range table for Tee subplan since the range tables for the
- * two parents may be different
- */
- if (node->rtentries)
- estate->es_range_table = node->rtentries;
- else
- estate->es_range_table = currentEstate->es_range_table;
-
- node->plan.state = estate;
-
-
- /*
- * create teeState structure
- */
- teeState = makeNode(TeeState);
- teeState->tee_leftPlace = 0;
- teeState->tee_rightPlace = 0;
- teeState->tee_lastPlace = 0;
- teeState->tee_bufferRel = NULL;
- teeState->tee_leftScanDesc = NULL;
- teeState->tee_rightScanDesc = NULL;
-
-
- node->teestate = teeState;
-
- /* ----------------
- * Miscellanious initialization
- *
- * + assign node's base_id
- * + assign debugging hooks and
- * + create expression context for node
- * ----------------
- */
- ExecAssignNodeBaseInfo(estate, &(teeState->cstate), parent);
- ExecAssignExprContext(estate, &(teeState->cstate));
-
-#define TEE_NSLOTS 2
-
- /*
- * initialize tuple slots
- */
- ExecInitResultTupleSlot(estate, &(teeState->cstate));
-
- /* initialize child nodes */
- outerPlan = outerPlan((Plan *) node);
- ExecInitNode(outerPlan, estate, (Plan *) node);
-
- /*
- * the tuple type info is from the outer plan of this node the result
- * type is also the same as the outerplan
- */
- ExecAssignResultTypeFromOuterPlan((Plan *) node, &(teeState->cstate));
- ExecAssignProjectionInfo((Plan *) node, &teeState->cstate);
-
- /*
- * initialize temporary relation to buffer tuples
- */
- tupType = ExecGetResultType(&(teeState->cstate));
- len = ExecTargetListLength(((Plan *) node)->targetlist);
-
- /*
- * create a catalogued relation even though this is a temporary
- * relation
- */
- /* cleanup of catalogued relations is easier to do */
-
- if (node->teeTableName[0] != '\0')
- {
- Relation r;
-
- teeState->tee_bufferRelname = pstrdup(node->teeTableName);
-
- /*
- * we are given an tee table name, if a relation by that name
- * exists, then we open it, else we create it and then open it
- */
- r = RelationNameGetRelation(teeState->tee_bufferRelname);
-
- if (RelationIsValid(r))
- bufferRel = heap_openr(teeState->tee_bufferRelname);
- else
- bufferRel = heap_open(
- heap_create_with_catalog(teeState->tee_bufferRelname,
- tupType, RELKIND_RELATION, false));
- }
- else
- {
- sprintf(teeState->tee_bufferRelname,
- "ttemp_%d", /* 'ttemp' for 'tee' temporary */
- newoid());
- bufferRel = heap_open(
- heap_create_with_catalog(teeState->tee_bufferRelname,
- tupType, RELKIND_RELATION, false));
- }
-
- teeState->tee_bufferRel = bufferRel;
-
- /*
- * initialize a memory context for allocating thing like scan
- * descriptors
- */
-
- /*
- * we do this so that on cleanup of the tee, we can free things. if we
- * didn't have our own memory context, we would be in the memory
- * context of the portal that we happen to be using at the moment
- */
-
- teeState->tee_mcxt = (MemoryContext) CreateGlobalMemory(teeState->tee_bufferRelname);
-
- /*
- * don't initialize the scan descriptors here because it's not good to
- * initialize scan descriptors on empty rels. Wait until the scan
- * descriptors are needed before initializing them.
- */
-
- teeState->tee_leftScanDesc = NULL;
- teeState->tee_rightScanDesc = NULL;
-
- return TRUE;
-}
-
-int
-ExecCountSlotsTee(Tee * node)
-{
- /* Tee nodes can't have innerPlans */
- return ExecCountSlotsNode(outerPlan(node)) + TEE_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- initTeeScanDescs
- initializes the left and right scandescs on the temporary
- relation of a Tee node
-
- must open two separate scan descriptors,
- because the left and right scans may be at different points
-* ----------------------------------------------------------------
-*/
-static void
-initTeeScanDescs(Tee * node)
-{
- TeeState *teeState;
- Relation bufferRel;
- ScanDirection dir;
- Snapshot snapshot;
- MemoryContext orig;
-
- teeState = node->teestate;
- if (teeState->tee_leftScanDesc && teeState->tee_rightScanDesc)
- return;
-
- orig = CurrentMemoryContext;
- MemoryContextSwitchTo(teeState->tee_mcxt);
-
- bufferRel = teeState->tee_bufferRel;
- dir = ((Plan *) node)->state->es_direction; /* backwards not handled
- * yet XXX */
- snapshot = ((Plan *) node)->state->es_snapshot;
-
- if (teeState->tee_leftScanDesc == NULL)
- {
- teeState->tee_leftScanDesc = heap_beginscan(bufferRel,
- ScanDirectionIsBackward(dir),
- snapshot,
- 0, /* num scan keys */
- NULL /* scan keys */
- );
- }
- if (teeState->tee_rightScanDesc == NULL)
- {
- teeState->tee_rightScanDesc = heap_beginscan(bufferRel,
- ScanDirectionIsBackward(dir),
- snapshot,
- 0, /* num scan keys */
- NULL /* scan keys */
- );
- }
-
- MemoryContextSwitchTo(orig);
-}
-
-/* ----------------------------------------------------------------
- * ExecTee(node)
- *
- *
- * A Tee serves to connect a subplan to multiple parents.
- * the subplan is always the outplan of the Tee node.
- *
- * The Tee gets requests from either leftParent or rightParent,
- * fetches the result tuple from the child, and then
- * stored the result into a temporary relation (serving as a queue).
- * leftPlace and rightPlace keep track of where the left and rightParents
- * are.
- * If a parent requests a tuple and that parent is not at the end
- * of the temporary relation, then the request is satisfied from
- * the queue instead of by executing the child plan
- *
- * ----------------------------------------------------------------
- */
-
-TupleTableSlot *
-ExecTee(Tee * node, Plan *parent)
-{
- EState *estate;
- TeeState *teeState;
- int leftPlace,
- rightPlace,
- lastPlace;
- int branch;
- TupleTableSlot *result;
- TupleTableSlot *slot;
- Plan *childNode;
- ScanDirection dir;
- HeapTuple heapTuple;
- Relation bufferRel;
- HeapScanDesc scanDesc;
-
- estate = ((Plan *) node)->state;
- teeState = node->teestate;
- leftPlace = teeState->tee_leftPlace;
- rightPlace = teeState->tee_rightPlace;
- lastPlace = teeState->tee_lastPlace;
- bufferRel = teeState->tee_bufferRel;
-
- childNode = outerPlan(node);
-
- dir = estate->es_direction;
-
- /* XXX doesn't handle backwards direction yet */
-
- if (parent == node->leftParent)
- branch = leftPlace;
- else if ((parent == node->rightParent) || (parent == (Plan *) node))
-
- /*
- * the tee node could be the root node of the plan, in which case,
- * we treat it like a right-parent pull
- */
- branch = rightPlace;
- else
- {
- elog(ERROR, "A Tee node can only be executed from its left or right parent\n");
- return NULL;
- }
-
- if (branch == lastPlace)
- { /* we're at the end of the queue already,
- * - get a new tuple from the child plan,
- * - store it in the queue, - increment
- * lastPlace, - increment leftPlace or
- * rightPlace as appropriate, - and return
- * result */
- slot = ExecProcNode(childNode, (Plan *) node);
- if (!TupIsNull(slot))
- {
- /*
- * heap_insert changes something...
- */
- if (slot->ttc_buffer != InvalidBuffer)
- heapTuple = heap_copytuple(slot->val);
- else
- heapTuple = slot->val;
-
- /* insert into temporary relation */
- heap_insert(bufferRel, heapTuple);
-
- if (slot->ttc_buffer != InvalidBuffer)
- heap_freetuple(heapTuple);
-
- /*
- * once there is data in the temporary relation, ensure that
- * the left and right scandescs are initialized
- */
- initTeeScanDescs(node);
-
- scanDesc = (parent == node->leftParent) ?
- teeState->tee_leftScanDesc : teeState->tee_rightScanDesc;
-
- {
- /*
- * move the scandesc forward so we don't re-read this
- * tuple later
- */
- HeapTuple throwAway;
-
- /* Buffer buffer; */
- throwAway = heap_getnext(scanDesc, ScanDirectionIsBackward(dir));
- }
-
- /*
- * set the shouldFree field of the child's slot so that when
- * the child's slot is free'd, this tuple isn't free'd also
- */
-
- /*
- * does this mean this tuple has to be garbage collected
- * later??
- */
- slot->ttc_shouldFree = false;
-
- teeState->tee_lastPlace = lastPlace + 1;
- }
- result = slot;
- }
- else
- { /* the desired data already exists in the
- * temporary relation */
- scanDesc = (parent == node->leftParent) ?
- teeState->tee_leftScanDesc : teeState->tee_rightScanDesc;
-
- heapTuple = heap_getnext(scanDesc, ScanDirectionIsBackward(dir));
-
- /*
- * Increase the pin count on the buffer page, because the tuple
- * stored in the slot also points to it (as well as the scan
- * descriptor). If we don't, ExecStoreTuple will decrease the pin
- * count on the next iteration.
- */
-
- if (scanDesc->rs_cbuf != InvalidBuffer)
- IncrBufferRefCount(scanDesc->rs_cbuf);
-
- slot = teeState->cstate.cs_ResultTupleSlot;
- slot->ttc_tupleDescriptor = RelationGetDescr(bufferRel);
-
- result = ExecStoreTuple(heapTuple, /* tuple to store */
- slot, /* slot to store in */
- scanDesc->rs_cbuf, /* this tuple's buffer */
- false); /* don't free stuff from
- * heap_getnext */
-
- }
-
- if (parent == node->leftParent)
- teeState->tee_leftPlace = leftPlace + 1;
- else
- teeState->tee_rightPlace = rightPlace + 1;
-
- return result;
-}
-
-/* ---------------------------------------------------------------------
- * ExecEndTee
- *
- * End the Tee node, and free up any storage
- * since a Tee node can be downstream of multiple parent nodes,
- * only free when both parents are done
- * --------------------------------------------------------------------
- */
-
-void
-ExecEndTee(Tee * node, Plan *parent)
-{
- EState *estate;
- TeeState *teeState;
- int leftPlace,
- rightPlace,
- lastPlace;
- Relation bufferRel;
- MemoryContext orig;
-
- estate = ((Plan *) node)->state;
- teeState = node->teestate;
- leftPlace = teeState->tee_leftPlace;
- rightPlace = teeState->tee_rightPlace;
- lastPlace = teeState->tee_lastPlace;
-
- if (!node->leftParent || parent == node->leftParent)
- leftPlace = -1;
-
- if (!node->rightParent || parent == node->rightParent)
- rightPlace = -1;
-
- if (parent == (Plan *) node)
- rightPlace = leftPlace = -1;
-
- teeState->tee_leftPlace = leftPlace;
- teeState->tee_rightPlace = rightPlace;
- if ((leftPlace == -1) && (rightPlace == -1))
- {
- /* remove the temporary relations */
- /* and close the scan descriptors */
-
- bufferRel = teeState->tee_bufferRel;
- if (bufferRel)
- {
- heap_drop(bufferRel);
- teeState->tee_bufferRel = NULL;
- if (teeState->tee_mcxt)
- {
- orig = CurrentMemoryContext;
- MemoryContextSwitchTo(teeState->tee_mcxt);
- }
- else
- orig = 0;
-
- if (teeState->tee_leftScanDesc)
- {
- heap_endscan(teeState->tee_leftScanDesc);
- teeState->tee_leftScanDesc = NULL;
- }
- if (teeState->tee_rightScanDesc)
- {
- heap_endscan(teeState->tee_rightScanDesc);
- teeState->tee_rightScanDesc = NULL;
- }
-
- if (teeState->tee_mcxt)
- {
- MemoryContextSwitchTo(orig);
- teeState->tee_mcxt = NULL;
- }
- }
- }
-
-}
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
deleted file mode 100644
index 7ceb7cd2c6f..00000000000
--- a/src/backend/executor/execAmi.c
+++ /dev/null
@@ -1,258 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * execAmi.c
- * miscellaneous executor access method routines
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $Id: execAmi.c,v 1.64 2002/06/20 20:29:27 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "access/genam.h"
-#include "access/heapam.h"
-#include "catalog/heap.h"
-#include "executor/execdebug.h"
-#include "executor/instrument.h"
-#include "executor/nodeAgg.h"
-#include "executor/nodeAppend.h"
-#include "executor/nodeGroup.h"
-#include "executor/nodeGroup.h"
-#include "executor/nodeHash.h"
-#include "executor/nodeHashjoin.h"
-#include "executor/nodeIndexscan.h"
-#include "executor/nodeTidscan.h"
-#include "executor/nodeLimit.h"
-#include "executor/nodeMaterial.h"
-#include "executor/nodeMergejoin.h"
-#include "executor/nodeNestloop.h"
-#include "executor/nodeResult.h"
-#include "executor/nodeSeqscan.h"
-#include "executor/nodeSetOp.h"
-#include "executor/nodeSort.h"
-#include "executor/nodeSubplan.h"
-#include "executor/nodeSubqueryscan.h"
-#include "executor/nodeFunctionscan.h"
-#include "executor/nodeUnique.h"
-
-
-/* ----------------------------------------------------------------
- * ExecReScan
- *
- * XXX this should be extended to cope with all the node types..
- *
- * takes the new expression context as an argument, so that
- * index scans needn't have their scan keys updated separately
- * - marcel 09/20/94
- * ----------------------------------------------------------------
- */
-void
-ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
-{
- if (node->instrument)
- InstrEndLoop(node->instrument);
-
- if (node->chgParam != NULL) /* Wow! */
- {
- List *lst;
-
- foreach(lst, node->initPlan)
- {
- Plan *splan = ((SubPlan *) lfirst(lst))->plan;
-
- if (splan->extParam != NULL) /* don't care about child
- * locParam */
- SetChangedParamList(splan, node->chgParam);
- if (splan->chgParam != NULL)
- ExecReScanSetParamPlan((SubPlan *) lfirst(lst), node);
- }
- foreach(lst, node->subPlan)
- {
- Plan *splan = ((SubPlan *) lfirst(lst))->plan;
-
- if (splan->extParam != NULL)
- SetChangedParamList(splan, node->chgParam);
- }
- /* Well. Now set chgParam for left/right trees. */
- if (node->lefttree != NULL)
- SetChangedParamList(node->lefttree, node->chgParam);
- if (node->righttree != NULL)
- SetChangedParamList(node->righttree, node->chgParam);
- }
-
- switch (nodeTag(node))
- {
- case T_SeqScan:
- ExecSeqReScan((SeqScan *) node, exprCtxt, parent);
- break;
-
- case T_IndexScan:
- ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
- break;
-
- case T_TidScan:
- ExecTidReScan((TidScan *) node, exprCtxt, parent);
- break;
-
- case T_SubqueryScan:
- ExecSubqueryReScan((SubqueryScan *) node, exprCtxt, parent);
- break;
-
- case T_FunctionScan:
- ExecFunctionReScan((FunctionScan *) node, exprCtxt, parent);
- break;
-
- case T_Material:
- ExecMaterialReScan((Material *) node, exprCtxt, parent);
- break;
-
- case T_NestLoop:
- ExecReScanNestLoop((NestLoop *) node, exprCtxt, parent);
- break;
-
- case T_HashJoin:
- ExecReScanHashJoin((HashJoin *) node, exprCtxt, parent);
- break;
-
- case T_Hash:
- ExecReScanHash((Hash *) node, exprCtxt, parent);
- break;
-
- case T_Agg:
- ExecReScanAgg((Agg *) node, exprCtxt, parent);
- break;
-
- case T_Group:
- ExecReScanGroup((Group *) node, exprCtxt, parent);
- break;
-
- case T_Result:
- ExecReScanResult((Result *) node, exprCtxt, parent);
- break;
-
- case T_Unique:
- ExecReScanUnique((Unique *) node, exprCtxt, parent);
- break;
-
- case T_SetOp:
- ExecReScanSetOp((SetOp *) node, exprCtxt, parent);
- break;
-
- case T_Limit:
- ExecReScanLimit((Limit *) node, exprCtxt, parent);
- break;
-
- case T_Sort:
- ExecReScanSort((Sort *) node, exprCtxt, parent);
- break;
-
- case T_MergeJoin:
- ExecReScanMergeJoin((MergeJoin *) node, exprCtxt, parent);
- break;
-
- case T_Append:
- ExecReScanAppend((Append *) node, exprCtxt, parent);
- break;
-
- default:
- elog(ERROR, "ExecReScan: node type %d not supported",
- nodeTag(node));
- return;
- }
-
- if (node->chgParam != NULL)
- {
- freeList(node->chgParam);
- node->chgParam = NULL;
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecMarkPos
- *
- * Marks the current scan position.
- *
- * XXX Needs to be extended to include all the node types,
- * or at least all the ones that can be directly below a mergejoin.
- * ----------------------------------------------------------------
- */
-void
-ExecMarkPos(Plan *node)
-{
- switch (nodeTag(node))
- {
- case T_SeqScan:
- ExecSeqMarkPos((SeqScan *) node);
- break;
-
- case T_IndexScan:
- ExecIndexMarkPos((IndexScan *) node);
- break;
-
- case T_FunctionScan:
- ExecFunctionMarkPos((FunctionScan *) node);
- break;
-
- case T_Material:
- ExecMaterialMarkPos((Material *) node);
- break;
-
- case T_Sort:
- ExecSortMarkPos((Sort *) node);
- break;
-
- case T_TidScan:
- ExecTidMarkPos((TidScan *) node);
- break;
-
- default:
- /* don't make hard error unless caller asks to restore... */
- elog(LOG, "ExecMarkPos: node type %d not supported",
- nodeTag(node));
- break;
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecRestrPos
- *
- * restores the scan position previously saved with ExecMarkPos()
- *
- * XXX Needs to be extended to include all the node types,
- * or at least all the ones that can be directly below a mergejoin.
- * ----------------------------------------------------------------
- */
-void
-ExecRestrPos(Plan *node)
-{
- switch (nodeTag(node))
- {
- case T_SeqScan:
- ExecSeqRestrPos((SeqScan *) node);
- break;
-
- case T_IndexScan:
- ExecIndexRestrPos((IndexScan *) node);
- break;
-
- case T_FunctionScan:
- ExecFunctionRestrPos((FunctionScan *) node);
- break;
-
- case T_Material:
- ExecMaterialRestrPos((Material *) node);
- break;
-
- case T_Sort:
- ExecSortRestrPos((Sort *) node);
- break;
-
- default:
- elog(ERROR, "ExecRestrPos: node type %d not supported",
- nodeTag(node));
- break;
- }
-}
diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c
deleted file mode 100644
index 94c375012e1..00000000000
--- a/src/backend/executor/execJunk.c
+++ /dev/null
@@ -1,431 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * junk.c
- * Junk attribute support stuff....
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.30 2002/06/20 20:29:27 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "executor/executor.h"
-#include "nodes/makefuncs.h"
-
-/*-------------------------------------------------------------------------
- * XXX this stuff should be rewritten to take advantage
- * of ExecProject() and the ProjectionInfo node.
- * -cim 6/3/91
- *
- * An attribute of a tuple living inside the executor, can be
- * either a normal attribute or a "junk" attribute. "junk" attributes
- * never make it out of the executor, i.e. they are never printed,
- * returned or stored in disk. Their only purpose in life is to
- * store some information useful only to the executor, mainly the values
- * of some system attributes like "ctid" or rule locks.
- *
- * The general idea is the following: A target list consists of a list of
- * Resdom nodes & expression pairs. Each Resdom node has an attribute
- * called 'resjunk'. If the value of this attribute is true then the
- * corresponding attribute is a "junk" attribute.
- *
- * When we initialize a plan we call 'ExecInitJunkFilter' to create
- * and store the appropriate information in the 'es_junkFilter' attribute of
- * EState.
- *
- * We then execute the plan ignoring the "resjunk" attributes.
- *
- * Finally, when at the top level we get back a tuple, we can call
- * 'ExecGetJunkAttribute' to retrieve the value of the junk attributes we
- * are interested in, and 'ExecRemoveJunk' to remove all the junk attributes
- * from a tuple. This new "clean" tuple is then printed, replaced, deleted
- * or inserted.
- *
- *-------------------------------------------------------------------------
- */
-
-/*-------------------------------------------------------------------------
- * ExecInitJunkFilter
- *
- * Initialize the Junk filter.
- *
- * The initial targetlist and associated tuple descriptor are passed in.
- * An optional resultSlot can be passed as well.
- *-------------------------------------------------------------------------
- */
-JunkFilter *
-ExecInitJunkFilter(List *targetList, TupleDesc tupType,
- TupleTableSlot *slot)
-{
- MemoryContext oldContext;
- MemoryContext junkContext;
- JunkFilter *junkfilter;
- List *cleanTargetList;
- int len,
- cleanLength;
- TupleDesc cleanTupType;
- List *t;
- TargetEntry *tle;
- Resdom *resdom,
- *cleanResdom;
- bool resjunk;
- AttrNumber cleanResno;
- AttrNumber *cleanMap;
- Node *expr;
-
- /*
- * Make a memory context that will hold the JunkFilter as well as all
- * the subsidiary structures we are about to create. We use smaller-
- * than-default sizing parameters since we don't expect a very large
- * volume of stuff here.
- */
- junkContext = AllocSetContextCreate(CurrentMemoryContext,
- "JunkFilterContext",
- 1024,
- 1024,
- ALLOCSET_DEFAULT_MAXSIZE);
- oldContext = MemoryContextSwitchTo(junkContext);
-
- /*
- * First find the "clean" target list, i.e. all the entries in the
- * original target list which have a false 'resjunk' NOTE: make copy
- * of the Resdom nodes, because we have to change the 'resno's...
- */
- cleanTargetList = NIL;
- cleanResno = 1;
-
- foreach(t, targetList)
- {
- TargetEntry *rtarget = lfirst(t);
-
- if (rtarget->resdom != NULL)
- {
- resdom = rtarget->resdom;
- expr = rtarget->expr;
- resjunk = resdom->resjunk;
- if (!resjunk)
- {
- /*
- * make a copy of the resdom node, changing its resno.
- */
- cleanResdom = (Resdom *) copyObject(resdom);
- cleanResdom->resno = cleanResno;
- cleanResno++;
-
- /*
- * create a new target list entry
- */
- tle = makeTargetEntry(cleanResdom, expr);
- cleanTargetList = lappend(cleanTargetList, tle);
- }
- }
- else
- {
-#ifdef SETS_FIXED
- List *fjListP;
- Fjoin *cleanFjoin;
- List *cleanFjList;
- List *fjList = lfirst(t);
- Fjoin *fjNode = (Fjoin *) tl_node(fjList);
-
- cleanFjoin = (Fjoin) copyObject((Node) fjNode);
- cleanFjList = makeList1(cleanFjoin);
-
- resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
- expr = lsecond(get_fj_innerNode(fjNode));
- cleanResdom = (Resdom) copyObject((Node) resdom);
- set_resno(cleanResdom, cleanResno);
- cleanResno++;
- tle = (List) makeTargetEntry(cleanResdom, (Node *) expr);
- set_fj_innerNode(cleanFjoin, tle);
-
- foreach(fjListP, lnext(fjList))
- {
- TargetEntry *tle = lfirst(fjListP);
-
- resdom = tle->resdom;
- expr = tle->expr;
- cleanResdom = (Resdom *) copyObject((Node) resdom);
- cleanResno++;
- cleanResdom->Resno = cleanResno;
-
- /*
- * create a new target list entry
- */
- tle = (List) makeTargetEntry(cleanResdom, (Node *) expr);
- cleanFjList = lappend(cleanFjList, tle);
- }
- lappend(cleanTargetList, cleanFjList);
-#endif
- }
- }
-
- /*
- * Now calculate the tuple type for the cleaned tuple (we were already
- * given the type for the original targetlist).
- */
- cleanTupType = ExecTypeFromTL(cleanTargetList);
-
- len = ExecTargetListLength(targetList);
- cleanLength = ExecTargetListLength(cleanTargetList);
-
- /*
- * Now calculate the "map" between the original tuple's attributes and
- * the "clean" tuple's attributes.
- *
- * The "map" is an array of "cleanLength" attribute numbers, i.e. one
- * entry for every attribute of the "clean" tuple. The value of this
- * entry is the attribute number of the corresponding attribute of the
- * "original" tuple.
- */
- if (cleanLength > 0)
- {
- cleanMap = (AttrNumber *) palloc(cleanLength * sizeof(AttrNumber));
- cleanResno = 1;
- foreach(t, targetList)
- {
- TargetEntry *tle = lfirst(t);
-
- if (tle->resdom != NULL)
- {
- resdom = tle->resdom;
- expr = tle->expr;
- resjunk = resdom->resjunk;
- if (!resjunk)
- {
- cleanMap[cleanResno - 1] = resdom->resno;
- cleanResno++;
- }
- }
- else
- {
-#ifdef SETS_FIXED
- List fjListP;
- List fjList = lfirst(t);
- Fjoin fjNode = (Fjoin) lfirst(fjList);
-
- /* what the hell is this????? */
- resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
-#endif
-
- cleanMap[cleanResno - 1] = tle->resdom->resno;
- cleanResno++;
-
-#ifdef SETS_FIXED
- foreach(fjListP, lnext(fjList))
- {
- TargetEntry *tle = lfirst(fjListP);
-
- resdom = tle->resdom;
- cleanMap[cleanResno - 1] = resdom->resno;
- cleanResno++;
- }
-#endif
- }
- }
- }
- else
- cleanMap = NULL;
-
- /*
- * Finally create and initialize the JunkFilter struct.
- */
- junkfilter = makeNode(JunkFilter);
-
- junkfilter->jf_targetList = targetList;
- junkfilter->jf_length = len;
- junkfilter->jf_tupType = tupType;
- junkfilter->jf_cleanTargetList = cleanTargetList;
- junkfilter->jf_cleanLength = cleanLength;
- junkfilter->jf_cleanTupType = cleanTupType;
- junkfilter->jf_cleanMap = cleanMap;
- junkfilter->jf_junkContext = junkContext;
- junkfilter->jf_resultSlot = slot;
-
- if (slot)
- ExecSetSlotDescriptor(slot, cleanTupType, false);
-
- MemoryContextSwitchTo(oldContext);
-
- return junkfilter;
-}
-
-/*-------------------------------------------------------------------------
- * ExecFreeJunkFilter
- *
- * Release the data structures created by ExecInitJunkFilter.
- *-------------------------------------------------------------------------
- */
-void
-ExecFreeJunkFilter(JunkFilter *junkfilter)
-{
- /*
- * Since the junkfilter is inside its own context, we just have to
- * delete the context and we're set.
- */
- MemoryContextDelete(junkfilter->jf_junkContext);
-}
-
-/*-------------------------------------------------------------------------
- * ExecGetJunkAttribute
- *
- * Given a tuple (slot), the junk filter and a junk attribute's name,
- * extract & return the value and isNull flag of this attribute.
- *
- * It returns false iff no junk attribute with such name was found.
- *-------------------------------------------------------------------------
- */
-bool
-ExecGetJunkAttribute(JunkFilter *junkfilter,
- TupleTableSlot *slot,
- char *attrName,
- Datum *value,
- bool *isNull)
-{
- List *targetList;
- List *t;
- Resdom *resdom;
- AttrNumber resno;
- char *resname;
- bool resjunk;
- TupleDesc tupType;
- HeapTuple tuple;
-
- /*
- * first look in the junkfilter's target list for an attribute with
- * the given name
- */
- resno = InvalidAttrNumber;
- targetList = junkfilter->jf_targetList;
-
- foreach(t, targetList)
- {
- TargetEntry *tle = lfirst(t);
-
- resdom = tle->resdom;
- resname = resdom->resname;
- resjunk = resdom->resjunk;
- if (resjunk && (strcmp(resname, attrName) == 0))
- {
- /* We found it ! */
- resno = resdom->resno;
- break;
- }
- }
-
- if (resno == InvalidAttrNumber)
- {
- /* Ooops! We couldn't find this attribute... */
- return false;
- }
-
- /*
- * Now extract the attribute value from the tuple.
- */
- tuple = slot->val;
- tupType = junkfilter->jf_tupType;
-
- *value = heap_getattr(tuple, resno, tupType, isNull);
-
- return true;
-}
-
-/*-------------------------------------------------------------------------
- * ExecRemoveJunk
- *
- * Construct and return a tuple with all the junk attributes removed.
- *
- * Note: for historical reasons, this does not store the constructed
- * tuple into the junkfilter's resultSlot. The caller should do that
- * if it wants to.
- *-------------------------------------------------------------------------
- */
-HeapTuple
-ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
-{
- HeapTuple tuple;
- HeapTuple cleanTuple;
- AttrNumber *cleanMap;
- TupleDesc cleanTupType;
- TupleDesc tupType;
- int cleanLength;
- bool isNull;
- int i;
- Datum *values;
- char *nulls;
- Datum values_array[64];
- char nulls_array[64];
-
- /*
- * get info from the slot and the junk filter
- */
- tuple = slot->val;
-
- tupType = junkfilter->jf_tupType;
- cleanTupType = junkfilter->jf_cleanTupType;
- cleanLength = junkfilter->jf_cleanLength;
- cleanMap = junkfilter->jf_cleanMap;
-
- /*
- * Handle the trivial case first.
- */
- if (cleanLength == 0)
- return (HeapTuple) NULL;
-
- /*
- * Create the arrays that will hold the attribute values and the null
- * information for the new "clean" tuple.
- *
- * Note: we use memory on the stack to optimize things when we are
- * dealing with a small number of tuples. for large tuples we just use
- * palloc.
- */
- if (cleanLength > 64)
- {
- values = (Datum *) palloc(cleanLength * sizeof(Datum));
- nulls = (char *) palloc(cleanLength * sizeof(char));
- }
- else
- {
- values = values_array;
- nulls = nulls_array;
- }
-
- /*
- * Exctract one by one all the values of the "clean" tuple.
- */
- for (i = 0; i < cleanLength; i++)
- {
- values[i] = heap_getattr(tuple, cleanMap[i], tupType, &isNull);
-
- if (isNull)
- nulls[i] = 'n';
- else
- nulls[i] = ' ';
- }
-
- /*
- * Now form the new tuple.
- */
- cleanTuple = heap_formtuple(cleanTupType,
- values,
- nulls);
-
- /*
- * We are done. Free any space allocated for 'values' and 'nulls' and
- * return the new tuple.
- */
- if (cleanLength > 64)
- {
- pfree(values);
- pfree(nulls);
- }
-
- return cleanTuple;
-}
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
deleted file mode 100644
index 27b187f2a5e..00000000000
--- a/src/backend/executor/execMain.c
+++ /dev/null
@@ -1,1941 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * execMain.c
- * top level executor interface routines
- *
- * INTERFACE ROUTINES
- * ExecutorStart()
- * ExecutorRun()
- * ExecutorEnd()
- *
- * The old ExecutorMain() has been replaced by ExecutorStart(),
- * ExecutorRun() and ExecutorEnd()
- *
- * These three procedures are the external interfaces to the executor.
- * In each case, the query descriptor and the execution state is required
- * as arguments
- *
- * ExecutorStart() must be called at the beginning of any execution of any
- * query plan and ExecutorEnd() should always be called at the end of
- * execution of a plan.
- *
- * ExecutorRun accepts direction and count arguments that specify whether
- * the plan is to be executed forwards, backwards, and for how many tuples.
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.165 2002/06/20 20:29:27 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "catalog/heap.h"
-#include "catalog/namespace.h"
-#include "commands/tablecmds.h"
-#include "commands/trigger.h"
-#include "executor/execdebug.h"
-#include "executor/execdefs.h"
-#include "miscadmin.h"
-#include "optimizer/var.h"
-#include "parser/parsetree.h"
-#include "utils/acl.h"
-#include "utils/lsyscache.h"
-
-
-/* decls for local routines only used within this module */
-static TupleDesc InitPlan(CmdType operation,
- Query *parseTree,
- Plan *plan,
- EState *estate);
-static void initResultRelInfo(ResultRelInfo *resultRelInfo,
- Index resultRelationIndex,
- List *rangeTable,
- CmdType operation);
-static void EndPlan(Plan *plan, EState *estate);
-static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
- CmdType operation,
- long numberTuples,
- ScanDirection direction,
- DestReceiver *destfunc);
-static void ExecRetrieve(TupleTableSlot *slot,
- DestReceiver *destfunc,
- EState *estate);
-static void ExecAppend(TupleTableSlot *slot, ItemPointer tupleid,
- EState *estate);
-static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
- EState *estate);
-static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid,
- EState *estate);
-static TupleTableSlot *EvalPlanQualNext(EState *estate);
-static void EndEvalPlanQual(EState *estate);
-static void ExecCheckQueryPerms(CmdType operation, Query *parseTree,
- Plan *plan);
-static void ExecCheckPlanPerms(Plan *plan, List *rangeTable,
- CmdType operation);
-static void ExecCheckRTPerms(List *rangeTable, CmdType operation);
-static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation);
-
-/* end of local decls */
-
-
-/* ----------------------------------------------------------------
- * ExecutorStart
- *
- * This routine must be called at the beginning of any execution of any
- * query plan
- *
- * returns a TupleDesc which describes the attributes of the tuples to
- * be returned by the query. (Same value is saved in queryDesc)
- *
- * 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
-ExecutorStart(QueryDesc *queryDesc, EState *estate)
-{
- TupleDesc result;
-
- /* sanity checks */
- Assert(queryDesc != NULL);
-
- if (queryDesc->plantree->nParamExec > 0)
- {
- estate->es_param_exec_vals = (ParamExecData *)
- palloc(queryDesc->plantree->nParamExec * sizeof(ParamExecData));
- MemSet(estate->es_param_exec_vals, 0,
- queryDesc->plantree->nParamExec * sizeof(ParamExecData));
- }
-
- /*
- * Make our own private copy of the current query snapshot data.
- *
- * This "freezes" our idea of which tuples are good and which are not
- * for the life of this query, even if it outlives the current command
- * and current snapshot.
- */
- estate->es_snapshot = CopyQuerySnapshot();
-
- /*
- * Initialize the plan
- */
- result = InitPlan(queryDesc->operation,
- queryDesc->parsetree,
- queryDesc->plantree,
- estate);
-
- queryDesc->tupDesc = result;
-
- return result;
-}
-
-/* ----------------------------------------------------------------
- * ExecutorRun
- *
- * This is the main routine of the executor module. It accepts
- * the query descriptor from the traffic cop and executes the
- * query plan.
- *
- * ExecutorStart must have been called already.
- *
- * If direction is NoMovementScanDirection then nothing is done
- * except to start up/shut down the destination. Otherwise,
- * we retrieve up to 'count' tuples in the specified direction.
- *
- * Note: count = 0 is interpreted as no portal limit, e.g. run to
- * completion.
- *
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecutorRun(QueryDesc *queryDesc, EState *estate,
- ScanDirection direction, long count)
-{
- CmdType operation;
- Plan *plan;
- CommandDest dest;
- DestReceiver *destfunc;
- TupleTableSlot *result;
-
- /*
- * sanity checks
- */
- Assert(queryDesc != NULL);
-
- /*
- * extract information from the query descriptor and the query
- * feature.
- */
- operation = queryDesc->operation;
- plan = queryDesc->plantree;
- dest = queryDesc->dest;
-
- /*
- * startup tuple receiver
- */
- estate->es_processed = 0;
- estate->es_lastoid = InvalidOid;
-
- destfunc = DestToFunction(dest);
- (*destfunc->setup) (destfunc, (int) operation,
- queryDesc->portalName, queryDesc->tupDesc);
-
- /*
- * run plan
- */
- if (direction == NoMovementScanDirection)
- result = NULL;
- else
- result = ExecutePlan(estate,
- plan,
- operation,
- count,
- direction,
- destfunc);
-
- /*
- * shutdown receiver
- */
- (*destfunc->cleanup) (destfunc);
-
- return result;
-}
-
-/* ----------------------------------------------------------------
- * ExecutorEnd
- *
- * This routine must be called at the end of execution of any
- * query plan
- * ----------------------------------------------------------------
- */
-void
-ExecutorEnd(QueryDesc *queryDesc, EState *estate)
-{
- /* sanity checks */
- Assert(queryDesc != NULL);
-
- EndPlan(queryDesc->plantree, estate);
-
- if (estate->es_snapshot != NULL)
- {
- if (estate->es_snapshot->xcnt > 0)
- pfree(estate->es_snapshot->xip);
- pfree(estate->es_snapshot);
- estate->es_snapshot = NULL;
- }
-
- if (estate->es_param_exec_vals != NULL)
- {
- pfree(estate->es_param_exec_vals);
- estate->es_param_exec_vals = NULL;
- }
-}
-
-
-/*
- * ExecCheckQueryPerms
- * Check access permissions for all relations referenced in a query.
- */
-static void
-ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
-{
- /*
- * Check RTEs in the query's primary rangetable.
- */
- ExecCheckRTPerms(parseTree->rtable, operation);
-
- /*
- * Search for subplans and APPEND nodes to check their rangetables.
- */
- ExecCheckPlanPerms(plan, parseTree->rtable, operation);
-}
-
-/*
- * ExecCheckPlanPerms
- * Recursively scan the plan tree to check access permissions in
- * subplans.
- */
-static void
-ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
-{
- List *subp;
-
- if (plan == NULL)
- return;
-
- /* Check subplans, which we assume are plain SELECT queries */
-
- foreach(subp, plan->initPlan)
- {
- SubPlan *subplan = (SubPlan *) lfirst(subp);
-
- ExecCheckRTPerms(subplan->rtable, CMD_SELECT);
- ExecCheckPlanPerms(subplan->plan, subplan->rtable, CMD_SELECT);
- }
- foreach(subp, plan->subPlan)
- {
- SubPlan *subplan = (SubPlan *) lfirst(subp);
-
- ExecCheckRTPerms(subplan->rtable, CMD_SELECT);
- ExecCheckPlanPerms(subplan->plan, subplan->rtable, CMD_SELECT);
- }
-
- /* Check lower plan nodes */
-
- ExecCheckPlanPerms(plan->lefttree, rangeTable, operation);
- ExecCheckPlanPerms(plan->righttree, rangeTable, operation);
-
- /* Do node-type-specific checks */
-
- switch (nodeTag(plan))
- {
- case T_SubqueryScan:
- {
- SubqueryScan *scan = (SubqueryScan *) plan;
- RangeTblEntry *rte;
-
- /* Recursively check the subquery */
- rte = rt_fetch(scan->scan.scanrelid, rangeTable);
- Assert(rte->rtekind == RTE_SUBQUERY);
- ExecCheckQueryPerms(operation, rte->subquery, scan->subplan);
- break;
- }
- case T_Append:
- {
- Append *app = (Append *) plan;
- List *appendplans;
-
- foreach(appendplans, app->appendplans)
- {
- ExecCheckPlanPerms((Plan *) lfirst(appendplans),
- rangeTable,
- operation);
- }
- break;
- }
-
- default:
- break;
- }
-}
-
-/*
- * ExecCheckRTPerms
- * Check access permissions for all relations listed in a range table.
- */
-static void
-ExecCheckRTPerms(List *rangeTable, CmdType operation)
-{
- List *lp;
-
- foreach(lp, rangeTable)
- {
- RangeTblEntry *rte = lfirst(lp);
-
- ExecCheckRTEPerms(rte, operation);
- }
-}
-
-/*
- * ExecCheckRTEPerms
- * Check access permissions for a single RTE.
- */
-static void
-ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
-{
- Oid relOid;
- Oid userid;
- AclResult aclcheck_result;
-
- /*
- * Only plain-relation RTEs need to be checked here. Subquery RTEs
- * will be checked when ExecCheckPlanPerms finds the SubqueryScan node,
- * and function RTEs are checked by init_fcache when the function is
- * prepared for execution. Join and special RTEs need no checks.
- */
- if (rte->rtekind != RTE_RELATION)
- return;
-
- relOid = rte->relid;
-
- /*
- * userid to check as: current user unless we have a setuid
- * indication.
- *
- * Note: GetUserId() is presently fast enough that there's no harm in
- * calling it separately for each RTE. If that stops being true, we
- * could call it once in ExecCheckQueryPerms and pass the userid down
- * from there. But for now, no need for the extra clutter.
- */
- userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
-
-#define CHECK(MODE) pg_class_aclcheck(relOid, userid, MODE)
-
- if (rte->checkForRead)
- {
- aclcheck_result = CHECK(ACL_SELECT);
- if (aclcheck_result != ACLCHECK_OK)
- aclcheck_error(aclcheck_result, get_rel_name(relOid));
- }
-
- if (rte->checkForWrite)
- {
- /*
- * Note: write access in a SELECT context means SELECT FOR UPDATE.
- * Right now we don't distinguish that from true update as far as
- * permissions checks are concerned.
- */
- switch (operation)
- {
- case CMD_INSERT:
- aclcheck_result = CHECK(ACL_INSERT);
- break;
- case CMD_SELECT:
- case CMD_UPDATE:
- aclcheck_result = CHECK(ACL_UPDATE);
- break;
- case CMD_DELETE:
- aclcheck_result = CHECK(ACL_DELETE);
- break;
- default:
- elog(ERROR, "ExecCheckRTEPerms: bogus operation %d",
- operation);
- aclcheck_result = ACLCHECK_OK; /* keep compiler quiet */
- break;
- }
- if (aclcheck_result != ACLCHECK_OK)
- aclcheck_error(aclcheck_result, get_rel_name(relOid));
- }
-}
-
-
-/* ===============================================================
- * ===============================================================
- static routines follow
- * ===============================================================
- * ===============================================================
- */
-
-typedef struct execRowMark
-{
- Relation relation;
- Index rti;
- char resname[32];
-} execRowMark;
-
-typedef struct evalPlanQual
-{
- Plan *plan;
- Index rti;
- EState estate;
- struct evalPlanQual *free;
-} evalPlanQual;
-
-/* ----------------------------------------------------------------
- * InitPlan
- *
- * Initializes the query plan: open files, allocate storage
- * and start up the rule manager
- * ----------------------------------------------------------------
- */
-static TupleDesc
-InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
-{
- List *rangeTable;
- Relation intoRelationDesc;
- TupleDesc tupType;
-
- /*
- * Do permissions checks.
- */
- ExecCheckQueryPerms(operation, parseTree, plan);
-
- /*
- * get information from query descriptor
- */
- rangeTable = parseTree->rtable;
-
- /*
- * initialize the node's execution state
- */
- estate->es_range_table = rangeTable;
-
- /*
- * if there is a result relation, initialize result relation stuff
- */
- if (parseTree->resultRelation != 0 && operation != CMD_SELECT)
- {
- List *resultRelations = parseTree->resultRelations;
- int numResultRelations;
- ResultRelInfo *resultRelInfos;
-
- if (resultRelations != NIL)
- {
- /*
- * Multiple result relations (due to inheritance)
- * parseTree->resultRelations identifies them all
- */
- ResultRelInfo *resultRelInfo;
-
- numResultRelations = length(resultRelations);
- resultRelInfos = (ResultRelInfo *)
- palloc(numResultRelations * sizeof(ResultRelInfo));
- resultRelInfo = resultRelInfos;
- while (resultRelations != NIL)
- {
- initResultRelInfo(resultRelInfo,
- lfirsti(resultRelations),
- rangeTable,
- operation);
- resultRelInfo++;
- resultRelations = lnext(resultRelations);
- }
- }
- else
- {
- /*
- * Single result relation identified by
- * parseTree->resultRelation
- */
- numResultRelations = 1;
- resultRelInfos = (ResultRelInfo *) palloc(sizeof(ResultRelInfo));
- initResultRelInfo(resultRelInfos,
- parseTree->resultRelation,
- rangeTable,
- operation);
- }
-
- estate->es_result_relations = resultRelInfos;
- estate->es_num_result_relations = numResultRelations;
- /* Initialize to first or only result rel */
- estate->es_result_relation_info = resultRelInfos;
- }
- else
- {
- /*
- * if no result relation, then set state appropriately
- */
- estate->es_result_relations = NULL;
- estate->es_num_result_relations = 0;
- estate->es_result_relation_info = NULL;
- }
-
- /*
- * Have to lock relations selected for update
- */
- estate->es_rowMark = NIL;
- if (parseTree->rowMarks != NIL)
- {
- List *l;
-
- foreach(l, parseTree->rowMarks)
- {
- Index rti = lfirsti(l);
- Oid relid = getrelid(rti, rangeTable);
- Relation relation;
- execRowMark *erm;
-
- relation = heap_open(relid, RowShareLock);
- erm = (execRowMark *) palloc(sizeof(execRowMark));
- erm->relation = relation;
- erm->rti = rti;
- sprintf(erm->resname, "ctid%u", rti);
- estate->es_rowMark = lappend(estate->es_rowMark, erm);
- }
- }
-
- /*
- * initialize the executor "tuple" table. We need slots for all the
- * plan nodes, plus possibly output slots for the junkfilter(s). At
- * this point we aren't sure if we need junkfilters, so just add slots
- * for them unconditionally.
- */
- {
- int nSlots = ExecCountSlotsNode(plan);
-
- if (parseTree->resultRelations != NIL)
- nSlots += length(parseTree->resultRelations);
- else
- nSlots += 1;
- estate->es_tupleTable = ExecCreateTupleTable(nSlots);
- }
-
- /* mark EvalPlanQual not active */
- estate->es_origPlan = plan;
- estate->es_evalPlanQual = NULL;
- estate->es_evTuple = NULL;
- estate->es_evTupleNull = NULL;
- estate->es_useEvalPlan = false;
-
- /*
- * initialize the private state information for all the nodes in the
- * query tree. This opens files, allocates storage and leaves us
- * ready to start processing tuples.
- */
- ExecInitNode(plan, estate, NULL);
-
- /*
- * Get the tuple descriptor describing the type of tuples to return.
- * (this is especially important if we are creating a relation with
- * "retrieve into")
- */
- tupType = ExecGetTupType(plan); /* tuple descriptor */
-
- /*
- * Initialize the junk filter if needed. SELECT and INSERT queries
- * need a filter if there are any junk attrs in the tlist. UPDATE and
- * DELETE always need one, since there's always a junk 'ctid'
- * attribute present --- no need to look first.
- */
- {
- bool junk_filter_needed = false;
- List *tlist;
-
- switch (operation)
- {
- case CMD_SELECT:
- case CMD_INSERT:
- foreach(tlist, plan->targetlist)
- {
- TargetEntry *tle = (TargetEntry *) lfirst(tlist);
-
- if (tle->resdom->resjunk)
- {
- junk_filter_needed = true;
- break;
- }
- }
- break;
- case CMD_UPDATE:
- case CMD_DELETE:
- junk_filter_needed = true;
- break;
- default:
- break;
- }
-
- if (junk_filter_needed)
- {
- /*
- * If there are multiple result relations, each one needs its
- * own junk filter. Note this is only possible for
- * UPDATE/DELETE, so we can't be fooled by some needing a
- * filter and some not.
- */
- if (parseTree->resultRelations != NIL)
- {
- List *subplans;
- ResultRelInfo *resultRelInfo;
-
- /* Top plan had better be an Append here. */
- Assert(IsA(plan, Append));
- Assert(((Append *) plan)->isTarget);
- subplans = ((Append *) plan)->appendplans;
- Assert(length(subplans) == estate->es_num_result_relations);
- resultRelInfo = estate->es_result_relations;
- while (subplans != NIL)
- {
- Plan *subplan = (Plan *) lfirst(subplans);
- JunkFilter *j;
-
- j = ExecInitJunkFilter(subplan->targetlist,
- ExecGetTupType(subplan),
- ExecAllocTableSlot(estate->es_tupleTable));
- resultRelInfo->ri_junkFilter = j;
- resultRelInfo++;
- subplans = lnext(subplans);
- }
-
- /*
- * Set active junkfilter too; at this point ExecInitAppend
- * has already selected an active result relation...
- */
- estate->es_junkFilter =
- estate->es_result_relation_info->ri_junkFilter;
- }
- else
- {
- /* Normal case with just one JunkFilter */
- JunkFilter *j;
-
- j = ExecInitJunkFilter(plan->targetlist,
- tupType,
- ExecAllocTableSlot(estate->es_tupleTable));
- estate->es_junkFilter = j;
- if (estate->es_result_relation_info)
- estate->es_result_relation_info->ri_junkFilter = j;
-
- /* For SELECT, want to return the cleaned tuple type */
- if (operation == CMD_SELECT)
- tupType = j->jf_cleanTupType;
- }
- }
- else
- estate->es_junkFilter = NULL;
- }
-
- /*
- * initialize the "into" relation
- */
- intoRelationDesc = (Relation) NULL;
-
- if (operation == CMD_SELECT)
- {
- if (!parseTree->isPortal)
- {
- /*
- * a select into table --- need to create the "into" table
- */
- if (parseTree->into != NULL)
- {
- char *intoName;
- Oid namespaceId;
- Oid intoRelationId;
- TupleDesc tupdesc;
-
- /*
- * find namespace to create in, check permissions
- */
- intoName = parseTree->into->relname;
- namespaceId = RangeVarGetCreationNamespace(parseTree->into);
-
- if (!isTempNamespace(namespaceId))
- {
- AclResult aclresult;
-
- aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
- ACL_CREATE);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult,
- get_namespace_name(namespaceId));
- }
-
- /*
- * have to copy tupType to get rid of constraints
- */
- tupdesc = CreateTupleDescCopy(tupType);
-
- intoRelationId =
- heap_create_with_catalog(intoName,
- namespaceId,
- tupdesc,
- RELKIND_RELATION,
- false,
- true,
- allowSystemTableMods);
-
- FreeTupleDesc(tupdesc);
-
- /*
- * Advance command counter so that the newly-created
- * relation's catalog tuples will be visible to heap_open.
- */
- CommandCounterIncrement();
-
- /*
- * If necessary, create a TOAST table for the into
- * relation. Note that AlterTableCreateToastTable ends
- * with CommandCounterIncrement(), so that the TOAST table
- * will be visible for insertion.
- */
- AlterTableCreateToastTable(intoRelationId, true);
-
- intoRelationDesc = heap_open(intoRelationId,
- AccessExclusiveLock);
- }
- }
- }
-
- estate->es_into_relation_descriptor = intoRelationDesc;
-
- return tupType;
-}
-
-/*
- * Initialize ResultRelInfo data for one result relation
- */
-static void
-initResultRelInfo(ResultRelInfo *resultRelInfo,
- Index resultRelationIndex,
- List *rangeTable,
- CmdType operation)
-{
- Oid resultRelationOid;
- Relation resultRelationDesc;
-
- resultRelationOid = getrelid(resultRelationIndex, rangeTable);
- resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock);
-
- switch (resultRelationDesc->rd_rel->relkind)
- {
- case RELKIND_SEQUENCE:
- elog(ERROR, "You can't change sequence relation %s",
- RelationGetRelationName(resultRelationDesc));
- break;
- case RELKIND_TOASTVALUE:
- elog(ERROR, "You can't change toast relation %s",
- RelationGetRelationName(resultRelationDesc));
- break;
- case RELKIND_VIEW:
- elog(ERROR, "You can't change view relation %s",
- RelationGetRelationName(resultRelationDesc));
- break;
- }
-
- MemSet(resultRelInfo, 0, sizeof(ResultRelInfo));
- resultRelInfo->type = T_ResultRelInfo;
- resultRelInfo->ri_RangeTableIndex = resultRelationIndex;
- resultRelInfo->ri_RelationDesc = resultRelationDesc;
- resultRelInfo->ri_NumIndices = 0;
- resultRelInfo->ri_IndexRelationDescs = NULL;
- resultRelInfo->ri_IndexRelationInfo = NULL;
- resultRelInfo->ri_TrigDesc = resultRelationDesc->trigdesc;
- resultRelInfo->ri_TrigFunctions = NULL;
- resultRelInfo->ri_ConstraintExprs = NULL;
- resultRelInfo->ri_junkFilter = NULL;
-
- /*
- * If there are indices on the result relation, open them and save
- * descriptors in the result relation info, so that we can add new
- * index entries for the tuples we add/update. We need not do this
- * for a DELETE, however, since deletion doesn't affect indexes.
- */
- if (resultRelationDesc->rd_rel->relhasindex &&
- operation != CMD_DELETE)
- ExecOpenIndices(resultRelInfo);
-}
-
-/* ----------------------------------------------------------------
- * EndPlan
- *
- * Cleans up the query plan -- closes files and free up storages
- * ----------------------------------------------------------------
- */
-static void
-EndPlan(Plan *plan, EState *estate)
-{
- ResultRelInfo *resultRelInfo;
- int i;
- List *l;
-
- /*
- * shut down any PlanQual processing we were doing
- */
- if (estate->es_evalPlanQual != NULL)
- EndEvalPlanQual(estate);
-
- /*
- * shut down the node-type-specific query processing
- */
- ExecEndNode(plan, NULL);
-
- /*
- * destroy the executor "tuple" table.
- */
- ExecDropTupleTable(estate->es_tupleTable, true);
- estate->es_tupleTable = NULL;
-
- /*
- * close the result relation(s) if any, but hold locks until xact
- * commit. Also clean up junkfilters if present.
- */
- resultRelInfo = estate->es_result_relations;
- for (i = estate->es_num_result_relations; i > 0; i--)
- {
- /* Close indices and then the relation itself */
- ExecCloseIndices(resultRelInfo);
- heap_close(resultRelInfo->ri_RelationDesc, NoLock);
- /* Delete the junkfilter if any */
- if (resultRelInfo->ri_junkFilter != NULL)
- ExecFreeJunkFilter(resultRelInfo->ri_junkFilter);
- resultRelInfo++;
- }
-
- /*
- * close the "into" relation if necessary, again keeping lock
- */
- if (estate->es_into_relation_descriptor != NULL)
- heap_close(estate->es_into_relation_descriptor, NoLock);
-
- /*
- * There might be a junkfilter without a result relation.
- */
- if (estate->es_num_result_relations == 0 &&
- estate->es_junkFilter != NULL)
- {
- ExecFreeJunkFilter(estate->es_junkFilter);
- estate->es_junkFilter = NULL;
- }
-
- /*
- * close any relations selected FOR UPDATE, again keeping locks
- */
- foreach(l, estate->es_rowMark)
- {
- execRowMark *erm = lfirst(l);
-
- heap_close(erm->relation, NoLock);
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecutePlan
- *
- * processes the query plan to retrieve 'numberTuples' tuples in the
- * direction specified.
- * Retrieves all tuples if numberTuples is 0
- *
- * result is either a slot containing the last tuple in the case
- * of a RETRIEVE or NULL otherwise.
- *
- * Note: the ctid attribute is a 'junk' attribute that is removed before the
- * user can see it
- * ----------------------------------------------------------------
- */
-static TupleTableSlot *
-ExecutePlan(EState *estate,
- Plan *plan,
- CmdType operation,
- long numberTuples,
- ScanDirection direction,
- DestReceiver *destfunc)
-{
- JunkFilter *junkfilter;
- TupleTableSlot *slot;
- ItemPointer tupleid = NULL;
- ItemPointerData tuple_ctid;
- long current_tuple_count;
- TupleTableSlot *result;
-
- /*
- * initialize local variables
- */
- slot = NULL;
- current_tuple_count = 0;
- result = NULL;
-
- /*
- * Set the direction.
- */
- estate->es_direction = direction;
-
- /*
- * Loop until we've processed the proper number of tuples from the
- * plan.
- */
-
- for (;;)
- {
- /* Reset the per-output-tuple exprcontext */
- ResetPerTupleExprContext(estate);
-
- /*
- * Execute the plan and obtain a tuple
- */
-lnext: ;
- if (estate->es_useEvalPlan)
- {
- slot = EvalPlanQualNext(estate);
- if (TupIsNull(slot))
- slot = ExecProcNode(plan, NULL);
- }
- else
- slot = ExecProcNode(plan, NULL);
-
- /*
- * if the tuple is null, then we assume there is nothing more to
- * process so we just return null...
- */
- if (TupIsNull(slot))
- {
- result = NULL;
- break;
- }
-
- /*
- * if we have a junk filter, then project a new tuple with the
- * junk removed.
- *
- * Store this new "clean" tuple in the junkfilter's resultSlot.
- * (Formerly, we stored it back over the "dirty" tuple, which is
- * WRONG because that tuple slot has the wrong descriptor.)
- *
- * Also, extract all the junk information we need.
- */
- if ((junkfilter = estate->es_junkFilter) != (JunkFilter *) NULL)
- {
- Datum datum;
- HeapTuple newTuple;
- bool isNull;
-
- /*
- * extract the 'ctid' junk attribute.
- */
- if (operation == CMD_UPDATE || operation == CMD_DELETE)
- {
- if (!ExecGetJunkAttribute(junkfilter,
- slot,
- "ctid",
- &datum,
- &isNull))
- elog(ERROR, "ExecutePlan: NO (junk) `ctid' was found!");
-
- /* shouldn't ever get a null result... */
- if (isNull)
- elog(ERROR, "ExecutePlan: (junk) `ctid' is NULL!");
-
- tupleid = (ItemPointer) DatumGetPointer(datum);
- tuple_ctid = *tupleid; /* make sure we don't free the
- * ctid!! */
- tupleid = &tuple_ctid;
- }
- else if (estate->es_rowMark != NIL)
- {
- List *l;
-
- lmark: ;
- foreach(l, estate->es_rowMark)
- {
- execRowMark *erm = lfirst(l);
- Buffer buffer;
- HeapTupleData tuple;
- TupleTableSlot *newSlot;
- int test;
-
- if (!ExecGetJunkAttribute(junkfilter,
- slot,
- erm->resname,
- &datum,
- &isNull))
- elog(ERROR, "ExecutePlan: NO (junk) `%s' was found!",
- erm->resname);
-
- /* shouldn't ever get a null result... */
- if (isNull)
- elog(ERROR, "ExecutePlan: (junk) `%s' is NULL!",
- erm->resname);
-
- tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
- test = heap_mark4update(erm->relation, &tuple, &buffer,
- estate->es_snapshot->curcid);
- ReleaseBuffer(buffer);
- switch (test)
- {
- case HeapTupleSelfUpdated:
- case HeapTupleMayBeUpdated:
- break;
-
- case HeapTupleUpdated:
- if (XactIsoLevel == XACT_SERIALIZABLE)
- elog(ERROR, "Can't serialize access due to concurrent update");
- if (!(ItemPointerEquals(&(tuple.t_self),
- (ItemPointer) DatumGetPointer(datum))))
- {
- newSlot = EvalPlanQual(estate, erm->rti, &(tuple.t_self));
- if (!(TupIsNull(newSlot)))
- {
- slot = newSlot;
- estate->es_useEvalPlan = true;
- goto lmark;
- }
- }
-
- /*
- * if tuple was deleted or PlanQual failed for
- * updated tuple - we must not return this
- * tuple!
- */
- goto lnext;
-
- default:
- elog(ERROR, "Unknown status %u from heap_mark4update", test);
- return (NULL);
- }
- }
- }
-
- /*
- * Finally create a new "clean" tuple with all junk attributes
- * removed
- */
- newTuple = ExecRemoveJunk(junkfilter, slot);
-
- slot = ExecStoreTuple(newTuple, /* tuple to store */
- junkfilter->jf_resultSlot, /* dest slot */
- InvalidBuffer, /* this tuple has no
- * buffer */
- true); /* tuple should be pfreed */
- } /* if (junkfilter... */
-
- /*
- * now that we have a tuple, do the appropriate thing with it..
- * either return it to the user, add it to a relation someplace,
- * delete it from a relation, or modify some of its attributes.
- */
-
- switch (operation)
- {
- case CMD_SELECT:
- ExecRetrieve(slot, /* slot containing tuple */
- destfunc, /* destination's tuple-receiver
- * obj */
- estate); /* */
- result = slot;
- break;
-
- case CMD_INSERT:
- ExecAppend(slot, tupleid, estate);
- result = NULL;
- break;
-
- case CMD_DELETE:
- ExecDelete(slot, tupleid, estate);
- result = NULL;
- break;
-
- case CMD_UPDATE:
- ExecReplace(slot, tupleid, estate);
- result = NULL;
- break;
-
- default:
- elog(LOG, "ExecutePlan: unknown operation in queryDesc");
- result = NULL;
- break;
- }
-
- /*
- * check our tuple count.. if we've processed the proper number
- * then quit, else loop again and process more tuples..
- */
- current_tuple_count++;
- if (numberTuples == current_tuple_count)
- break;
- }
-
- /*
- * here, result is either a slot containing a tuple in the case of a
- * RETRIEVE or NULL otherwise.
- */
- return result;
-}
-
-/* ----------------------------------------------------------------
- * ExecRetrieve
- *
- * RETRIEVEs are easy.. we just pass the tuple to the appropriate
- * print function. The only complexity is when we do a
- * "retrieve into", in which case we insert the tuple into
- * the appropriate relation (note: this is a newly created relation
- * so we don't need to worry about indices or locks.)
- * ----------------------------------------------------------------
- */
-static void
-ExecRetrieve(TupleTableSlot *slot,
- DestReceiver *destfunc,
- EState *estate)
-{
- HeapTuple tuple;
- TupleDesc attrtype;
-
- /*
- * get the heap tuple out of the tuple table slot
- */
- tuple = slot->val;
- attrtype = slot->ttc_tupleDescriptor;
-
- /*
- * insert the tuple into the "into relation"
- */
- if (estate->es_into_relation_descriptor != NULL)
- {
- heap_insert(estate->es_into_relation_descriptor, tuple,
- estate->es_snapshot->curcid);
- IncrAppended();
- }
-
- /*
- * send the tuple to the front end (or the screen)
- */
- (*destfunc->receiveTuple) (tuple, attrtype, destfunc);
- IncrRetrieved();
- (estate->es_processed)++;
-}
-
-/* ----------------------------------------------------------------
- * ExecAppend
- *
- * APPENDs are trickier.. we have to insert the tuple into
- * the base relation and insert appropriate tuples into the
- * index relations.
- * ----------------------------------------------------------------
- */
-
-static void
-ExecAppend(TupleTableSlot *slot,
- ItemPointer tupleid,
- EState *estate)
-{
- HeapTuple tuple;
- ResultRelInfo *resultRelInfo;
- Relation resultRelationDesc;
- int numIndices;
- Oid newId;
-
- /*
- * get the heap tuple out of the tuple table slot
- */
- tuple = slot->val;
-
- /*
- * get information on the (current) result relation
- */
- resultRelInfo = estate->es_result_relation_info;
- resultRelationDesc = resultRelInfo->ri_RelationDesc;
-
- /* BEFORE ROW INSERT Triggers */
- if (resultRelInfo->ri_TrigDesc &&
- resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
- {
- HeapTuple newtuple;
-
- newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple);
-
- if (newtuple == NULL) /* "do nothing" */
- return;
-
- if (newtuple != tuple) /* modified by Trigger(s) */
- {
- /*
- * Insert modified tuple into tuple table slot, replacing the
- * original. We assume that it was allocated in per-tuple
- * memory context, and therefore will go away by itself. The
- * tuple table slot should not try to clear it.
- */
- ExecStoreTuple(newtuple, slot, InvalidBuffer, false);
- tuple = newtuple;
- }
- }
-
- /*
- * Check the constraints of the tuple
- */
- if (resultRelationDesc->rd_att->constr)
- ExecConstraints("ExecAppend", resultRelInfo, slot, estate);
-
- /*
- * insert the tuple
- */
- newId = heap_insert(resultRelationDesc, tuple,
- estate->es_snapshot->curcid);
-
- IncrAppended();
- (estate->es_processed)++;
- estate->es_lastoid = newId;
- setLastTid(&(tuple->t_self));
-
- /*
- * process indices
- *
- * Note: heap_insert adds a new tuple to a relation. As a side effect,
- * the tupleid of the new tuple is placed in the new tuple's t_ctid
- * field.
- */
- numIndices = resultRelInfo->ri_NumIndices;
- if (numIndices > 0)
- ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
-
- /* AFTER ROW INSERT Triggers */
- if (resultRelInfo->ri_TrigDesc)
- ExecARInsertTriggers(estate, resultRelInfo, tuple);
-}
-
-/* ----------------------------------------------------------------
- * ExecDelete
- *
- * DELETE is like append, we delete the tuple and its
- * index tuples.
- * ----------------------------------------------------------------
- */
-static void
-ExecDelete(TupleTableSlot *slot,
- ItemPointer tupleid,
- EState *estate)
-{
- ResultRelInfo *resultRelInfo;
- Relation resultRelationDesc;
- ItemPointerData ctid;
- int result;
-
- /*
- * get information on the (current) result relation
- */
- resultRelInfo = estate->es_result_relation_info;
- resultRelationDesc = resultRelInfo->ri_RelationDesc;
-
- /* BEFORE ROW DELETE Triggers */
- if (resultRelInfo->ri_TrigDesc &&
- resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_DELETE] > 0)
- {
- bool dodelete;
-
- dodelete = ExecBRDeleteTriggers(estate, resultRelInfo, tupleid);
-
- if (!dodelete) /* "do nothing" */
- return;
- }
-
- /*
- * delete the tuple
- */
-ldelete:;
- result = heap_delete(resultRelationDesc, tupleid,
- &ctid,
- estate->es_snapshot->curcid);
- switch (result)
- {
- case HeapTupleSelfUpdated:
- return;
-
- case HeapTupleMayBeUpdated:
- break;
-
- case HeapTupleUpdated:
- if (XactIsoLevel == XACT_SERIALIZABLE)
- elog(ERROR, "Can't serialize access due to concurrent update");
- else if (!(ItemPointerEquals(tupleid, &ctid)))
- {
- TupleTableSlot *epqslot = EvalPlanQual(estate,
- resultRelInfo->ri_RangeTableIndex, &ctid);
-
- if (!TupIsNull(epqslot))
- {
- *tupleid = ctid;
- goto ldelete;
- }
- }
- /* tuple already deleted; nothing to do */
- return;
-
- default:
- elog(ERROR, "Unknown status %u from heap_delete", result);
- return;
- }
-
- IncrDeleted();
- (estate->es_processed)++;
-
- /*
- * Note: Normally one would think that we have to delete index tuples
- * associated with the heap tuple now..
- *
- * ... but in POSTGRES, we have no need to do this because the vacuum
- * daemon automatically opens an index scan and deletes index tuples
- * when it finds deleted heap tuples. -cim 9/27/89
- */
-
- /* AFTER ROW DELETE Triggers */
- if (resultRelInfo->ri_TrigDesc)
- ExecARDeleteTriggers(estate, resultRelInfo, tupleid);
-}
-
-/* ----------------------------------------------------------------
- * ExecReplace
- *
- * note: we can't run replace queries with transactions
- * off because replaces are actually appends and our
- * scan will mistakenly loop forever, replacing the tuple
- * it just appended.. This should be fixed but until it
- * is, we don't want to get stuck in an infinite loop
- * which corrupts your database..
- * ----------------------------------------------------------------
- */
-static void
-ExecReplace(TupleTableSlot *slot,
- ItemPointer tupleid,
- EState *estate)
-{
- HeapTuple tuple;
- ResultRelInfo *resultRelInfo;
- Relation resultRelationDesc;
- ItemPointerData ctid;
- int result;
- int numIndices;
-
- /*
- * abort the operation if not running transactions
- */
- if (IsBootstrapProcessingMode())
- {
- elog(WARNING, "ExecReplace: replace can't run without transactions");
- return;
- }
-
- /*
- * get the heap tuple out of the tuple table slot
- */
- tuple = slot->val;
-
- /*
- * get information on the (current) result relation
- */
- resultRelInfo = estate->es_result_relation_info;
- resultRelationDesc = resultRelInfo->ri_RelationDesc;
-
- /* BEFORE ROW UPDATE Triggers */
- if (resultRelInfo->ri_TrigDesc &&
- resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0)
- {
- HeapTuple newtuple;
-
- newtuple = ExecBRUpdateTriggers(estate, resultRelInfo,
- tupleid, tuple);
-
- if (newtuple == NULL) /* "do nothing" */
- return;
-
- if (newtuple != tuple) /* modified by Trigger(s) */
- {
- /*
- * Insert modified tuple into tuple table slot, replacing the
- * original. We assume that it was allocated in per-tuple
- * memory context, and therefore will go away by itself. The
- * tuple table slot should not try to clear it.
- */
- ExecStoreTuple(newtuple, slot, InvalidBuffer, false);
- tuple = newtuple;
- }
- }
-
- /*
- * Check the constraints of the tuple
- *
- * If we generate a new candidate tuple after EvalPlanQual testing, we
- * must loop back here and recheck constraints. (We don't need to
- * redo triggers, however. If there are any BEFORE triggers then
- * trigger.c will have done mark4update to lock the correct tuple, so
- * there's no need to do them again.)
- */
-lreplace:;
- if (resultRelationDesc->rd_att->constr)
- ExecConstraints("ExecReplace", resultRelInfo, slot, estate);
-
- /*
- * replace the heap tuple
- */
- result = heap_update(resultRelationDesc, tupleid, tuple,
- &ctid,
- estate->es_snapshot->curcid);
- switch (result)
- {
- case HeapTupleSelfUpdated:
- return;
-
- case HeapTupleMayBeUpdated:
- break;
-
- case HeapTupleUpdated:
- if (XactIsoLevel == XACT_SERIALIZABLE)
- elog(ERROR, "Can't serialize access due to concurrent update");
- else if (!(ItemPointerEquals(tupleid, &ctid)))
- {
- TupleTableSlot *epqslot = EvalPlanQual(estate,
- resultRelInfo->ri_RangeTableIndex, &ctid);
-
- if (!TupIsNull(epqslot))
- {
- *tupleid = ctid;
- tuple = ExecRemoveJunk(estate->es_junkFilter, epqslot);
- slot = ExecStoreTuple(tuple,
- estate->es_junkFilter->jf_resultSlot,
- InvalidBuffer, true);
- goto lreplace;
- }
- }
- /* tuple already deleted; nothing to do */
- return;
-
- default:
- elog(ERROR, "Unknown status %u from heap_update", result);
- return;
- }
-
- IncrReplaced();
- (estate->es_processed)++;
-
- /*
- * Note: instead of having to update the old index tuples associated
- * with the heap tuple, all we do is form and insert new index tuples.
- * This is because replaces are actually deletes and inserts and index
- * tuple deletion is done automagically by the vacuum daemon. All we
- * do is insert new index tuples. -cim 9/27/89
- */
-
- /*
- * process indices
- *
- * heap_update updates a tuple in the base relation by invalidating it
- * and then appending a new tuple to the relation. As a side effect,
- * the tupleid of the new tuple is placed in the new tuple's t_ctid
- * field. So we now insert index tuples using the new tupleid stored
- * there.
- */
-
- numIndices = resultRelInfo->ri_NumIndices;
- if (numIndices > 0)
- ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
-
- /* AFTER ROW UPDATE Triggers */
- if (resultRelInfo->ri_TrigDesc)
- ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple);
-}
-
-static char *
-ExecRelCheck(ResultRelInfo *resultRelInfo,
- TupleTableSlot *slot, EState *estate)
-{
- Relation rel = resultRelInfo->ri_RelationDesc;
- int ncheck = rel->rd_att->constr->num_check;
- ConstrCheck *check = rel->rd_att->constr->check;
- ExprContext *econtext;
- MemoryContext oldContext;
- List *qual;
- int i;
-
- /*
- * If first time through for this result relation, build expression
- * nodetrees for rel's constraint expressions. Keep them in the
- * per-query memory context so they'll survive throughout the query.
- */
- if (resultRelInfo->ri_ConstraintExprs == NULL)
- {
- oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
- resultRelInfo->ri_ConstraintExprs =
- (List **) palloc(ncheck * sizeof(List *));
- for (i = 0; i < ncheck; i++)
- {
- qual = (List *) stringToNode(check[i].ccbin);
- resultRelInfo->ri_ConstraintExprs[i] = qual;
- }
- MemoryContextSwitchTo(oldContext);
- }
-
- /*
- * We will use the EState's per-tuple context for evaluating
- * constraint expressions (creating it if it's not already there).
- */
- econtext = GetPerTupleExprContext(estate);
-
- /* Arrange for econtext's scan tuple to be the tuple under test */
- econtext->ecxt_scantuple = slot;
-
- /* And evaluate the constraints */
- for (i = 0; i < ncheck; i++)
- {
- qual = resultRelInfo->ri_ConstraintExprs[i];
-
- /*
- * NOTE: SQL92 specifies that a NULL result from a constraint
- * expression is not to be treated as a failure. Therefore, tell
- * ExecQual to return TRUE for NULL.
- */
- if (!ExecQual(qual, econtext, true))
- return check[i].ccname;
- }
-
- /* NULL result means no error */
- return (char *) NULL;
-}
-
-void
-ExecConstraints(char *caller, ResultRelInfo *resultRelInfo,
- TupleTableSlot *slot, EState *estate)
-{
- Relation rel = resultRelInfo->ri_RelationDesc;
- HeapTuple tuple = slot->val;
- TupleConstr *constr = rel->rd_att->constr;
-
- Assert(constr);
-
- if (constr->has_not_null)
- {
- int natts = rel->rd_att->natts;
- int attrChk;
-
- for (attrChk = 1; attrChk <= natts; attrChk++)
- {
- if (rel->rd_att->attrs[attrChk - 1]->attnotnull &&
- heap_attisnull(tuple, attrChk))
- elog(ERROR, "%s: Fail to add null value in not null attribute %s",
- caller, NameStr(rel->rd_att->attrs[attrChk - 1]->attname));
- }
- }
-
- if (constr->num_check > 0)
- {
- char *failed;
-
- if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL)
- elog(ERROR, "%s: rejected due to CHECK constraint %s",
- caller, failed);
- }
-}
-
-/*
- * Check a modified tuple to see if we want to process its updated version
- * under READ COMMITTED rules.
- *
- * See backend/executor/README for some info about how this works.
- */
-TupleTableSlot *
-EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
-{
- evalPlanQual *epq;
- EState *epqstate;
- Relation relation;
- HeapTupleData tuple;
- HeapTuple copyTuple = NULL;
- int rtsize;
- bool endNode;
-
- Assert(rti != 0);
-
- /*
- * find relation containing target tuple
- */
- if (estate->es_result_relation_info != NULL &&
- estate->es_result_relation_info->ri_RangeTableIndex == rti)
- relation = estate->es_result_relation_info->ri_RelationDesc;
- else
- {
- List *l;
-
- relation = NULL;
- foreach(l, estate->es_rowMark)
- {
- if (((execRowMark *) lfirst(l))->rti == rti)
- {
- relation = ((execRowMark *) lfirst(l))->relation;
- break;
- }
- }
- if (relation == NULL)
- elog(ERROR, "EvalPlanQual: can't find RTE %d", (int) rti);
- }
-
- /*
- * fetch tid tuple
- *
- * Loop here to deal with updated or busy tuples
- */
- tuple.t_self = *tid;
- for (;;)
- {
- Buffer buffer;
-
- if (heap_fetch(relation, SnapshotDirty, &tuple, &buffer, false, NULL))
- {
- TransactionId xwait = SnapshotDirty->xmax;
-
- if (TransactionIdIsValid(SnapshotDirty->xmin))
- elog(ERROR, "EvalPlanQual: t_xmin is uncommitted ?!");
-
- /*
- * If tuple is being updated by other transaction then we have
- * to wait for its commit/abort.
- */
- if (TransactionIdIsValid(xwait))
- {
- ReleaseBuffer(buffer);
- XactLockTableWait(xwait);
- continue;
- }
-
- /*
- * We got tuple - now copy it for use by recheck query.
- */
- copyTuple = heap_copytuple(&tuple);
- ReleaseBuffer(buffer);
- break;
- }
-
- /*
- * Oops! Invalid tuple. Have to check is it updated or deleted.
- * Note that it's possible to get invalid SnapshotDirty->tid if
- * tuple updated by this transaction. Have we to check this ?
- */
- if (ItemPointerIsValid(&(SnapshotDirty->tid)) &&
- !(ItemPointerEquals(&(tuple.t_self), &(SnapshotDirty->tid))))
- {
- /* updated, so look at the updated copy */
- tuple.t_self = SnapshotDirty->tid;
- continue;
- }
-
- /*
- * Deleted or updated by this transaction; forget it.
- */
- return NULL;
- }
-
- /*
- * For UPDATE/DELETE we have to return tid of actual row we're
- * executing PQ for.
- */
- *tid = tuple.t_self;
-
- /*
- * Need to run a recheck subquery. Find or create a PQ stack entry.
- */
- epq = (evalPlanQual *) estate->es_evalPlanQual;
- rtsize = length(estate->es_range_table);
- endNode = true;
-
- if (epq != NULL && epq->rti == 0)
- {
- /* Top PQ stack entry is idle, so re-use it */
- Assert(!(estate->es_useEvalPlan) &&
- epq->estate.es_evalPlanQual == NULL);
- epq->rti = rti;
- endNode = false;
- }
-
- /*
- * If this is request for another RTE - Ra, - then we have to check
- * wasn't PlanQual requested for Ra already and if so then Ra' row was
- * updated again and we have to re-start old execution for Ra and
- * forget all what we done after Ra was suspended. Cool? -:))
- */
- if (epq != NULL && epq->rti != rti &&
- epq->estate.es_evTuple[rti - 1] != NULL)
- {
- do
- {
- evalPlanQual *oldepq;
-
- /* pop previous PlanQual from the stack */
- epqstate = &(epq->estate);
- oldepq = (evalPlanQual *) epqstate->es_evalPlanQual;
- Assert(oldepq->rti != 0);
- /* stop execution */
- ExecEndNode(epq->plan, NULL);
- ExecDropTupleTable(epqstate->es_tupleTable, true);
- epqstate->es_tupleTable = NULL;
- heap_freetuple(epqstate->es_evTuple[epq->rti - 1]);
- epqstate->es_evTuple[epq->rti - 1] = NULL;
- /* push current PQ to freePQ stack */
- oldepq->free = epq;
- epq = oldepq;
- estate->es_evalPlanQual = (Pointer) epq;
- } while (epq->rti != rti);
- }
-
- /*
- * If we are requested for another RTE then we have to suspend
- * execution of current PlanQual and start execution for new one.
- */
- if (epq == NULL || epq->rti != rti)
- {
- /* try to reuse plan used previously */
- evalPlanQual *newepq = (epq != NULL) ? epq->free : NULL;
-
- if (newepq == NULL) /* first call or freePQ stack is empty */
- {
- newepq = (evalPlanQual *) palloc(sizeof(evalPlanQual));
- newepq->free = NULL;
-
- /*
- * Each stack level has its own copy of the plan tree. This
- * is wasteful, but necessary as long as plan nodes point to
- * exec state nodes rather than vice versa. Note that
- * copyfuncs.c doesn't attempt to copy the exec state nodes,
- * which is a good thing in this situation.
- */
- newepq->plan = copyObject(estate->es_origPlan);
-
- /*
- * Init stack level's EState. We share top level's copy of
- * es_result_relations array and other non-changing status. We
- * need our own tupletable, es_param_exec_vals, and other
- * changeable state.
- */
- epqstate = &(newepq->estate);
- memcpy(epqstate, estate, sizeof(EState));
- epqstate->es_direction = ForwardScanDirection;
- if (estate->es_origPlan->nParamExec > 0)
- epqstate->es_param_exec_vals = (ParamExecData *)
- palloc(estate->es_origPlan->nParamExec *
- sizeof(ParamExecData));
- epqstate->es_tupleTable = NULL;
- epqstate->es_per_tuple_exprcontext = NULL;
-
- /*
- * Each epqstate must have its own es_evTupleNull state, but
- * all the stack entries share es_evTuple state. This allows
- * sub-rechecks to inherit the value being examined by an
- * outer recheck.
- */
- epqstate->es_evTupleNull = (bool *) palloc(rtsize * sizeof(bool));
- if (epq == NULL)
- {
- /* first PQ stack entry */
- epqstate->es_evTuple = (HeapTuple *)
- palloc(rtsize * sizeof(HeapTuple));
- memset(epqstate->es_evTuple, 0, rtsize * sizeof(HeapTuple));
- }
- else
- {
- /* later stack entries share the same storage */
- epqstate->es_evTuple = epq->estate.es_evTuple;
- }
- }
- else
- {
- /* recycle previously used EState */
- epqstate = &(newepq->estate);
- }
- /* push current PQ to the stack */
- epqstate->es_evalPlanQual = (Pointer) epq;
- epq = newepq;
- estate->es_evalPlanQual = (Pointer) epq;
- epq->rti = rti;
- endNode = false;
- }
-
- Assert(epq->rti == rti);
- epqstate = &(epq->estate);
-
- /*
- * Ok - we're requested for the same RTE. Unfortunately we still have
- * to end and restart execution of the plan, because ExecReScan
- * wouldn't ensure that upper plan nodes would reset themselves. We
- * could make that work if insertion of the target tuple were
- * integrated with the Param mechanism somehow, so that the upper plan
- * nodes know that their children's outputs have changed.
- */
- if (endNode)
- {
- /* stop execution */
- ExecEndNode(epq->plan, NULL);
- ExecDropTupleTable(epqstate->es_tupleTable, true);
- epqstate->es_tupleTable = NULL;
- }
-
- /*
- * free old RTE' tuple, if any, and store target tuple where
- * relation's scan node will see it
- */
- if (epqstate->es_evTuple[rti - 1] != NULL)
- heap_freetuple(epqstate->es_evTuple[rti - 1]);
- epqstate->es_evTuple[rti - 1] = copyTuple;
-
- /*
- * Initialize for new recheck query; be careful to copy down state
- * that might have changed in top EState.
- */
- epqstate->es_result_relation_info = estate->es_result_relation_info;
- epqstate->es_junkFilter = estate->es_junkFilter;
- if (estate->es_origPlan->nParamExec > 0)
- memset(epqstate->es_param_exec_vals, 0,
- estate->es_origPlan->nParamExec * sizeof(ParamExecData));
- memset(epqstate->es_evTupleNull, false, rtsize * sizeof(bool));
- epqstate->es_useEvalPlan = false;
- Assert(epqstate->es_tupleTable == NULL);
- epqstate->es_tupleTable =
- ExecCreateTupleTable(estate->es_tupleTable->size);
-
- ExecInitNode(epq->plan, epqstate, NULL);
-
- return EvalPlanQualNext(estate);
-}
-
-static TupleTableSlot *
-EvalPlanQualNext(EState *estate)
-{
- evalPlanQual *epq = (evalPlanQual *) estate->es_evalPlanQual;
- EState *epqstate = &(epq->estate);
- evalPlanQual *oldepq;
- TupleTableSlot *slot;
-
- Assert(epq->rti != 0);
-
-lpqnext:;
- slot = ExecProcNode(epq->plan, NULL);
-
- /*
- * No more tuples for this PQ. Continue previous one.
- */
- if (TupIsNull(slot))
- {
- /* stop execution */
- ExecEndNode(epq->plan, NULL);
- ExecDropTupleTable(epqstate->es_tupleTable, true);
- epqstate->es_tupleTable = NULL;
- heap_freetuple(epqstate->es_evTuple[epq->rti - 1]);
- epqstate->es_evTuple[epq->rti - 1] = NULL;
- /* pop old PQ from the stack */
- oldepq = (evalPlanQual *) epqstate->es_evalPlanQual;
- if (oldepq == (evalPlanQual *) NULL)
- {
- epq->rti = 0; /* this is the first (oldest) */
- estate->es_useEvalPlan = false; /* PQ - mark as free and */
- return (NULL); /* continue Query execution */
- }
- Assert(oldepq->rti != 0);
- /* push current PQ to freePQ stack */
- oldepq->free = epq;
- epq = oldepq;
- epqstate = &(epq->estate);
- estate->es_evalPlanQual = (Pointer) epq;
- goto lpqnext;
- }
-
- return (slot);
-}
-
-static void
-EndEvalPlanQual(EState *estate)
-{
- evalPlanQual *epq = (evalPlanQual *) estate->es_evalPlanQual;
- EState *epqstate = &(epq->estate);
- evalPlanQual *oldepq;
-
- if (epq->rti == 0) /* plans already shutdowned */
- {
- Assert(epq->estate.es_evalPlanQual == NULL);
- return;
- }
-
- for (;;)
- {
- /* stop execution */
- ExecEndNode(epq->plan, NULL);
- ExecDropTupleTable(epqstate->es_tupleTable, true);
- epqstate->es_tupleTable = NULL;
- if (epqstate->es_evTuple[epq->rti - 1] != NULL)
- {
- heap_freetuple(epqstate->es_evTuple[epq->rti - 1]);
- epqstate->es_evTuple[epq->rti - 1] = NULL;
- }
- /* pop old PQ from the stack */
- oldepq = (evalPlanQual *) epqstate->es_evalPlanQual;
- if (oldepq == (evalPlanQual *) NULL)
- {
- epq->rti = 0; /* this is the first (oldest) */
- estate->es_useEvalPlan = false; /* PQ - mark as free */
- break;
- }
- Assert(oldepq->rti != 0);
- /* push current PQ to freePQ stack */
- oldepq->free = epq;
- epq = oldepq;
- epqstate = &(epq->estate);
- estate->es_evalPlanQual = (Pointer) epq;
- }
-}
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
deleted file mode 100644
index 2054a6cf059..00000000000
--- a/src/backend/executor/execProcnode.c
+++ /dev/null
@@ -1,759 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * execProcnode.c
- * contains dispatch functions which call the appropriate "initialize",
- * "get a tuple", and "cleanup" routines for the given node type.
- * If the node has children, then it will presumably call ExecInitNode,
- * ExecProcNode, or ExecEndNode on its subnodes and do the appropriate
- * processing..
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.30 2002/06/20 20:29:27 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecInitNode - initialize a plan node and its subplans
- * ExecProcNode - get a tuple by executing the plan node
- * ExecEndNode - shut down a plan node and its subplans
- * ExecCountSlotsNode - count tuple slots needed by plan tree
- * ExecGetTupType - get result tuple type of a plan node
- *
- * NOTES
- * This used to be three files. It is now all combined into
- * one file so that it is easier to keep ExecInitNode, ExecProcNode,
- * and ExecEndNode in sync when new nodes are added.
- *
- * EXAMPLE
- * suppose we want the age of the manager of the shoe department and
- * the number of employees in that department. so we have the query:
- *
- * retrieve (DEPT.no_emps, EMP.age)
- * where EMP.name = DEPT.mgr and
- * DEPT.name = "shoe"
- *
- * Suppose the planner gives us the following plan:
- *
- * Nest Loop (DEPT.mgr = EMP.name)
- * / \
- * / \
- * Seq Scan Seq Scan
- * DEPT EMP
- * (name = "shoe")
- *
- * ExecStart() is called first.
- * It calls InitPlan() which calls ExecInitNode() on
- * the root of the plan -- the nest loop node.
- *
- * * ExecInitNode() notices that it is looking at a nest loop and
- * as the code below demonstrates, it calls ExecInitNestLoop().
- * Eventually this calls ExecInitNode() on the right and left subplans
- * and so forth until the entire plan is initialized.
- *
- * * Then when ExecRun() is called, it calls ExecutePlan() which
- * calls ExecProcNode() repeatedly on the top node of the plan.
- * Each time this happens, ExecProcNode() will end up calling
- * ExecNestLoop(), which calls ExecProcNode() on its subplans.
- * Each of these subplans is a sequential scan so ExecSeqScan() is
- * called. The slots returned by ExecSeqScan() may contain
- * tuples which contain the attributes ExecNestLoop() uses to
- * form the tuples it returns.
- *
- * * Eventually ExecSeqScan() stops returning tuples and the nest
- * loop join ends. Lastly, ExecEnd() calls ExecEndNode() which
- * calls ExecEndNestLoop() which in turn calls ExecEndNode() on
- * its subplans which result in ExecEndSeqScan().
- *
- * This should show how the executor works by having
- * ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch
- * their work to the appopriate node support routines which may
- * in turn call these routines themselves on their subplans.
- *
- */
-#include "postgres.h"
-
-#include "executor/executor.h"
-#include "executor/instrument.h"
-#include "executor/nodeAgg.h"
-#include "executor/nodeAppend.h"
-#include "executor/nodeGroup.h"
-#include "executor/nodeHash.h"
-#include "executor/nodeHashjoin.h"
-#include "executor/nodeIndexscan.h"
-#include "executor/nodeTidscan.h"
-#include "executor/nodeLimit.h"
-#include "executor/nodeMaterial.h"
-#include "executor/nodeMergejoin.h"
-#include "executor/nodeNestloop.h"
-#include "executor/nodeResult.h"
-#include "executor/nodeSeqscan.h"
-#include "executor/nodeSetOp.h"
-#include "executor/nodeSort.h"
-#include "executor/nodeSubplan.h"
-#include "executor/nodeSubqueryscan.h"
-#include "executor/nodeFunctionscan.h"
-#include "executor/nodeUnique.h"
-#include "miscadmin.h"
-#include "tcop/tcopprot.h"
-
-/* ------------------------------------------------------------------------
- * ExecInitNode
- *
- * Recursively initializes all the nodes in the plan rooted
- * at 'node'.
- *
- * Initial States:
- * 'node' is the plan produced by the query planner
- *
- * returns TRUE/FALSE on whether the plan was successfully initialized
- * ------------------------------------------------------------------------
- */
-bool
-ExecInitNode(Plan *node, EState *estate, Plan *parent)
-{
- bool result;
- List *subp;
-
- /*
- * do nothing when we get to the end of a leaf on tree.
- */
- if (node == NULL)
- return FALSE;
-
- /* Set up instrumentation for this node if the parent has it */
- if (!node->instrument && parent && parent->instrument)
- node->instrument = InstrAlloc();
-
- foreach(subp, node->initPlan)
- {
- result = ExecInitSubPlan((SubPlan *) lfirst(subp), estate, node);
- if (result == FALSE)
- return FALSE;
- }
-
- switch (nodeTag(node))
- {
- /*
- * control nodes
- */
- case T_Result:
- result = ExecInitResult((Result *) node, estate, parent);
- break;
-
- case T_Append:
- result = ExecInitAppend((Append *) node, estate, parent);
- break;
-
- /*
- * scan nodes
- */
- case T_SeqScan:
- result = ExecInitSeqScan((SeqScan *) node, estate, parent);
- break;
-
- case T_IndexScan:
- result = ExecInitIndexScan((IndexScan *) node, estate, parent);
- break;
-
- case T_TidScan:
- result = ExecInitTidScan((TidScan *) node, estate, parent);
- break;
-
- case T_SubqueryScan:
- result = ExecInitSubqueryScan((SubqueryScan *) node, estate,
- parent);
- break;
-
- case T_FunctionScan:
- result = ExecInitFunctionScan((FunctionScan *) node, estate,
- parent);
- break;
-
- /*
- * join nodes
- */
- case T_NestLoop:
- result = ExecInitNestLoop((NestLoop *) node, estate, parent);
- break;
-
- case T_MergeJoin:
- result = ExecInitMergeJoin((MergeJoin *) node, estate, parent);
- break;
-
- case T_Hash:
- result = ExecInitHash((Hash *) node, estate, parent);
- break;
-
- case T_HashJoin:
- result = ExecInitHashJoin((HashJoin *) node, estate, parent);
- break;
-
- /*
- * materialization nodes
- */
- case T_Material:
- result = ExecInitMaterial((Material *) node, estate, parent);
- break;
-
- case T_Sort:
- result = ExecInitSort((Sort *) node, estate, parent);
- break;
-
- case T_Unique:
- result = ExecInitUnique((Unique *) node, estate, parent);
- break;
-
- case T_SetOp:
- result = ExecInitSetOp((SetOp *) node, estate, parent);
- break;
-
- case T_Limit:
- result = ExecInitLimit((Limit *) node, estate, parent);
- break;
-
- case T_Group:
- result = ExecInitGroup((Group *) node, estate, parent);
- break;
-
- case T_Agg:
- result = ExecInitAgg((Agg *) node, estate, parent);
- break;
-
- default:
- elog(ERROR, "ExecInitNode: node type %d unsupported",
- (int) nodeTag(node));
- result = FALSE;
- break;
- }
-
- if (result != FALSE)
- {
- foreach(subp, node->subPlan)
- {
- result = ExecInitSubPlan((SubPlan *) lfirst(subp), estate, node);
- if (result == FALSE)
- return FALSE;
- }
- }
-
- return result;
-}
-
-
-/* ----------------------------------------------------------------
- * ExecProcNode
- *
- * Initial States:
- * the query tree must be initialized once by calling ExecInit.
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecProcNode(Plan *node, Plan *parent)
-{
- TupleTableSlot *result;
-
- CHECK_FOR_INTERRUPTS();
-
- /*
- * deal with NULL nodes..
- */
- if (node == NULL)
- return NULL;
-
- if (node->chgParam != NULL) /* something changed */
- ExecReScan(node, NULL, parent); /* let ReScan handle this */
-
- if (node->instrument)
- InstrStartNode(node->instrument);
-
- switch (nodeTag(node))
- {
- /*
- * control nodes
- */
- case T_Result:
- result = ExecResult((Result *) node);
- break;
-
- case T_Append:
- result = ExecProcAppend((Append *) node);
- break;
-
- /*
- * scan nodes
- */
- case T_SeqScan:
- result = ExecSeqScan((SeqScan *) node);
- break;
-
- case T_IndexScan:
- result = ExecIndexScan((IndexScan *) node);
- break;
-
- case T_TidScan:
- result = ExecTidScan((TidScan *) node);
- break;
-
- case T_SubqueryScan:
- result = ExecSubqueryScan((SubqueryScan *) node);
- break;
-
- case T_FunctionScan:
- result = ExecFunctionScan((FunctionScan *) node);
- break;
-
- /*
- * join nodes
- */
- case T_NestLoop:
- result = ExecNestLoop((NestLoop *) node);
- break;
-
- case T_MergeJoin:
- result = ExecMergeJoin((MergeJoin *) node);
- break;
-
- case T_Hash:
- result = ExecHash((Hash *) node);
- break;
-
- case T_HashJoin:
- result = ExecHashJoin((HashJoin *) node);
- break;
-
- /*
- * materialization nodes
- */
- case T_Material:
- result = ExecMaterial((Material *) node);
- break;
-
- case T_Sort:
- result = ExecSort((Sort *) node);
- break;
-
- case T_Unique:
- result = ExecUnique((Unique *) node);
- break;
-
- case T_SetOp:
- result = ExecSetOp((SetOp *) node);
- break;
-
- case T_Limit:
- result = ExecLimit((Limit *) node);
- break;
-
- case T_Group:
- result = ExecGroup((Group *) node);
- break;
-
- case T_Agg:
- result = ExecAgg((Agg *) node);
- break;
-
- default:
- elog(ERROR, "ExecProcNode: node type %d unsupported",
- (int) nodeTag(node));
- result = NULL;
- break;
- }
-
- if (node->instrument)
- InstrStopNode(node->instrument, !TupIsNull(result));
-
- return result;
-}
-
-int
-ExecCountSlotsNode(Plan *node)
-{
- if (node == (Plan *) NULL)
- return 0;
-
- switch (nodeTag(node))
- {
- /*
- * control nodes
- */
- case T_Result:
- return ExecCountSlotsResult((Result *) node);
-
- case T_Append:
- return ExecCountSlotsAppend((Append *) node);
-
- /*
- * scan nodes
- */
- case T_SeqScan:
- return ExecCountSlotsSeqScan((SeqScan *) node);
-
- case T_IndexScan:
- return ExecCountSlotsIndexScan((IndexScan *) node);
-
- case T_TidScan:
- return ExecCountSlotsTidScan((TidScan *) node);
-
- case T_SubqueryScan:
- return ExecCountSlotsSubqueryScan((SubqueryScan *) node);
-
- case T_FunctionScan:
- return ExecCountSlotsFunctionScan((FunctionScan *) node);
-
- /*
- * join nodes
- */
- case T_NestLoop:
- return ExecCountSlotsNestLoop((NestLoop *) node);
-
- case T_MergeJoin:
- return ExecCountSlotsMergeJoin((MergeJoin *) node);
-
- case T_Hash:
- return ExecCountSlotsHash((Hash *) node);
-
- case T_HashJoin:
- return ExecCountSlotsHashJoin((HashJoin *) node);
-
- /*
- * materialization nodes
- */
- case T_Material:
- return ExecCountSlotsMaterial((Material *) node);
-
- case T_Sort:
- return ExecCountSlotsSort((Sort *) node);
-
- case T_Unique:
- return ExecCountSlotsUnique((Unique *) node);
-
- case T_SetOp:
- return ExecCountSlotsSetOp((SetOp *) node);
-
- case T_Limit:
- return ExecCountSlotsLimit((Limit *) node);
-
- case T_Group:
- return ExecCountSlotsGroup((Group *) node);
-
- case T_Agg:
- return ExecCountSlotsAgg((Agg *) node);
-
- default:
- elog(ERROR, "ExecCountSlotsNode: node type %d unsupported",
- (int) nodeTag(node));
- break;
- }
- return 0;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndNode
- *
- * Recursively cleans up all the nodes in the plan rooted
- * at 'node'.
- *
- * After this operation, the query plan will not be able to
- * processed any further. This should be called only after
- * the query plan has been fully executed.
- * ----------------------------------------------------------------
- */
-void
-ExecEndNode(Plan *node, Plan *parent)
-{
- List *subp;
-
- /*
- * do nothing when we get to the end of a leaf on tree.
- */
- if (node == NULL)
- return;
-
- foreach(subp, node->initPlan)
- ExecEndSubPlan((SubPlan *) lfirst(subp));
- foreach(subp, node->subPlan)
- ExecEndSubPlan((SubPlan *) lfirst(subp));
- if (node->chgParam != NULL)
- {
- freeList(node->chgParam);
- node->chgParam = NULL;
- }
-
- switch (nodeTag(node))
- {
- /*
- * control nodes
- */
- case T_Result:
- ExecEndResult((Result *) node);
- break;
-
- case T_Append:
- ExecEndAppend((Append *) node);
- break;
-
- /*
- * scan nodes
- */
- case T_SeqScan:
- ExecEndSeqScan((SeqScan *) node);
- break;
-
- case T_IndexScan:
- ExecEndIndexScan((IndexScan *) node);
- break;
-
- case T_TidScan:
- ExecEndTidScan((TidScan *) node);
- break;
-
- case T_SubqueryScan:
- ExecEndSubqueryScan((SubqueryScan *) node);
- break;
-
- case T_FunctionScan:
- ExecEndFunctionScan((FunctionScan *) node);
- break;
-
- /*
- * join nodes
- */
- case T_NestLoop:
- ExecEndNestLoop((NestLoop *) node);
- break;
-
- case T_MergeJoin:
- ExecEndMergeJoin((MergeJoin *) node);
- break;
-
- case T_Hash:
- ExecEndHash((Hash *) node);
- break;
-
- case T_HashJoin:
- ExecEndHashJoin((HashJoin *) node);
- break;
-
- /*
- * materialization nodes
- */
- case T_Material:
- ExecEndMaterial((Material *) node);
- break;
-
- case T_Sort:
- ExecEndSort((Sort *) node);
- break;
-
- case T_Unique:
- ExecEndUnique((Unique *) node);
- break;
-
- case T_SetOp:
- ExecEndSetOp((SetOp *) node);
- break;
-
- case T_Limit:
- ExecEndLimit((Limit *) node);
- break;
-
- case T_Group:
- ExecEndGroup((Group *) node);
- break;
-
- case T_Agg:
- ExecEndAgg((Agg *) node);
- break;
-
- default:
- elog(ERROR, "ExecEndNode: node type %d unsupported",
- (int) nodeTag(node));
- break;
- }
-
- if (node->instrument)
- InstrEndLoop(node->instrument);
-}
-
-
-/* ----------------------------------------------------------------
- * ExecGetTupType
- *
- * this gives you the tuple descriptor for tuples returned
- * by this node. I really wish I could ditch this routine,
- * but since not all nodes store their type info in the same
- * place, we have to do something special for each node type.
- *
- * ----------------------------------------------------------------
- */
-TupleDesc
-ExecGetTupType(Plan *node)
-{
- TupleTableSlot *slot;
-
- if (node == NULL)
- return NULL;
-
- switch (nodeTag(node))
- {
- case T_Result:
- {
- ResultState *resstate = ((Result *) node)->resstate;
-
- slot = resstate->cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_SeqScan:
- {
- CommonScanState *scanstate = ((SeqScan *) node)->scanstate;
-
- slot = scanstate->cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_NestLoop:
- {
- NestLoopState *nlstate = ((NestLoop *) node)->nlstate;
-
- slot = nlstate->jstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_Append:
- {
- AppendState *appendstate = ((Append *) node)->appendstate;
-
- slot = appendstate->cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_IndexScan:
- {
- CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate;
-
- slot = scanstate->cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_TidScan:
- {
- CommonScanState *scanstate = ((TidScan *) node)->scan.scanstate;
-
- slot = scanstate->cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_SubqueryScan:
- {
- CommonScanState *scanstate = ((SubqueryScan *) node)->scan.scanstate;
-
- slot = scanstate->cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_FunctionScan:
- {
- CommonScanState *scanstate = ((FunctionScan *) node)->scan.scanstate;
-
- slot = scanstate->cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_Material:
- {
- MaterialState *matstate = ((Material *) node)->matstate;
-
- slot = matstate->csstate.css_ScanTupleSlot;
- }
- break;
-
- case T_Sort:
- {
- SortState *sortstate = ((Sort *) node)->sortstate;
-
- slot = sortstate->csstate.css_ScanTupleSlot;
- }
- break;
-
- case T_Agg:
- {
- AggState *aggstate = ((Agg *) node)->aggstate;
-
- slot = aggstate->csstate.cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_Group:
- {
- GroupState *grpstate = ((Group *) node)->grpstate;
-
- slot = grpstate->csstate.cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_Hash:
- {
- HashState *hashstate = ((Hash *) node)->hashstate;
-
- slot = hashstate->cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_Unique:
- {
- UniqueState *uniquestate = ((Unique *) node)->uniquestate;
-
- slot = uniquestate->cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_SetOp:
- {
- SetOpState *setopstate = ((SetOp *) node)->setopstate;
-
- slot = setopstate->cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_Limit:
- {
- LimitState *limitstate = ((Limit *) node)->limitstate;
-
- slot = limitstate->cstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_MergeJoin:
- {
- MergeJoinState *mergestate = ((MergeJoin *) node)->mergestate;
-
- slot = mergestate->jstate.cs_ResultTupleSlot;
- }
- break;
-
- case T_HashJoin:
- {
- HashJoinState *hashjoinstate = ((HashJoin *) node)->hashjoinstate;
-
- slot = hashjoinstate->jstate.cs_ResultTupleSlot;
- }
- break;
-
- default:
-
- /*
- * should never get here
- */
- elog(ERROR, "ExecGetTupType: node type %d unsupported",
- (int) nodeTag(node));
- return NULL;
- }
-
- return slot->ttc_tupleDescriptor;
-}
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
deleted file mode 100644
index 0b2f24d917a..00000000000
--- a/src/backend/executor/execQual.c
+++ /dev/null
@@ -1,1929 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * execQual.c
- * Routines to evaluate qualification and targetlist expressions
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.94 2002/06/20 20:29:27 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecEvalExpr - evaluate an expression and return a datum
- * ExecEvalExprSwitchContext - same, but switch into eval memory context
- * ExecQual - return true/false if qualification is satisfied
- * ExecProject - form a new tuple by projecting the given tuple
- *
- * NOTES
- * ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster
- * will speed up the entire system. Unfortunately they are currently
- * implemented recursively. Eliminating the recursion is bound to
- * improve the speed of the executor.
- *
- * ExecProject() is used to make tuple projections. Rather then
- * trying to speed it up, the execution plan should be pre-processed
- * to facilitate attribute sharing between nodes wherever possible,
- * instead of doing needless copying. -cim 5/31/91
- *
- */
-
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "executor/execdebug.h"
-#include "executor/functions.h"
-#include "executor/nodeSubplan.h"
-#include "utils/array.h"
-#include "utils/builtins.h"
-#include "utils/fcache.h"
-
-
-/* static function decls */
-static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
- bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
- bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
- bool *isNull, ExprDoneCond *isDone);
-static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
- List *argList, ExprContext *econtext);
-static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
- bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalNullTest(NullTest *ntest, ExprContext *econtext,
- bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
- bool *isNull, ExprDoneCond *isDone);
-
-
-/*----------
- * ExecEvalArrayRef
- *
- * This function takes an ArrayRef and returns the extracted Datum
- * if it's a simple reference, or the modified array value if it's
- * an array assignment (i.e., array element or slice insertion).
- *
- * NOTE: if we get a NULL result from a subexpression, we return NULL when
- * it's an array reference, or the unmodified source array when it's an
- * array assignment. This may seem peculiar, but if we return NULL (as was
- * done in versions up through 7.0) then an assignment like
- * UPDATE table SET arrayfield[4] = NULL
- * will result in setting the whole array to NULL, which is certainly not
- * very desirable. By returning the source array we make the assignment
- * into a no-op, instead. (Eventually we need to redesign arrays so that
- * individual elements can be NULL, but for now, let's try to protect users
- * from shooting themselves in the foot.)
- *
- * NOTE: we deliberately refrain from applying DatumGetArrayTypeP() here,
- * even though that might seem natural, because this code needs to support
- * both varlena arrays and fixed-length array types. DatumGetArrayTypeP()
- * only works for the varlena kind. The routines we call in arrayfuncs.c
- * have to know the difference (that's what they need refattrlength for).
- *----------
- */
-static Datum
-ExecEvalArrayRef(ArrayRef *arrayRef,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- ArrayType *array_source;
- ArrayType *resultArray;
- bool isAssignment = (arrayRef->refassgnexpr != NULL);
- List *elt;
- int i = 0,
- j = 0;
- IntArray upper,
- lower;
- int *lIndex;
-
- if (arrayRef->refexpr != NULL)
- {
- array_source = (ArrayType *)
- DatumGetPointer(ExecEvalExpr(arrayRef->refexpr,
- econtext,
- isNull,
- isDone));
-
- /*
- * If refexpr yields NULL, result is always NULL, for now anyway.
- * (This means you cannot assign to an element or slice of an
- * array that's NULL; it'll just stay NULL.)
- */
- if (*isNull)
- return (Datum) NULL;
- }
- else
- {
- /*
- * Empty refexpr indicates we are doing an INSERT into an array
- * column. For now, we just take the refassgnexpr (which the
- * parser will have ensured is an array value) and return it
- * as-is, ignoring any subscripts that may have been supplied in
- * the INSERT column list. This is a kluge, but it's not real
- * clear what the semantics ought to be...
- */
- array_source = NULL;
- }
-
- foreach(elt, arrayRef->refupperindexpr)
- {
- if (i >= MAXDIM)
- elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
- MAXDIM);
-
- upper.indx[i++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
- econtext,
- isNull,
- NULL));
- /* If any index expr yields NULL, result is NULL or source array */
- if (*isNull)
- {
- if (!isAssignment || array_source == NULL)
- return (Datum) NULL;
- *isNull = false;
- return PointerGetDatum(array_source);
- }
- }
-
- if (arrayRef->reflowerindexpr != NIL)
- {
- foreach(elt, arrayRef->reflowerindexpr)
- {
- if (j >= MAXDIM)
- elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
- MAXDIM);
-
- lower.indx[j++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
- econtext,
- isNull,
- NULL));
-
- /*
- * If any index expr yields NULL, result is NULL or source
- * array
- */
- if (*isNull)
- {
- if (!isAssignment || array_source == NULL)
- return (Datum) NULL;
- *isNull = false;
- return PointerGetDatum(array_source);
- }
- }
- if (i != j)
- elog(ERROR,
- "ExecEvalArrayRef: upper and lower indices mismatch");
- lIndex = lower.indx;
- }
- else
- lIndex = NULL;
-
- if (isAssignment)
- {
- Datum sourceData = ExecEvalExpr(arrayRef->refassgnexpr,
- econtext,
- isNull,
- NULL);
-
- /*
- * For now, can't cope with inserting NULL into an array, so make
- * it a no-op per discussion above...
- */
- if (*isNull)
- {
- if (array_source == NULL)
- return (Datum) NULL;
- *isNull = false;
- return PointerGetDatum(array_source);
- }
-
- if (array_source == NULL)
- return sourceData; /* XXX do something else? */
-
- if (lIndex == NULL)
- resultArray = array_set(array_source, i,
- upper.indx,
- sourceData,
- arrayRef->refelembyval,
- arrayRef->refelemlength,
- arrayRef->refattrlength,
- isNull);
- else
- resultArray = array_set_slice(array_source, i,
- upper.indx, lower.indx,
- (ArrayType *) DatumGetPointer(sourceData),
- arrayRef->refelembyval,
- arrayRef->refelemlength,
- arrayRef->refattrlength,
- isNull);
- return PointerGetDatum(resultArray);
- }
-
- if (lIndex == NULL)
- return array_ref(array_source, i,
- upper.indx,
- arrayRef->refelembyval,
- arrayRef->refelemlength,
- arrayRef->refattrlength,
- isNull);
- else
- {
- resultArray = array_get_slice(array_source, i,
- upper.indx, lower.indx,
- arrayRef->refelembyval,
- arrayRef->refelemlength,
- arrayRef->refattrlength,
- isNull);
- return PointerGetDatum(resultArray);
- }
-}
-
-
-/* ----------------------------------------------------------------
- * ExecEvalAggref
- *
- * Returns a Datum whose value is the value of the precomputed
- * aggregate found in the given expression context.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull)
-{
- if (econtext->ecxt_aggvalues == NULL) /* safety check */
- elog(ERROR, "ExecEvalAggref: no aggregates in this expression context");
-
- *isNull = econtext->ecxt_aggnulls[aggref->aggno];
- return econtext->ecxt_aggvalues[aggref->aggno];
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalVar
- *
- * Returns a Datum whose value is the value of a range
- * variable with respect to given expression context.
- *
- *
- * As an entry condition, we expect that the datatype the
- * plan expects to get (as told by our "variable" argument) is in
- * fact the datatype of the attribute the plan says to fetch (as
- * seen in the current context, identified by our "econtext"
- * argument).
- *
- * If we fetch a Type A attribute and Caller treats it as if it
- * were Type B, there will be undefined results (e.g. crash).
- * One way these might mismatch now is that we're accessing a
- * catalog class and the type information in the pg_attribute
- * class does not match the hardcoded pg_attribute information
- * (in pg_attribute.h) for the class in question.
- *
- * We have an Assert to make sure this entry condition is met.
- *
- * ---------------------------------------------------------------- */
-static Datum
-ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
-{
- Datum result;
- TupleTableSlot *slot;
- AttrNumber attnum;
- HeapTuple heapTuple;
- TupleDesc tuple_type;
-
- /*
- * get the slot we want
- */
- switch (variable->varno)
- {
- case INNER: /* get the tuple from the inner node */
- slot = econtext->ecxt_innertuple;
- break;
-
- case OUTER: /* get the tuple from the outer node */
- slot = econtext->ecxt_outertuple;
- break;
-
- default: /* get the tuple from the relation being
- * scanned */
- slot = econtext->ecxt_scantuple;
- break;
- }
-
- /*
- * extract tuple information from the slot
- */
- heapTuple = slot->val;
- tuple_type = slot->ttc_tupleDescriptor;
-
- attnum = variable->varattno;
-
- /* (See prolog for explanation of this Assert) */
- Assert(attnum <= 0 ||
- (attnum - 1 <= tuple_type->natts - 1 &&
- tuple_type->attrs[attnum - 1] != NULL &&
- variable->vartype == tuple_type->attrs[attnum - 1]->atttypid));
-
- /*
- * If the attribute number is invalid, then we are supposed to return
- * the entire tuple; we give back a whole slot so that callers know
- * what the tuple looks like.
- *
- * XXX this is a horrid crock: since the pointer to the slot might live
- * longer than the current evaluation context, we are forced to copy
- * the tuple and slot into a long-lived context --- we use
- * TransactionCommandContext which should be safe enough. This
- * represents a serious memory leak if many such tuples are processed
- * in one command, however. We ought to redesign the representation
- * of whole-tuple datums so that this is not necessary.
- *
- * We assume it's OK to point to the existing tupleDescriptor, rather
- * than copy that too.
- */
- if (attnum == InvalidAttrNumber)
- {
- MemoryContext oldContext;
- TupleTableSlot *tempSlot;
- HeapTuple tup;
-
- oldContext = MemoryContextSwitchTo(TransactionCommandContext);
- tempSlot = MakeTupleTableSlot();
- tup = heap_copytuple(heapTuple);
- ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
- ExecSetSlotDescriptor(tempSlot, tuple_type, false);
- MemoryContextSwitchTo(oldContext);
- return PointerGetDatum(tempSlot);
- }
-
- result = heap_getattr(heapTuple, /* tuple containing attribute */
- attnum, /* attribute number of desired
- * attribute */
- tuple_type, /* tuple descriptor of tuple */
- isNull); /* return: is attribute null? */
-
- return result;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalParam
- *
- * Returns the value of a parameter. A param node contains
- * something like ($.name) and the expression context contains
- * the current parameter bindings (name = "sam") (age = 34)...
- * so our job is to replace the param node with the datum
- * containing the appropriate information ("sam").
- *
- * Q: if we have a parameter ($.foo) without a binding, i.e.
- * there is no (foo = xxx) in the parameter list info,
- * is this a fatal error or should this be a "not available"
- * (in which case we shoud return a Const node with the
- * isnull flag) ? -cim 10/13/89
- *
- * Minor modification: Param nodes now have an extra field,
- * `paramkind' which specifies the type of parameter
- * (see params.h). So while searching the paramList for
- * a paramname/value pair, we have also to check for `kind'.
- *
- * NOTE: The last entry in `paramList' is always an
- * entry with kind == PARAM_INVALID.
- * ----------------------------------------------------------------
- */
-Datum
-ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
-{
- char *thisParameterName;
- int thisParameterKind = expression->paramkind;
- AttrNumber thisParameterId = expression->paramid;
- int matchFound;
- ParamListInfo paramList;
-
- if (thisParameterKind == PARAM_EXEC)
- {
- ParamExecData *prm;
-
- prm = &(econtext->ecxt_param_exec_vals[thisParameterId]);
- if (prm->execPlan != NULL)
- {
- ExecSetParamPlan(prm->execPlan, econtext);
- /* ExecSetParamPlan should have processed this param... */
- Assert(prm->execPlan == NULL);
- }
- *isNull = prm->isnull;
- return prm->value;
- }
-
- thisParameterName = expression->paramname;
- paramList = econtext->ecxt_param_list_info;
-
- *isNull = false;
-
- /*
- * search the list with the parameter info to find a matching name. An
- * entry with an InvalidName denotes the last element in the array.
- */
- matchFound = 0;
- if (paramList != NULL)
- {
- /*
- * search for an entry in 'paramList' that matches the
- * `expression'.
- */
- while (paramList->kind != PARAM_INVALID && !matchFound)
- {
- switch (thisParameterKind)
- {
- case PARAM_NAMED:
- if (thisParameterKind == paramList->kind &&
- strcmp(paramList->name, thisParameterName) == 0)
- matchFound = 1;
- break;
- case PARAM_NUM:
- if (thisParameterKind == paramList->kind &&
- paramList->id == thisParameterId)
- matchFound = 1;
- break;
- case PARAM_OLD:
- case PARAM_NEW:
- if (thisParameterKind == paramList->kind &&
- paramList->id == thisParameterId)
- {
- matchFound = 1;
-
- /*
- * sanity check
- */
- if (strcmp(paramList->name, thisParameterName) != 0)
- {
- elog(ERROR,
- "ExecEvalParam: new/old params with same id & diff names");
- }
- }
- break;
- default:
-
- /*
- * oops! this is not supposed to happen!
- */
- elog(ERROR, "ExecEvalParam: invalid paramkind %d",
- thisParameterKind);
- }
- if (!matchFound)
- paramList++;
- } /* while */
- } /* if */
-
- if (!matchFound)
- {
- /*
- * ooops! we couldn't find this parameter in the parameter list.
- * Signal an error
- */
- elog(ERROR, "ExecEvalParam: Unknown value for parameter %s",
- thisParameterName);
- }
-
- /*
- * return the value.
- */
- *isNull = paramList->isnull;
- return paramList->value;
-}
-
-
-/* ----------------------------------------------------------------
- * ExecEvalOper / ExecEvalFunc support routines
- * ----------------------------------------------------------------
- */
-
-/*
- * GetAttributeByName
- * GetAttributeByNum
- *
- * These are functions which return the value of the
- * named attribute out of the tuple from the arg slot. User defined
- * C functions which take a tuple as an argument are expected
- * to use this. Ex: overpaid(EMP) might call GetAttributeByNum().
- */
-Datum
-GetAttributeByNum(TupleTableSlot *slot,
- AttrNumber attrno,
- bool *isNull)
-{
- Datum retval;
-
- if (!AttributeNumberIsValid(attrno))
- elog(ERROR, "GetAttributeByNum: Invalid attribute number");
-
- if (!AttrNumberIsForUserDefinedAttr(attrno))
- elog(ERROR, "GetAttributeByNum: cannot access system attributes here");
-
- if (isNull == (bool *) NULL)
- elog(ERROR, "GetAttributeByNum: a NULL isNull flag was passed");
-
- if (TupIsNull(slot))
- {
- *isNull = true;
- return (Datum) 0;
- }
-
- retval = heap_getattr(slot->val,
- attrno,
- slot->ttc_tupleDescriptor,
- isNull);
- if (*isNull)
- return (Datum) 0;
-
- return retval;
-}
-
-Datum
-GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
-{
- AttrNumber attrno;
- TupleDesc tupdesc;
- Datum retval;
- int natts;
- int i;
-
- if (attname == NULL)
- elog(ERROR, "GetAttributeByName: Invalid attribute name");
-
- if (isNull == (bool *) NULL)
- elog(ERROR, "GetAttributeByName: a NULL isNull flag was passed");
-
- if (TupIsNull(slot))
- {
- *isNull = true;
- return (Datum) 0;
- }
-
- tupdesc = slot->ttc_tupleDescriptor;
- natts = slot->val->t_data->t_natts;
-
- attrno = InvalidAttrNumber;
- for (i = 0; i < tupdesc->natts; i++)
- {
- if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0)
- {
- attrno = tupdesc->attrs[i]->attnum;
- break;
- }
- }
-
- if (attrno == InvalidAttrNumber)
- elog(ERROR, "GetAttributeByName: attribute %s not found", attname);
-
- retval = heap_getattr(slot->val,
- attrno,
- tupdesc,
- isNull);
- if (*isNull)
- return (Datum) 0;
-
- return retval;
-}
-
-/*
- * Evaluate arguments for a function.
- */
-static ExprDoneCond
-ExecEvalFuncArgs(FunctionCallInfo fcinfo,
- List *argList,
- ExprContext *econtext)
-{
- ExprDoneCond argIsDone;
- int i;
- List *arg;
-
- argIsDone = ExprSingleResult; /* default assumption */
-
- i = 0;
- foreach(arg, argList)
- {
- ExprDoneCond thisArgIsDone;
-
- fcinfo->arg[i] = ExecEvalExpr((Node *) lfirst(arg),
- econtext,
- &fcinfo->argnull[i],
- &thisArgIsDone);
-
- if (thisArgIsDone != ExprSingleResult)
- {
- /*
- * We allow only one argument to have a set value; we'd need
- * much more complexity to keep track of multiple set
- * arguments (cf. ExecTargetList) and it doesn't seem worth
- * it.
- */
- if (argIsDone != ExprSingleResult)
- elog(ERROR, "Functions and operators can take only one set argument");
- argIsDone = thisArgIsDone;
- }
- i++;
- }
-
- fcinfo->nargs = i;
-
- return argIsDone;
-}
-
-/*
- * ExecMakeFunctionResult
- *
- * Evaluate the arguments to a function and then the function itself.
- *
- * NOTE: econtext is used only for evaluating the argument expressions;
- * it is not passed to the function itself.
- */
-Datum
-ExecMakeFunctionResult(FunctionCachePtr fcache,
- List *arguments,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- Datum result;
- FunctionCallInfoData fcinfo;
- ReturnSetInfo rsinfo; /* for functions returning sets */
- ExprDoneCond argDone;
- bool hasSetArg;
- int i;
-
- /*
- * arguments is a list of expressions to evaluate before passing to
- * the function manager. We skip the evaluation if it was already
- * done in the previous call (ie, we are continuing the evaluation of
- * a set-valued function). Otherwise, collect the current argument
- * values into fcinfo.
- */
- if (!fcache->setArgsValid)
- {
- /* Need to prep callinfo structure */
- MemSet(&fcinfo, 0, sizeof(fcinfo));
- fcinfo.flinfo = &(fcache->func);
- argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext);
- if (argDone == ExprEndResult)
- {
- /* input is an empty set, so return an empty set. */
- *isNull = true;
- if (isDone)
- *isDone = ExprEndResult;
- else
- elog(ERROR, "Set-valued function called in context that cannot accept a set");
- return (Datum) 0;
- }
- hasSetArg = (argDone != ExprSingleResult);
- }
- else
- {
- /* Copy callinfo from previous evaluation */
- memcpy(&fcinfo, &fcache->setArgs, sizeof(fcinfo));
- hasSetArg = fcache->setHasSetArg;
- /* Reset flag (we may set it again below) */
- fcache->setArgsValid = false;
- }
-
- /*
- * If function returns set, prepare a resultinfo node for
- * communication
- */
- if (fcache->func.fn_retset)
- {
- fcinfo.resultinfo = (Node *) &rsinfo;
- rsinfo.type = T_ReturnSetInfo;
- rsinfo.econtext = econtext;
- }
-
- /*
- * now return the value gotten by calling the function manager,
- * passing the function the evaluated parameter values.
- */
- if (fcache->func.fn_retset || hasSetArg)
- {
- /*
- * We need to return a set result. Complain if caller not ready
- * to accept one.
- */
- if (isDone == NULL)
- elog(ERROR, "Set-valued function called in context that cannot accept a set");
-
- /*
- * This loop handles the situation where we have both a set
- * argument and a set-valued function. Once we have exhausted the
- * function's value(s) for a particular argument value, we have to
- * get the next argument value and start the function over again.
- * We might have to do it more than once, if the function produces
- * an empty result set for a particular input value.
- */
- for (;;)
- {
- /*
- * If function is strict, and there are any NULL arguments,
- * skip calling the function (at least for this set of args).
- */
- bool callit = true;
-
- if (fcache->func.fn_strict)
- {
- for (i = 0; i < fcinfo.nargs; i++)
- {
- if (fcinfo.argnull[i])
- {
- callit = false;
- break;
- }
- }
- }
-
- if (callit)
- {
- fcinfo.isnull = false;
- rsinfo.isDone = ExprSingleResult;
- result = FunctionCallInvoke(&fcinfo);
- *isNull = fcinfo.isnull;
- *isDone = rsinfo.isDone;
- }
- else
- {
- result = (Datum) 0;
- *isNull = true;
- *isDone = ExprEndResult;
- }
-
- if (*isDone != ExprEndResult)
- {
- /*
- * Got a result from current argument. If function itself
- * returns set, save the current argument values to re-use
- * on the next call.
- */
- if (fcache->func.fn_retset)
- {
- memcpy(&fcache->setArgs, &fcinfo, sizeof(fcinfo));
- fcache->setHasSetArg = hasSetArg;
- fcache->setArgsValid = true;
- }
-
- /*
- * Make sure we say we are returning a set, even if the
- * function itself doesn't return sets.
- */
- *isDone = ExprMultipleResult;
- break;
- }
-
- /* Else, done with this argument */
- if (!hasSetArg)
- break; /* input not a set, so done */
-
- /* Re-eval args to get the next element of the input set */
- argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext);
-
- if (argDone != ExprMultipleResult)
- {
- /* End of argument set, so we're done. */
- *isNull = true;
- *isDone = ExprEndResult;
- result = (Datum) 0;
- break;
- }
-
- /*
- * If we reach here, loop around to run the function on the
- * new argument.
- */
- }
- }
- else
- {
- /*
- * Non-set case: much easier.
- *
- * If function is strict, and there are any NULL arguments, skip
- * calling the function and return NULL.
- */
- if (fcache->func.fn_strict)
- {
- for (i = 0; i < fcinfo.nargs; i++)
- {
- if (fcinfo.argnull[i])
- {
- *isNull = true;
- return (Datum) 0;
- }
- }
- }
- fcinfo.isnull = false;
- result = FunctionCallInvoke(&fcinfo);
- *isNull = fcinfo.isnull;
- }
-
- return result;
-}
-
-
-/* ----------------------------------------------------------------
- * ExecEvalOper
- * ExecEvalFunc
- *
- * Evaluate the functional result of a list of arguments by calling the
- * function manager.
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- * ExecEvalOper
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalOper(Expr *opClause,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- Oper *op;
- List *argList;
- FunctionCachePtr fcache;
-
- /*
- * we extract the oid of the function associated with the op and then
- * pass the work onto ExecMakeFunctionResult which evaluates the
- * arguments and returns the result of calling the function on the
- * evaluated arguments.
- */
- op = (Oper *) opClause->oper;
- argList = opClause->args;
-
- /*
- * get the fcache from the Oper node. If it is NULL, then initialize
- * it
- */
- fcache = op->op_fcache;
- if (fcache == NULL)
- {
- fcache = init_fcache(op->opid, length(argList),
- econtext->ecxt_per_query_memory);
- op->op_fcache = fcache;
- }
-
- return ExecMakeFunctionResult(fcache, argList, econtext,
- isNull, isDone);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalFunc
- * ----------------------------------------------------------------
- */
-
-static Datum
-ExecEvalFunc(Expr *funcClause,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- Func *func;
- List *argList;
- FunctionCachePtr fcache;
-
- /*
- * we extract the oid of the function associated with the func node
- * and then pass the work onto ExecMakeFunctionResult which evaluates
- * the arguments and returns the result of calling the function on the
- * evaluated arguments.
- *
- * this is nearly identical to the ExecEvalOper code.
- */
- func = (Func *) funcClause->oper;
- argList = funcClause->args;
-
- /*
- * get the fcache from the Func node. If it is NULL, then initialize
- * it
- */
- fcache = func->func_fcache;
- if (fcache == NULL)
- {
- fcache = init_fcache(func->funcid, length(argList),
- econtext->ecxt_per_query_memory);
- func->func_fcache = fcache;
- }
-
- return ExecMakeFunctionResult(fcache, argList, econtext,
- isNull, isDone);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalNot
- * ExecEvalOr
- * ExecEvalAnd
- *
- * Evaluate boolean expressions. Evaluation of 'or' is
- * short-circuited when the first true (or null) value is found.
- *
- * The query planner reformulates clause expressions in the
- * qualification to conjunctive normal form. If we ever get
- * an AND to evaluate, we can be sure that it's not a top-level
- * clause in the qualification, but appears lower (as a function
- * argument, for example), or in the target list. Not that you
- * need to know this, mind you...
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
-{
- Node *clause;
- Datum expr_value;
-
- clause = lfirst(notclause->args);
-
- expr_value = ExecEvalExpr(clause, econtext, isNull, NULL);
-
- /*
- * if the expression evaluates to null, then we just cascade the null
- * back to whoever called us.
- */
- if (*isNull)
- return expr_value;
-
- /*
- * evaluation of 'not' is simple.. expr is false, then return 'true'
- * and vice versa.
- */
- return BoolGetDatum(!DatumGetBool(expr_value));
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalOr
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
-{
- List *clauses;
- List *clause;
- bool AnyNull;
- Datum clause_value;
-
- clauses = orExpr->args;
- AnyNull = false;
-
- /*
- * If any of the clauses is TRUE, the OR result is TRUE regardless of
- * the states of the rest of the clauses, so we can stop evaluating
- * and return TRUE immediately. If none are TRUE and one or more is
- * NULL, we return NULL; otherwise we return FALSE. This makes sense
- * when you interpret NULL as "don't know": if we have a TRUE then the
- * OR is TRUE even if we aren't sure about some of the other inputs.
- * If all the known inputs are FALSE, but we have one or more "don't
- * knows", then we have to report that we "don't know" what the OR's
- * result should be --- perhaps one of the "don't knows" would have
- * been TRUE if we'd known its value. Only when all the inputs are
- * known to be FALSE can we state confidently that the OR's result is
- * FALSE.
- */
- foreach(clause, clauses)
- {
- clause_value = ExecEvalExpr((Node *) lfirst(clause),
- econtext, isNull, NULL);
-
- /*
- * if we have a non-null true result, then return it.
- */
- if (*isNull)
- AnyNull = true; /* remember we got a null */
- else if (DatumGetBool(clause_value))
- return clause_value;
- }
-
- /* AnyNull is true if at least one clause evaluated to NULL */
- *isNull = AnyNull;
- return BoolGetDatum(false);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalAnd
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
-{
- List *clauses;
- List *clause;
- bool AnyNull;
- Datum clause_value;
-
- clauses = andExpr->args;
- AnyNull = false;
-
- /*
- * If any of the clauses is FALSE, the AND result is FALSE regardless
- * of the states of the rest of the clauses, so we can stop evaluating
- * and return FALSE immediately. If none are FALSE and one or more is
- * NULL, we return NULL; otherwise we return TRUE. This makes sense
- * when you interpret NULL as "don't know", using the same sort of
- * reasoning as for OR, above.
- */
- foreach(clause, clauses)
- {
- clause_value = ExecEvalExpr((Node *) lfirst(clause),
- econtext, isNull, NULL);
-
- /*
- * if we have a non-null false result, then return it.
- */
- if (*isNull)
- AnyNull = true; /* remember we got a null */
- else if (!DatumGetBool(clause_value))
- return clause_value;
- }
-
- /* AnyNull is true if at least one clause evaluated to NULL */
- *isNull = AnyNull;
- return BoolGetDatum(!AnyNull);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalCase
- *
- * Evaluate a CASE clause. Will have boolean expressions
- * inside the WHEN clauses, and will have expressions
- * for results.
- * - thomas 1998-11-09
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
- bool *isNull, ExprDoneCond *isDone)
-{
- List *clauses;
- List *clause;
- Datum clause_value;
-
- clauses = caseExpr->args;
-
- /*
- * we evaluate each of the WHEN clauses in turn, as soon as one is
- * true we return the corresponding result. If none are true then we
- * return the value of the default clause, or NULL if there is none.
- */
- foreach(clause, clauses)
- {
- CaseWhen *wclause = lfirst(clause);
-
- clause_value = ExecEvalExpr(wclause->expr,
- econtext,
- isNull,
- NULL);
-
- /*
- * if we have a true test, then we return the result, since the
- * case statement is satisfied. A NULL result from the test is
- * not considered true.
- */
- if (DatumGetBool(clause_value) && !*isNull)
- {
- return ExecEvalExpr(wclause->result,
- econtext,
- isNull,
- isDone);
- }
- }
-
- if (caseExpr->defresult)
- {
- return ExecEvalExpr(caseExpr->defresult,
- econtext,
- isNull,
- isDone);
- }
-
- *isNull = true;
- return (Datum) 0;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalNullTest
- *
- * Evaluate a NullTest node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalNullTest(NullTest *ntest,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- Datum result;
-
- result = ExecEvalExpr(ntest->arg, econtext, isNull, isDone);
- switch (ntest->nulltesttype)
- {
- case IS_NULL:
- if (*isNull)
- {
- *isNull = false;
- return BoolGetDatum(true);
- }
- else
- return BoolGetDatum(false);
- case IS_NOT_NULL:
- if (*isNull)
- {
- *isNull = false;
- return BoolGetDatum(false);
- }
- else
- return BoolGetDatum(true);
- default:
- elog(ERROR, "ExecEvalNullTest: unexpected nulltesttype %d",
- (int) ntest->nulltesttype);
- return (Datum) 0; /* keep compiler quiet */
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalBooleanTest
- *
- * Evaluate a BooleanTest node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalBooleanTest(BooleanTest *btest,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- Datum result;
-
- result = ExecEvalExpr(btest->arg, econtext, isNull, isDone);
- switch (btest->booltesttype)
- {
- case IS_TRUE:
- if (*isNull)
- {
- *isNull = false;
- return BoolGetDatum(false);
- }
- else if (DatumGetBool(result))
- return BoolGetDatum(true);
- else
- return BoolGetDatum(false);
- case IS_NOT_TRUE:
- if (*isNull)
- {
- *isNull = false;
- return BoolGetDatum(true);
- }
- else if (DatumGetBool(result))
- return BoolGetDatum(false);
- else
- return BoolGetDatum(true);
- case IS_FALSE:
- if (*isNull)
- {
- *isNull = false;
- return BoolGetDatum(false);
- }
- else if (DatumGetBool(result))
- return BoolGetDatum(false);
- else
- return BoolGetDatum(true);
- case IS_NOT_FALSE:
- if (*isNull)
- {
- *isNull = false;
- return BoolGetDatum(true);
- }
- else if (DatumGetBool(result))
- return BoolGetDatum(true);
- else
- return BoolGetDatum(false);
- case IS_UNKNOWN:
- if (*isNull)
- {
- *isNull = false;
- return BoolGetDatum(true);
- }
- else
- return BoolGetDatum(false);
- case IS_NOT_UNKNOWN:
- if (*isNull)
- {
- *isNull = false;
- return BoolGetDatum(false);
- }
- else
- return BoolGetDatum(true);
- default:
- elog(ERROR, "ExecEvalBooleanTest: unexpected booltesttype %d",
- (int) btest->booltesttype);
- return (Datum) 0; /* keep compiler quiet */
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalFieldSelect
- *
- * Evaluate a FieldSelect node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalFieldSelect(FieldSelect *fselect,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- Datum result;
- TupleTableSlot *resSlot;
-
- result = ExecEvalExpr(fselect->arg, econtext, isNull, isDone);
- if (*isNull)
- return result;
- resSlot = (TupleTableSlot *) DatumGetPointer(result);
- Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot));
- result = heap_getattr(resSlot->val,
- fselect->fieldnum,
- resSlot->ttc_tupleDescriptor,
- isNull);
- return result;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalExpr
- *
- * Recursively evaluate a targetlist or qualification expression.
- *
- * Inputs:
- * expression: the expression tree to evaluate
- * econtext: evaluation context information
- *
- * Outputs:
- * return value: Datum value of result
- * *isNull: set to TRUE if result is NULL (actual return value is
- * meaningless if so); set to FALSE if non-null result
- * *isDone: set to indicator of set-result status
- *
- * A caller that can only accept a singleton (non-set) result should pass
- * NULL for isDone; if the expression computes a set result then an elog()
- * error will be reported. If the caller does pass an isDone pointer then
- * *isDone is set to one of these three states:
- * ExprSingleResult singleton result (not a set)
- * ExprMultipleResult return value is one element of a set
- * ExprEndResult there are no more elements in the set
- * When ExprMultipleResult is returned, the caller should invoke
- * ExecEvalExpr() repeatedly until ExprEndResult is returned. ExprEndResult
- * is returned after the last real set element. For convenience isNull will
- * always be set TRUE when ExprEndResult is returned, but this should not be
- * taken as indicating a NULL element of the set. Note that these return
- * conventions allow us to distinguish among a singleton NULL, a NULL element
- * of a set, and an empty set.
- *
- * 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.
- * ----------------------------------------------------------------
- */
-Datum
-ExecEvalExpr(Node *expression,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- Datum retDatum;
-
- /* Set default values for result flags: non-null, not a set result */
- *isNull = false;
- if (isDone)
- *isDone = ExprSingleResult;
-
- /* Is this still necessary? Doubtful... */
- if (expression == NULL)
- {
- *isNull = true;
- return (Datum) 0;
- }
-
- /*
- * here we dispatch the work to the appropriate type of function given
- * the type of our expression.
- */
- switch (nodeTag(expression))
- {
- case T_Var:
- retDatum = ExecEvalVar((Var *) expression, econtext, isNull);
- break;
- case T_Const:
- {
- Const *con = (Const *) expression;
-
- retDatum = con->constvalue;
- *isNull = con->constisnull;
- break;
- }
- case T_Param:
- retDatum = ExecEvalParam((Param *) expression, econtext, isNull);
- break;
- case T_Aggref:
- retDatum = ExecEvalAggref((Aggref *) expression, econtext, isNull);
- break;
- case T_ArrayRef:
- retDatum = ExecEvalArrayRef((ArrayRef *) expression,
- econtext,
- isNull,
- isDone);
- break;
- case T_Expr:
- {
- Expr *expr = (Expr *) expression;
-
- switch (expr->opType)
- {
- case OP_EXPR:
- retDatum = ExecEvalOper(expr, econtext,
- isNull, isDone);
- break;
- case FUNC_EXPR:
- retDatum = ExecEvalFunc(expr, econtext,
- isNull, isDone);
- break;
- case OR_EXPR:
- retDatum = ExecEvalOr(expr, econtext, isNull);
- break;
- case AND_EXPR:
- retDatum = ExecEvalAnd(expr, econtext, isNull);
- break;
- case NOT_EXPR:
- retDatum = ExecEvalNot(expr, econtext, isNull);
- break;
- case SUBPLAN_EXPR:
- retDatum = ExecSubPlan((SubPlan *) expr->oper,
- expr->args, econtext,
- isNull);
- break;
- default:
- elog(ERROR, "ExecEvalExpr: unknown expression type %d",
- expr->opType);
- retDatum = 0; /* keep compiler quiet */
- break;
- }
- break;
- }
- case T_FieldSelect:
- retDatum = ExecEvalFieldSelect((FieldSelect *) expression,
- econtext,
- isNull,
- isDone);
- break;
- case T_RelabelType:
- retDatum = ExecEvalExpr(((RelabelType *) expression)->arg,
- econtext,
- isNull,
- isDone);
- break;
- case T_CaseExpr:
- retDatum = ExecEvalCase((CaseExpr *) expression,
- econtext,
- isNull,
- isDone);
- break;
- case T_NullTest:
- retDatum = ExecEvalNullTest((NullTest *) expression,
- econtext,
- isNull,
- isDone);
- break;
- case T_BooleanTest:
- retDatum = ExecEvalBooleanTest((BooleanTest *) expression,
- econtext,
- isNull,
- isDone);
- break;
-
- default:
- elog(ERROR, "ExecEvalExpr: unknown expression type %d",
- nodeTag(expression));
- retDatum = 0; /* keep compiler quiet */
- break;
- }
-
- return retDatum;
-} /* ExecEvalExpr() */
-
-
-/*
- * Same as above, but get into the right allocation context explicitly.
- */
-Datum
-ExecEvalExprSwitchContext(Node *expression,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- Datum retDatum;
- MemoryContext oldContext;
-
- oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
- retDatum = ExecEvalExpr(expression, econtext, isNull, isDone);
- MemoryContextSwitchTo(oldContext);
- return retDatum;
-}
-
-
-/* ----------------------------------------------------------------
- * ExecQual / ExecTargetList / ExecProject
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- * ExecQual
- *
- * Evaluates a conjunctive boolean expression (qual list) and
- * returns true iff none of the subexpressions are false.
- * (We also return true if the list is empty.)
- *
- * If some of the subexpressions yield NULL but none yield FALSE,
- * then the result of the conjunction is NULL (ie, unknown)
- * according to three-valued boolean logic. In this case,
- * we return the value specified by the "resultForNull" parameter.
- *
- * Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- * since SQL specifies that tuples with null WHERE results do not
- * get selected. On the other hand, callers evaluating constraint
- * conditions should pass resultForNull=TRUE, since SQL also specifies
- * that NULL constraint conditions are not failures.
- *
- * NOTE: it would not be correct to use this routine to evaluate an
- * AND subclause of a boolean expression; for that purpose, a NULL
- * result must be returned as NULL so that it can be properly treated
- * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- * This routine is only used in contexts where a complete expression
- * is being evaluated and we know that NULL can be treated the same
- * as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
- bool result;
- MemoryContext oldContext;
- List *qlist;
-
- /*
- * debugging stuff
- */
- EV_printf("ExecQual: qual is ");
- EV_nodeDisplay(qual);
- EV_printf("\n");
-
- IncrProcessed();
-
- /*
- * Run in short-lived per-tuple context while computing expressions.
- */
- oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
- /*
- * Evaluate the qual conditions one at a time. If we find a FALSE
- * result, we can stop evaluating and return FALSE --- the AND result
- * must be FALSE. Also, if we find a NULL result when resultForNull
- * is FALSE, we can stop and return FALSE --- the AND result must be
- * FALSE or NULL in that case, and the caller doesn't care which.
- *
- * If we get to the end of the list, we can return TRUE. This will
- * happen when the AND result is indeed TRUE, or when the AND result
- * is NULL (one or more NULL subresult, with all the rest TRUE) and
- * the caller has specified resultForNull = TRUE.
- */
- result = true;
-
- foreach(qlist, qual)
- {
- Node *clause = (Node *) lfirst(qlist);
- Datum expr_value;
- bool isNull;
-
- expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
- if (isNull)
- {
- if (resultForNull == false)
- {
- result = false; /* treat NULL as FALSE */
- break;
- }
- }
- else
- {
- if (!DatumGetBool(expr_value))
- {
- result = false; /* definitely FALSE */
- break;
- }
- }
- }
-
- MemoryContextSwitchTo(oldContext);
-
- return result;
-}
-
-/*
- * Number of items in a tlist (including any resjunk items!)
- */
-int
-ExecTargetListLength(List *targetlist)
-{
- int len = 0;
- List *tl;
-
- foreach(tl, targetlist)
- {
- TargetEntry *curTle = (TargetEntry *) lfirst(tl);
-
- if (curTle->resdom != NULL)
- len++;
- else
- len += curTle->fjoin->fj_nNodes;
- }
- return len;
-}
-
-/*
- * Number of items in a tlist, not including any resjunk items
- */
-int
-ExecCleanTargetListLength(List *targetlist)
-{
- int len = 0;
- List *tl;
-
- foreach(tl, targetlist)
- {
- TargetEntry *curTle = (TargetEntry *) lfirst(tl);
-
- if (curTle->resdom != NULL)
- {
- if (!curTle->resdom->resjunk)
- len++;
- }
- else
- len += curTle->fjoin->fj_nNodes;
- }
- return len;
-}
-
-/* ----------------------------------------------------------------
- * ExecTargetList
- *
- * Evaluates a targetlist with respect to the current
- * expression context and return a tuple.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples. Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * ----------------------------------------------------------------
- */
-static HeapTuple
-ExecTargetList(List *targetlist,
- int nodomains,
- TupleDesc targettype,
- Datum *values,
- ExprContext *econtext,
- ExprDoneCond *isDone)
-{
- MemoryContext oldContext;
-
-#define NPREALLOCDOMAINS 64
- char nullsArray[NPREALLOCDOMAINS];
- bool fjIsNullArray[NPREALLOCDOMAINS];
- ExprDoneCond itemIsDoneArray[NPREALLOCDOMAINS];
- char *nulls;
- bool *fjIsNull;
- ExprDoneCond *itemIsDone;
- List *tl;
- TargetEntry *tle;
- AttrNumber resind;
- HeapTuple newTuple;
- bool isNull;
- bool haveDoneSets;
- static struct tupleDesc NullTupleDesc; /* we assume this inits to
- * zeroes */
-
- /*
- * debugging stuff
- */
- EV_printf("ExecTargetList: tl is ");
- EV_nodeDisplay(targetlist);
- EV_printf("\n");
-
- /*
- * Run in short-lived per-tuple context while computing expressions.
- */
- oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
- /*
- * There used to be some klugy and demonstrably broken code here that
- * special-cased the situation where targetlist == NIL. Now we just
- * fall through and return an empty-but-valid tuple. We do, however,
- * have to cope with the possibility that targettype is NULL ---
- * heap_formtuple won't like that, so pass a dummy descriptor with
- * natts = 0 to deal with it.
- */
- if (targettype == NULL)
- targettype = &NullTupleDesc;
-
- /*
- * allocate an array of char's to hold the "null" information only if
- * we have a really large targetlist. otherwise we use the stack.
- *
- * We also allocate a bool array that is used to hold fjoin result state,
- * and another array that holds the isDone status for each targetlist
- * item. The isDone status is needed so that we can iterate,
- * generating multiple tuples, when one or more tlist items return
- * sets. (We expect the caller to call us again if we return:
- *
- * isDone = ExprMultipleResult.)
- */
- if (nodomains > NPREALLOCDOMAINS)
- {
- nulls = (char *) palloc(nodomains * sizeof(char));
- fjIsNull = (bool *) palloc(nodomains * sizeof(bool));
- itemIsDone = (ExprDoneCond *) palloc(nodomains * sizeof(ExprDoneCond));
- }
- else
- {
- nulls = nullsArray;
- fjIsNull = fjIsNullArray;
- itemIsDone = itemIsDoneArray;
- }
-
- /*
- * evaluate all the expressions in the target list
- */
-
- if (isDone)
- *isDone = ExprSingleResult; /* until proven otherwise */
-
- haveDoneSets = false; /* any exhausted set exprs in tlist? */
-
- foreach(tl, targetlist)
- {
- tle = lfirst(tl);
-
- if (tle->resdom != NULL)
- {
- resind = tle->resdom->resno - 1;
-
- values[resind] = ExecEvalExpr(tle->expr,
- econtext,
- &isNull,
- &itemIsDone[resind]);
- nulls[resind] = isNull ? 'n' : ' ';
-
- if (itemIsDone[resind] != ExprSingleResult)
- {
- /* We have a set-valued expression in the tlist */
- if (isDone == NULL)
- elog(ERROR, "Set-valued function called in context that cannot accept a set");
- if (itemIsDone[resind] == ExprMultipleResult)
- {
- /* we have undone sets in the tlist, set flag */
- *isDone = ExprMultipleResult;
- }
- else
- {
- /* we have done sets in the tlist, set flag for that */
- haveDoneSets = true;
- }
- }
- }
- else
- {
-#ifdef SETS_FIXED
- int curNode;
- Resdom *fjRes;
- List *fjTlist = (List *) tle->expr;
- Fjoin *fjNode = tle->fjoin;
- int nNodes = fjNode->fj_nNodes;
- DatumPtr results = fjNode->fj_results;
-
- ExecEvalFjoin(tle, econtext, fjIsNull, isDone);
-
- /*
- * XXX this is wrong, but since fjoin code is completely
- * broken anyway, I'm not going to worry about it now --- tgl
- * 8/23/00
- */
- if (isDone && *isDone == ExprEndResult)
- {
- MemoryContextSwitchTo(oldContext);
- newTuple = NULL;
- goto exit;
- }
-
- /*
- * get the result from the inner node
- */
- fjRes = (Resdom *) fjNode->fj_innerNode;
- resind = fjRes->resno - 1;
- values[resind] = results[0];
- nulls[resind] = fjIsNull[0] ? 'n' : ' ';
-
- /*
- * Get results from all of the outer nodes
- */
- for (curNode = 1;
- curNode < nNodes;
- curNode++, fjTlist = lnext(fjTlist))
- {
- Node *outernode = lfirst(fjTlist);
-
- fjRes = (Resdom *) outernode->iterexpr;
- resind = fjRes->resno - 1;
- values[resind] = results[curNode];
- nulls[resind] = fjIsNull[curNode] ? 'n' : ' ';
- }
-#else
- elog(ERROR, "ExecTargetList: fjoin nodes not currently supported");
-#endif
- }
- }
-
- if (haveDoneSets)
- {
- /*
- * note: can't get here unless we verified isDone != NULL
- */
- if (*isDone == ExprSingleResult)
- {
- /*
- * all sets are done, so report that tlist expansion is
- * complete.
- */
- *isDone = ExprEndResult;
- MemoryContextSwitchTo(oldContext);
- newTuple = NULL;
- goto exit;
- }
- else
- {
- /*
- * We have some done and some undone sets. Restart the done
- * ones so that we can deliver a tuple (if possible).
- */
- foreach(tl, targetlist)
- {
- tle = lfirst(tl);
-
- if (tle->resdom != NULL)
- {
- resind = tle->resdom->resno - 1;
-
- if (itemIsDone[resind] == ExprEndResult)
- {
- values[resind] = ExecEvalExpr(tle->expr,
- econtext,
- &isNull,
- &itemIsDone[resind]);
- nulls[resind] = isNull ? 'n' : ' ';
-
- if (itemIsDone[resind] == ExprEndResult)
- {
- /*
- * Oh dear, this item is returning an empty
- * set. Guess we can't make a tuple after all.
- */
- *isDone = ExprEndResult;
- break;
- }
- }
- }
- }
-
- /*
- * If we cannot make a tuple because some sets are empty, we
- * still have to cycle the nonempty sets to completion, else
- * resources will not be released from subplans etc.
- */
- if (*isDone == ExprEndResult)
- {
- foreach(tl, targetlist)
- {
- tle = lfirst(tl);
-
- if (tle->resdom != NULL)
- {
- resind = tle->resdom->resno - 1;
-
- while (itemIsDone[resind] == ExprMultipleResult)
- {
- (void) ExecEvalExpr(tle->expr,
- econtext,
- &isNull,
- &itemIsDone[resind]);
- }
- }
- }
-
- MemoryContextSwitchTo(oldContext);
- newTuple = NULL;
- goto exit;
- }
- }
- }
-
- /*
- * form the new result tuple (in the caller's memory context!)
- */
- MemoryContextSwitchTo(oldContext);
-
- newTuple = (HeapTuple) heap_formtuple(targettype, values, nulls);
-
-exit:
-
- /*
- * free the status arrays if we palloc'd them
- */
- if (nodomains > NPREALLOCDOMAINS)
- {
- pfree(nulls);
- pfree(fjIsNull);
- pfree(itemIsDone);
- }
-
- return newTuple;
-}
-
-/* ----------------------------------------------------------------
- * ExecProject
- *
- * projects a tuple based on projection info and stores
- * it in the specified tuple table slot.
- *
- * Note: someday soon the executor can be extended to eliminate
- * redundant projections by storing pointers to datums
- * in the tuple table and then passing these around when
- * possible. this should make things much quicker.
- * -cim 6/3/91
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
- TupleTableSlot *slot;
- List *targetlist;
- int len;
- TupleDesc tupType;
- Datum *tupValue;
- ExprContext *econtext;
- HeapTuple newTuple;
-
- /*
- * sanity checks
- */
- if (projInfo == NULL)
- return (TupleTableSlot *) NULL;
-
- /*
- * get the projection info we want
- */
- slot = projInfo->pi_slot;
- targetlist = projInfo->pi_targetlist;
- len = projInfo->pi_len;
- tupType = slot->ttc_tupleDescriptor;
-
- tupValue = projInfo->pi_tupValue;
- econtext = projInfo->pi_exprContext;
-
- /*
- * form a new result tuple (if possible --- result can be NULL)
- */
- newTuple = ExecTargetList(targetlist,
- len,
- tupType,
- tupValue,
- econtext,
- isDone);
-
- /*
- * store the tuple in the projection slot and return the slot.
- */
- return ExecStoreTuple(newTuple, /* tuple to store */
- slot, /* slot to store in */
- InvalidBuffer, /* tuple has no buffer */
- true);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
deleted file mode 100644
index e8df691c8ea..00000000000
--- a/src/backend/executor/execScan.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * execScan.c
- * This code provides support for generalized relation scans. ExecScan
- * is passed a node and a pointer to a function to "do the right thing"
- * and return a tuple from the relation. ExecScan then does the tedious
- * stuff - checking the qualification and projecting the tuple
- * appropriately.
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.20 2002/06/20 20:29:27 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include <sys/types.h>
-#include <sys/file.h>
-
-#include "executor/executor.h"
-#include "miscadmin.h"
-#include "utils/memutils.h"
-
-
-/* ----------------------------------------------------------------
- * ExecScan
- *
- * Scans the relation using the 'access method' indicated and
- * returns the next qualifying tuple in the direction specified
- * in the global variable ExecDirection.
- * The access method returns the next tuple and execScan() is
- * responsible for checking the tuple returned against the qual-clause.
- *
- * Conditions:
- * -- the "cursor" maintained by the AMI is positioned at the tuple
- * returned previously.
- *
- * Initial States:
- * -- the relation indicated is opened for scanning so that the
- * "cursor" is positioned before the first qualifying tuple.
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecScan(Scan *node,
- ExecScanAccessMtd accessMtd) /* function returning a tuple */
-{
- CommonScanState *scanstate;
- EState *estate;
- ExprContext *econtext;
- List *qual;
- ExprDoneCond isDone;
- TupleTableSlot *resultSlot;
-
- /*
- * Fetch data from node
- */
- estate = node->plan.state;
- scanstate = node->scanstate;
- econtext = scanstate->cstate.cs_ExprContext;
- qual = node->plan.qual;
-
- /*
- * 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)
- {
- resultSlot = ExecProject(scanstate->cstate.cs_ProjInfo, &isDone);
- if (isDone == ExprMultipleResult)
- return resultSlot;
- /* Done with that source tuple... */
- scanstate->cstate.cs_TupFromTlist = false;
- }
-
- /*
- * Reset per-tuple memory context to free any expression evaluation
- * storage allocated in the previous tuple cycle. Note this can't
- * happen until we're done projecting out tuples from a scan tuple.
- */
- ResetExprContext(econtext);
-
- /*
- * get a tuple from the access method loop until we obtain a tuple
- * which passes the qualification.
- */
- for (;;)
- {
- TupleTableSlot *slot;
-
- CHECK_FOR_INTERRUPTS();
-
- slot = (*accessMtd) (node);
-
- /*
- * if the slot returned by the accessMtd contains NULL, then it
- * means there is nothing more to scan so we just return an empty
- * slot, being careful to use the projection result slot so it has
- * correct tupleDesc.
- */
- if (TupIsNull(slot))
- {
- return ExecStoreTuple(NULL,
- scanstate->cstate.cs_ProjInfo->pi_slot,
- InvalidBuffer,
- true);
- }
-
- /*
- * place the current tuple into the expr context
- */
- econtext->ecxt_scantuple = slot;
-
- /*
- * check that the current tuple satisfies the qual-clause
- *
- * check for non-nil qual here to avoid a function call to ExecQual()
- * when the qual is nil ... saves only a few cycles, but they add
- * up ...
- */
- if (!qual || ExecQual(qual, econtext, false))
- {
- /*
- * Found a satisfactory scan tuple.
- *
- * Form a projection tuple, store it in the result tuple slot and
- * return it --- unless we find we can project no tuples from
- * this scan tuple, in which case continue scan.
- */
- resultSlot = ExecProject(scanstate->cstate.cs_ProjInfo, &isDone);
- if (isDone != ExprEndResult)
- {
- scanstate->cstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
- return resultSlot;
- }
- }
-
- /*
- * Tuple fails qual, so free per-tuple memory and try again.
- */
- ResetExprContext(econtext);
- }
-}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
deleted file mode 100644
index 86a16ddb04f..00000000000
--- a/src/backend/executor/execTuples.c
+++ /dev/null
@@ -1,795 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * execTuples.c
- * Routines dealing with the executor tuple tables. These are used to
- * ensure that the executor frees copies of tuples (made by
- * ExecTargetList) properly.
- *
- * Routines dealing with the type information for tuples. Currently,
- * the type information for a tuple is an array of FormData_pg_attribute.
- * This information is needed by routines manipulating tuples
- * (getattribute, formtuple, etc.).
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.53 2002/06/20 20:29:27 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- *
- * TABLE CREATE/DELETE
- * ExecCreateTupleTable - create a new tuple table
- * ExecDropTupleTable - destroy a table
- *
- * SLOT RESERVATION
- * ExecAllocTableSlot - find an available slot in the table
- *
- * SLOT ACCESSORS
- * ExecStoreTuple - store a tuple in the table
- * ExecFetchTuple - fetch a tuple from the table
- * ExecClearTuple - clear contents of a table slot
- * ExecSetSlotDescriptor - set a slot's tuple descriptor
- * ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag
- *
- * SLOT STATUS PREDICATES
- * TupIsNull - true when slot contains no tuple(Macro)
- *
- * CONVENIENCE INITIALIZATION ROUTINES
- * ExecInitResultTupleSlot \ convenience routines to initialize
- * ExecInitScanTupleSlot \ the various tuple slots for nodes
- * ExecInitExtraTupleSlot / which store copies of tuples.
- * ExecInitNullTupleSlot /
- *
- * Routines that probably belong somewhere else:
- * ExecTypeFromTL - form a TupleDesc from a target list
- *
- * EXAMPLE OF HOW TABLE ROUTINES WORK
- * Suppose we have a query such as retrieve (EMP.name) and we have
- * a single SeqScan node in the query plan.
- *
- * At ExecStart()
- * ----------------
- * - InitPlan() calls ExecCreateTupleTable() to create the tuple
- * table which will hold tuples processed by the executor.
- *
- * - ExecInitSeqScan() calls ExecInitScanTupleSlot() and
- * ExecInitResultTupleSlot() to reserve places in the tuple
- * table for the tuples returned by the access methods and the
- * tuples resulting from preforming target list projections.
- *
- * During ExecRun()
- * ----------------
- * - SeqNext() calls ExecStoreTuple() to place the tuple returned
- * by the access methods into the scan tuple slot.
- *
- * - ExecSeqScan() calls ExecStoreTuple() to take the result
- * tuple from ExecProject() and place it into the result tuple slot.
- *
- * - ExecutePlan() calls ExecRetrieve() which gets the tuple out of
- * the slot passed to it by calling ExecFetchTuple(). this tuple
- * is then returned.
- *
- * At ExecEnd()
- * ----------------
- * - EndPlan() calls ExecDropTupleTable() to clean up any remaining
- * tuples left over from executing the query.
- *
- * The important thing to watch in the executor code is how pointers
- * to the slots containing tuples are passed instead of the tuples
- * themselves. This facilitates the communication of related information
- * (such as whether or not a tuple should be pfreed, what buffer contains
- * this tuple, the tuple's tuple descriptor, etc). Note that much of
- * this information is also kept in the ExprContext of each node.
- * Soon the executor will be redesigned and ExprContext's will contain
- * only slot pointers. -cim 3/14/91
- *
- * NOTES
- * The tuple table stuff is relatively new, put here to alleviate
- * the process growth problems in the executor. The other routines
- * are old (from the original lisp system) and may someday become
- * obsolete. -cim 6/23/90
- *
- * In the implementation of nested-dot queries such as
- * "retrieve (EMP.hobbies.all)", a single scan may return tuples
- * of many types, so now we return pointers to tuple descriptors
- * along with tuples returned via the tuple table. This means
- * we now have a bunch of routines to diddle the slot descriptors
- * too. -cim 1/18/90
- *
- * The tuple table stuff depends on the executor/tuptable.h macros,
- * and the TupleTableSlot node in execnodes.h.
- *
- */
-#include "postgres.h"
-
-#include "funcapi.h"
-#include "access/heapam.h"
-#include "catalog/pg_type.h"
-#include "executor/executor.h"
-
-/* ----------------------------------------------------------------
- * tuple table create/delete functions
- * ----------------------------------------------------------------
- */
-/* --------------------------------
- * ExecCreateTupleTable
- *
- * This creates a new tuple table of the specified initial
- * size. If the size is insufficient, ExecAllocTableSlot()
- * will grow the table as necessary.
- *
- * This should be used by InitPlan() to allocate the table.
- * The table's address will be stored in the EState structure.
- * --------------------------------
- */
-TupleTable /* return: address of table */
-ExecCreateTupleTable(int initialSize) /* initial number of slots in
- * table */
-{
- TupleTable newtable; /* newly allocated table */
- TupleTableSlot *array; /* newly allocated slot array */
-
- /*
- * sanity checks
- */
- Assert(initialSize >= 1);
-
- /*
- * Now allocate our new table along with space for the pointers to the
- * tuples.
- */
-
- newtable = (TupleTable) palloc(sizeof(TupleTableData));
- array = (TupleTableSlot *) palloc(initialSize * sizeof(TupleTableSlot));
-
- /*
- * clean out the slots we just allocated
- */
- MemSet(array, 0, initialSize * sizeof(TupleTableSlot));
-
- /*
- * initialize the new table and return it to the caller.
- */
- newtable->size = initialSize;
- newtable->next = 0;
- newtable->array = array;
-
- return newtable;
-}
-
-/* --------------------------------
- * ExecDropTupleTable
- *
- * This frees the storage used by the tuple table itself
- * and optionally frees the contents of the table also.
- * It is expected that this routine be called by EndPlan().
- * --------------------------------
- */
-void
-ExecDropTupleTable(TupleTable table, /* tuple table */
- bool shouldFree) /* true if we should free slot
- * contents */
-{
- int next; /* next available slot */
- TupleTableSlot *array; /* start of table array */
- int i; /* counter */
-
- /*
- * sanity checks
- */
- Assert(table != NULL);
-
- /*
- * get information from the table
- */
- array = table->array;
- next = table->next;
-
- /*
- * first free all the valid pointers in the tuple array and drop
- * refcounts of any referenced buffers, if that's what the caller
- * wants. (There is probably no good reason for the caller ever not
- * to want it!)
- */
- if (shouldFree)
- {
- for (i = 0; i < next; i++)
- {
- ExecClearTuple(&array[i]);
- if (array[i].ttc_shouldFreeDesc &&
- array[i].ttc_tupleDescriptor != NULL)
- FreeTupleDesc(array[i].ttc_tupleDescriptor);
- }
- }
-
- /*
- * finally free the tuple array and the table itself.
- */
- pfree(array);
- pfree(table);
-}
-
-
-/* ----------------------------------------------------------------
- * tuple table slot reservation functions
- * ----------------------------------------------------------------
- */
-/* --------------------------------
- * ExecAllocTableSlot
- *
- * This routine is used to reserve slots in the table for
- * use by the various plan nodes. It is expected to be
- * called by the node init routines (ex: ExecInitNestLoop)
- * once per slot needed by the node. Not all nodes need
- * slots (some just pass tuples around).
- * --------------------------------
- */
-TupleTableSlot *
-ExecAllocTableSlot(TupleTable table)
-{
- int slotnum; /* new slot number */
- TupleTableSlot *slot;
-
- /*
- * sanity checks
- */
- Assert(table != NULL);
-
- /*
- * if our table is full we have to allocate a larger size table. Since
- * ExecAllocTableSlot() is only called before the table is ever used
- * to store tuples, we don't have to worry about the contents of the
- * old table. If this changes, then we will have to preserve the
- * contents. -cim 6/23/90
- *
- * Unfortunately, we *cannot* do this. All of the nodes in the plan that
- * have already initialized their slots will have pointers into
- * _freed_ memory. This leads to bad ends. We now count the number
- * of slots we will need and create all the slots we will need ahead
- * of time. The if below should never happen now. Fail if it does.
- * -mer 4 Aug 1992
- */
- 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");
-
- /*
- * at this point, space in the table is guaranteed so we reserve the
- * next slot, initialize and return it.
- */
- slotnum = table->next;
- table->next++;
-
- slot = &(table->array[slotnum]);
-
- /* Make sure the allocated slot is valid (and empty) */
- slot->type = T_TupleTableSlot;
- slot->val = (HeapTuple) NULL;
- slot->ttc_shouldFree = true;
- slot->ttc_descIsNew = true;
- slot->ttc_shouldFreeDesc = true;
- slot->ttc_tupleDescriptor = (TupleDesc) NULL;
- slot->ttc_buffer = InvalidBuffer;
-
- return slot;
-}
-
-/* --------------------------------
- * MakeTupleTableSlot
- *
- * This routine makes an empty standalone TupleTableSlot.
- * It really shouldn't exist, but there are a few places
- * that do this, so we may as well centralize the knowledge
- * of what's in one ...
- * --------------------------------
- */
-TupleTableSlot *
-MakeTupleTableSlot(void)
-{
- TupleTableSlot *slot = makeNode(TupleTableSlot);
-
- /* This should match ExecAllocTableSlot() */
- slot->val = (HeapTuple) NULL;
- slot->ttc_shouldFree = true;
- slot->ttc_descIsNew = true;
- slot->ttc_shouldFreeDesc = true;
- slot->ttc_tupleDescriptor = (TupleDesc) NULL;
- slot->ttc_buffer = InvalidBuffer;
-
- return slot;
-}
-
-/* ----------------------------------------------------------------
- * tuple table slot accessor functions
- * ----------------------------------------------------------------
- */
-
-/* --------------------------------
- * ExecStoreTuple
- *
- * This function is used to store a tuple into a specified
- * slot in the tuple table.
- *
- * tuple: tuple to store
- * slot: slot to store it in
- * buffer: disk buffer if tuple is in a disk page, else InvalidBuffer
- * shouldFree: true if ExecClearTuple should pfree() the tuple
- * when done with it
- *
- * If 'buffer' is not InvalidBuffer, the tuple table code acquires a pin
- * on the buffer which is held until the slot is cleared, so that the tuple
- * won't go away on us.
- *
- * shouldFree is normally set 'true' for tuples constructed on-the-fly.
- * It must always be 'false' for tuples that are stored in disk pages,
- * since we don't want to try to pfree those.
- *
- * Another case where it is 'false' is when the referenced tuple is held
- * in a tuple table slot belonging to a lower-level executor Proc node.
- * In this case the lower-level slot retains ownership and responsibility
- * for eventually releasing the tuple. When this method is used, we must
- * be certain that the upper-level Proc node will lose interest in the tuple
- * sooner than the lower-level one does! If you're not certain, copy the
- * lower-level tuple with heap_copytuple and let the upper-level table
- * slot assume ownership of the copy!
- *
- * Return value is just the passed-in slot pointer.
- * --------------------------------
- */
-TupleTableSlot *
-ExecStoreTuple(HeapTuple tuple,
- TupleTableSlot *slot,
- Buffer buffer,
- bool shouldFree)
-{
- /*
- * sanity checks
- */
- Assert(slot != NULL);
- /* passing shouldFree=true for a tuple on a disk page is not sane */
- Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
-
- /* clear out any old contents of the slot */
- ExecClearTuple(slot);
-
- /*
- * store the new tuple into the specified slot and return the slot
- * into which we stored the tuple.
- */
- slot->val = tuple;
- slot->ttc_buffer = buffer;
- slot->ttc_shouldFree = shouldFree;
-
- /*
- * If tuple is on a disk page, keep the page pinned as long as we hold
- * a pointer into it.
- */
- if (BufferIsValid(buffer))
- IncrBufferRefCount(buffer);
-
- return slot;
-}
-
-/* --------------------------------
- * ExecClearTuple
- *
- * This function is used to clear out a slot in the tuple table.
- *
- * NB: only the tuple is cleared, not the tuple descriptor (if any).
- * --------------------------------
- */
-TupleTableSlot * /* return: slot passed */
-ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
-{
- HeapTuple oldtuple; /* prior contents of slot */
-
- /*
- * sanity checks
- */
- Assert(slot != NULL);
-
- /*
- * get information from the tuple table
- */
- oldtuple = slot->val;
-
- /*
- * free the old contents of the specified slot if necessary.
- */
- if (slot->ttc_shouldFree && oldtuple != NULL)
- heap_freetuple(oldtuple);
-
- slot->val = (HeapTuple) NULL;
-
- slot->ttc_shouldFree = true; /* probably useless code... */
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->ttc_buffer))
- ReleaseBuffer(slot->ttc_buffer);
-
- slot->ttc_buffer = InvalidBuffer;
-
- return slot;
-}
-
-/* --------------------------------
- * ExecSetSlotDescriptor
- *
- * This function is used to set the tuple descriptor associated
- * with the slot's tuple.
- * --------------------------------
- */
-void
-ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
- TupleDesc tupdesc, /* new tuple descriptor */
- bool shouldFree) /* is desc owned by slot? */
-{
- if (slot->ttc_shouldFreeDesc &&
- slot->ttc_tupleDescriptor != NULL)
- FreeTupleDesc(slot->ttc_tupleDescriptor);
-
- slot->ttc_tupleDescriptor = tupdesc;
- slot->ttc_shouldFreeDesc = shouldFree;
-}
-
-/* --------------------------------
- * ExecSetSlotDescriptorIsNew
- *
- * This function is used to change the setting of the "isNew" flag
- * --------------------------------
- */
-void
-ExecSetSlotDescriptorIsNew(TupleTableSlot *slot, /* slot to change */
- bool isNew) /* "isNew" setting */
-{
- slot->ttc_descIsNew = isNew;
-}
-
-/* ----------------------------------------------------------------
- * tuple table slot status predicates
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- * convenience initialization routines
- * ----------------------------------------------------------------
- */
-/* --------------------------------
- * ExecInit{Result,Scan,Extra}TupleSlot
- *
- * These are convenience routines to initialize the specified slot
- * in nodes inheriting the appropriate state. ExecInitExtraTupleSlot
- * is used for initializing special-purpose slots.
- * --------------------------------
- */
-#define INIT_SLOT_DEFS \
- TupleTable tupleTable; \
- TupleTableSlot* slot
-
-#define INIT_SLOT_ALLOC \
- tupleTable = (TupleTable) estate->es_tupleTable; \
- slot = ExecAllocTableSlot(tupleTable);
-
-/* ----------------
- * ExecInitResultTupleSlot
- * ----------------
- */
-void
-ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
-{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- commonstate->cs_ResultTupleSlot = slot;
-}
-
-/* ----------------
- * ExecInitScanTupleSlot
- * ----------------
- */
-void
-ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
-{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- commonscanstate->css_ScanTupleSlot = slot;
-}
-
-/* ----------------
- * ExecInitExtraTupleSlot
- * ----------------
- */
-TupleTableSlot *
-ExecInitExtraTupleSlot(EState *estate)
-{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- return slot;
-}
-
-/* ----------------
- * ExecInitNullTupleSlot
- *
- * Build a slot containing an all-nulls tuple of the given type.
- * This is used as a substitute for an input tuple when performing an
- * outer join.
- * ----------------
- */
-TupleTableSlot *
-ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
-{
- TupleTableSlot *slot = ExecInitExtraTupleSlot(estate);
-
- /*
- * Since heap_getattr() will treat attributes beyond a tuple's t_natts
- * as being NULL, we can make an all-nulls tuple just by making it be
- * of zero length. However, the slot descriptor must match the real
- * tupType.
- */
- HeapTuple nullTuple;
- Datum values[1];
- char nulls[1];
- static struct tupleDesc NullTupleDesc; /* we assume this inits to
- * zeroes */
-
- ExecSetSlotDescriptor(slot, tupType, false);
-
- nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
-
- return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
-}
-
-/* ----------------------------------------------------------------
- * ExecTypeFromTL
- *
- * Generate a tuple descriptor for the result tuple of a targetlist.
- * Note that resjunk columns, if any, are included in the result.
- *
- * Currently there are about 4 different places where we create
- * TupleDescriptors. They should all be merged, or perhaps
- * be rewritten to call BuildDesc().
- * ----------------------------------------------------------------
- */
-TupleDesc
-ExecTypeFromTL(List *targetList)
-{
- List *tlitem;
- TupleDesc typeInfo;
- Resdom *resdom;
- Oid restype;
- int len;
-
- /*
- * examine targetlist - if empty then return NULL
- */
- len = ExecTargetListLength(targetList);
-
- if (len == 0)
- return NULL;
-
- /*
- * allocate a new typeInfo
- */
- typeInfo = CreateTemplateTupleDesc(len);
-
- /*
- * scan list, generate type info for each entry
- */
- foreach(tlitem, targetList)
- {
- TargetEntry *tle = lfirst(tlitem);
-
- if (tle->resdom != NULL)
- {
- resdom = tle->resdom;
- restype = resdom->restype;
-
- TupleDescInitEntry(typeInfo,
- resdom->resno,
- resdom->resname,
- restype,
- resdom->restypmod,
- 0,
- false);
-
-#ifdef NOT_USED
- ExecSetTypeInfo(resdom->resno - 1,
- typeInfo,
- (Oid) restype,
- resdom->resno,
- resdom->reslen,
- NameStr(*resdom->resname),
- get_typbyval(restype),
- get_typalign(restype));
-#endif
- }
- else
- {
- /* XXX this branch looks fairly broken ... tgl 12/2000 */
- Resdom *fjRes;
- List *fjTlistP;
- List *fjList = lfirst(tlitem);
-
-#ifdef SETS_FIXED
- TargetEntry *tle;
- Fjoin *fjNode = ((TargetEntry *) lfirst(fjList))->fjoin;
-
- tle = fjNode->fj_innerNode; /* ??? */
-#endif
- fjRes = tle->resdom;
- restype = fjRes->restype;
-
- TupleDescInitEntry(typeInfo,
- fjRes->resno,
- fjRes->resname,
- restype,
- fjRes->restypmod,
- 0,
- false);
-#ifdef NOT_USED
- ExecSetTypeInfo(fjRes->resno - 1,
- typeInfo,
- (Oid) restype,
- fjRes->resno,
- fjRes->reslen,
- (char *) fjRes->resname,
- get_typbyval(restype),
- get_typalign(restype));
-#endif
-
- foreach(fjTlistP, lnext(fjList))
- {
- TargetEntry *fjTle = lfirst(fjTlistP);
-
- fjRes = fjTle->resdom;
-
- TupleDescInitEntry(typeInfo,
- fjRes->resno,
- fjRes->resname,
- restype,
- fjRes->restypmod,
- 0,
- false);
-
-#ifdef NOT_USED
- ExecSetTypeInfo(fjRes->resno - 1,
- typeInfo,
- (Oid) fjRes->restype,
- fjRes->resno,
- fjRes->reslen,
- (char *) fjRes->resname,
- get_typbyval(fjRes->restype),
- get_typalign(fjRes->restype));
-#endif
- }
- }
- }
-
- return typeInfo;
-}
-
-/*
- * TupleDescGetSlot - Initialize a slot based on the supplied
- * tupledesc
- */
-TupleTableSlot *
-TupleDescGetSlot(TupleDesc tupdesc)
-{
- TupleTableSlot *slot;
-
- /* Make a standalone slot */
- slot = MakeTupleTableSlot();
-
- /* Bind the tuple description to the slot */
- ExecSetSlotDescriptor(slot, tupdesc, true);
-
- /* Return the slot */
- return slot;
-}
-
-/*
- * TupleDescGetAttInMetadata - Get a pointer to AttInMetadata based on the
- * supplied TupleDesc. AttInMetadata can be used in conjunction with C strings
- * to produce a properly formed tuple.
- */
-AttInMetadata *
-TupleDescGetAttInMetadata(TupleDesc tupdesc)
-{
- int natts;
- int i;
- Oid atttypeid;
- Oid attinfuncid;
- Oid attelem;
- FmgrInfo *attinfuncinfo;
- Oid *attelems;
- int4 *atttypmods;
- AttInMetadata *attinmeta;
-
- attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata));
- natts = tupdesc->natts;
-
- /*
- * Gather info needed later to call the "in" function for each attribute
- */
- attinfuncinfo = (FmgrInfo *) palloc(natts * sizeof(FmgrInfo));
- attelems = (Oid *) palloc(natts * sizeof(Oid));
- atttypmods = (int4 *) palloc(natts * sizeof(int4));
-
- for (i = 0; i < natts; i++)
- {
- atttypeid = tupdesc->attrs[i]->atttypid;
- get_type_metadata(atttypeid, &attinfuncid, &attelem);
-
- fmgr_info(attinfuncid, &attinfuncinfo[i]);
- attelems[i] = attelem;
- atttypmods[i] = tupdesc->attrs[i]->atttypmod;
- }
- attinmeta->tupdesc = tupdesc;
- attinmeta->attinfuncs = attinfuncinfo;
- attinmeta->attelems = attelems;
- attinmeta->atttypmods = atttypmods;
-
- return attinmeta;
-}
-
-/*
- * BuildTupleFromCStrings - build a HeapTuple given user data in C string form.
- * values is an array of C strings, one for each attribute of the return tuple.
- */
-HeapTuple
-BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
-{
- TupleDesc tupdesc;
- int natts;
- HeapTuple tuple;
- char *nulls;
- int i;
- Datum *dvalues;
- FmgrInfo attinfuncinfo;
- Oid attelem;
- int4 atttypmod;
-
- tupdesc = attinmeta->tupdesc;
- natts = tupdesc->natts;
-
- dvalues = (Datum *) palloc(natts * sizeof(Datum));
-
- /* Call the "in" function for each attribute */
- for (i = 0; i < natts; i++)
- {
- if (values[i] != NULL)
- {
- attinfuncinfo = attinmeta->attinfuncs[i];
- attelem = attinmeta->attelems[i];
- atttypmod = attinmeta->atttypmods[i];
-
- dvalues[i] = FunctionCall3(&attinfuncinfo, CStringGetDatum(values[i]),
- ObjectIdGetDatum(attelem),
- Int32GetDatum(atttypmod));
- }
- else
- dvalues[i] = PointerGetDatum(NULL);
- }
-
- /*
- * Form a tuple
- */
- nulls = (char *) palloc(natts * sizeof(char));
- for (i = 0; i < natts; i++)
- {
- if (DatumGetPointer(dvalues[i]) != NULL)
- nulls[i] = ' ';
- else
- nulls[i] = 'n';
- }
- tuple = heap_formtuple(tupdesc, dvalues, nulls);
-
- return tuple;
-}
-
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
deleted file mode 100644
index 122417efd61..00000000000
--- a/src/backend/executor/execUtils.c
+++ /dev/null
@@ -1,793 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * execUtils.c
- * miscellaneous executor utility routines
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.83 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecAssignExprContext Common code for plan node init routines.
- *
- * ExecOpenIndices \
- * ExecCloseIndices | referenced by InitPlan, EndPlan,
- * ExecInsertIndexTuples / ExecAppend, ExecReplace
- *
- * RegisterExprContextCallback Register function shutdown callback
- * UnregisterExprContextCallback Deregister function shutdown callback
- *
- * NOTES
- * This file has traditionally been the place to stick misc.
- * executor support stuff that doesn't really go anyplace else.
- *
- */
-
-#include "postgres.h"
-
-#include "access/genam.h"
-#include "access/heapam.h"
-#include "catalog/catname.h"
-#include "catalog/index.h"
-#include "catalog/catalog.h"
-#include "catalog/pg_index.h"
-#include "executor/execdebug.h"
-#include "miscadmin.h"
-#include "utils/builtins.h"
-#include "utils/fmgroids.h"
-#include "utils/memutils.h"
-#include "utils/relcache.h"
-#include "utils/syscache.h"
-
-
-/* ----------------------------------------------------------------
- * global counters for number of tuples processed, retrieved,
- * appended, replaced, deleted.
- * ----------------------------------------------------------------
- */
-int NTupleProcessed;
-int NTupleRetrieved;
-int NTupleReplaced;
-int NTupleAppended;
-int NTupleDeleted;
-int NIndexTupleInserted;
-extern int NIndexTupleProcessed; /* have to be defined in the
- * access method level so that the
- * cinterface.a will link ok. */
-
-
-static void ShutdownExprContext(ExprContext *econtext);
-
-/* ----------------------------------------------------------------
- * statistic functions
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- * ResetTupleCount
- * ----------------------------------------------------------------
- */
-#ifdef NOT_USED
-void
-ResetTupleCount(void)
-{
- NTupleProcessed = 0;
- NTupleRetrieved = 0;
- NTupleAppended = 0;
- NTupleDeleted = 0;
- NTupleReplaced = 0;
- NIndexTupleProcessed = 0;
-}
-#endif
-
-/* ----------------------------------------------------------------
- * PrintTupleCount
- * ----------------------------------------------------------------
- */
-#ifdef NOT_USED
-void
-DisplayTupleCount(FILE *statfp)
-{
- if (NTupleProcessed > 0)
- fprintf(statfp, "!\t%d tuple%s processed, ", NTupleProcessed,
- (NTupleProcessed == 1) ? "" : "s");
- else
- {
- fprintf(statfp, "!\tno tuples processed.\n");
- return;
- }
- if (NIndexTupleProcessed > 0)
- fprintf(statfp, "%d indextuple%s processed, ", NIndexTupleProcessed,
- (NIndexTupleProcessed == 1) ? "" : "s");
- if (NIndexTupleInserted > 0)
- fprintf(statfp, "%d indextuple%s inserted, ", NIndexTupleInserted,
- (NIndexTupleInserted == 1) ? "" : "s");
- if (NTupleRetrieved > 0)
- fprintf(statfp, "%d tuple%s retrieved. ", NTupleRetrieved,
- (NTupleRetrieved == 1) ? "" : "s");
- if (NTupleAppended > 0)
- fprintf(statfp, "%d tuple%s appended. ", NTupleAppended,
- (NTupleAppended == 1) ? "" : "s");
- if (NTupleDeleted > 0)
- fprintf(statfp, "%d tuple%s deleted. ", NTupleDeleted,
- (NTupleDeleted == 1) ? "" : "s");
- if (NTupleReplaced > 0)
- fprintf(statfp, "%d tuple%s replaced. ", NTupleReplaced,
- (NTupleReplaced == 1) ? "" : "s");
- fprintf(statfp, "\n");
-}
-#endif
-
-/* ----------------------------------------------------------------
- * miscellaneous node-init support functions
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- * ExecAssignExprContext
- *
- * This initializes the ExprContext field. It is only necessary
- * to do this for nodes which use ExecQual or ExecProject
- * because those routines depend on econtext. Other nodes that
- * don't have to evaluate expressions don't need to do this.
- *
- * Note: we assume CurrentMemoryContext is the correct per-query context.
- * This should be true during plan node initialization.
- * ----------------
- */
-void
-ExecAssignExprContext(EState *estate, CommonState *commonstate)
-{
- ExprContext *econtext = makeNode(ExprContext);
-
- econtext->ecxt_scantuple = NULL;
- econtext->ecxt_innertuple = NULL;
- econtext->ecxt_outertuple = NULL;
- econtext->ecxt_per_query_memory = CurrentMemoryContext;
-
- /*
- * Create working memory for expression evaluation in this context.
- */
- econtext->ecxt_per_tuple_memory =
- AllocSetContextCreate(CurrentMemoryContext,
- "PlanExprContext",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
- econtext->ecxt_param_list_info = estate->es_param_list_info;
- econtext->ecxt_aggvalues = NULL;
- econtext->ecxt_aggnulls = NULL;
- econtext->ecxt_callbacks = NULL;
-
- 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_callbacks = NULL;
-
- 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)
-{
- /* Call any registered callbacks */
- ShutdownExprContext(econtext);
- /* And clean up the memory used */
- MemoryContextDelete(econtext->ecxt_per_tuple_memory);
- pfree(econtext);
-}
-
-/*
- * Build a per-output-tuple ExprContext for an EState.
- *
- * This is normally invoked via GetPerTupleExprContext() macro.
- */
-ExprContext *
-MakePerTupleExprContext(EState *estate)
-{
- if (estate->es_per_tuple_exprcontext == NULL)
- {
- MemoryContext oldContext;
-
- oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
- estate->es_per_tuple_exprcontext =
- MakeExprContext(NULL, estate->es_query_cxt);
- MemoryContextSwitchTo(oldContext);
- }
- return estate->es_per_tuple_exprcontext;
-}
-
-/* ----------------------------------------------------------------
- * Result slot tuple type and ProjectionInfo support
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- * ExecAssignResultType
- * ----------------
- */
-void
-ExecAssignResultType(CommonState *commonstate,
- TupleDesc tupDesc, bool shouldFree)
-{
- TupleTableSlot *slot = commonstate->cs_ResultTupleSlot;
-
- ExecSetSlotDescriptor(slot, tupDesc, shouldFree);
-}
-
-/* ----------------
- * ExecAssignResultTypeFromOuterPlan
- * ----------------
- */
-void
-ExecAssignResultTypeFromOuterPlan(Plan *node, CommonState *commonstate)
-{
- Plan *outerPlan;
- TupleDesc tupDesc;
-
- outerPlan = outerPlan(node);
- tupDesc = ExecGetTupType(outerPlan);
-
- ExecAssignResultType(commonstate, tupDesc, false);
-}
-
-/* ----------------
- * ExecAssignResultTypeFromTL
- * ----------------
- */
-void
-ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
-{
- TupleDesc tupDesc;
-
- tupDesc = ExecTypeFromTL(node->targetlist);
- ExecAssignResultType(commonstate, tupDesc, true);
-}
-
-/* ----------------
- * ExecGetResultType
- * ----------------
- */
-TupleDesc
-ExecGetResultType(CommonState *commonstate)
-{
- TupleTableSlot *slot = commonstate->cs_ResultTupleSlot;
-
- return slot->ttc_tupleDescriptor;
-}
-
-/* ----------------
- * ExecAssignProjectionInfo
- forms the projection information from the node's targetlist
- * ----------------
- */
-void
-ExecAssignProjectionInfo(Plan *node, CommonState *commonstate)
-{
- ProjectionInfo *projInfo;
- List *targetList;
- int len;
-
- targetList = node->targetlist;
- len = ExecTargetListLength(targetList);
-
- projInfo = makeNode(ProjectionInfo);
- projInfo->pi_targetlist = targetList;
- projInfo->pi_len = len;
- projInfo->pi_tupValue = (len <= 0) ? NULL : (Datum *) palloc(sizeof(Datum) * len);
- projInfo->pi_exprContext = commonstate->cs_ExprContext;
- projInfo->pi_slot = commonstate->cs_ResultTupleSlot;
-
- commonstate->cs_ProjInfo = projInfo;
-}
-
-
-/* ----------------
- * ExecFreeProjectionInfo
- * ----------------
- */
-void
-ExecFreeProjectionInfo(CommonState *commonstate)
-{
- ProjectionInfo *projInfo;
-
- /*
- * get projection info. if NULL then this node has none so we just
- * return.
- */
- projInfo = commonstate->cs_ProjInfo;
- if (projInfo == NULL)
- return;
-
- /*
- * clean up memory used.
- */
- if (projInfo->pi_tupValue != NULL)
- pfree(projInfo->pi_tupValue);
-
- pfree(projInfo);
- commonstate->cs_ProjInfo = NULL;
-}
-
-/* ----------------
- * ExecFreeExprContext
- * ----------------
- */
-void
-ExecFreeExprContext(CommonState *commonstate)
-{
- ExprContext *econtext;
-
- /*
- * get expression context. if NULL then this node has none so we just
- * return.
- */
- econtext = commonstate->cs_ExprContext;
- if (econtext == NULL)
- return;
-
- /*
- * clean up any registered callbacks
- */
- ShutdownExprContext(econtext);
-
- /*
- * clean up memory used.
- */
- MemoryContextDelete(econtext->ecxt_per_tuple_memory);
- pfree(econtext);
- commonstate->cs_ExprContext = NULL;
-}
-
-/* ----------------------------------------------------------------
- * the following scan type support functions are for
- * those nodes which are stubborn and return tuples in
- * their Scan tuple slot instead of their Result tuple
- * slot.. luck fur us, these nodes do not do projections
- * so we don't have to worry about getting the ProjectionInfo
- * right for them... -cim 6/3/91
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- * ExecGetScanType
- * ----------------
- */
-TupleDesc
-ExecGetScanType(CommonScanState *csstate)
-{
- TupleTableSlot *slot = csstate->css_ScanTupleSlot;
-
- return slot->ttc_tupleDescriptor;
-}
-
-/* ----------------
- * ExecAssignScanType
- * ----------------
- */
-void
-ExecAssignScanType(CommonScanState *csstate,
- TupleDesc tupDesc, bool shouldFree)
-{
- TupleTableSlot *slot = csstate->css_ScanTupleSlot;
-
- ExecSetSlotDescriptor(slot, tupDesc, shouldFree);
-}
-
-/* ----------------
- * ExecAssignScanTypeFromOuterPlan
- * ----------------
- */
-void
-ExecAssignScanTypeFromOuterPlan(Plan *node, CommonScanState *csstate)
-{
- Plan *outerPlan;
- TupleDesc tupDesc;
-
- outerPlan = outerPlan(node);
- tupDesc = ExecGetTupType(outerPlan);
-
- ExecAssignScanType(csstate, tupDesc, false);
-}
-
-
-/* ----------------------------------------------------------------
- * ExecInsertIndexTuples support
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- * ExecOpenIndices
- *
- * Find the indices associated with a result relation, open them,
- * and save information about them in the result ResultRelInfo.
- *
- * At entry, caller has already opened and locked
- * resultRelInfo->ri_RelationDesc.
- *
- * This used to be horribly ugly code, and slow too because it
- * did a sequential scan of pg_index. Now we rely on the relcache
- * to cache a list of the OIDs of the indices associated with any
- * specific relation, and we use the pg_index syscache to get the
- * entries we need from pg_index.
- * ----------------------------------------------------------------
- */
-void
-ExecOpenIndices(ResultRelInfo *resultRelInfo)
-{
- Relation resultRelation = resultRelInfo->ri_RelationDesc;
- List *indexoidlist,
- *indexoidscan;
- int len,
- i;
- RelationPtr relationDescs;
- IndexInfo **indexInfoArray;
-
- resultRelInfo->ri_NumIndices = 0;
-
- /* checks for disabled indexes */
- if (!RelationGetForm(resultRelation)->relhasindex)
- return;
- if (IsIgnoringSystemIndexes() &&
- IsSystemRelation(resultRelation))
- return;
-
- /*
- * Get cached list of index OIDs
- */
- indexoidlist = RelationGetIndexList(resultRelation);
- len = length(indexoidlist);
- if (len == 0)
- return;
-
- /*
- * allocate space for result arrays
- */
- relationDescs = (RelationPtr) palloc(len * sizeof(Relation));
- indexInfoArray = (IndexInfo **) palloc(len * sizeof(IndexInfo *));
-
- resultRelInfo->ri_NumIndices = len;
- resultRelInfo->ri_IndexRelationDescs = relationDescs;
- resultRelInfo->ri_IndexRelationInfo = indexInfoArray;
-
- /*
- * For each index, open the index relation and save pg_index info.
- */
- i = 0;
- foreach(indexoidscan, indexoidlist)
- {
- Oid indexOid = lfirsti(indexoidscan);
- Relation indexDesc;
- IndexInfo *ii;
-
- /*
- * Open (and lock, if necessary) the index relation
- *
- * If the index AM is not safe for concurrent updates, obtain an
- * exclusive lock on the index to lock out other updaters as well
- * as readers (index_beginscan places AccessShareLock). We will
- * release this lock in ExecCloseIndices.
- *
- * If the index AM supports concurrent updates, we obtain no lock
- * here at all, which is a tad weird, but safe since any critical
- * operation on the index (like deleting it) will acquire
- * exclusive lock on the parent table. Perhaps someday we should
- * acquire RowExclusiveLock on the index here?
- *
- * If there are multiple not-concurrent-safe indexes, all backends
- * must lock the indexes in the same order or we will get
- * deadlocks here during concurrent updates. This is guaranteed
- * by RelationGetIndexList(), which promises to return the index
- * list in OID order.
- */
- indexDesc = index_open(indexOid);
-
- if (!indexDesc->rd_am->amconcurrent)
- LockRelation(indexDesc, AccessExclusiveLock);
-
- /*
- * extract index key information from the index's pg_index tuple
- */
- ii = BuildIndexInfo(indexDesc->rd_index);
-
- relationDescs[i] = indexDesc;
- indexInfoArray[i] = ii;
- i++;
- }
-
- freeList(indexoidlist);
-}
-
-/* ----------------------------------------------------------------
- * ExecCloseIndices
- *
- * Close the index relations stored in resultRelInfo
- * ----------------------------------------------------------------
- */
-void
-ExecCloseIndices(ResultRelInfo *resultRelInfo)
-{
- int i;
- int numIndices;
- RelationPtr indexDescs;
-
- numIndices = resultRelInfo->ri_NumIndices;
- indexDescs = resultRelInfo->ri_IndexRelationDescs;
-
- for (i = 0; i < numIndices; i++)
- {
- if (indexDescs[i] == NULL)
- continue;
-
- /* Drop lock, if one was acquired by ExecOpenIndices */
- if (!indexDescs[i]->rd_am->amconcurrent)
- UnlockRelation(indexDescs[i], AccessExclusiveLock);
-
- index_close(indexDescs[i]);
- }
-
- /*
- * XXX should free indexInfo array here too.
- */
-}
-
-/* ----------------------------------------------------------------
- * ExecInsertIndexTuples
- *
- * This routine takes care of inserting index tuples
- * into all the relations indexing the result relation
- * when a heap tuple is inserted into the result relation.
- * Much of this code should be moved into the genam
- * stuff as it only exists here because the genam stuff
- * doesn't provide the functionality needed by the
- * executor.. -cim 9/27/89
- * ----------------------------------------------------------------
- */
-void
-ExecInsertIndexTuples(TupleTableSlot *slot,
- ItemPointer tupleid,
- EState *estate,
- bool is_vacuum)
-{
- HeapTuple heapTuple;
- ResultRelInfo *resultRelInfo;
- int i;
- int numIndices;
- RelationPtr relationDescs;
- Relation heapRelation;
- TupleDesc heapDescriptor;
- IndexInfo **indexInfoArray;
- ExprContext *econtext;
- Datum datum[INDEX_MAX_KEYS];
- char nullv[INDEX_MAX_KEYS];
-
- heapTuple = slot->val;
-
- /*
- * Get information from the result relation info structure.
- */
- resultRelInfo = estate->es_result_relation_info;
- numIndices = resultRelInfo->ri_NumIndices;
- relationDescs = resultRelInfo->ri_IndexRelationDescs;
- indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
- heapRelation = resultRelInfo->ri_RelationDesc;
- heapDescriptor = RelationGetDescr(heapRelation);
-
- /*
- * We will use the EState's per-tuple context for evaluating
- * predicates and functional-index functions (creating it if it's not
- * already there).
- */
- econtext = GetPerTupleExprContext(estate);
-
- /* Arrange for econtext's scan tuple to be the tuple under test */
- econtext->ecxt_scantuple = slot;
-
- /*
- * for each index, form and insert the index tuple
- */
- for (i = 0; i < numIndices; i++)
- {
- IndexInfo *indexInfo;
- List *predicate;
- InsertIndexResult result;
-
- if (relationDescs[i] == NULL)
- continue;
-
- indexInfo = indexInfoArray[i];
- predicate = indexInfo->ii_Predicate;
- if (predicate != NIL)
- {
- /* Skip this index-update if the predicate isn't satisfied */
- if (!ExecQual(predicate, econtext, false))
- continue;
- }
-
- /*
- * FormIndexDatum fills in its datum and null parameters with
- * attribute information taken from the given heap tuple.
- */
- FormIndexDatum(indexInfo,
- heapTuple,
- heapDescriptor,
- econtext->ecxt_per_tuple_memory,
- datum,
- nullv);
-
- /*
- * The index AM does the rest. Note we suppress unique-index
- * checks if we are being called from VACUUM, since VACUUM may
- * need to move dead tuples that have the same keys as live ones.
- */
- result = index_insert(relationDescs[i], /* index relation */
- datum, /* array of heaptuple Datums */
- nullv, /* info on nulls */
- &(heapTuple->t_self), /* tid of heap tuple */
- heapRelation,
- relationDescs[i]->rd_uniqueindex && !is_vacuum);
-
- /*
- * keep track of index inserts for debugging
- */
- IncrIndexInserted();
-
- if (result)
- pfree(result);
- }
-}
-
-void
-SetChangedParamList(Plan *node, List *newchg)
-{
- List *nl;
-
- foreach(nl, newchg)
- {
- int paramId = lfirsti(nl);
-
- /* if this node doesn't depend on a param ... */
- if (!intMember(paramId, node->extParam) &&
- !intMember(paramId, node->locParam))
- continue;
- /* if this param is already in list of changed ones ... */
- if (intMember(paramId, node->chgParam))
- continue;
- /* else - add this param to the list */
- node->chgParam = lappendi(node->chgParam, paramId);
- }
-}
-
-/*
- * Register a shutdown callback in an ExprContext.
- *
- * Shutdown callbacks will be called (in reverse order of registration)
- * when the ExprContext is deleted or rescanned. This provides a hook
- * for functions called in the context to do any cleanup needed --- it's
- * particularly useful for functions returning sets. Note that the
- * callback will *not* be called in the event that execution is aborted
- * by an error.
- */
-void
-RegisterExprContextCallback(ExprContext *econtext,
- ExprContextCallbackFunction function,
- Datum arg)
-{
- ExprContext_CB *ecxt_callback;
-
- /* Save the info in appropriate memory context */
- ecxt_callback = (ExprContext_CB *)
- MemoryContextAlloc(econtext->ecxt_per_query_memory,
- sizeof(ExprContext_CB));
-
- ecxt_callback->function = function;
- ecxt_callback->arg = arg;
-
- /* link to front of list for appropriate execution order */
- ecxt_callback->next = econtext->ecxt_callbacks;
- econtext->ecxt_callbacks = ecxt_callback;
-}
-
-/*
- * Deregister a shutdown callback in an ExprContext.
- *
- * Any list entries matching the function and arg will be removed.
- * This can be used if it's no longer necessary to call the callback.
- */
-void
-UnregisterExprContextCallback(ExprContext *econtext,
- ExprContextCallbackFunction function,
- Datum arg)
-{
- ExprContext_CB **prev_callback;
- ExprContext_CB *ecxt_callback;
-
- prev_callback = &econtext->ecxt_callbacks;
-
- while ((ecxt_callback = *prev_callback) != NULL)
- {
- if (ecxt_callback->function == function && ecxt_callback->arg == arg)
- {
- *prev_callback = ecxt_callback->next;
- pfree(ecxt_callback);
- }
- else
- {
- prev_callback = &ecxt_callback->next;
- }
- }
-}
-
-/*
- * Call all the shutdown callbacks registered in an ExprContext.
- *
- * The callback list is emptied (important in case this is only a rescan
- * reset, and not deletion of the ExprContext).
- */
-static void
-ShutdownExprContext(ExprContext *econtext)
-{
- ExprContext_CB *ecxt_callback;
-
- /*
- * Call each callback function in reverse registration order.
- */
- while ((ecxt_callback = econtext->ecxt_callbacks) != NULL)
- {
- econtext->ecxt_callbacks = ecxt_callback->next;
- (*ecxt_callback->function) (ecxt_callback->arg);
- pfree(ecxt_callback);
- }
-}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
deleted file mode 100644
index c7b7c398eb2..00000000000
--- a/src/backend/executor/functions.c
+++ /dev/null
@@ -1,611 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * functions.c
- * Routines to handle functions called from the executor
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.52 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "catalog/pg_proc.h"
-#include "catalog/pg_type.h"
-#include "executor/execdefs.h"
-#include "executor/executor.h"
-#include "executor/functions.h"
-#include "tcop/pquery.h"
-#include "tcop/tcopprot.h"
-#include "tcop/utility.h"
-#include "utils/builtins.h"
-#include "utils/syscache.h"
-
-
-/*
- * We have an execution_state record for each query in a function.
- */
-typedef enum
-{
- F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE
-} ExecStatus;
-
-typedef struct local_es
-{
- QueryDesc *qd;
- EState *estate;
- struct local_es *next;
- ExecStatus status;
-} execution_state;
-
-#define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *) NULL)
-
-
-/*
- * An SQLFunctionCache record is built during the first call,
- * and linked to from the fn_extra field of the FmgrInfo struct.
- */
-
-typedef struct
-{
- int typlen; /* length of the return type */
- bool typbyval; /* true if return type is pass by value */
- bool returnsTuple; /* true if return type is a tuple */
- bool shutdown_reg; /* true if registered shutdown callback */
-
- TupleTableSlot *funcSlot; /* if one result we need to copy it before
- * we end execution of the function and
- * free stuff */
-
- /* head of linked list of execution_state records */
- execution_state *func_state;
-} SQLFunctionCache;
-
-typedef SQLFunctionCache *SQLFunctionCachePtr;
-
-
-/* non-export function prototypes */
-static execution_state *init_execution_state(char *src,
- Oid *argOidVect, int nargs);
-static void init_sql_fcache(FmgrInfo *finfo);
-static void postquel_start(execution_state *es);
-static TupleTableSlot *postquel_getnext(execution_state *es);
-static void postquel_end(execution_state *es);
-static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo);
-static Datum postquel_execute(execution_state *es,
- FunctionCallInfo fcinfo,
- SQLFunctionCachePtr fcache);
-static void ShutdownSQLFunction(Datum arg);
-
-
-static execution_state *
-init_execution_state(char *src, Oid *argOidVect, int nargs)
-{
- execution_state *newes;
- execution_state *nextes;
- execution_state *preves;
- List *queryTree_list,
- *qtl_item;
-
- newes = (execution_state *) palloc(sizeof(execution_state));
- nextes = newes;
- preves = (execution_state *) NULL;
-
- queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
-
- foreach(qtl_item, queryTree_list)
- {
- Query *queryTree = lfirst(qtl_item);
- Plan *planTree;
- EState *estate;
-
- planTree = pg_plan_query(queryTree);
-
- if (!nextes)
- nextes = (execution_state *) palloc(sizeof(execution_state));
- if (preves)
- preves->next = nextes;
-
- nextes->next = NULL;
- nextes->status = F_EXEC_START;
-
- nextes->qd = CreateQueryDesc(queryTree, planTree, None, NULL);
- estate = CreateExecutorState();
-
- if (nargs > 0)
- {
- int i;
- ParamListInfo paramLI;
-
- paramLI = (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
-
- MemSet(paramLI, 0, nargs * sizeof(ParamListInfoData));
-
- estate->es_param_list_info = paramLI;
-
- for (i = 0; i < nargs; paramLI++, i++)
- {
- paramLI->kind = PARAM_NUM;
- paramLI->id = i + 1;
- paramLI->isnull = false;
- paramLI->value = (Datum) NULL;
- }
- paramLI->kind = PARAM_INVALID;
- }
- else
- estate->es_param_list_info = (ParamListInfo) NULL;
- nextes->estate = estate;
- preves = nextes;
- nextes = (execution_state *) NULL;
- }
-
- return newes;
-}
-
-
-static void
-init_sql_fcache(FmgrInfo *finfo)
-{
- Oid foid = finfo->fn_oid;
- HeapTuple procedureTuple;
- HeapTuple typeTuple;
- Form_pg_proc procedureStruct;
- Form_pg_type typeStruct;
- SQLFunctionCachePtr fcache;
- Oid *argOidVect;
- char *src;
- int nargs;
- Datum tmp;
- bool isNull;
-
- /*
- * get the procedure tuple corresponding to the given function Oid
- */
- procedureTuple = SearchSysCache(PROCOID,
- ObjectIdGetDatum(foid),
- 0, 0, 0);
- if (!HeapTupleIsValid(procedureTuple))
- elog(ERROR, "init_sql_fcache: Cache lookup failed for procedure %u",
- foid);
-
- procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
-
- /*
- * get the return type from the procedure tuple
- */
- typeTuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(procedureStruct->prorettype),
- 0, 0, 0);
- if (!HeapTupleIsValid(typeTuple))
- elog(ERROR, "init_sql_fcache: Cache lookup failed for type %u",
- procedureStruct->prorettype);
-
- typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
-
- fcache = (SQLFunctionCachePtr) palloc(sizeof(SQLFunctionCache));
- MemSet(fcache, 0, sizeof(SQLFunctionCache));
-
- /*
- * get the type length and by-value flag from the type tuple
- */
- fcache->typlen = typeStruct->typlen;
- if (typeStruct->typrelid == InvalidOid)
- {
- /* The return type is not a relation, so just use byval */
- fcache->typbyval = typeStruct->typbyval;
- fcache->returnsTuple = false;
- }
- else
- {
- /*
- * This is a hack. We assume here that any function returning a
- * tuple returns it by reference. This needs to be fixed, since
- * actually the mechanism isn't quite like return-by-reference.
- */
- fcache->typbyval = false;
- fcache->returnsTuple = true;
- }
-
- /*
- * If we are returning exactly one result then we have to copy tuples
- * and by reference results because we have to end the execution
- * before we return the results. When you do this everything
- * allocated by the executor (i.e. slots and tuples) is freed.
- */
- if (!finfo->fn_retset && !fcache->typbyval)
- fcache->funcSlot = MakeTupleTableSlot();
- else
- fcache->funcSlot = NULL;
-
- nargs = procedureStruct->pronargs;
-
- if (nargs > 0)
- {
- argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
- memcpy(argOidVect,
- procedureStruct->proargtypes,
- nargs * sizeof(Oid));
- }
- else
- argOidVect = (Oid *) NULL;
-
- tmp = SysCacheGetAttr(PROCOID,
- procedureTuple,
- Anum_pg_proc_prosrc,
- &isNull);
- if (isNull)
- elog(ERROR, "init_sql_fcache: null prosrc for procedure %u",
- foid);
- src = DatumGetCString(DirectFunctionCall1(textout, tmp));
-
- fcache->func_state = init_execution_state(src, argOidVect, nargs);
-
- pfree(src);
-
- ReleaseSysCache(typeTuple);
- ReleaseSysCache(procedureTuple);
-
- finfo->fn_extra = (void *) fcache;
-}
-
-
-static void
-postquel_start(execution_state *es)
-{
- /*
- * Do nothing for utility commands. (create, destroy...) DZ -
- * 30-8-1996
- */
- if (es->qd->operation == CMD_UTILITY)
- return;
- ExecutorStart(es->qd, es->estate);
-}
-
-static TupleTableSlot *
-postquel_getnext(execution_state *es)
-{
- long count;
-
- if (es->qd->operation == CMD_UTILITY)
- {
- /*
- * Process a utility command. (create, destroy...) DZ - 30-8-1996
- */
- ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest, NULL);
- if (!LAST_POSTQUEL_COMMAND(es))
- CommandCounterIncrement();
- return (TupleTableSlot *) NULL;
- }
-
- /* If it's not the last command, just run it to completion */
- count = (LAST_POSTQUEL_COMMAND(es)) ? 1L : 0L;
-
- return ExecutorRun(es->qd, es->estate, ForwardScanDirection, count);
-}
-
-static void
-postquel_end(execution_state *es)
-{
- /*
- * Do nothing for utility commands. (create, destroy...) DZ -
- * 30-8-1996
- */
- if (es->qd->operation == CMD_UTILITY)
- return;
- ExecutorEnd(es->qd, es->estate);
-}
-
-static void
-postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo)
-{
- EState *estate;
- ParamListInfo paramLI;
-
- estate = es->estate;
- paramLI = estate->es_param_list_info;
-
- while (paramLI->kind != PARAM_INVALID)
- {
- if (paramLI->kind == PARAM_NUM)
- {
- Assert(paramLI->id <= fcinfo->nargs);
- paramLI->value = fcinfo->arg[paramLI->id - 1];
- paramLI->isnull = fcinfo->argnull[paramLI->id - 1];
- }
- paramLI++;
- }
-}
-
-static TupleTableSlot *
-copy_function_result(SQLFunctionCachePtr fcache,
- TupleTableSlot *resultSlot)
-{
- TupleTableSlot *funcSlot;
- TupleDesc resultTd;
- HeapTuple resultTuple;
- HeapTuple newTuple;
-
- Assert(!TupIsNull(resultSlot));
- resultTuple = resultSlot->val;
-
- funcSlot = fcache->funcSlot;
-
- if (funcSlot == NULL)
- return resultSlot; /* no need to copy result */
-
- /*
- * If first time through, we have to initialize the funcSlot's tuple
- * descriptor.
- */
- if (funcSlot->ttc_tupleDescriptor == NULL)
- {
- resultTd = CreateTupleDescCopy(resultSlot->ttc_tupleDescriptor);
- ExecSetSlotDescriptor(funcSlot, resultTd, true);
- ExecSetSlotDescriptorIsNew(funcSlot, true);
- }
-
- newTuple = heap_copytuple(resultTuple);
-
- return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
-}
-
-static Datum
-postquel_execute(execution_state *es,
- FunctionCallInfo fcinfo,
- SQLFunctionCachePtr fcache)
-{
- TupleTableSlot *slot;
- Datum value;
-
- /*
- * It's more right place to do it (before
- * postquel_start->ExecutorStart). Now
- * ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But
- * note: I HOPE we can do it here). - vadim 01/22/97
- */
- if (fcinfo->nargs > 0)
- postquel_sub_params(es, fcinfo);
-
- if (es->status == F_EXEC_START)
- {
- postquel_start(es);
- es->status = F_EXEC_RUN;
- }
-
- slot = postquel_getnext(es);
-
- if (TupIsNull(slot))
- {
- postquel_end(es);
- es->status = F_EXEC_DONE;
- fcinfo->isnull = true;
-
- /*
- * If this isn't the last command for the function we have to
- * increment the command counter so that subsequent commands can
- * see changes made by previous ones.
- */
- if (!LAST_POSTQUEL_COMMAND(es))
- CommandCounterIncrement();
- return (Datum) NULL;
- }
-
- if (LAST_POSTQUEL_COMMAND(es))
- {
- TupleTableSlot *resSlot;
-
- /*
- * Copy the result. copy_function_result is smart enough to do
- * nothing when no action is called for. This helps reduce the
- * logic and code redundancy here.
- */
- resSlot = copy_function_result(fcache, slot);
-
- /*
- * If we are supposed to return a tuple, we return the tuple slot
- * pointer converted to Datum. If we are supposed to return a
- * simple value, then project out the first attribute of the
- * result tuple (ie, take the first result column of the final
- * SELECT).
- */
- if (fcache->returnsTuple)
- {
- /*
- * XXX do we need to remove junk attrs from the result tuple?
- * Probably OK to leave them, as long as they are at the end.
- */
- value = PointerGetDatum(resSlot);
- fcinfo->isnull = false;
- }
- else
- {
- value = heap_getattr(resSlot->val,
- 1,
- resSlot->ttc_tupleDescriptor,
- &(fcinfo->isnull));
-
- /*
- * Note: if result type is pass-by-reference then we are
- * returning a pointer into the tuple copied by
- * copy_function_result. This is OK.
- */
- }
-
- /*
- * If this is a single valued function we have to end the function
- * execution now.
- */
- if (!fcinfo->flinfo->fn_retset)
- {
- postquel_end(es);
- es->status = F_EXEC_DONE;
- }
-
- return value;
- }
-
- /*
- * If this isn't the last command for the function, we don't return
- * any results, but we have to increment the command counter so that
- * subsequent commands can see changes made by previous ones.
- */
- CommandCounterIncrement();
- return (Datum) NULL;
-}
-
-Datum
-fmgr_sql(PG_FUNCTION_ARGS)
-{
- MemoryContext oldcontext;
- SQLFunctionCachePtr fcache;
- execution_state *es;
- Datum result = 0;
-
- /*
- * 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(fcinfo->flinfo->fn_mcxt);
-
- /*
- * Initialize fcache and execution state if first time through.
- */
- fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
- if (fcache == NULL)
- {
- init_sql_fcache(fcinfo->flinfo);
- fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
- }
- es = fcache->func_state;
- Assert(es);
-
- /*
- * Find first unfinished query in function.
- */
- while (es && es->status == F_EXEC_DONE)
- es = es->next;
-
- Assert(es);
-
- /*
- * Execute each command in the function one after another until we're
- * executing the final command and get a result or we run out of
- * commands.
- */
- while (es != (execution_state *) NULL)
- {
- result = postquel_execute(es, fcinfo, fcache);
- if (es->status != F_EXEC_DONE)
- break;
- es = es->next;
- }
-
- /*
- * If we've gone through every command in this function, we are done.
- */
- if (es == (execution_state *) NULL)
- {
- /*
- * Reset the execution states to start over again
- */
- es = fcache->func_state;
- while (es)
- {
- es->status = F_EXEC_START;
- es = es->next;
- }
-
- /*
- * Let caller know we're finished.
- */
- if (fcinfo->flinfo->fn_retset)
- {
- ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-
- if (rsi && IsA(rsi, ReturnSetInfo))
- rsi->isDone = ExprEndResult;
- else
- elog(ERROR, "Set-valued function called in context that cannot accept a set");
- fcinfo->isnull = true;
- result = (Datum) 0;
-
- /* Deregister shutdown callback, if we made one */
- if (fcache->shutdown_reg)
- {
- UnregisterExprContextCallback(rsi->econtext,
- ShutdownSQLFunction,
- PointerGetDatum(fcache));
- fcache->shutdown_reg = false;
- }
- }
-
- MemoryContextSwitchTo(oldcontext);
-
- return result;
- }
-
- /*
- * If we got a result from a command within the function it has to be
- * the final command. All others shouldn't be returning anything.
- */
- Assert(LAST_POSTQUEL_COMMAND(es));
-
- /*
- * Let caller know we're not finished.
- */
- if (fcinfo->flinfo->fn_retset)
- {
- ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-
- if (rsi && IsA(rsi, ReturnSetInfo))
- rsi->isDone = ExprMultipleResult;
- else
- elog(ERROR, "Set-valued function called in context that cannot accept a set");
-
- /*
- * Ensure we will get shut down cleanly if the exprcontext is
- * not run to completion.
- */
- if (!fcache->shutdown_reg)
- {
- RegisterExprContextCallback(rsi->econtext,
- ShutdownSQLFunction,
- PointerGetDatum(fcache));
- fcache->shutdown_reg = true;
- }
- }
-
- MemoryContextSwitchTo(oldcontext);
-
- return result;
-}
-
-/*
- * callback function in case a function-returning-set needs to be shut down
- * before it has been run to completion
- */
-static void
-ShutdownSQLFunction(Datum arg)
-{
- SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) DatumGetPointer(arg);
- execution_state *es = fcache->func_state;
-
- while (es != NULL)
- {
- /* Shut down anything still running */
- if (es->status == F_EXEC_RUN)
- postquel_end(es);
- /* Reset states to START in case we're called again */
- es->status = F_EXEC_START;
- es = es->next;
- }
-
- /* execUtils will deregister the callback... */
- fcache->shutdown_reg = false;
-}
diff --git a/src/backend/executor/instrument.c b/src/backend/executor/instrument.c
deleted file mode 100644
index 3b95544706b..00000000000
--- a/src/backend/executor/instrument.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * instrument.c
- * functions for instrumentation of plan execution
- *
- *
- * Copyright (c) 2001, PostgreSQL Global Development Group
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/instrument.c,v 1.3 2002/03/02 21:39:25 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include <unistd.h>
-
-#include "executor/instrument.h"
-
-
-/* Allocate new instrumentation structure */
-Instrumentation *
-InstrAlloc(void)
-{
- Instrumentation *instr = palloc(sizeof(Instrumentation));
-
- memset(instr, 0, sizeof(Instrumentation));
-
- return instr;
-}
-
-/* Entry to a plan node */
-void
-InstrStartNode(Instrumentation *instr)
-{
- if (!instr)
- return;
-
- if (instr->starttime.tv_sec != 0 || instr->starttime.tv_usec != 0)
- elog(LOG, "InstrStartTimer called twice in a row");
- else
- gettimeofday(&instr->starttime, NULL);
-}
-
-/* Exit from a plan node */
-void
-InstrStopNode(Instrumentation *instr, bool returnedTuple)
-{
- struct timeval endtime;
-
- if (!instr)
- return;
-
- if (instr->starttime.tv_sec == 0 && instr->starttime.tv_usec == 0)
- {
- elog(LOG, "InstrStopNode without start");
- return;
- }
-
- gettimeofday(&endtime, NULL);
-
- instr->counter.tv_sec += endtime.tv_sec - instr->starttime.tv_sec;
- instr->counter.tv_usec += endtime.tv_usec - instr->starttime.tv_usec;
-
- /* Normalize after each add to avoid overflow/underflow of tv_usec */
- while (instr->counter.tv_usec < 0)
- {
- instr->counter.tv_usec += 1000000;
- instr->counter.tv_sec--;
- }
- while (instr->counter.tv_usec >= 1000000)
- {
- instr->counter.tv_usec -= 1000000;
- instr->counter.tv_sec++;
- }
-
- instr->starttime.tv_sec = 0;
- instr->starttime.tv_usec = 0;
-
- /* Is this the first tuple of this cycle? */
- if (!instr->running)
- {
- instr->running = true;
- instr->firsttuple = (double) instr->counter.tv_sec +
- (double) instr->counter.tv_usec / 1000000.0;
- }
-
- if (returnedTuple)
- instr->tuplecount += 1;
-}
-
-/* Finish a run cycle for a plan node */
-void
-InstrEndLoop(Instrumentation *instr)
-{
- double totaltime;
-
- if (!instr)
- return;
-
- /* Skip if nothing has happened, or already shut down */
- if (!instr->running)
- return;
-
- /* Accumulate statistics */
- totaltime = (double) instr->counter.tv_sec +
- (double) instr->counter.tv_usec / 1000000.0;
-
- instr->startup += instr->firsttuple;
- instr->total += totaltime;
- instr->ntuples += instr->tuplecount;
- instr->nloops += 1;
-
- /* Reset for next cycle (if any) */
- instr->running = false;
- instr->starttime.tv_sec = 0;
- instr->starttime.tv_usec = 0;
- instr->counter.tv_sec = 0;
- instr->counter.tv_usec = 0;
- instr->firsttuple = 0;
- instr->tuplecount = 0;
-}
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
deleted file mode 100644
index de839269cc3..00000000000
--- a/src/backend/executor/nodeAgg.c
+++ /dev/null
@@ -1,1053 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeAgg.c
- * Routines to handle aggregate nodes.
- *
- * ExecAgg evaluates each aggregate in the following steps:
- *
- * transvalue = initcond
- * foreach input_value do
- * transvalue = transfunc(transvalue, input_value)
- * result = finalfunc(transvalue)
- *
- * If a finalfunc is not supplied then the result is just the ending
- * value of transvalue.
- *
- * If transfunc is marked "strict" in pg_proc and initcond is NULL,
- * then the first non-NULL input_value is assigned directly to transvalue,
- * and transfunc isn't applied until the second non-NULL input_value.
- * The agg's input type and transtype must be the same in this case!
- *
- * If transfunc is marked "strict" then NULL input_values are skipped,
- * keeping the previous transvalue. If transfunc is not strict then it
- * is called for every input tuple and must deal with NULL initcond
- * or NULL input_value for itself.
- *
- * If finalfunc is marked "strict" then it is not called when the
- * ending transvalue is NULL, instead a NULL result is created
- * automatically (this is just the usual handling of strict functions,
- * of course). A non-strict finalfunc can make its own choice of
- * what to return for a NULL ending transvalue.
- *
- * When the transvalue datatype is pass-by-reference, we have to be
- * careful to ensure that the values survive across tuple cycles yet
- * are not allowed to accumulate until end of query. We do this by
- * "ping-ponging" between two memory contexts; successive calls to the
- * transfunc are executed in alternate contexts, passing the previous
- * transvalue that is in the other context. At the beginning of each
- * tuple cycle we can reset the current output context to avoid memory
- * usage growth. Note: we must use MemoryContextContains() to check
- * whether the transfunc has perhaps handed us back one of its input
- * values rather than a freshly palloc'd value; if so, we copy the value
- * to the context we want it in.
- *
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.85 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "catalog/pg_aggregate.h"
-#include "catalog/pg_operator.h"
-#include "executor/executor.h"
-#include "executor/nodeAgg.h"
-#include "miscadmin.h"
-#include "optimizer/clauses.h"
-#include "parser/parse_coerce.h"
-#include "parser/parse_expr.h"
-#include "parser/parse_oper.h"
-#include "utils/acl.h"
-#include "utils/builtins.h"
-#include "utils/lsyscache.h"
-#include "utils/syscache.h"
-#include "utils/tuplesort.h"
-#include "utils/datum.h"
-
-
-/*
- * AggStatePerAggData - per-aggregate working state for the Agg scan
- */
-typedef struct AggStatePerAggData
-{
- /*
- * These values are set up during ExecInitAgg() and do not change
- * thereafter:
- */
-
- /* Link to Aggref node this working state is for */
- Aggref *aggref;
-
- /* Oids of transfer functions */
- Oid transfn_oid;
- Oid finalfn_oid; /* may be InvalidOid */
-
- /*
- * fmgr lookup data for transfer functions --- only valid when
- * corresponding oid is not InvalidOid. Note in particular that
- * fn_strict flags are kept here.
- */
- FmgrInfo transfn;
- FmgrInfo finalfn;
-
- /*
- * Type of input data and Oid of sort operator to use for it; only
- * set/used when aggregate has DISTINCT flag. (These are not used
- * directly by nodeAgg, but must be passed to the Tuplesort object.)
- */
- Oid inputType;
- Oid sortOperator;
-
- /*
- * fmgr lookup data for input type's equality operator --- only
- * set/used when aggregate has DISTINCT flag.
- */
- FmgrInfo equalfn;
-
- /*
- * initial value from pg_aggregate entry
- */
- Datum initValue;
- bool initValueIsNull;
-
- /*
- * We need the len and byval info for the agg's input, result, and
- * transition data types in order to know how to copy/delete values.
- */
- int16 inputtypeLen,
- resulttypeLen,
- transtypeLen;
- bool inputtypeByVal,
- resulttypeByVal,
- transtypeByVal;
-
- /*
- * These values are working state that is initialized at the start of
- * an input tuple group and updated for each input tuple.
- *
- * For a simple (non DISTINCT) aggregate, we just feed the input values
- * straight to the transition function. If it's DISTINCT, we pass the
- * input values into a Tuplesort object; then at completion of the
- * input tuple group, we scan the sorted values, eliminate duplicates,
- * and run the transition function on the rest.
- */
-
- Tuplesortstate *sortstate; /* sort object, if a DISTINCT agg */
-
- Datum transValue;
- bool transValueIsNull;
-
- bool noTransValue; /* true if transValue not set yet */
-
- /*
- * Note: noTransValue initially has the same value as
- * transValueIsNull, and if true both are cleared to false at the same
- * time. They are not the same though: if transfn later returns a
- * NULL, we want to keep that NULL and not auto-replace it with a
- * later input value. Only the first non-NULL input will be
- * auto-substituted.
- */
-} AggStatePerAggData;
-
-
-static void initialize_aggregate(AggStatePerAgg peraggstate);
-static void advance_transition_function(AggStatePerAgg peraggstate,
- Datum newVal, bool isNull);
-static void process_sorted_aggregate(AggState *aggstate,
- AggStatePerAgg peraggstate);
-static void finalize_aggregate(AggStatePerAgg peraggstate,
- Datum *resultVal, bool *resultIsNull);
-static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
-
-
-/*
- * Initialize one aggregate for a new set of input values.
- *
- * When called, CurrentMemoryContext should be the per-query context.
- */
-static void
-initialize_aggregate(AggStatePerAgg peraggstate)
-{
- Aggref *aggref = peraggstate->aggref;
-
- /*
- * Start a fresh sort operation for each DISTINCT aggregate.
- */
- if (aggref->aggdistinct)
- {
- /*
- * In case of rescan, maybe there could be an uncompleted sort
- * operation? Clean it up if so.
- */
- if (peraggstate->sortstate)
- tuplesort_end(peraggstate->sortstate);
-
- peraggstate->sortstate =
- tuplesort_begin_datum(peraggstate->inputType,
- peraggstate->sortOperator,
- false);
- }
-
- /*
- * (Re)set transValue to the initial value.
- *
- * Note that when the initial value is pass-by-ref, we just reuse it
- * without copying for each group. Hence, transition function had
- * better not scribble on its input, or it will fail for GROUP BY!
- */
- peraggstate->transValue = peraggstate->initValue;
- peraggstate->transValueIsNull = peraggstate->initValueIsNull;
-
- /*
- * If the initial value for the transition state doesn't exist in the
- * pg_aggregate table then we will let the first non-NULL value
- * returned from the outer procNode become the initial value. (This is
- * useful for aggregates like max() and min().) The noTransValue flag
- * signals that we still need to do this.
- */
- peraggstate->noTransValue = peraggstate->initValueIsNull;
-}
-
-/*
- * Given a new input value, advance the transition function of an aggregate.
- *
- * When called, CurrentMemoryContext should be the context we want the
- * transition function result to be delivered into on this cycle.
- */
-static void
-advance_transition_function(AggStatePerAgg peraggstate,
- Datum newVal, bool isNull)
-{
- FunctionCallInfoData fcinfo;
-
- if (peraggstate->transfn.fn_strict)
- {
- if (isNull)
- {
- /*
- * For a strict transfn, nothing happens at a NULL input
- * tuple; we just keep the prior transValue. However, if the
- * transtype is pass-by-ref, we have to copy it into the new
- * context because the old one is going to get reset.
- */
- if (!peraggstate->transValueIsNull)
- peraggstate->transValue = datumCopy(peraggstate->transValue,
- peraggstate->transtypeByVal,
- peraggstate->transtypeLen);
- return;
- }
- if (peraggstate->noTransValue)
- {
- /*
- * transValue has not been initialized. This is the first
- * non-NULL input value. We use it as the initial value for
- * transValue. (We already checked that the agg's input type
- * is binary-compatible with its transtype, so straight copy
- * here is OK.)
- *
- * We had better copy the datum if it is pass-by-ref, since the
- * given pointer may be pointing into a scan tuple that will
- * be freed on the next iteration of the scan.
- */
- peraggstate->transValue = datumCopy(newVal,
- peraggstate->transtypeByVal,
- peraggstate->transtypeLen);
- peraggstate->transValueIsNull = false;
- peraggstate->noTransValue = false;
- return;
- }
- if (peraggstate->transValueIsNull)
- {
- /*
- * Don't call a strict function with NULL inputs. Note it is
- * possible to get here despite the above tests, if the
- * transfn is strict *and* returned a NULL on a prior cycle.
- * If that happens we will propagate the NULL all the way to
- * the end.
- */
- return;
- }
- }
-
- /* OK to call the transition function */
- MemSet(&fcinfo, 0, sizeof(fcinfo));
- fcinfo.flinfo = &peraggstate->transfn;
- fcinfo.nargs = 2;
- fcinfo.arg[0] = peraggstate->transValue;
- fcinfo.argnull[0] = peraggstate->transValueIsNull;
- fcinfo.arg[1] = newVal;
- fcinfo.argnull[1] = isNull;
-
- newVal = FunctionCallInvoke(&fcinfo);
-
- /*
- * 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->transtypeByVal && !fcinfo.isnull &&
- !MemoryContextContains(CurrentMemoryContext,
- DatumGetPointer(newVal)))
- newVal = datumCopy(newVal,
- peraggstate->transtypeByVal,
- peraggstate->transtypeLen);
-
- peraggstate->transValue = newVal;
- peraggstate->transValueIsNull = fcinfo.isnull;
-}
-
-/*
- * Run the transition function 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 values in sorted order,
- * and run the transition function on each non-duplicate value.
- *
- * When called, CurrentMemoryContext should be the per-query context.
- */
-static void
-process_sorted_aggregate(AggState *aggstate,
- AggStatePerAgg peraggstate)
-{
- Datum oldVal = (Datum) 0;
- bool haveOldVal = false;
- MemoryContext oldContext;
- Datum newVal;
- bool isNull;
-
- tuplesort_performsort(peraggstate->sortstate);
-
- /*
- * Note: if input type is pass-by-ref, the datums returned by the sort
- * are freshly palloc'd in the per-query context, so we must be
- * careful to pfree them when they are no longer needed.
- */
-
- while (tuplesort_getdatum(peraggstate->sortstate, true,
- &newVal, &isNull))
- {
- /*
- * DISTINCT always suppresses nulls, per SQL spec, regardless of
- * the transition function's strictness.
- */
- if (isNull)
- continue;
-
- /*
- * Clear and select the current working context for evaluation of
- * the equality function and transition function.
- */
- 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, so no need to
- * copy prior transValue to other context.
- */
- }
- else
- {
- advance_transition_function(peraggstate, newVal, false);
-
- /*
- * Make the other context current so that this transition
- * result is preserved.
- */
- aggstate->which_cxt = 1 - aggstate->which_cxt;
- /* forget the old value, if any */
- if (haveOldVal && !peraggstate->inputtypeByVal)
- pfree(DatumGetPointer(oldVal));
- oldVal = newVal;
- haveOldVal = true;
- }
-
- MemoryContextSwitchTo(oldContext);
- }
-
- if (haveOldVal && !peraggstate->inputtypeByVal)
- pfree(DatumGetPointer(oldVal));
-
- tuplesort_end(peraggstate->sortstate);
- peraggstate->sortstate = NULL;
-}
-
-/*
- * Compute the final value of one aggregate.
- *
- * When called, CurrentMemoryContext should be the context where we want
- * final values delivered (ie, the per-output-tuple expression context).
- */
-static void
-finalize_aggregate(AggStatePerAgg peraggstate,
- Datum *resultVal, bool *resultIsNull)
-{
- /*
- * Apply the agg's finalfn if one is provided, else return transValue.
- */
- if (OidIsValid(peraggstate->finalfn_oid))
- {
- FunctionCallInfoData fcinfo;
-
- MemSet(&fcinfo, 0, sizeof(fcinfo));
- fcinfo.flinfo = &peraggstate->finalfn;
- fcinfo.nargs = 1;
- fcinfo.arg[0] = peraggstate->transValue;
- fcinfo.argnull[0] = peraggstate->transValueIsNull;
- if (fcinfo.flinfo->fn_strict && peraggstate->transValueIsNull)
- {
- /* don't call a strict function with NULL inputs */
- *resultVal = (Datum) 0;
- *resultIsNull = true;
- }
- else
- {
- *resultVal = FunctionCallInvoke(&fcinfo);
- *resultIsNull = fcinfo.isnull;
- }
- }
- else
- {
- *resultVal = peraggstate->transValue;
- *resultIsNull = peraggstate->transValueIsNull;
- }
-
- /*
- * If result is pass-by-ref, make sure it is in the right context.
- */
- if (!peraggstate->resulttypeByVal && !*resultIsNull &&
- !MemoryContextContains(CurrentMemoryContext,
- DatumGetPointer(*resultVal)))
- *resultVal = datumCopy(*resultVal,
- peraggstate->resulttypeByVal,
- peraggstate->resulttypeLen);
-}
-
-
-/* ---------------------------------------
- *
- * ExecAgg -
- *
- * ExecAgg receives tuples from its outer subplan and aggregates over
- * the appropriate attribute for each aggregate function use (Aggref
- * node) appearing in the targetlist or qual of the node. The number
- * of tuples to aggregate over depends on whether a GROUP BY clause is
- * present. We can produce an aggregate result row per group, or just
- * one for the whole query. The value of each aggregate is stored in
- * the expression context to be used when ExecProject evaluates the
- * result tuple.
- *
- * If the outer subplan is a Group node, ExecAgg returns as many tuples
- * as there are groups.
- *
- * ------------------------------------------
- */
-TupleTableSlot *
-ExecAgg(Agg *node)
-{
- AggState *aggstate;
- EState *estate;
- Plan *outerPlan;
- ExprContext *econtext;
- ProjectionInfo *projInfo;
- Datum *aggvalues;
- bool *aggnulls;
- AggStatePerAgg peragg;
- MemoryContext oldContext;
- TupleTableSlot *resultSlot;
- HeapTuple inputTuple;
- int aggno;
- bool isNull;
-
- /*
- * get state info from node
- */
- aggstate = node->aggstate;
- estate = node->plan.state;
- outerPlan = outerPlan(node);
- econtext = aggstate->csstate.cstate.cs_ExprContext;
- aggvalues = econtext->ecxt_aggvalues;
- aggnulls = econtext->ecxt_aggnulls;
- projInfo = aggstate->csstate.cstate.cs_ProjInfo;
- peragg = aggstate->peragg;
-
- /*
- * We loop retrieving groups until we find one matching
- * node->plan.qual
- */
- do
- {
- if (aggstate->agg_done)
- return NULL;
-
- /*
- * Clear the per-output-tuple context for each group
- */
- MemoryContextReset(aggstate->tup_cxt);
-
- /*
- * Initialize working state for a new input tuple group
- */
- for (aggno = 0; aggno < aggstate->numaggs; aggno++)
- {
- AggStatePerAgg peraggstate = &peragg[aggno];
-
- initialize_aggregate(peraggstate);
- }
-
- inputTuple = NULL; /* no saved input tuple yet */
-
- /*
- * for each tuple from the outer plan, update all the aggregates
- */
- for (;;)
- {
- TupleTableSlot *outerslot;
-
- outerslot = ExecProcNode(outerPlan, (Plan *) node);
- if (TupIsNull(outerslot))
- break;
- econtext->ecxt_scantuple = outerslot;
-
- /*
- * Clear and select the current working context for evaluation
- * of the input expressions and transition functions at this
- * input tuple.
- */
- econtext->ecxt_per_tuple_memory =
- aggstate->agg_cxt[aggstate->which_cxt];
- ResetExprContext(econtext);
- oldContext =
- MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
- for (aggno = 0; aggno < aggstate->numaggs; aggno++)
- {
- AggStatePerAgg peraggstate = &peragg[aggno];
- Aggref *aggref = peraggstate->aggref;
- Datum newVal;
-
- newVal = ExecEvalExpr(aggref->target, econtext,
- &isNull, NULL);
-
- if (aggref->aggdistinct)
- {
- /* in DISTINCT mode, we may ignore nulls */
- if (isNull)
- continue;
- /* putdatum has to be called in per-query context */
- MemoryContextSwitchTo(oldContext);
- tuplesort_putdatum(peraggstate->sortstate,
- newVal, isNull);
- MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
- }
- else
- {
- advance_transition_function(peraggstate,
- newVal, isNull);
- }
- }
-
- /*
- * Make the other context current so that these transition
- * results are preserved.
- */
- aggstate->which_cxt = 1 - aggstate->which_cxt;
-
- MemoryContextSwitchTo(oldContext);
-
- /*
- * Keep a copy of the first input tuple for the projection.
- * (We only need one since only the GROUP BY columns in it can
- * be referenced, and these will be the same for all tuples
- * aggregated over.)
- */
- if (!inputTuple)
- inputTuple = heap_copytuple(outerslot->val);
- }
-
- /*
- * Done scanning input tuple group. Finalize each aggregate
- * calculation, and stash results in the per-output-tuple context.
- *
- * This is a bit tricky when there are both DISTINCT and plain
- * aggregates: we must first finalize all the plain aggs and then
- * all the DISTINCT ones. This is needed because the last
- * transition values for the plain aggs are stored in the
- * not-current working context, and we have to evaluate those aggs
- * (and stash the results in the output tup_cxt!) before we start
- * flipping contexts again in process_sorted_aggregate.
- */
- oldContext = MemoryContextSwitchTo(aggstate->tup_cxt);
- for (aggno = 0; aggno < aggstate->numaggs; aggno++)
- {
- AggStatePerAgg peraggstate = &peragg[aggno];
-
- if (!peraggstate->aggref->aggdistinct)
- finalize_aggregate(peraggstate,
- &aggvalues[aggno], &aggnulls[aggno]);
- }
- MemoryContextSwitchTo(oldContext);
- for (aggno = 0; aggno < aggstate->numaggs; aggno++)
- {
- AggStatePerAgg peraggstate = &peragg[aggno];
-
- if (peraggstate->aggref->aggdistinct)
- {
- process_sorted_aggregate(aggstate, peraggstate);
- oldContext = MemoryContextSwitchTo(aggstate->tup_cxt);
- finalize_aggregate(peraggstate,
- &aggvalues[aggno], &aggnulls[aggno]);
- MemoryContextSwitchTo(oldContext);
- }
- }
-
- /*
- * If the outerPlan is a Group node, we will reach here after each
- * group. We are not done unless the Group node is done (a little
- * ugliness here while we reach into the Group's state to find
- * out). Furthermore, when grouping we return nothing at all
- * unless we had some input tuple(s). By the nature of Group,
- * there are no empty groups, so if we get here with no input the
- * whole scan is empty.
- *
- * If the outerPlan isn't a Group, we are done when we get here, and
- * we will emit a (single) tuple even if there were no input
- * tuples.
- */
- if (IsA(outerPlan, Group))
- {
- /* aggregation over groups */
- aggstate->agg_done = ((Group *) outerPlan)->grpstate->grp_done;
- /* check for no groups */
- if (inputTuple == NULL)
- return NULL;
- }
- else
- {
- aggstate->agg_done = true;
-
- /*
- * If inputtuple==NULL (ie, the outerPlan didn't return
- * anything), create a dummy all-nulls input tuple for use by
- * ExecProject. 99.44% of the time this is a waste of cycles,
- * because ordinarily the projected output tuple's targetlist
- * cannot contain any direct (non-aggregated) references to
- * input columns, so the dummy tuple will not be referenced.
- * However there are special cases where this isn't so --- in
- * particular an UPDATE involving an aggregate will have a
- * targetlist reference to ctid. We need to return a null for
- * ctid in that situation, not coredump.
- *
- * The values returned for the aggregates will be the initial
- * values of the transition functions.
- */
- if (inputTuple == NULL)
- {
- TupleDesc tupType;
- Datum *tupValue;
- char *null_array;
- AttrNumber attnum;
-
- tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor;
- tupValue = projInfo->pi_tupValue;
- /* watch out for null input tuples, though... */
- if (tupType && tupValue)
- {
- null_array = (char *) palloc(sizeof(char) * tupType->natts);
- for (attnum = 0; attnum < tupType->natts; attnum++)
- null_array[attnum] = 'n';
- inputTuple = heap_formtuple(tupType, tupValue, null_array);
- pfree(null_array);
- }
- }
- }
-
- /*
- * Store the representative input tuple in the tuple table slot
- * reserved for it. The tuple will be deleted when it is cleared
- * from the slot.
- */
- ExecStoreTuple(inputTuple,
- aggstate->csstate.css_ScanTupleSlot,
- InvalidBuffer,
- true);
- econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
-
- /*
- * Do projection and qual check in the per-output-tuple context.
- */
- econtext->ecxt_per_tuple_memory = aggstate->tup_cxt;
-
- /*
- * Form a projection tuple using the aggregate results and the
- * representative input tuple. Store it in the result tuple slot.
- * Note we do not support aggregates returning sets ...
- */
- resultSlot = ExecProject(projInfo, NULL);
-
- /*
- * If the completed tuple does not match the qualifications, it is
- * ignored and we loop back to try to process another group.
- * Otherwise, return the tuple.
- */
- }
- while (!ExecQual(node->plan.qual, econtext, false));
-
- return resultSlot;
-}
-
-/* -----------------
- * ExecInitAgg
- *
- * Creates the run-time information for the agg node produced by the
- * planner and initializes its outer subtree
- * -----------------
- */
-bool
-ExecInitAgg(Agg *node, EState *estate, Plan *parent)
-{
- AggState *aggstate;
- AggStatePerAgg peragg;
- Plan *outerPlan;
- ExprContext *econtext;
- int numaggs,
- aggno;
- List *alist;
-
- /*
- * assign the node's execution state
- */
- node->plan.state = estate;
-
- /*
- * create state structure
- */
- aggstate = makeNode(AggState);
- node->aggstate = aggstate;
- aggstate->agg_done = false;
-
- /*
- * find aggregates in targetlist and quals
- *
- * Note: pull_agg_clauses also checks that no aggs contain other agg
- * calls in their arguments. This would make no sense under SQL
- * semantics anyway (and it's forbidden by the spec). Because that is
- * true, we don't need to worry about evaluating the aggs in any
- * particular order.
- */
- aggstate->aggs = nconc(pull_agg_clause((Node *) node->plan.targetlist),
- pull_agg_clause((Node *) node->plan.qual));
- aggstate->numaggs = numaggs = length(aggstate->aggs);
- if (numaggs <= 0)
- {
- /*
- * This used to be treated as an error, but we can't do that
- * anymore because constant-expression simplification could
- * optimize away all of the Aggrefs in the targetlist and qual.
- * So, just make a debug note, and force numaggs positive so that
- * palloc()s below don't choke.
- */
- elog(LOG, "ExecInitAgg: could not find any aggregate functions");
- numaggs = 1;
- }
-
- /*
- * Create expression context
- */
- 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
-
- /*
- * tuple table initialization
- */
- ExecInitScanTupleSlot(estate, &aggstate->csstate);
- ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate);
-
- /*
- * Set up aggregate-result storage in the expr context, and also
- * allocate my private per-agg working storage
- */
- econtext = aggstate->csstate.cstate.cs_ExprContext;
- econtext->ecxt_aggvalues = (Datum *) palloc(sizeof(Datum) * numaggs);
- MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * numaggs);
- econtext->ecxt_aggnulls = (bool *) palloc(sizeof(bool) * numaggs);
- MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * numaggs);
-
- peragg = (AggStatePerAgg) palloc(sizeof(AggStatePerAggData) * numaggs);
- MemSet(peragg, 0, sizeof(AggStatePerAggData) * numaggs);
- aggstate->peragg = peragg;
-
- /*
- * initialize child nodes
- */
- outerPlan = outerPlan(node);
- ExecInitNode(outerPlan, estate, (Plan *) node);
-
- /*
- * initialize source tuple type.
- */
- ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate);
-
- /*
- * Initialize result tuple type and projection info.
- */
- ExecAssignResultTypeFromTL((Plan *) node, &aggstate->csstate.cstate);
- ExecAssignProjectionInfo((Plan *) node, &aggstate->csstate.cstate);
-
- /*
- * Perform lookups of aggregate function info, and initialize the
- * unchanging fields of the per-agg data
- */
- aggno = -1;
- foreach(alist, aggstate->aggs)
- {
- Aggref *aggref = (Aggref *) lfirst(alist);
- AggStatePerAgg peraggstate = &peragg[++aggno];
- HeapTuple aggTuple;
- Form_pg_aggregate aggform;
- AclResult aclresult;
- Oid transfn_oid,
- finalfn_oid;
- Datum textInitVal;
-
- /* Mark Aggref node with its associated index in the result array */
- aggref->aggno = aggno;
-
- /* Fill in the peraggstate data */
- peraggstate->aggref = aggref;
-
- aggTuple = SearchSysCache(AGGFNOID,
- ObjectIdGetDatum(aggref->aggfnoid),
- 0, 0, 0);
- if (!HeapTupleIsValid(aggTuple))
- elog(ERROR, "ExecAgg: cache lookup failed for aggregate %u",
- aggref->aggfnoid);
- aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
-
- /* Check permission to call aggregate function */
- aclresult = pg_proc_aclcheck(aggref->aggfnoid, GetUserId(),
- ACL_EXECUTE);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, get_func_name(aggref->aggfnoid));
-
- get_typlenbyval(aggref->aggtype,
- &peraggstate->resulttypeLen,
- &peraggstate->resulttypeByVal);
- get_typlenbyval(aggform->aggtranstype,
- &peraggstate->transtypeLen,
- &peraggstate->transtypeByVal);
-
- /*
- * initval is potentially null, so don't try to access it as a struct
- * field. Must do it the hard way with SysCacheGetAttr.
- */
- textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple,
- Anum_pg_aggregate_agginitval,
- &peraggstate->initValueIsNull);
-
- if (peraggstate->initValueIsNull)
- peraggstate->initValue = (Datum) 0;
- else
- peraggstate->initValue = GetAggInitVal(textInitVal,
- aggform->aggtranstype);
-
- peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
- peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
-
- fmgr_info(transfn_oid, &peraggstate->transfn);
- if (OidIsValid(finalfn_oid))
- fmgr_info(finalfn_oid, &peraggstate->finalfn);
-
- /*
- * If the transfn is strict and the initval is NULL, make sure
- * input type and transtype are the same (or at least binary-
- * compatible), so that it's OK to use the first input value as
- * the initial transValue. This should have been checked at agg
- * definition time, but just in case...
- */
- if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
- {
- /*
- * Note: use the type from the input expression here, not
- * from pg_proc.proargtypes, because the latter might be 0.
- * (Consider COUNT(*).)
- */
- Oid inputType = exprType(aggref->target);
-
- if (!IsBinaryCompatible(inputType, aggform->aggtranstype))
- elog(ERROR, "Aggregate %u needs to have compatible input type and transition type",
- aggref->aggfnoid);
- }
-
- if (aggref->aggdistinct)
- {
- /*
- * Note: use the type from the input expression here, not
- * from pg_proc.proargtypes, because the latter might be 0.
- * (Consider COUNT(*).)
- */
- Oid inputType = exprType(aggref->target);
- Oid eq_function;
-
- peraggstate->inputType = inputType;
- get_typlenbyval(inputType,
- &peraggstate->inputtypeLen,
- &peraggstate->inputtypeByVal);
-
- eq_function = compatible_oper_funcid(makeList1(makeString("=")),
- inputType, inputType,
- true);
- if (!OidIsValid(eq_function))
- elog(ERROR, "Unable to identify an equality operator for type %s",
- format_type_be(inputType));
- fmgr_info(eq_function, &(peraggstate->equalfn));
- peraggstate->sortOperator = any_ordering_op(inputType);
- peraggstate->sortstate = NULL;
- }
-
- ReleaseSysCache(aggTuple);
- }
-
- return TRUE;
-}
-
-static Datum
-GetAggInitVal(Datum textInitVal, Oid transtype)
-{
- char *strInitVal;
- HeapTuple tup;
- Oid typinput,
- typelem;
- Datum initVal;
-
- strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal));
-
- tup = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(transtype),
- 0, 0, 0);
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "GetAggInitVal: cache lookup failed on aggregate transition function return type %u", transtype);
-
- typinput = ((Form_pg_type) GETSTRUCT(tup))->typinput;
- typelem = ((Form_pg_type) GETSTRUCT(tup))->typelem;
- ReleaseSysCache(tup);
-
- initVal = OidFunctionCall3(typinput,
- CStringGetDatum(strInitVal),
- ObjectIdGetDatum(typelem),
- Int32GetDatum(-1));
-
- pfree(strInitVal);
- return initVal;
-}
-
-int
-ExecCountSlotsAgg(Agg *node)
-{
- return ExecCountSlotsNode(outerPlan(node)) +
- ExecCountSlotsNode(innerPlan(node)) +
- AGG_NSLOTS;
-}
-
-void
-ExecEndAgg(Agg *node)
-{
- AggState *aggstate = node->aggstate;
- Plan *outerPlan;
-
- ExecFreeProjectionInfo(&aggstate->csstate.cstate);
-
- /*
- * Make sure ExecFreeExprContext() frees the right expr context...
- */
- aggstate->csstate.cstate.cs_ExprContext->ecxt_per_tuple_memory =
- aggstate->tup_cxt;
- ExecFreeExprContext(&aggstate->csstate.cstate);
-
- /*
- * ... and I free the others.
- */
- MemoryContextDelete(aggstate->agg_cxt[0]);
- MemoryContextDelete(aggstate->agg_cxt[1]);
-
- outerPlan = outerPlan(node);
- ExecEndNode(outerPlan, (Plan *) node);
-
- /* clean up tuple table */
- ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
-}
-
-void
-ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent)
-{
- AggState *aggstate = node->aggstate;
- ExprContext *econtext = aggstate->csstate.cstate.cs_ExprContext;
-
- aggstate->agg_done = false;
- MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * aggstate->numaggs);
- MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * aggstate->numaggs);
-
- /*
- * if chgParam of subnode is not null then plan will be re-scanned by
- * first ExecProcNode.
- */
- if (((Plan *) node)->lefttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-}
-
-/*
- * aggregate_dummy - dummy execution routine for aggregate functions
- *
- * This function is listed as the implementation (prosrc field) of pg_proc
- * entries for aggregate functions. Its only purpose is to throw an error
- * if someone mistakenly executes such a function in the normal way.
- *
- * Perhaps someday we could assign real meaning to the prosrc field of
- * an aggregate?
- */
-Datum
-aggregate_dummy(PG_FUNCTION_ARGS)
-{
- elog(ERROR, "Aggregate function %u called as normal function",
- fcinfo->flinfo->fn_oid);
- return (Datum) 0; /* keep compiler quiet */
-}
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
deleted file mode 100644
index 724f0c8ce89..00000000000
--- a/src/backend/executor/nodeAppend.c
+++ /dev/null
@@ -1,406 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeAppend.c
- * routines to handle append nodes.
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.45 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/* INTERFACE ROUTINES
- * ExecInitAppend - initialize the append node
- * ExecProcAppend - retrieve the next tuple from the node
- * ExecEndAppend - shut down the append node
- * ExecReScanAppend - rescan the append node
- *
- * NOTES
- * Each append node contains a list of one or more subplans which
- * must be iteratively processed (forwards or backwards).
- * Tuples are retrieved by executing the 'whichplan'th subplan
- * until the subplan stops returning tuples, at which point that
- * plan is shut down and the next started up.
- *
- * Append nodes don't make use of their left and right
- * subtrees, rather they maintain a list of subplans so
- * a typical append node looks like this in the plan tree:
- *
- * ...
- * /
- * Append -------+------+------+--- nil
- * / \ | | |
- * nil nil ... ... ...
- * subplans
- *
- * Append nodes are currently used for unions, and to support
- * inheritance queries, where several relations need to be scanned.
- * For example, in our standard person/student/employee/student-emp
- * example, where student and employee inherit from person
- * and student-emp inherits from student and employee, the
- * query:
- *
- * retrieve (e.name) from e in person*
- *
- * generates the plan:
- *
- * |
- * Append -------+-------+--------+--------+
- * / \ | | | |
- * nil nil Scan Scan Scan Scan
- * | | | |
- * person employee student student-emp
- */
-
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "executor/execdebug.h"
-#include "executor/nodeAppend.h"
-#include "parser/parsetree.h"
-
-static bool exec_append_initialize_next(Append *node);
-
-
-/* ----------------------------------------------------------------
- * exec_append_initialize_next
- *
- * Sets up the append node state (i.e. the append state node)
- * for the "next" scan.
- *
- * Returns t iff there is a "next" scan to process.
- * ----------------------------------------------------------------
- */
-static bool
-exec_append_initialize_next(Append *node)
-{
- EState *estate;
- AppendState *appendstate;
- int whichplan;
-
- /*
- * get information from the append node
- */
- estate = node->plan.state;
- appendstate = node->appendstate;
- whichplan = appendstate->as_whichplan;
-
- if (whichplan < appendstate->as_firstplan)
- {
- /*
- * if scanning in reverse, we start at the last scan in the list
- * and then proceed back to the first.. in any case we inform
- * ExecProcAppend that we are at the end of the line by returning
- * FALSE
- */
- appendstate->as_whichplan = appendstate->as_firstplan;
- return FALSE;
- }
- else if (whichplan > appendstate->as_lastplan)
- {
- /*
- * as above, end the scan if we go beyond the last scan in our
- * list..
- */
- appendstate->as_whichplan = appendstate->as_lastplan;
- return FALSE;
- }
- else
- {
- /*
- * initialize the scan
- *
- * If we are controlling the target relation, select the proper
- * active ResultRelInfo and junk filter for this target.
- */
- if (node->isTarget)
- {
- Assert(whichplan < estate->es_num_result_relations);
- estate->es_result_relation_info =
- estate->es_result_relations + whichplan;
- estate->es_junkFilter =
- estate->es_result_relation_info->ri_junkFilter;
- }
-
- return TRUE;
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecInitAppend
- *
- * Begins all of the subscans of the append node, storing the
- * scan structures in the 'initialized' vector of the append-state
- * structure.
- *
- * (This is potentially wasteful, since the entire result of the
- * append node may not be scanned, but this way all of the
- * structures get allocated in the executor's top level memory
- * block instead of that of the call to ExecProcAppend.)
- *
- * Special case: during an EvalPlanQual recheck query of an inherited
- * target relation, we only want to initialize and scan the single
- * subplan that corresponds to the target relation being checked.
- * ----------------------------------------------------------------
- */
-bool
-ExecInitAppend(Append *node, EState *estate, Plan *parent)
-{
- AppendState *appendstate;
- int nplans;
- List *appendplans;
- bool *initialized;
- int i;
- Plan *initNode;
-
- CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
-
- /*
- * assign execution state to node and get information for append state
- */
- node->plan.state = estate;
-
- appendplans = node->appendplans;
- nplans = length(appendplans);
-
- initialized = (bool *) palloc(nplans * sizeof(bool));
- MemSet(initialized, 0, nplans * sizeof(bool));
-
- /*
- * create new AppendState for our append node
- */
- appendstate = makeNode(AppendState);
- appendstate->as_nplans = nplans;
- appendstate->as_initialized = initialized;
-
- node->appendstate = appendstate;
-
- /*
- * Do we want to scan just one subplan? (Special case for
- * EvalPlanQual) XXX pretty dirty way of determining that this case
- * applies ...
- */
- if (node->isTarget && estate->es_evTuple != NULL)
- {
- int tplan;
-
- tplan = estate->es_result_relation_info - estate->es_result_relations;
- Assert(tplan >= 0 && tplan < nplans);
-
- appendstate->as_firstplan = tplan;
- appendstate->as_lastplan = tplan;
- }
- else
- {
- /* normal case, scan all subplans */
- appendstate->as_firstplan = 0;
- appendstate->as_lastplan = nplans - 1;
- }
-
- /*
- * Miscellaneous initialization
- *
- * Append plans don't have expression contexts because they never call
- * ExecQual or ExecProject.
- */
-
-#define APPEND_NSLOTS 1
-
- /*
- * append nodes still have Result slots, which hold pointers to
- * tuples, so we have to initialize them.
- */
- ExecInitResultTupleSlot(estate, &appendstate->cstate);
-
- /*
- * call ExecInitNode on each of the plans to be executed and save the
- * results into the array "initialized"
- */
- for (i = appendstate->as_firstplan; i <= appendstate->as_lastplan; i++)
- {
- appendstate->as_whichplan = i;
- exec_append_initialize_next(node);
-
- initNode = (Plan *) nth(i, appendplans);
- initialized[i] = ExecInitNode(initNode, estate, (Plan *) node);
- }
-
- /*
- * initialize tuple type
- */
- ExecAssignResultTypeFromTL((Plan *) node, &appendstate->cstate);
- appendstate->cstate.cs_ProjInfo = NULL;
-
- /*
- * return the result from the first subplan's initialization
- */
- appendstate->as_whichplan = appendstate->as_firstplan;
- exec_append_initialize_next(node);
-
- return TRUE;
-}
-
-int
-ExecCountSlotsAppend(Append *node)
-{
- List *plan;
- int nSlots = 0;
-
- foreach(plan, node->appendplans)
- nSlots += ExecCountSlotsNode((Plan *) lfirst(plan));
- return nSlots + APPEND_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecProcAppend
- *
- * Handles the iteration over the multiple scans.
- *
- * NOTE: Can't call this ExecAppend, that name is used in execMain.
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecProcAppend(Append *node)
-{
- EState *estate;
- AppendState *appendstate;
- int whichplan;
- List *appendplans;
- Plan *subnode;
- TupleTableSlot *result;
- TupleTableSlot *result_slot;
- ScanDirection direction;
-
- /*
- * get information from the node
- */
- appendstate = node->appendstate;
- estate = node->plan.state;
- direction = estate->es_direction;
- appendplans = node->appendplans;
- whichplan = appendstate->as_whichplan;
- result_slot = appendstate->cstate.cs_ResultTupleSlot;
-
- /*
- * figure out which subplan we are currently processing
- */
- subnode = (Plan *) nth(whichplan, appendplans);
-
- if (subnode == NULL)
- elog(LOG, "ExecProcAppend: subnode is NULL");
-
- /*
- * get a tuple from the subplan
- */
- result = ExecProcNode(subnode, (Plan *) node);
-
- if (!TupIsNull(result))
- {
- /*
- * if the subplan gave us something then place a copy of whatever
- * we get into our result slot and return it.
- *
- * Note we rely on the subplan to retain ownership of the tuple for
- * as long as we need it --- we don't copy it.
- */
- return ExecStoreTuple(result->val, result_slot, InvalidBuffer, false);
- }
- else
- {
- /*
- * .. go on to the "next" subplan in the appropriate direction and
- * try processing again (recursively)
- */
- if (ScanDirectionIsForward(direction))
- appendstate->as_whichplan++;
- else
- appendstate->as_whichplan--;
-
- /*
- * return something from next node or an empty slot if all of our
- * subplans have been exhausted.
- */
- if (exec_append_initialize_next(node))
- {
- ExecSetSlotDescriptorIsNew(result_slot, true);
- return ExecProcAppend(node);
- }
- else
- return ExecClearTuple(result_slot);
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecEndAppend
- *
- * Shuts down the subscans of the append node.
- *
- * Returns nothing of interest.
- * ----------------------------------------------------------------
- */
-void
-ExecEndAppend(Append *node)
-{
- EState *estate;
- AppendState *appendstate;
- int nplans;
- List *appendplans;
- bool *initialized;
- int i;
-
- /*
- * get information from the node
- */
- appendstate = node->appendstate;
- estate = node->plan.state;
- appendplans = node->appendplans;
- nplans = appendstate->as_nplans;
- initialized = appendstate->as_initialized;
-
- /*
- * shut down each of the subscans
- */
- for (i = 0; i < nplans; i++)
- {
- if (initialized[i])
- ExecEndNode((Plan *) nth(i, appendplans), (Plan *) node);
- }
-}
-
-void
-ExecReScanAppend(Append *node, ExprContext *exprCtxt, Plan *parent)
-{
- AppendState *appendstate = node->appendstate;
- int i;
-
- for (i = appendstate->as_firstplan; i <= appendstate->as_lastplan; i++)
- {
- Plan *subnode;
-
- subnode = (Plan *) nth(i, node->appendplans);
-
- /*
- * ExecReScan doesn't know about my subplans, so I have to do
- * changed-parameter signaling myself.
- */
- if (node->plan.chgParam != NULL)
- SetChangedParamList(subnode, node->plan.chgParam);
-
- /*
- * if chgParam of subnode is not null then plan will be re-scanned
- * by first ExecProcNode.
- */
- if (subnode->chgParam == NULL)
- {
- /* make sure estate is correct for this subnode (needed??) */
- appendstate->as_whichplan = i;
- exec_append_initialize_next(node);
- ExecReScan(subnode, exprCtxt, (Plan *) node);
- }
- }
- appendstate->as_whichplan = appendstate->as_firstplan;
- exec_append_initialize_next(node);
-}
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
deleted file mode 100644
index 3645a188334..00000000000
--- a/src/backend/executor/nodeFunctionscan.c
+++ /dev/null
@@ -1,469 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeFunctionscan.c
- * Support routines for scanning RangeFunctions (functions in rangetable).
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.2 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecFunctionScan scans a function.
- * ExecFunctionNext retrieve next tuple in sequential order.
- * ExecInitFunctionScan creates and initializes a functionscan node.
- * ExecEndFunctionScan releases any storage allocated.
- * ExecFunctionReScan rescans the function
- */
-#include "postgres.h"
-
-#include "miscadmin.h"
-#include "access/heapam.h"
-#include "catalog/pg_type.h"
-#include "executor/execdebug.h"
-#include "executor/execdefs.h"
-#include "executor/execdesc.h"
-#include "executor/nodeFunctionscan.h"
-#include "parser/parsetree.h"
-#include "parser/parse_expr.h"
-#include "parser/parse_type.h"
-#include "storage/lmgr.h"
-#include "tcop/pquery.h"
-#include "utils/lsyscache.h"
-#include "utils/syscache.h"
-#include "utils/tuplestore.h"
-
-static TupleTableSlot *FunctionNext(FunctionScan *node);
-static TupleTableSlot *function_getonetuple(TupleTableSlot *slot,
- Node *expr,
- ExprContext *econtext,
- TupleDesc tupdesc,
- bool returnsTuple,
- bool *isNull,
- ExprDoneCond *isDone);
-static FunctionMode get_functionmode(Node *expr);
-
-/* ----------------------------------------------------------------
- * Scan Support
- * ----------------------------------------------------------------
- */
-/* ----------------------------------------------------------------
- * FunctionNext
- *
- * This is a workhorse for ExecFunctionScan
- * ----------------------------------------------------------------
- */
-static TupleTableSlot *
-FunctionNext(FunctionScan *node)
-{
- TupleTableSlot *slot;
- Node *expr;
- ExprContext *econtext;
- TupleDesc tupdesc;
- EState *estate;
- ScanDirection direction;
- Tuplestorestate *tuplestorestate;
- FunctionScanState *scanstate;
- bool should_free;
- HeapTuple heapTuple;
-
- /*
- * get information from the estate and scan state
- */
- scanstate = (FunctionScanState *) node->scan.scanstate;
- estate = node->scan.plan.state;
- direction = estate->es_direction;
- econtext = scanstate->csstate.cstate.cs_ExprContext;
-
- tuplestorestate = scanstate->tuplestorestate;
- tupdesc = scanstate->tupdesc;
- expr = scanstate->funcexpr;
-
- /*
- * If first time through, read all tuples from function and pass them to
- * tuplestore.c. Subsequent calls just fetch tuples from tuplestore.
- */
- if (tuplestorestate == NULL)
- {
- /*
- * Initialize tuplestore module.
- */
- tuplestorestate = tuplestore_begin_heap(true, /* randomAccess */
- SortMem);
-
- scanstate->tuplestorestate = (void *) tuplestorestate;
-
- /*
- * Compute all the function tuples and pass to tuplestore.
- */
- for (;;)
- {
- bool isNull;
- ExprDoneCond isDone;
-
- isNull = false;
- isDone = ExprSingleResult;
- slot = function_getonetuple(scanstate->csstate.css_ScanTupleSlot,
- expr, econtext, tupdesc,
- scanstate->returnsTuple,
- &isNull, &isDone);
- if (TupIsNull(slot))
- break;
-
- tuplestore_puttuple(tuplestorestate, (void *) slot->val);
- ExecClearTuple(slot);
-
- if (isDone != ExprMultipleResult)
- break;
- }
-
- /*
- * Complete the store.
- */
- tuplestore_donestoring(tuplestorestate);
- }
-
- /*
- * Get the next tuple from tuplestore. Return NULL if no more tuples.
- */
- slot = scanstate->csstate.css_ScanTupleSlot;
- heapTuple = tuplestore_getheaptuple(tuplestorestate,
- ScanDirectionIsForward(direction),
- &should_free);
-
- return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
-}
-
-/* ----------------------------------------------------------------
- * ExecFunctionScan(node)
- *
- * Scans the Function sequentially and returns the next qualifying
- * tuple.
- * It calls the ExecScan() routine and passes it the access method
- * which retrieve tuples sequentially.
- *
- */
-
-TupleTableSlot *
-ExecFunctionScan(FunctionScan *node)
-{
- /*
- * use FunctionNext as access method
- */
- return ExecScan(&node->scan, (ExecScanAccessMtd) FunctionNext);
-}
-
-/* ----------------------------------------------------------------
- * ExecInitFunctionScan
- * ----------------------------------------------------------------
- */
-bool
-ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
-{
- FunctionScanState *scanstate;
- RangeTblEntry *rte;
- Oid funcrettype;
- Oid funcrelid;
- TupleDesc tupdesc;
-
- /*
- * FunctionScan should not have any children.
- */
- Assert(outerPlan((Plan *) node) == NULL);
- Assert(innerPlan((Plan *) node) == NULL);
-
- /*
- * assign the node's execution state
- */
- node->scan.plan.state = estate;
-
- /*
- * create new ScanState for node
- */
- scanstate = makeNode(FunctionScanState);
- node->scan.scanstate = &scanstate->csstate;
-
- /*
- * Miscellaneous initialization
- *
- * create expression context for node
- */
- ExecAssignExprContext(estate, &scanstate->csstate.cstate);
-
-#define FUNCTIONSCAN_NSLOTS 2
-
- /*
- * tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &scanstate->csstate.cstate);
- ExecInitScanTupleSlot(estate, &scanstate->csstate);
-
- /*
- * get info about function
- */
- rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
- Assert(rte->rtekind == RTE_FUNCTION);
- funcrettype = exprType(rte->funcexpr);
- funcrelid = typeidTypeRelid(funcrettype);
-
- /*
- * Build a suitable tupledesc representing the output rows
- */
- if (OidIsValid(funcrelid))
- {
- /*
- * Composite data type, i.e. a table's row type
- * Same as ordinary relation RTE
- */
- Relation rel;
-
- rel = relation_open(funcrelid, AccessShareLock);
- tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
- relation_close(rel, AccessShareLock);
- scanstate->returnsTuple = true;
- }
- else
- {
- /*
- * Must be a base data type, i.e. scalar
- */
- char *attname = strVal(lfirst(rte->eref->colnames));
-
- tupdesc = CreateTemplateTupleDesc(1);
- TupleDescInitEntry(tupdesc,
- (AttrNumber) 1,
- attname,
- funcrettype,
- -1,
- 0,
- false);
- scanstate->returnsTuple = false;
- }
- scanstate->tupdesc = tupdesc;
- ExecSetSlotDescriptor(scanstate->csstate.css_ScanTupleSlot,
- tupdesc, false);
-
- /*
- * Other node-specific setup
- */
- scanstate->tuplestorestate = NULL;
- scanstate->funcexpr = rte->funcexpr;
-
- scanstate->functionmode = get_functionmode(rte->funcexpr);
-
- scanstate->csstate.cstate.cs_TupFromTlist = false;
-
- /*
- * initialize tuple type
- */
- ExecAssignResultTypeFromTL((Plan *) node, &scanstate->csstate.cstate);
- ExecAssignProjectionInfo((Plan *) node, &scanstate->csstate.cstate);
-
- return TRUE;
-}
-
-int
-ExecCountSlotsFunctionScan(FunctionScan *node)
-{
- return ExecCountSlotsNode(outerPlan(node)) +
- ExecCountSlotsNode(innerPlan(node)) +
- FUNCTIONSCAN_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndFunctionScan
- *
- * frees any storage allocated through C routines.
- * ----------------------------------------------------------------
- */
-void
-ExecEndFunctionScan(FunctionScan *node)
-{
- FunctionScanState *scanstate;
- EState *estate;
-
- /*
- * get information from node
- */
- scanstate = (FunctionScanState *) node->scan.scanstate;
- estate = node->scan.plan.state;
-
- /*
- * Free the projection info and the scan attribute info
- *
- * Note: we don't ExecFreeResultType(scanstate) because the rule manager
- * depends on the tupType returned by ExecMain(). So for now, this is
- * freed at end-transaction time. -cim 6/2/91
- */
- ExecFreeProjectionInfo(&scanstate->csstate.cstate);
- ExecFreeExprContext(&scanstate->csstate.cstate);
-
- /*
- * clean out the tuple table
- */
- ExecClearTuple(scanstate->csstate.cstate.cs_ResultTupleSlot);
- ExecClearTuple(scanstate->csstate.css_ScanTupleSlot);
-
- /*
- * Release tuplestore resources
- */
- if (scanstate->tuplestorestate != NULL)
- tuplestore_end((Tuplestorestate *) scanstate->tuplestorestate);
- scanstate->tuplestorestate = NULL;
-}
-
-/* ----------------------------------------------------------------
- * ExecFunctionMarkPos
- *
- * Calls tuplestore to save the current position in the stored file.
- * ----------------------------------------------------------------
- */
-void
-ExecFunctionMarkPos(FunctionScan *node)
-{
- FunctionScanState *scanstate;
-
- scanstate = (FunctionScanState *) node->scan.scanstate;
-
- /*
- * if we haven't materialized yet, just return.
- */
- if (!scanstate->tuplestorestate)
- return;
-
- tuplestore_markpos((Tuplestorestate *) scanstate->tuplestorestate);
-}
-
-/* ----------------------------------------------------------------
- * ExecFunctionRestrPos
- *
- * Calls tuplestore to restore the last saved file position.
- * ----------------------------------------------------------------
- */
-void
-ExecFunctionRestrPos(FunctionScan *node)
-{
- FunctionScanState *scanstate;
-
- scanstate = (FunctionScanState *) node->scan.scanstate;
-
- /*
- * if we haven't materialized yet, just return.
- */
- if (!scanstate->tuplestorestate)
- return;
-
- tuplestore_restorepos((Tuplestorestate *) scanstate->tuplestorestate);
-}
-
-/* ----------------------------------------------------------------
- * ExecFunctionReScan
- *
- * Rescans the relation.
- * ----------------------------------------------------------------
- */
-void
-ExecFunctionReScan(FunctionScan *node, ExprContext *exprCtxt, Plan *parent)
-{
- FunctionScanState *scanstate;
-
- /*
- * get information from node
- */
- scanstate = (FunctionScanState *) node->scan.scanstate;
-
- ExecClearTuple(scanstate->csstate.cstate.cs_ResultTupleSlot);
-
- /*
- * If we haven't materialized yet, just return.
- */
- if (!scanstate->tuplestorestate)
- return;
-
- /*
- * Here we have a choice whether to drop the tuplestore (and recompute
- * the function outputs) or just rescan it. This should depend on
- * whether the function expression contains parameters and/or is
- * marked volatile. FIXME soon.
- */
- if (node->scan.plan.chgParam != NULL)
- {
- tuplestore_end((Tuplestorestate *) scanstate->tuplestorestate);
- scanstate->tuplestorestate = NULL;
- }
- else
- tuplestore_rescan((Tuplestorestate *) scanstate->tuplestorestate);
-}
-
-/*
- * Run the underlying function to get the next tuple
- */
-static TupleTableSlot *
-function_getonetuple(TupleTableSlot *slot,
- Node *expr,
- ExprContext *econtext,
- TupleDesc tupdesc,
- bool returnsTuple,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- HeapTuple tuple;
- Datum retDatum;
- char nullflag;
-
- /*
- * get the next Datum from the function
- */
- retDatum = ExecEvalExprSwitchContext(expr, econtext, isNull, isDone);
-
- /*
- * check to see if we're really done
- */
- if (*isDone == ExprEndResult)
- slot = NULL;
- else
- {
- if (returnsTuple)
- {
- /*
- * Composite data type, i.e. a table's row type
- * function returns pointer to tts??
- */
- slot = (TupleTableSlot *) retDatum;
- }
- else
- {
- /*
- * Must be a base data type, i.e. scalar
- * turn it into a tuple
- */
- nullflag = *isNull ? 'n' : ' ';
- tuple = heap_formtuple(tupdesc, &retDatum, &nullflag);
-
- /*
- * save the tuple in the scan tuple slot and return the slot.
- */
- slot = ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- InvalidBuffer, /* buffer associated with
- * this tuple */
- true); /* pfree this pointer */
- }
- }
-
- return slot;
-}
-
-static FunctionMode
-get_functionmode(Node *expr)
-{
- /*
- * for the moment, hardwire this
- */
- return PM_REPEATEDCALL;
-}
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
deleted file mode 100644
index 5d7f6a69924..00000000000
--- a/src/backend/executor/nodeGroup.c
+++ /dev/null
@@ -1,502 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeGroup.c
- * Routines to handle group nodes (used for queries with GROUP BY clause).
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * DESCRIPTION
- * The Group node is designed for handling queries with a GROUP BY clause.
- * Its outer plan must deliver tuples that are sorted in the order
- * specified by the grouping columns (ie. tuples from the same group are
- * consecutive). That way, we just have to compare adjacent tuples to
- * locate group boundaries.
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.47 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "catalog/pg_operator.h"
-#include "executor/executor.h"
-#include "executor/nodeGroup.h"
-#include "parser/parse_oper.h"
-#include "utils/builtins.h"
-#include "utils/lsyscache.h"
-#include "utils/syscache.h"
-
-static TupleTableSlot *ExecGroupEveryTuple(Group *node);
-static TupleTableSlot *ExecGroupOneTuple(Group *node);
-
-/* ---------------------------------------
- * ExecGroup -
- *
- * There are two modes in which tuples are returned by ExecGroup. If
- * tuplePerGroup is TRUE, every tuple from the same group will be
- * returned, followed by a NULL at the end of each group. This is
- * useful for Agg node which needs to aggregate over tuples of the same
- * group. (eg. SELECT salary, count(*) FROM emp GROUP BY salary)
- *
- * If tuplePerGroup is FALSE, only one tuple per group is returned. The
- * tuple returned contains only the group columns. NULL is returned only
- * at the end when no more groups are present. This is useful when
- * the query does not involve aggregates. (eg. SELECT salary FROM emp
- * GROUP BY salary)
- * ------------------------------------------
- */
-TupleTableSlot *
-ExecGroup(Group *node)
-{
- if (node->tuplePerGroup)
- return ExecGroupEveryTuple(node);
- else
- return ExecGroupOneTuple(node);
-}
-
-/*
- * ExecGroupEveryTuple -
- * return every tuple with a NULL between each group
- */
-static TupleTableSlot *
-ExecGroupEveryTuple(Group *node)
-{
- GroupState *grpstate;
- EState *estate;
- ExprContext *econtext;
- TupleDesc tupdesc;
- HeapTuple outerTuple = NULL;
- HeapTuple firsttuple;
- TupleTableSlot *outerslot;
- ProjectionInfo *projInfo;
- TupleTableSlot *resultSlot;
-
- /*
- * get state info from node
- */
- grpstate = node->grpstate;
- if (grpstate->grp_done)
- return NULL;
- estate = node->plan.state;
- econtext = grpstate->csstate.cstate.cs_ExprContext;
- tupdesc = ExecGetScanType(&grpstate->csstate);
-
- /*
- * We need not call ResetExprContext here because execTuplesMatch will
- * reset the per-tuple memory context once per input tuple.
- */
-
- /* if we haven't returned first tuple of a new group yet ... */
- if (grpstate->grp_useFirstTuple)
- {
- grpstate->grp_useFirstTuple = FALSE;
-
- /*
- * note we rely on subplan to hold ownership of the tuple for as
- * long as we need it; we don't copy it.
- */
- ExecStoreTuple(grpstate->grp_firstTuple,
- grpstate->csstate.css_ScanTupleSlot,
- InvalidBuffer, false);
- }
- else
- {
- outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
- if (TupIsNull(outerslot))
- {
- grpstate->grp_done = TRUE;
- return NULL;
- }
- outerTuple = outerslot->val;
-
- firsttuple = grpstate->grp_firstTuple;
- if (firsttuple == NULL)
- {
- /* this should occur on the first call only */
- grpstate->grp_firstTuple = heap_copytuple(outerTuple);
- }
- else
- {
- /*
- * Compare with first tuple and see if this tuple is of the
- * same group.
- */
- if (!execTuplesMatch(firsttuple, outerTuple,
- tupdesc,
- node->numCols, node->grpColIdx,
- grpstate->eqfunctions,
- econtext->ecxt_per_tuple_memory))
- {
- /*
- * No; save the tuple to return it next time, and return
- * NULL
- */
- grpstate->grp_useFirstTuple = TRUE;
- heap_freetuple(firsttuple);
- grpstate->grp_firstTuple = heap_copytuple(outerTuple);
-
- return NULL; /* signifies the end of the group */
- }
- }
-
- /*
- * note we rely on subplan to hold ownership of the tuple for as
- * long as we need it; we don't copy it.
- */
- ExecStoreTuple(outerTuple,
- grpstate->csstate.css_ScanTupleSlot,
- InvalidBuffer, false);
- }
-
- /*
- * form a projection tuple, store it in the result tuple slot and
- * return it.
- */
- projInfo = grpstate->csstate.cstate.cs_ProjInfo;
-
- econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
- resultSlot = ExecProject(projInfo, NULL);
-
- return resultSlot;
-}
-
-/*
- * ExecGroupOneTuple -
- * returns one tuple per group, a NULL at the end when there are no more
- * tuples.
- */
-static TupleTableSlot *
-ExecGroupOneTuple(Group *node)
-{
- GroupState *grpstate;
- EState *estate;
- ExprContext *econtext;
- TupleDesc tupdesc;
- HeapTuple outerTuple = NULL;
- HeapTuple firsttuple;
- TupleTableSlot *outerslot;
- ProjectionInfo *projInfo;
- TupleTableSlot *resultSlot;
-
- /*
- * get state info from node
- */
- grpstate = node->grpstate;
- if (grpstate->grp_done)
- return NULL;
- estate = node->plan.state;
- econtext = node->grpstate->csstate.cstate.cs_ExprContext;
- tupdesc = ExecGetScanType(&grpstate->csstate);
-
- /*
- * We need not call ResetExprContext here because execTuplesMatch will
- * reset the per-tuple memory context once per input tuple.
- */
-
- firsttuple = grpstate->grp_firstTuple;
- if (firsttuple == NULL)
- {
- /* this should occur on the first call only */
- outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
- if (TupIsNull(outerslot))
- {
- grpstate->grp_done = TRUE;
- return NULL;
- }
- grpstate->grp_firstTuple = firsttuple =
- heap_copytuple(outerslot->val);
- }
-
- /*
- * find all tuples that belong to a group
- */
- for (;;)
- {
- outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
- if (TupIsNull(outerslot))
- {
- grpstate->grp_done = TRUE;
- outerTuple = NULL;
- break;
- }
- outerTuple = outerslot->val;
-
- /*
- * Compare with first tuple and see if this tuple is of the same
- * group.
- */
- if (!execTuplesMatch(firsttuple, outerTuple,
- tupdesc,
- node->numCols, node->grpColIdx,
- grpstate->eqfunctions,
- econtext->ecxt_per_tuple_memory))
- break;
- }
-
- /*
- * form a projection tuple, store it in the result tuple slot and
- * return it.
- */
- projInfo = grpstate->csstate.cstate.cs_ProjInfo;
-
- /*
- * note we rely on subplan to hold ownership of the tuple for as long
- * as we need it; we don't copy it.
- */
- ExecStoreTuple(firsttuple,
- grpstate->csstate.css_ScanTupleSlot,
- InvalidBuffer, false);
- econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
- resultSlot = ExecProject(projInfo, NULL);
-
- /* save outerTuple if we are not done yet */
- if (!grpstate->grp_done)
- {
- heap_freetuple(firsttuple);
- grpstate->grp_firstTuple = heap_copytuple(outerTuple);
- }
-
- return resultSlot;
-}
-
-/* -----------------
- * ExecInitGroup
- *
- * Creates the run-time information for the group node produced by the
- * planner and initializes its outer subtree
- * -----------------
- */
-bool
-ExecInitGroup(Group *node, EState *estate, Plan *parent)
-{
- GroupState *grpstate;
- Plan *outerPlan;
-
- /*
- * assign the node's execution state
- */
- node->plan.state = estate;
-
- /*
- * create state structure
- */
- grpstate = makeNode(GroupState);
- node->grpstate = grpstate;
- grpstate->grp_useFirstTuple = FALSE;
- grpstate->grp_done = FALSE;
- grpstate->grp_firstTuple = NULL;
-
- /*
- * create expression context
- */
- ExecAssignExprContext(estate, &grpstate->csstate.cstate);
-
-#define GROUP_NSLOTS 2
-
- /*
- * tuple table initialization
- */
- ExecInitScanTupleSlot(estate, &grpstate->csstate);
- ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
-
- /*
- * initializes child nodes
- */
- outerPlan = outerPlan(node);
- ExecInitNode(outerPlan, estate, (Plan *) node);
-
- /*
- * initialize tuple type.
- */
- ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
-
- /*
- * Initialize tuple type for both result and scan. This node does no
- * projection
- */
- ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
- ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
-
- /*
- * Precompute fmgr lookup data for inner loop
- */
- grpstate->eqfunctions =
- execTuplesMatchPrepare(ExecGetScanType(&grpstate->csstate),
- node->numCols,
- node->grpColIdx);
-
- return TRUE;
-}
-
-int
-ExecCountSlotsGroup(Group *node)
-{
- return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
-}
-
-/* ------------------------
- * ExecEndGroup(node)
- *
- * -----------------------
- */
-void
-ExecEndGroup(Group *node)
-{
- GroupState *grpstate;
- Plan *outerPlan;
-
- grpstate = node->grpstate;
-
- ExecFreeProjectionInfo(&grpstate->csstate.cstate);
- ExecFreeExprContext(&grpstate->csstate.cstate);
-
- outerPlan = outerPlan(node);
- ExecEndNode(outerPlan, (Plan *) node);
-
- /* clean up tuple table */
- ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
- if (grpstate->grp_firstTuple != NULL)
- {
- heap_freetuple(grpstate->grp_firstTuple);
- grpstate->grp_firstTuple = NULL;
- }
-}
-
-void
-ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
-{
- GroupState *grpstate = node->grpstate;
-
- grpstate->grp_useFirstTuple = FALSE;
- grpstate->grp_done = FALSE;
- if (grpstate->grp_firstTuple != NULL)
- {
- heap_freetuple(grpstate->grp_firstTuple);
- grpstate->grp_firstTuple = NULL;
- }
-
- if (((Plan *) node)->lefttree &&
- ((Plan *) node)->lefttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-}
-
-/*****************************************************************************
- * Code shared with nodeUnique.c
- *****************************************************************************/
-
-/*
- * execTuplesMatch
- * Return true if two tuples match in all the indicated fields.
- * This is used to detect group boundaries in nodeGroup, and to
- * decide whether two tuples are distinct or not in nodeUnique.
- *
- * tuple1, tuple2: the tuples to compare
- * tupdesc: tuple descriptor applying to both tuples
- * numCols: the number of attributes to be examined
- * matchColIdx: array of attribute column numbers
- * eqFunctions: array of fmgr lookup info for the equality functions to use
- * evalContext: short-term memory context for executing the functions
- *
- * NB: evalContext is reset each time!
- */
-bool
-execTuplesMatch(HeapTuple tuple1,
- HeapTuple tuple2,
- TupleDesc tupdesc,
- int numCols,
- AttrNumber *matchColIdx,
- FmgrInfo *eqfunctions,
- MemoryContext evalContext)
-{
- MemoryContext oldContext;
- bool result;
- int i;
-
- /* Reset and switch into the temp context. */
- MemoryContextReset(evalContext);
- oldContext = MemoryContextSwitchTo(evalContext);
-
- /*
- * We cannot report a match without checking all the fields, but we
- * can report a non-match as soon as we find unequal fields. So,
- * start comparing at the last field (least significant sort key).
- * That's the most likely to be different...
- */
- result = true;
-
- for (i = numCols; --i >= 0;)
- {
- AttrNumber att = matchColIdx[i];
- Datum attr1,
- attr2;
- bool isNull1,
- isNull2;
-
- attr1 = heap_getattr(tuple1,
- att,
- tupdesc,
- &isNull1);
-
- attr2 = heap_getattr(tuple2,
- att,
- tupdesc,
- &isNull2);
-
- if (isNull1 != isNull2)
- {
- result = false; /* one null and one not; they aren't equal */
- break;
- }
-
- if (isNull1)
- continue; /* both are null, treat as equal */
-
- /* Apply the type-specific equality function */
-
- if (!DatumGetBool(FunctionCall2(&eqfunctions[i],
- attr1, attr2)))
- {
- result = false; /* they aren't equal */
- break;
- }
- }
-
- MemoryContextSwitchTo(oldContext);
-
- return result;
-}
-
-/*
- * execTuplesMatchPrepare
- * Look up the equality functions needed for execTuplesMatch.
- * The result is a palloc'd array.
- */
-FmgrInfo *
-execTuplesMatchPrepare(TupleDesc tupdesc,
- int numCols,
- AttrNumber *matchColIdx)
-{
- FmgrInfo *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
- int i;
-
- for (i = 0; i < numCols; i++)
- {
- AttrNumber att = matchColIdx[i];
- Oid typid = tupdesc->attrs[att - 1]->atttypid;
- Oid eq_function;
-
- eq_function = compatible_oper_funcid(makeList1(makeString("=")),
- typid, typid, true);
- if (!OidIsValid(eq_function))
- elog(ERROR, "Unable to identify an equality operator for type %s",
- format_type_be(typid));
- fmgr_info(eq_function, &eqfunctions[i]);
- }
-
- return eqfunctions;
-}
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
deleted file mode 100644
index b91c70ab10a..00000000000
--- a/src/backend/executor/nodeHash.c
+++ /dev/null
@@ -1,736 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeHash.c
- * Routines to hash relations for hashjoin
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * $Id: nodeHash.c,v 1.63 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecHash - generate an in-memory hash table of the relation
- * ExecInitHash - initialize node and subnodes
- * ExecEndHash - shutdown node and subnodes
- */
-#include "postgres.h"
-
-#include <sys/types.h>
-#include <math.h>
-
-#include "access/hash.h"
-#include "executor/execdebug.h"
-#include "executor/nodeHash.h"
-#include "executor/nodeHashjoin.h"
-#include "miscadmin.h"
-#include "parser/parse_expr.h"
-#include "utils/memutils.h"
-#include "utils/lsyscache.h"
-
-
-static uint32 hashFunc(Datum key, int len, bool byVal);
-
-/* ----------------------------------------------------------------
- * ExecHash
- *
- * build hash table for hashjoin, all do partitioning if more
- * than one batches are required.
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecHash(Hash *node)
-{
- EState *estate;
- HashState *hashstate;
- Plan *outerNode;
- Node *hashkey;
- HashJoinTable hashtable;
- TupleTableSlot *slot;
- ExprContext *econtext;
- int nbatch;
- int i;
-
- /*
- * get state info from node
- */
-
- hashstate = node->hashstate;
- estate = node->plan.state;
- outerNode = outerPlan(node);
-
- hashtable = hashstate->hashtable;
- if (hashtable == NULL)
- elog(ERROR, "ExecHash: hash table is NULL.");
-
- nbatch = hashtable->nbatch;
-
- if (nbatch > 0)
- {
- /*
- * Open temp files for inner batches, if needed. Note that file
- * buffers are palloc'd in regular executor context.
- */
- for (i = 0; i < nbatch; i++)
- hashtable->innerBatchFile[i] = BufFileCreateTemp();
- }
-
- /*
- * set expression context
- */
- hashkey = node->hashkey;
- econtext = hashstate->cstate.cs_ExprContext;
-
- /*
- * get all inner tuples and insert into the hash table (or temp files)
- */
- for (;;)
- {
- slot = ExecProcNode(outerNode, (Plan *) node);
- if (TupIsNull(slot))
- break;
- econtext->ecxt_innertuple = slot;
- ExecHashTableInsert(hashtable, econtext, hashkey);
- ExecClearTuple(slot);
- }
-
- /*
- * Return the slot so that we have the tuple descriptor when we need
- * to save/restore them. -Jeff 11 July 1991
- */
- return slot;
-}
-
-/* ----------------------------------------------------------------
- * ExecInitHash
- *
- * Init routine for Hash node
- * ----------------------------------------------------------------
- */
-bool
-ExecInitHash(Hash *node, EState *estate, Plan *parent)
-{
- HashState *hashstate;
- Plan *outerPlan;
-
- SO_printf("ExecInitHash: initializing hash node\n");
-
- /*
- * assign the node's execution state
- */
- node->plan.state = estate;
-
- /*
- * create state structure
- */
- hashstate = makeNode(HashState);
- node->hashstate = hashstate;
- hashstate->hashtable = NULL;
-
- /*
- * Miscellaneous initialization
- *
- * create expression context for node
- */
- ExecAssignExprContext(estate, &hashstate->cstate);
-
-#define HASH_NSLOTS 1
-
- /*
- * initialize our result slot
- */
- ExecInitResultTupleSlot(estate, &hashstate->cstate);
-
- /*
- * initializes child nodes
- */
- outerPlan = outerPlan(node);
- ExecInitNode(outerPlan, estate, (Plan *) node);
-
- /*
- * initialize tuple type. no need to initialize projection info
- * because this node doesn't do projections
- */
- ExecAssignResultTypeFromOuterPlan((Plan *) node, &hashstate->cstate);
- hashstate->cstate.cs_ProjInfo = NULL;
-
- return TRUE;
-}
-
-int
-ExecCountSlotsHash(Hash *node)
-{
- return ExecCountSlotsNode(outerPlan(node)) +
- ExecCountSlotsNode(innerPlan(node)) +
- HASH_NSLOTS;
-}
-
-/* ---------------------------------------------------------------
- * ExecEndHash
- *
- * clean up routine for Hash node
- * ----------------------------------------------------------------
- */
-void
-ExecEndHash(Hash *node)
-{
- HashState *hashstate;
- Plan *outerPlan;
-
- /*
- * get info from the hash state
- */
- hashstate = node->hashstate;
-
- /*
- * free projection info. no need to free result type info because
- * that came from the outer plan...
- */
- ExecFreeProjectionInfo(&hashstate->cstate);
- ExecFreeExprContext(&hashstate->cstate);
-
- /*
- * shut down the subplan
- */
- outerPlan = outerPlan(node);
- ExecEndNode(outerPlan, (Plan *) node);
-}
-
-
-/* ----------------------------------------------------------------
- * ExecHashTableCreate
- *
- * create a hashtable in shared memory for hashjoin.
- * ----------------------------------------------------------------
- */
-HashJoinTable
-ExecHashTableCreate(Hash *node)
-{
- HashJoinTable hashtable;
- Plan *outerNode;
- int totalbuckets;
- int nbuckets;
- int nbatch;
- int i;
- MemoryContext oldcxt;
-
- /*
- * Get information about the size of the relation to be hashed (it's
- * the "outer" subtree of this node, but the inner relation of the
- * hashjoin). Compute the appropriate size of the hash table.
- */
- outerNode = outerPlan(node);
-
- ExecChooseHashTableSize(outerNode->plan_rows, outerNode->plan_width,
- &totalbuckets, &nbuckets, &nbatch);
-
-#ifdef HJDEBUG
- printf("nbatch = %d, totalbuckets = %d, nbuckets = %d\n",
- nbatch, totalbuckets, nbuckets);
-#endif
-
- /*
- * Initialize the hash table control block.
- *
- * The hashtable control block is just palloc'd from the executor's
- * per-query memory context.
- */
- hashtable = (HashJoinTable) palloc(sizeof(HashTableData));
- hashtable->nbuckets = nbuckets;
- hashtable->totalbuckets = totalbuckets;
- hashtable->buckets = NULL;
- hashtable->nbatch = nbatch;
- hashtable->curbatch = 0;
- hashtable->innerBatchFile = NULL;
- hashtable->outerBatchFile = NULL;
- hashtable->innerBatchSize = NULL;
- hashtable->outerBatchSize = NULL;
-
- /*
- * Get info about the datatype of the hash key.
- */
- get_typlenbyval(exprType(node->hashkey),
- &hashtable->typLen,
- &hashtable->typByVal);
-
- /*
- * Create temporary memory contexts in which to keep the hashtable
- * working storage. See notes in executor/hashjoin.h.
- */
- hashtable->hashCxt = AllocSetContextCreate(CurrentMemoryContext,
- "HashTableContext",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
-
- hashtable->batchCxt = AllocSetContextCreate(hashtable->hashCxt,
- "HashBatchContext",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
-
- /* Allocate data that will live for the life of the hashjoin */
-
- oldcxt = MemoryContextSwitchTo(hashtable->hashCxt);
-
- if (nbatch > 0)
- {
- /*
- * allocate and initialize the file arrays in hashCxt
- */
- hashtable->innerBatchFile = (BufFile **)
- palloc(nbatch * sizeof(BufFile *));
- hashtable->outerBatchFile = (BufFile **)
- palloc(nbatch * sizeof(BufFile *));
- hashtable->innerBatchSize = (long *)
- palloc(nbatch * sizeof(long));
- hashtable->outerBatchSize = (long *)
- palloc(nbatch * sizeof(long));
- for (i = 0; i < nbatch; i++)
- {
- hashtable->innerBatchFile[i] = NULL;
- hashtable->outerBatchFile[i] = NULL;
- hashtable->innerBatchSize[i] = 0;
- hashtable->outerBatchSize[i] = 0;
- }
- /* The files will not be opened until later... */
- }
-
- /*
- * Prepare context for the first-scan space allocations; allocate the
- * hashbucket array therein, and set each bucket "empty".
- */
- MemoryContextSwitchTo(hashtable->batchCxt);
-
- hashtable->buckets = (HashJoinTuple *)
- palloc(nbuckets * sizeof(HashJoinTuple));
-
- if (hashtable->buckets == NULL)
- elog(ERROR, "Insufficient memory for hash table.");
-
- for (i = 0; i < nbuckets; i++)
- hashtable->buckets[i] = NULL;
-
- MemoryContextSwitchTo(oldcxt);
-
- return hashtable;
-}
-
-
-/*
- * Compute appropriate size for hashtable given the estimated size of the
- * relation to be hashed (number of rows and average row width).
- *
- * Caution: the input is only the planner's estimates, and so can't be
- * trusted too far. Apply a healthy fudge factor.
- *
- * This is exported so that the planner's costsize.c can use it.
- */
-
-/* Target bucket loading (tuples per bucket) */
-#define NTUP_PER_BUCKET 10
-/* Fudge factor to allow for inaccuracy of input estimates */
-#define FUDGE_FAC 2.0
-
-void
-ExecChooseHashTableSize(double ntuples, int tupwidth,
- int *virtualbuckets,
- int *physicalbuckets,
- int *numbatches)
-{
- int tupsize;
- double inner_rel_bytes;
- double hash_table_bytes;
- int nbatch;
- int nbuckets;
- int totalbuckets;
- int bucketsize;
-
- /* Force a plausible relation size if no info */
- if (ntuples <= 0.0)
- ntuples = 1000.0;
-
- /*
- * Estimate tupsize based on footprint of tuple in hashtable... but
- * what about palloc overhead?
- */
- tupsize = MAXALIGN(tupwidth) + MAXALIGN(sizeof(HashJoinTupleData));
- inner_rel_bytes = ntuples * tupsize * FUDGE_FAC;
-
- /*
- * Target hashtable size is SortMem kilobytes, but not less than
- * sqrt(estimated inner rel size), so as to avoid horrible
- * performance.
- */
- hash_table_bytes = sqrt(inner_rel_bytes);
- if (hash_table_bytes < (SortMem * 1024L))
- hash_table_bytes = SortMem * 1024L;
-
- /*
- * Count the number of hash buckets we want for the whole relation,
- * for an average bucket load of NTUP_PER_BUCKET (per virtual
- * bucket!).
- */
- totalbuckets = (int) ceil(ntuples * FUDGE_FAC / NTUP_PER_BUCKET);
-
- /*
- * Count the number of buckets we think will actually fit in the
- * target memory size, at a loading of NTUP_PER_BUCKET (physical
- * buckets). NOTE: FUDGE_FAC here determines the fraction of the
- * hashtable space reserved to allow for nonuniform distribution of
- * hash values. Perhaps this should be a different number from the
- * other uses of FUDGE_FAC, but since we have no real good way to pick
- * either one...
- */
- bucketsize = NTUP_PER_BUCKET * tupsize;
- nbuckets = (int) (hash_table_bytes / (bucketsize * FUDGE_FAC));
- if (nbuckets <= 0)
- nbuckets = 1;
-
- if (totalbuckets <= nbuckets)
- {
- /*
- * We have enough space, so no batching. In theory we could even
- * reduce nbuckets, but since that could lead to poor behavior if
- * estimated ntuples is much less than reality, it seems better to
- * make more buckets instead of fewer.
- */
- totalbuckets = nbuckets;
- nbatch = 0;
- }
- else
- {
- /*
- * Need to batch; compute how many batches we want to use. Note
- * that nbatch doesn't have to have anything to do with the ratio
- * totalbuckets/nbuckets; in fact, it is the number of groups we
- * will use for the part of the data that doesn't fall into the
- * first nbuckets hash buckets.
- */
- nbatch = (int) ceil((inner_rel_bytes - hash_table_bytes) /
- hash_table_bytes);
- if (nbatch <= 0)
- nbatch = 1;
- }
-
- /*
- * Now, totalbuckets is the number of (virtual) hashbuckets for the
- * whole relation, and nbuckets is the number of physical hashbuckets
- * we will use in the first pass. Data falling into the first
- * nbuckets virtual hashbuckets gets handled in the first pass;
- * everything else gets divided into nbatch batches to be processed in
- * additional passes.
- */
- *virtualbuckets = totalbuckets;
- *physicalbuckets = nbuckets;
- *numbatches = nbatch;
-}
-
-
-/* ----------------------------------------------------------------
- * ExecHashTableDestroy
- *
- * destroy a hash table
- * ----------------------------------------------------------------
- */
-void
-ExecHashTableDestroy(HashJoinTable hashtable)
-{
- int i;
-
- /* Make sure all the temp files are closed */
- for (i = 0; i < hashtable->nbatch; i++)
- {
- if (hashtable->innerBatchFile[i])
- BufFileClose(hashtable->innerBatchFile[i]);
- if (hashtable->outerBatchFile[i])
- BufFileClose(hashtable->outerBatchFile[i]);
- }
-
- /* Release working memory (batchCxt is a child, so it goes away too) */
- MemoryContextDelete(hashtable->hashCxt);
-
- /* And drop the control block */
- pfree(hashtable);
-}
-
-/* ----------------------------------------------------------------
- * ExecHashTableInsert
- *
- * insert a tuple into the hash table depending on the hash value
- * it may just go to a tmp file for other batches
- * ----------------------------------------------------------------
- */
-void
-ExecHashTableInsert(HashJoinTable hashtable,
- ExprContext *econtext,
- Node *hashkey)
-{
- int bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
- TupleTableSlot *slot = econtext->ecxt_innertuple;
- HeapTuple heapTuple = slot->val;
-
- /*
- * decide whether to put the tuple in the hash table or a tmp file
- */
- if (bucketno < hashtable->nbuckets)
- {
- /*
- * put the tuple in hash table
- */
- HashJoinTuple hashTuple;
- int hashTupleSize;
-
- hashTupleSize = MAXALIGN(sizeof(*hashTuple)) + heapTuple->t_len;
- hashTuple = (HashJoinTuple) MemoryContextAlloc(hashtable->batchCxt,
- hashTupleSize);
- if (hashTuple == NULL)
- elog(ERROR, "Insufficient memory for hash table.");
- memcpy((char *) &hashTuple->htup,
- (char *) heapTuple,
- sizeof(hashTuple->htup));
- hashTuple->htup.t_datamcxt = hashtable->batchCxt;
- hashTuple->htup.t_data = (HeapTupleHeader)
- (((char *) hashTuple) + MAXALIGN(sizeof(*hashTuple)));
- memcpy((char *) hashTuple->htup.t_data,
- (char *) heapTuple->t_data,
- heapTuple->t_len);
- hashTuple->next = hashtable->buckets[bucketno];
- hashtable->buckets[bucketno] = hashTuple;
- }
- else
- {
- /*
- * put the tuple into a tmp file for other batches
- */
- int batchno = (hashtable->nbatch * (bucketno - hashtable->nbuckets)) /
- (hashtable->totalbuckets - hashtable->nbuckets);
-
- hashtable->innerBatchSize[batchno]++;
- ExecHashJoinSaveTuple(heapTuple,
- hashtable->innerBatchFile[batchno]);
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecHashGetBucket
- *
- * Get the hash value for a tuple
- * ----------------------------------------------------------------
- */
-int
-ExecHashGetBucket(HashJoinTable hashtable,
- ExprContext *econtext,
- Node *hashkey)
-{
- int bucketno;
- Datum keyval;
- bool isNull;
- MemoryContext oldContext;
-
- /*
- * We reset the eval context each time to reclaim any memory leaked in
- * the hashkey expression or hashFunc itself.
- */
- ResetExprContext(econtext);
-
- oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
- /*
- * Get the join attribute value of the tuple
- */
- keyval = ExecEvalExpr(hashkey, econtext, &isNull, NULL);
-
- /*
- * Compute the hash function
- */
- if (isNull)
- bucketno = 0;
- else
- {
- bucketno = hashFunc(keyval,
- (int) hashtable->typLen,
- hashtable->typByVal)
- % (uint32) hashtable->totalbuckets;
- }
-
-#ifdef HJDEBUG
- if (bucketno >= hashtable->nbuckets)
- printf("hash(%ld) = %d SAVED\n", (long) keyval, bucketno);
- else
- printf("hash(%ld) = %d\n", (long) keyval, bucketno);
-#endif
-
- MemoryContextSwitchTo(oldContext);
-
- return bucketno;
-}
-
-/* ----------------------------------------------------------------
- * ExecScanHashBucket
- *
- * scan a hash bucket of matches
- * ----------------------------------------------------------------
- */
-HeapTuple
-ExecScanHashBucket(HashJoinState *hjstate,
- List *hjclauses,
- ExprContext *econtext)
-{
- HashJoinTable hashtable = hjstate->hj_HashTable;
- HashJoinTuple hashTuple = hjstate->hj_CurTuple;
-
- /*
- * hj_CurTuple is NULL to start scanning a new bucket, or the address
- * of the last tuple returned from the current bucket.
- */
- if (hashTuple == NULL)
- hashTuple = hashtable->buckets[hjstate->hj_CurBucketNo];
- else
- hashTuple = hashTuple->next;
-
- while (hashTuple != NULL)
- {
- HeapTuple heapTuple = &hashTuple->htup;
- TupleTableSlot *inntuple;
-
- /* insert hashtable's tuple into exec slot so ExecQual sees it */
- inntuple = ExecStoreTuple(heapTuple, /* tuple to store */
- hjstate->hj_HashTupleSlot, /* slot */
- InvalidBuffer,
- false); /* do not pfree this tuple */
- econtext->ecxt_innertuple = inntuple;
-
- /* reset temp memory each time to avoid leaks from qual expression */
- ResetExprContext(econtext);
-
- if (ExecQual(hjclauses, econtext, false))
- {
- hjstate->hj_CurTuple = hashTuple;
- return heapTuple;
- }
-
- hashTuple = hashTuple->next;
- }
-
- /*
- * no match
- */
- return NULL;
-}
-
-/* ----------------------------------------------------------------
- * hashFunc
- *
- * the hash function for hash joins
- *
- * XXX this probably ought to be replaced with datatype-specific
- * hash functions, such as those already implemented for hash indexes.
- * ----------------------------------------------------------------
- */
-static uint32
-hashFunc(Datum key, int len, bool byVal)
-{
- unsigned char *k;
-
- if (byVal)
- {
- /*
- * If it's a by-value data type, just hash the whole Datum value.
- * This assumes that datatypes narrower than Datum are consistently
- * padded (either zero-extended or sign-extended, but not random
- * bits) to fill Datum; see the XXXGetDatum macros in postgres.h.
- * NOTE: it would not work to do hash_any(&key, len) since this
- * would get the wrong bytes on a big-endian machine.
- */
- k = (unsigned char *) &key;
- len = sizeof(Datum);
- }
- else
- {
- /*
- * If this is a variable length type, then 'key' points to a
- * "struct varlena" and len == -1. NOTE: VARSIZE returns the
- * "real" data length plus the sizeof the "vl_len" attribute of
- * varlena (the length information). 'key' points to the beginning
- * of the varlena struct, so we have to use "VARDATA" to find the
- * beginning of the "real" data. Also, we have to be careful to
- * detoast the datum if it's toasted. (We don't worry about
- * freeing the detoasted copy; that happens for free when the
- * per-tuple memory context is reset in ExecHashGetBucket.)
- */
- if (len < 0)
- {
- struct varlena *vkey = PG_DETOAST_DATUM(key);
-
- len = VARSIZE(vkey) - VARHDRSZ;
- k = (unsigned char *) VARDATA(vkey);
- }
- else
- k = (unsigned char *) DatumGetPointer(key);
- }
-
- return DatumGetUInt32(hash_any(k, len));
-}
-
-/* ----------------------------------------------------------------
- * ExecHashTableReset
- *
- * reset hash table header for new batch
- *
- * ntuples is the number of tuples in the inner relation's batch
- * (which we currently don't actually use...)
- * ----------------------------------------------------------------
- */
-void
-ExecHashTableReset(HashJoinTable hashtable, long ntuples)
-{
- MemoryContext oldcxt;
- int nbuckets = hashtable->nbuckets;
- int i;
-
- /*
- * Release all the hash buckets and tuples acquired in the prior pass,
- * and reinitialize the context for a new pass.
- */
- MemoryContextReset(hashtable->batchCxt);
- oldcxt = MemoryContextSwitchTo(hashtable->batchCxt);
-
- /*
- * We still use the same number of physical buckets as in the first
- * pass. (It could be different; but we already decided how many
- * buckets would be appropriate for the allowed memory, so stick with
- * that number.) We MUST set totalbuckets to equal nbuckets, because
- * from now on no tuples will go out to temp files; there are no more
- * virtual buckets, only real buckets. (This implies that tuples will
- * go into different bucket numbers than they did on the first pass,
- * but that's OK.)
- */
- hashtable->totalbuckets = nbuckets;
-
- /* Reallocate and reinitialize the hash bucket headers. */
- hashtable->buckets = (HashJoinTuple *)
- palloc(nbuckets * sizeof(HashJoinTuple));
-
- if (hashtable->buckets == NULL)
- elog(ERROR, "Insufficient memory for hash table.");
-
- for (i = 0; i < nbuckets; i++)
- hashtable->buckets[i] = NULL;
-
- MemoryContextSwitchTo(oldcxt);
-}
-
-void
-ExecReScanHash(Hash *node, ExprContext *exprCtxt, Plan *parent)
-{
- /*
- * if chgParam of subnode is not null then plan will be re-scanned by
- * first ExecProcNode.
- */
- if (((Plan *) node)->lefttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-}
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
deleted file mode 100644
index b741f8983fd..00000000000
--- a/src/backend/executor/nodeHashjoin.c
+++ /dev/null
@@ -1,713 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeHashjoin.c
- * Routines to handle hash join nodes
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.40 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include <sys/types.h>
-
-#include "postgres.h"
-
-#include "executor/executor.h"
-#include "executor/nodeHash.h"
-#include "executor/nodeHashjoin.h"
-#include "optimizer/clauses.h"
-#include "utils/memutils.h"
-
-
-static TupleTableSlot *ExecHashJoinOuterGetTuple(Plan *node, Plan *parent,
- HashJoinState *hjstate);
-static TupleTableSlot *ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
- BufFile *file,
- TupleTableSlot *tupleSlot);
-static int ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable);
-static int ExecHashJoinNewBatch(HashJoinState *hjstate);
-
-
-/* ----------------------------------------------------------------
- * ExecHashJoin
- *
- * This function implements the Hybrid Hashjoin algorithm.
- * recursive partitioning remains to be added.
- * Note: the relation we build hash table on is the inner
- * the other one is outer.
- * ----------------------------------------------------------------
- */
-TupleTableSlot * /* return: a tuple or NULL */
-ExecHashJoin(HashJoin *node)
-{
- HashJoinState *hjstate;
- EState *estate;
- Plan *outerNode;
- Hash *hashNode;
- List *hjclauses;
- Expr *clause;
- List *joinqual;
- List *otherqual;
- ScanDirection dir;
- TupleTableSlot *inntuple;
- Node *outerVar;
- ExprContext *econtext;
- ExprDoneCond isDone;
- HashJoinTable hashtable;
- HeapTuple curtuple;
- TupleTableSlot *outerTupleSlot;
- TupleTableSlot *innerTupleSlot;
- int i;
- bool hashPhaseDone;
-
- /*
- * get information from HashJoin node
- */
- hjstate = node->hashjoinstate;
- hjclauses = node->hashclauses;
- clause = lfirst(hjclauses);
- estate = node->join.plan.state;
- joinqual = node->join.joinqual;
- otherqual = node->join.plan.qual;
- hashNode = (Hash *) innerPlan(node);
- outerNode = outerPlan(node);
- hashPhaseDone = hjstate->hj_hashdone;
- dir = estate->es_direction;
-
- /*
- * get information from HashJoin state
- */
- hashtable = hjstate->hj_HashTable;
- econtext = hjstate->jstate.cs_ExprContext;
-
- /*
- * Check to see if we're still projecting out tuples from a previous
- * join tuple (because there is a function-returning-set in the
- * projection expressions). If so, try to project another one.
- */
- if (hjstate->jstate.cs_TupFromTlist)
- {
- TupleTableSlot *result;
-
- result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
- if (isDone == ExprMultipleResult)
- return result;
- /* Done with that source tuple... */
- hjstate->jstate.cs_TupFromTlist = false;
- }
-
- /*
- * Reset per-tuple memory context to free any expression evaluation
- * storage allocated in the previous tuple cycle. Note this can't
- * happen until we're done projecting out tuples from a join tuple.
- */
- ResetExprContext(econtext);
-
- /*
- * if this is the first call, build the hash table for inner relation
- */
- if (!hashPhaseDone)
- { /* if the hash phase not completed */
- if (hashtable == NULL)
- { /* if the hash table has not been created */
-
- /*
- * create the hash table
- */
- hashtable = ExecHashTableCreate(hashNode);
- hjstate->hj_HashTable = hashtable;
- hjstate->hj_InnerHashKey = hashNode->hashkey;
-
- /*
- * execute the Hash node, to build the hash table
- */
- hashNode->hashstate->hashtable = hashtable;
- innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node);
- }
- hjstate->hj_hashdone = true;
-
- /*
- * Open temp files for outer batches, if needed. Note that file
- * buffers are palloc'd in regular executor context.
- */
- for (i = 0; i < hashtable->nbatch; i++)
- hashtable->outerBatchFile[i] = BufFileCreateTemp();
- }
- else if (hashtable == NULL)
- return NULL;
-
- /*
- * Now get an outer tuple and probe into the hash table for matches
- */
- outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
- outerVar = (Node *) get_leftop(clause);
-
- for (;;)
- {
- /*
- * If we don't have an outer tuple, get the next one
- */
- if (hjstate->hj_NeedNewOuter)
- {
- outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode,
- (Plan *) node,
- hjstate);
- if (TupIsNull(outerTupleSlot))
- {
- /*
- * when the last batch runs out, clean up and exit
- */
- ExecHashTableDestroy(hashtable);
- hjstate->hj_HashTable = NULL;
- return NULL;
- }
-
- hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- hjstate->hj_NeedNewOuter = false;
- hjstate->hj_MatchedOuter = false;
-
- /*
- * now we have an outer tuple, find the corresponding bucket
- * for this tuple from the hash table
- */
- hjstate->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext,
- outerVar);
- hjstate->hj_CurTuple = NULL;
-
- /*
- * Now we've got an outer tuple and the corresponding hash
- * bucket, but this tuple may not belong to the current batch.
- * This need only be checked in the first pass.
- */
- if (hashtable->curbatch == 0)
- {
- int batch = ExecHashJoinGetBatch(hjstate->hj_CurBucketNo,
- hashtable);
-
- if (batch > 0)
- {
- /*
- * Need to postpone this outer tuple to a later batch.
- * Save it in the corresponding outer-batch file.
- */
- int batchno = batch - 1;
-
- hashtable->outerBatchSize[batchno]++;
- ExecHashJoinSaveTuple(outerTupleSlot->val,
- hashtable->outerBatchFile[batchno]);
- hjstate->hj_NeedNewOuter = true;
- continue; /* loop around for a new outer tuple */
- }
- }
- }
-
- /*
- * OK, scan the selected hash bucket for matches
- */
- for (;;)
- {
- curtuple = ExecScanHashBucket(hjstate,
- hjclauses,
- econtext);
- if (curtuple == NULL)
- break; /* out of matches */
-
- /*
- * we've got a match, but still need to test non-hashed quals
- */
- inntuple = ExecStoreTuple(curtuple,
- hjstate->hj_HashTupleSlot,
- InvalidBuffer,
- false); /* don't pfree this tuple */
- econtext->ecxt_innertuple = inntuple;
-
- /* reset temp memory each time to avoid leaks from qual expr */
- ResetExprContext(econtext);
-
- /*
- * if we pass the qual, then save state for next call and have
- * ExecProject form the projection, store it in the tuple
- * table, and return the slot.
- *
- * Only the joinquals determine MatchedOuter status, but all
- * quals must pass to actually return the tuple.
- */
- if (ExecQual(joinqual, econtext, false))
- {
- hjstate->hj_MatchedOuter = true;
-
- if (otherqual == NIL || ExecQual(otherqual, econtext, false))
- {
- TupleTableSlot *result;
-
- result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
-
- if (isDone != ExprEndResult)
- {
- hjstate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
- }
-
- /*
- * Now the current outer tuple has run out of matches, so check
- * whether to emit a dummy outer-join tuple. If not, loop around
- * to get a new outer tuple.
- */
- hjstate->hj_NeedNewOuter = true;
-
- if (!hjstate->hj_MatchedOuter &&
- node->join.jointype == JOIN_LEFT)
- {
- /*
- * We are doing an outer join and there were no join matches
- * for this outer tuple. Generate a fake join tuple with
- * nulls for the inner tuple, and return it if it passes the
- * non-join quals.
- */
- econtext->ecxt_innertuple = hjstate->hj_NullInnerTupleSlot;
-
- if (ExecQual(otherqual, econtext, false))
- {
- /*
- * qualification was satisfied so we project and return
- * the slot containing the result tuple using
- * ExecProject().
- */
- TupleTableSlot *result;
-
- result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
-
- if (isDone != ExprEndResult)
- {
- hjstate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecInitHashJoin
- *
- * Init routine for HashJoin node.
- * ----------------------------------------------------------------
- */
-bool /* return: initialization status */
-ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
-{
- HashJoinState *hjstate;
- Plan *outerNode;
- Hash *hashNode;
-
- /*
- * assign the node's execution state
- */
- node->join.plan.state = estate;
-
- /*
- * create state structure
- */
- hjstate = makeNode(HashJoinState);
- node->hashjoinstate = hjstate;
-
- /*
- * Miscellaneous initialization
- *
- * create expression context for node
- */
- ExecAssignExprContext(estate, &hjstate->jstate);
-
- /*
- * initializes child nodes
- */
- outerNode = outerPlan((Plan *) node);
- hashNode = (Hash *) innerPlan((Plan *) node);
-
- ExecInitNode(outerNode, estate, (Plan *) node);
- ExecInitNode((Plan *) hashNode, estate, (Plan *) node);
-
-#define HASHJOIN_NSLOTS 3
-
- /*
- * tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &hjstate->jstate);
- hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate);
-
- switch (node->join.jointype)
- {
- case JOIN_INNER:
- break;
- case JOIN_LEFT:
- hjstate->hj_NullInnerTupleSlot =
- ExecInitNullTupleSlot(estate,
- ExecGetTupType((Plan *) hashNode));
- break;
- default:
- elog(ERROR, "ExecInitHashJoin: unsupported join type %d",
- (int) node->join.jointype);
- }
-
- /*
- * now for some voodoo. our temporary tuple slot is actually the
- * result tuple slot of the Hash node (which is our inner plan). we
- * do this because Hash nodes don't return tuples via ExecProcNode()
- * -- instead the hash join node uses ExecScanHashBucket() to get at
- * the contents of the hash table. -cim 6/9/91
- */
- {
- HashState *hashstate = hashNode->hashstate;
- TupleTableSlot *slot = hashstate->cstate.cs_ResultTupleSlot;
-
- hjstate->hj_HashTupleSlot = slot;
- }
-
- /*
- * initialize tuple type and projection info
- */
- ExecAssignResultTypeFromTL((Plan *) node, &hjstate->jstate);
- ExecAssignProjectionInfo((Plan *) node, &hjstate->jstate);
-
- ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,
- ExecGetTupType(outerNode),
- false);
-
- /*
- * initialize hash-specific info
- */
-
- hjstate->hj_hashdone = false;
-
- hjstate->hj_HashTable = (HashJoinTable) NULL;
- hjstate->hj_CurBucketNo = 0;
- hjstate->hj_CurTuple = (HashJoinTuple) NULL;
- hjstate->hj_InnerHashKey = (Node *) NULL;
-
- hjstate->jstate.cs_OuterTupleSlot = NULL;
- hjstate->jstate.cs_TupFromTlist = false;
- hjstate->hj_NeedNewOuter = true;
- hjstate->hj_MatchedOuter = false;
-
- return TRUE;
-}
-
-int
-ExecCountSlotsHashJoin(HashJoin *node)
-{
- return ExecCountSlotsNode(outerPlan(node)) +
- ExecCountSlotsNode(innerPlan(node)) +
- HASHJOIN_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndHashJoin
- *
- * clean up routine for HashJoin node
- * ----------------------------------------------------------------
- */
-void
-ExecEndHashJoin(HashJoin *node)
-{
- HashJoinState *hjstate;
-
- /*
- * get info from the HashJoin state
- */
- hjstate = node->hashjoinstate;
-
- /*
- * free hash table in case we end plan before all tuples are retrieved
- */
- if (hjstate->hj_HashTable)
- {
- ExecHashTableDestroy(hjstate->hj_HashTable);
- hjstate->hj_HashTable = NULL;
- }
-
- /*
- * Free the projection info and the scan attribute info
- *
- * Note: we don't ExecFreeResultType(hjstate) because the rule manager
- * depends on the tupType returned by ExecMain(). So for now, this is
- * freed at end-transaction time. -cim 6/2/91
- */
- ExecFreeProjectionInfo(&hjstate->jstate);
- ExecFreeExprContext(&hjstate->jstate);
-
- /*
- * clean up subtrees
- */
- ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
- ExecEndNode(innerPlan((Plan *) node), (Plan *) node);
-
- /*
- * clean out the tuple table
- */
- ExecClearTuple(hjstate->jstate.cs_ResultTupleSlot);
- ExecClearTuple(hjstate->hj_OuterTupleSlot);
- ExecClearTuple(hjstate->hj_HashTupleSlot);
-
-}
-
-/* ----------------------------------------------------------------
- * ExecHashJoinOuterGetTuple
- *
- * get the next outer tuple for hashjoin: either by
- * executing a plan node as in the first pass, or from
- * the tmp files for the hashjoin batches.
- * ----------------------------------------------------------------
- */
-
-static TupleTableSlot *
-ExecHashJoinOuterGetTuple(Plan *node, Plan *parent, HashJoinState *hjstate)
-{
- HashJoinTable hashtable = hjstate->hj_HashTable;
- int curbatch = hashtable->curbatch;
- TupleTableSlot *slot;
-
- if (curbatch == 0)
- { /* if it is the first pass */
- slot = ExecProcNode(node, parent);
- if (!TupIsNull(slot))
- return slot;
-
- /*
- * We have just reached the end of the first pass. Try to switch
- * to a saved batch.
- */
- curbatch = ExecHashJoinNewBatch(hjstate);
- }
-
- /*
- * Try to read from a temp file. Loop allows us to advance to new
- * batch as needed.
- */
- while (curbatch <= hashtable->nbatch)
- {
- slot = ExecHashJoinGetSavedTuple(hjstate,
- hashtable->outerBatchFile[curbatch - 1],
- hjstate->hj_OuterTupleSlot);
- if (!TupIsNull(slot))
- return slot;
- curbatch = ExecHashJoinNewBatch(hjstate);
- }
-
- /* Out of batches... */
- return NULL;
-}
-
-/* ----------------------------------------------------------------
- * ExecHashJoinGetSavedTuple
- *
- * read the next tuple from a tmp file
- * ----------------------------------------------------------------
- */
-
-static TupleTableSlot *
-ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
- BufFile *file,
- TupleTableSlot *tupleSlot)
-{
- HeapTupleData htup;
- size_t nread;
- HeapTuple heapTuple;
-
- nread = BufFileRead(file, (void *) &htup, sizeof(HeapTupleData));
- if (nread == 0)
- return NULL; /* end of file */
- if (nread != sizeof(HeapTupleData))
- elog(ERROR, "Read from hashjoin temp file failed");
- heapTuple = palloc(HEAPTUPLESIZE + htup.t_len);
- memcpy((char *) heapTuple, (char *) &htup, sizeof(HeapTupleData));
- heapTuple->t_datamcxt = CurrentMemoryContext;
- heapTuple->t_data = (HeapTupleHeader)
- ((char *) heapTuple + HEAPTUPLESIZE);
- nread = BufFileRead(file, (void *) heapTuple->t_data, htup.t_len);
- if (nread != (size_t) htup.t_len)
- elog(ERROR, "Read from hashjoin temp file failed");
- return ExecStoreTuple(heapTuple, tupleSlot, InvalidBuffer, true);
-}
-
-/* ----------------------------------------------------------------
- * ExecHashJoinNewBatch
- *
- * switch to a new hashjoin batch
- * ----------------------------------------------------------------
- */
-static int
-ExecHashJoinNewBatch(HashJoinState *hjstate)
-{
- HashJoinTable hashtable = hjstate->hj_HashTable;
- int nbatch = hashtable->nbatch;
- int newbatch = hashtable->curbatch + 1;
- long *innerBatchSize = hashtable->innerBatchSize;
- long *outerBatchSize = hashtable->outerBatchSize;
- BufFile *innerFile;
- TupleTableSlot *slot;
- ExprContext *econtext;
- Node *innerhashkey;
-
- if (newbatch > 1)
- {
- /*
- * We no longer need the previous outer batch file; close it right
- * away to free disk space.
- */
- BufFileClose(hashtable->outerBatchFile[newbatch - 2]);
- hashtable->outerBatchFile[newbatch - 2] = NULL;
- }
-
- /*
- * We can skip over any batches that are empty on either side. Release
- * associated temp files right away.
- */
- while (newbatch <= nbatch &&
- (innerBatchSize[newbatch - 1] == 0L ||
- outerBatchSize[newbatch - 1] == 0L))
- {
- BufFileClose(hashtable->innerBatchFile[newbatch - 1]);
- hashtable->innerBatchFile[newbatch - 1] = NULL;
- BufFileClose(hashtable->outerBatchFile[newbatch - 1]);
- hashtable->outerBatchFile[newbatch - 1] = NULL;
- newbatch++;
- }
-
- if (newbatch > nbatch)
- return newbatch; /* no more batches */
-
- /*
- * Rewind inner and outer batch files for this batch, so that we can
- * start reading them.
- */
- if (BufFileSeek(hashtable->outerBatchFile[newbatch - 1], 0, 0L, SEEK_SET))
- elog(ERROR, "Failed to rewind hash temp file");
-
- innerFile = hashtable->innerBatchFile[newbatch - 1];
-
- if (BufFileSeek(innerFile, 0, 0L, SEEK_SET))
- elog(ERROR, "Failed to rewind hash temp file");
-
- /*
- * Reload the hash table with the new inner batch
- */
- ExecHashTableReset(hashtable, innerBatchSize[newbatch - 1]);
-
- econtext = hjstate->jstate.cs_ExprContext;
- innerhashkey = hjstate->hj_InnerHashKey;
-
- while ((slot = ExecHashJoinGetSavedTuple(hjstate,
- innerFile,
- hjstate->hj_HashTupleSlot))
- && !TupIsNull(slot))
- {
- econtext->ecxt_innertuple = slot;
- ExecHashTableInsert(hashtable, econtext, innerhashkey);
- }
-
- /*
- * after we build the hash table, the inner batch file is no longer
- * needed
- */
- BufFileClose(innerFile);
- hashtable->innerBatchFile[newbatch - 1] = NULL;
-
- hashtable->curbatch = newbatch;
- return newbatch;
-}
-
-/* ----------------------------------------------------------------
- * ExecHashJoinGetBatch
- *
- * determine the batch number for a bucketno
- * +----------------+-------+-------+ ... +-------+
- * 0 nbuckets totalbuckets
- * batch 0 1 2 ...
- * ----------------------------------------------------------------
- */
-static int
-ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable)
-{
- int b;
-
- if (bucketno < hashtable->nbuckets || hashtable->nbatch == 0)
- return 0;
-
- b = (hashtable->nbatch * (bucketno - hashtable->nbuckets)) /
- (hashtable->totalbuckets - hashtable->nbuckets);
- return b + 1;
-}
-
-/* ----------------------------------------------------------------
- * ExecHashJoinSaveTuple
- *
- * save a tuple to a tmp file.
- *
- * The data recorded in the file for each tuple is an image of its
- * HeapTupleData (with meaningless t_data pointer) followed by the
- * HeapTupleHeader and tuple data.
- * ----------------------------------------------------------------
- */
-
-void
-ExecHashJoinSaveTuple(HeapTuple heapTuple,
- BufFile *file)
-{
- size_t written;
-
- written = BufFileWrite(file, (void *) heapTuple, sizeof(HeapTupleData));
- if (written != sizeof(HeapTupleData))
- elog(ERROR, "Write to hashjoin temp file failed");
- written = BufFileWrite(file, (void *) heapTuple->t_data, heapTuple->t_len);
- if (written != (size_t) heapTuple->t_len)
- elog(ERROR, "Write to hashjoin temp file failed");
-}
-
-void
-ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
-{
- HashJoinState *hjstate = node->hashjoinstate;
-
- if (!hjstate->hj_hashdone)
- return;
-
- hjstate->hj_hashdone = false;
-
- /*
- * Unfortunately, currently we have to destroy hashtable in all
- * cases...
- */
- if (hjstate->hj_HashTable)
- {
- ExecHashTableDestroy(hjstate->hj_HashTable);
- hjstate->hj_HashTable = NULL;
- }
-
- hjstate->hj_CurBucketNo = 0;
- hjstate->hj_CurTuple = (HashJoinTuple) NULL;
- hjstate->hj_InnerHashKey = (Node *) NULL;
-
- hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
- hjstate->jstate.cs_TupFromTlist = false;
- hjstate->hj_NeedNewOuter = true;
- hjstate->hj_MatchedOuter = false;
-
- /*
- * if chgParam of subnodes is not null then plans will be re-scanned
- * by first ExecProcNode.
- */
- if (((Plan *) node)->lefttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
- if (((Plan *) node)->righttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->righttree, exprCtxt, (Plan *) node);
-}
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
deleted file mode 100644
index d33147793f8..00000000000
--- a/src/backend/executor/nodeIndexscan.c
+++ /dev/null
@@ -1,1050 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeIndexscan.c
- * Routines to support indexes and indexed scans of relations
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.69 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecIndexScan scans a relation using indices
- * ExecIndexNext using index to retrieve next tuple
- * ExecInitIndexScan creates and initializes state info.
- * ExecIndexReScan rescans the indexed relation.
- * ExecEndIndexScan releases all storage.
- * ExecIndexMarkPos marks scan position.
- * ExecIndexRestrPos restores scan position.
- */
-#include "postgres.h"
-
-#include "access/genam.h"
-#include "access/heapam.h"
-#include "executor/execdebug.h"
-#include "executor/nodeIndexscan.h"
-#include "nodes/nodeFuncs.h"
-#include "optimizer/clauses.h"
-#include "parser/parsetree.h"
-
-/* ----------------
- * Misc stuff to move to executor.h soon -cim 6/5/90
- * ----------------
- */
-#define NO_OP 0
-#define LEFT_OP 1
-#define RIGHT_OP 2
-
-static TupleTableSlot *IndexNext(IndexScan *node);
-
-/* ----------------------------------------------------------------
- * IndexNext
- *
- * Retrieve a tuple from the IndexScan node's currentRelation
- * using the indices in the IndexScanState information.
- *
- * note: the old code mentions 'Primary indices'. to my knowledge
- * we only support a single secondary index. -cim 9/11/89
- *
- * old comments:
- * retrieve a tuple from relation using the indices given.
- * The indices are used in the order they appear in 'indices'.
- * The indices may be primary or secondary indices:
- * * primary index -- scan the relation 'relID' using keys supplied.
- * * secondary index -- scan the index relation to get the 'tid' for
- * a tuple in the relation 'relID'.
- * If the current index(pointed by 'indexPtr') fails to return a
- * tuple, the next index in the indices is used.
- *
- * bug fix so that it should retrieve on a null scan key.
- * ----------------------------------------------------------------
- */
-static TupleTableSlot *
-IndexNext(IndexScan *node)
-{
- EState *estate;
- CommonScanState *scanstate;
- IndexScanState *indexstate;
- ExprContext *econtext;
- ScanDirection direction;
- IndexScanDescPtr scanDescs;
- IndexScanDesc scandesc;
- HeapTuple tuple;
- TupleTableSlot *slot;
- int numIndices;
- bool bBackward;
- int indexNumber;
-
- /*
- * extract necessary information from index scan node
- */
- estate = node->scan.plan.state;
- direction = estate->es_direction;
- if (ScanDirectionIsBackward(node->indxorderdir))
- {
- if (ScanDirectionIsForward(direction))
- direction = BackwardScanDirection;
- else if (ScanDirectionIsBackward(direction))
- direction = ForwardScanDirection;
- }
- scanstate = node->scan.scanstate;
- indexstate = node->indxstate;
- scanDescs = indexstate->iss_ScanDescs;
- numIndices = indexstate->iss_NumIndices;
- econtext = scanstate->cstate.cs_ExprContext;
- slot = scanstate->css_ScanTupleSlot;
-
- /*
- * Check if we are evaluating PlanQual for tuple of this relation.
- * Additional checking is not good, but no other way for now. We could
- * introduce new nodes for this case and handle IndexScan --> NewNode
- * switching in Init/ReScan plan...
- */
- if (estate->es_evTuple != NULL &&
- estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
- {
- List *qual;
-
- ExecClearTuple(slot);
- if (estate->es_evTupleNull[node->scan.scanrelid - 1])
- return slot; /* return empty slot */
-
- ExecStoreTuple(estate->es_evTuple[node->scan.scanrelid - 1],
- slot, InvalidBuffer, false);
-
- /* Does the tuple meet any of the OR'd indxqual conditions? */
- econtext->ecxt_scantuple = slot;
-
- ResetExprContext(econtext);
-
- foreach(qual, node->indxqualorig)
- {
- if (ExecQual((List *) lfirst(qual), econtext, false))
- break;
- }
- if (qual == NIL) /* would not be returned by indices */
- slot->val = NULL;
-
- /* Flag for the next call that no more tuples */
- estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
-
- return slot;
- }
-
- /*
- * ok, now that we have what we need, fetch an index tuple. if
- * scanning this index succeeded then return the appropriate heap
- * tuple.. else return NULL.
- */
- bBackward = ScanDirectionIsBackward(direction);
- if (bBackward)
- {
- indexNumber = numIndices - indexstate->iss_IndexPtr - 1;
- if (indexNumber < 0)
- {
- indexNumber = 0;
- indexstate->iss_IndexPtr = numIndices - 1;
- }
- }
- else
- {
- if ((indexNumber = indexstate->iss_IndexPtr) < 0)
- {
- indexNumber = 0;
- indexstate->iss_IndexPtr = 0;
- }
- }
- while (indexNumber < numIndices)
- {
- scandesc = scanDescs[indexstate->iss_IndexPtr];
- while ((tuple = index_getnext(scandesc, direction)) != NULL)
- {
- /*
- * store the scanned tuple in the scan tuple slot of the
- * scan state. Note: we pass 'false' because tuples
- * returned by amgetnext are pointers onto disk pages and
- * must not be pfree()'d.
- */
- ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- scandesc->xs_cbuf, /* buffer containing tuple */
- false); /* don't pfree */
-
- /*
- * We must check to see if the current tuple was already
- * matched by an earlier index, so we don't double-report
- * it. We do this by passing the tuple through ExecQual
- * and checking for failure with all previous
- * qualifications.
- */
- if (indexstate->iss_IndexPtr > 0)
- {
- bool prev_matches = false;
- int prev_index;
- List *qual;
-
- econtext->ecxt_scantuple = slot;
- ResetExprContext(econtext);
- qual = node->indxqualorig;
- for (prev_index = 0;
- prev_index < indexstate->iss_IndexPtr;
- prev_index++)
- {
- if (ExecQual((List *) lfirst(qual), econtext, false))
- {
- prev_matches = true;
- break;
- }
- qual = lnext(qual);
- }
- if (prev_matches)
- {
- /* Duplicate, so drop it and loop back for another */
- ExecClearTuple(slot);
- continue;
- }
- }
-
- return slot; /* OK to return tuple */
- }
-
- if (indexNumber < numIndices)
- {
- indexNumber++;
- if (bBackward)
- indexstate->iss_IndexPtr--;
- else
- indexstate->iss_IndexPtr++;
- }
- }
-
- /*
- * if we get here it means the index scan failed so we are at the end
- * of the scan..
- */
- return ExecClearTuple(slot);
-}
-
-/* ----------------------------------------------------------------
- * ExecIndexScan(node)
- *
- * old comments:
- * Scans the relation using primary or secondary indices and returns
- * the next qualifying tuple in the direction specified.
- * It calls ExecScan() and passes it the access methods which returns
- * the next tuple using the indices.
- *
- * Conditions:
- * -- the "cursor" maintained by the AMI is positioned at the tuple
- * returned previously.
- *
- * Initial States:
- * -- the relation indicated is opened for scanning so that the
- * "cursor" is positioned before the first qualifying tuple.
- * -- all index realtions are opened for scanning.
- * -- indexPtr points to the first index.
- * -- state variable ruleFlag = nil.
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecIndexScan(IndexScan *node)
-{
- IndexScanState *indexstate = node->indxstate;
-
- /*
- * If we have runtime keys and they've not already been set up, do it
- * now.
- */
- if (indexstate->iss_RuntimeKeyInfo && !indexstate->iss_RuntimeKeysReady)
- ExecReScan((Plan *) node, NULL, NULL);
-
- /*
- * use IndexNext as access method
- */
- return ExecScan(&node->scan, (ExecScanAccessMtd) IndexNext);
-}
-
-/* ----------------------------------------------------------------
- * ExecIndexReScan(node)
- *
- * Recalculates the value of the scan keys whose value depends on
- * information known at runtime and rescans the indexed relation.
- * Updating the scan key was formerly done separately in
- * ExecUpdateIndexScanKeys. Integrating it into ReScan makes
- * rescans of indices and relations/general streams more uniform.
- *
- * ----------------------------------------------------------------
- */
-void
-ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
-{
- EState *estate;
- IndexScanState *indexstate;
- ExprContext *econtext;
- int numIndices;
- IndexScanDescPtr scanDescs;
- ScanKey *scanKeys;
- int **runtimeKeyInfo;
- int *numScanKeys;
- int i;
- int j;
-
- estate = node->scan.plan.state;
- indexstate = node->indxstate;
- econtext = indexstate->iss_RuntimeContext; /* context for runtime
- * keys */
- numIndices = indexstate->iss_NumIndices;
- scanDescs = indexstate->iss_ScanDescs;
- scanKeys = indexstate->iss_ScanKeys;
- runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
- numScanKeys = indexstate->iss_NumScanKeys;
-
- if (econtext)
- {
- /*
- * If we are being passed an outer tuple, save it for runtime key
- * calc. We also need to link it into the "regular" per-tuple
- * econtext, so it can be used during indexqualorig evaluations.
- */
- if (exprCtxt != NULL)
- {
- ExprContext *stdecontext;
-
- econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
- stdecontext = node->scan.scanstate->cstate.cs_ExprContext;
- stdecontext->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 we are doing runtime key calculations (ie, the index keys depend
- * on data from an outer scan), compute the new key values
- */
- if (runtimeKeyInfo)
- {
- List *indxqual;
-
- indxqual = node->indxqual;
- for (i = 0; i < numIndices; i++)
- {
- List *qual = lfirst(indxqual);
- int n_keys;
- ScanKey scan_keys;
- int *run_keys;
- List *listscan;
-
- indxqual = lnext(indxqual);
- n_keys = numScanKeys[i];
- scan_keys = scanKeys[i];
- run_keys = runtimeKeyInfo[i];
-
- listscan = qual;
- for (j = 0; j < n_keys; j++)
- {
- Expr *clause = lfirst(listscan);
-
- listscan = lnext(listscan);
-
- /*
- * If we have a run-time key, then extract the run-time
- * expression and evaluate it with respect to the current
- * outer tuple. We then stick the result into the scan
- * key.
- *
- * Note: the result of the eval could be a pass-by-ref value
- * that's stored in the outer scan's tuple, not in
- * econtext->ecxt_per_tuple_memory. We assume that the
- * outer tuple will stay put throughout our scan. If this
- * is wrong, we could copy the result into our context
- * explicitly, but I think that's not necessary...
- */
- if (run_keys[j] != NO_OP)
- {
- Node *scanexpr;
- Datum scanvalue;
- bool isNull;
-
- scanexpr = (run_keys[j] == RIGHT_OP) ?
- (Node *) get_rightop(clause) :
- (Node *) get_leftop(clause);
-
- scanvalue = ExecEvalExprSwitchContext(scanexpr,
- econtext,
- &isNull,
- NULL);
- scan_keys[j].sk_argument = scanvalue;
- if (isNull)
- scan_keys[j].sk_flags |= SK_ISNULL;
- else
- scan_keys[j].sk_flags &= ~SK_ISNULL;
- }
- }
- }
-
- indexstate->iss_RuntimeKeysReady = true;
- }
-
- /* If this is re-scanning of PlanQual ... */
- if (estate->es_evTuple != NULL &&
- estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
- {
- estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
- return;
- }
-
- /* reset index scans */
- if (ScanDirectionIsBackward(node->indxorderdir))
- indexstate->iss_IndexPtr = numIndices;
- else
- indexstate->iss_IndexPtr = -1;
-
- for (i = 0; i < numIndices; i++)
- {
- IndexScanDesc scan = scanDescs[i];
- ScanKey skey = scanKeys[i];
-
- index_rescan(scan, skey);
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecEndIndexScan
- *
- * old comments
- * Releases any storage allocated through C routines.
- * Returns nothing.
- * ----------------------------------------------------------------
- */
-void
-ExecEndIndexScan(IndexScan *node)
-{
- CommonScanState *scanstate;
- IndexScanState *indexstate;
- int **runtimeKeyInfo;
- ScanKey *scanKeys;
- List *indxqual;
- int *numScanKeys;
- int numIndices;
- Relation relation;
- RelationPtr indexRelationDescs;
- IndexScanDescPtr indexScanDescs;
- int i;
-
- scanstate = node->scan.scanstate;
- indexstate = node->indxstate;
- indxqual = node->indxqual;
- runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
-
- /*
- * extract information from the node
- */
- numIndices = indexstate->iss_NumIndices;
- scanKeys = indexstate->iss_ScanKeys;
- numScanKeys = indexstate->iss_NumScanKeys;
- indexRelationDescs = indexstate->iss_RelationDescs;
- indexScanDescs = indexstate->iss_ScanDescs;
- relation = scanstate->css_currentRelation;
-
- /*
- * Free the projection info and the scan attribute info
- *
- * Note: we don't ExecFreeResultType(scanstate) because the rule manager
- * depends on the tupType returned by ExecMain(). So for now, this is
- * freed at end-transaction time. -cim 6/2/91
- */
- ExecFreeProjectionInfo(&scanstate->cstate);
- ExecFreeExprContext(&scanstate->cstate);
- if (indexstate->iss_RuntimeContext)
- FreeExprContext(indexstate->iss_RuntimeContext);
-
- /*
- * close the index relations
- */
- for (i = 0; i < numIndices; i++)
- {
- if (indexScanDescs[i] != NULL)
- index_endscan(indexScanDescs[i]);
-
- if (indexRelationDescs[i] != NULL)
- index_close(indexRelationDescs[i]);
- }
-
- /*
- * close the heap relation.
- *
- * Currently, we do not release the AccessShareLock acquired by
- * ExecInitIndexScan. This lock should be held till end of transaction.
- * (There is a faction that considers this too much locking, however.)
- */
- heap_close(relation, NoLock);
-
- /*
- * free the scan keys used in scanning the indices
- */
- for (i = 0; i < numIndices; i++)
- {
- if (scanKeys[i] != NULL)
- pfree(scanKeys[i]);
- }
- pfree(scanKeys);
- pfree(numScanKeys);
-
- if (runtimeKeyInfo)
- {
- for (i = 0; i < numIndices; i++)
- {
- if (runtimeKeyInfo[i] != NULL)
- pfree(runtimeKeyInfo[i]);
- }
- pfree(runtimeKeyInfo);
- }
-
- /*
- * clear out tuple table slots
- */
- ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
- ExecClearTuple(scanstate->css_ScanTupleSlot);
-}
-
-/* ----------------------------------------------------------------
- * ExecIndexMarkPos
- *
- * old comments
- * Marks scan position by marking the current index.
- * Returns nothing.
- * ----------------------------------------------------------------
- */
-void
-ExecIndexMarkPos(IndexScan *node)
-{
- IndexScanState *indexstate;
- IndexScanDescPtr indexScanDescs;
- IndexScanDesc scanDesc;
- int indexPtr;
-
- indexstate = node->indxstate;
- indexPtr = indexstate->iss_MarkIndexPtr = indexstate->iss_IndexPtr;
- indexScanDescs = indexstate->iss_ScanDescs;
- scanDesc = indexScanDescs[indexPtr];
-
-#ifdef NOT_USED
- IndexScanMarkPosition(scanDesc);
-#endif
- index_markpos(scanDesc);
-}
-
-/* ----------------------------------------------------------------
- * ExecIndexRestrPos
- *
- * old comments
- * Restores scan position by restoring the current index.
- * Returns nothing.
- *
- * XXX Assumes previously marked scan position belongs to current index
- * ----------------------------------------------------------------
- */
-void
-ExecIndexRestrPos(IndexScan *node)
-{
- IndexScanState *indexstate;
- IndexScanDescPtr indexScanDescs;
- IndexScanDesc scanDesc;
- int indexPtr;
-
- indexstate = node->indxstate;
- indexPtr = indexstate->iss_IndexPtr = indexstate->iss_MarkIndexPtr;
- indexScanDescs = indexstate->iss_ScanDescs;
- scanDesc = indexScanDescs[indexPtr];
-
-#ifdef NOT_USED
- IndexScanRestorePosition(scanDesc);
-#endif
- index_restrpos(scanDesc);
-}
-
-/* ----------------------------------------------------------------
- * ExecInitIndexScan
- *
- * Initializes the index scan's state information, creates
- * scan keys, and opens the base and index relations.
- *
- * Note: index scans have 2 sets of state information because
- * we have to keep track of the base relation and the
- * index relations.
- *
- * old comments
- * Creates the run-time state information for the node and
- * sets the relation id to contain relevant descriptors.
- *
- * Parameters:
- * node: IndexNode node produced by the planner.
- * estate: the execution state initialized in InitPlan.
- * ----------------------------------------------------------------
- */
-bool
-ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
-{
- IndexScanState *indexstate;
- CommonScanState *scanstate;
- List *indxqual;
- List *indxid;
- List *listscan;
- int i;
- int numIndices;
- int indexPtr;
- ScanKey *scanKeys;
- int *numScanKeys;
- RelationPtr indexDescs;
- IndexScanDescPtr scanDescs;
- int **runtimeKeyInfo;
- bool have_runtime_keys;
- RangeTblEntry *rtentry;
- Index relid;
- Oid reloid;
- Relation currentRelation;
-
- /*
- * assign execution state to node
- */
- node->scan.plan.state = estate;
-
- /*
- * Part 1) initialize scan state
- *
- * create new CommonScanState for node
- */
- scanstate = makeNode(CommonScanState);
- node->scan.scanstate = scanstate;
-
- /*
- * Miscellaneous initialization
- *
- * create expression context for node
- */
- ExecAssignExprContext(estate, &scanstate->cstate);
-
-#define INDEXSCAN_NSLOTS 2
-
- /*
- * tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &scanstate->cstate);
- ExecInitScanTupleSlot(estate, scanstate);
-
- /*
- * initialize projection info. result type comes from scan desc
- * below..
- */
- ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
-
- /*
- * Part 2) initialize index scan state
- *
- * create new IndexScanState for node
- */
- indexstate = makeNode(IndexScanState);
- indexstate->iss_NumIndices = 0;
- indexstate->iss_IndexPtr = -1;
- indexstate->iss_ScanKeys = NULL;
- indexstate->iss_NumScanKeys = NULL;
- indexstate->iss_RuntimeKeyInfo = NULL;
- indexstate->iss_RuntimeContext = NULL;
- indexstate->iss_RuntimeKeysReady = false;
- indexstate->iss_RelationDescs = NULL;
- indexstate->iss_ScanDescs = NULL;
-
- node->indxstate = indexstate;
-
- /*
- * get the index node information
- */
- indxid = node->indxid;
- numIndices = length(indxid);
- indexPtr = -1;
-
- CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
-
- /*
- * scanKeys is used to keep track of the ScanKey's. This is needed
- * because a single scan may use several indices and each index has
- * its own ScanKey.
- */
- numScanKeys = (int *) palloc(numIndices * sizeof(int));
- scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey));
- indexDescs = (RelationPtr) palloc(numIndices * sizeof(Relation));
- scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
-
- /*
- * initialize space for runtime key info (may not be needed)
- */
- have_runtime_keys = false;
- runtimeKeyInfo = (int **) palloc(numIndices * sizeof(int *));
-
- /*
- * build the index scan keys from the index qualification
- */
- indxqual = node->indxqual;
- for (i = 0; i < numIndices; i++)
- {
- int j;
- List *qual;
- int n_keys;
- ScanKey scan_keys;
- int *run_keys;
-
- qual = lfirst(indxqual);
- indxqual = lnext(indxqual);
- n_keys = length(qual);
- scan_keys = (n_keys <= 0) ? (ScanKey) NULL :
- (ScanKey) palloc(n_keys * sizeof(ScanKeyData));
- run_keys = (n_keys <= 0) ? (int *) NULL :
- (int *) palloc(n_keys * sizeof(int));
-
- CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
-
- /*
- * for each opclause in the given qual, convert each qual's
- * opclause into a single scan key
- */
- listscan = qual;
- for (j = 0; j < n_keys; j++)
- {
- Expr *clause; /* one clause of index qual */
- Oper *op; /* operator used in clause */
- Node *leftop; /* expr on lhs of operator */
- Node *rightop; /* expr on rhs ... */
- bits16 flags = 0;
-
- int scanvar; /* which var identifies varattno */
- AttrNumber varattno = 0; /* att number used in scan */
- Oid opid; /* operator id used in scan */
- Datum scanvalue = 0; /* value used in scan (if const) */
-
- /*
- * extract clause information from the qualification
- */
- clause = lfirst(listscan);
- listscan = lnext(listscan);
-
- op = (Oper *) clause->oper;
- if (!IsA(clause, Expr) ||!IsA(op, Oper))
- elog(ERROR, "ExecInitIndexScan: indxqual not an opclause!");
-
- opid = op->opid;
-
- /*
- * Here we figure out the contents of the index qual. The
- * usual case is (var op const) or (const op var) which means
- * we form a scan key for the attribute listed in the var node
- * and use the value of the const.
- *
- * If we don't have a const node, then it means that one of the
- * var nodes refers to the "scan" tuple and is used to
- * determine which attribute to scan, and the other expression
- * is used to calculate the value used in scanning the index.
- *
- * This means our index scan's scan key is a function of
- * information obtained during the execution of the plan in
- * which case we need to recalculate the index scan key at run
- * time.
- *
- * Hence, we set have_runtime_keys to true and then set the
- * appropriate flag in run_keys to LEFT_OP or RIGHT_OP. The
- * corresponding scan keys are recomputed at run time.
- *
- * XXX Although this code *thinks* it can handle an indexqual
- * with the indexkey on either side, in fact it cannot.
- * Indexscans only work with quals that have the indexkey on
- * the left (the planner/optimizer makes sure it never passes
- * anything else). The reason: the scankey machinery has no
- * provision for distinguishing which side of the operator is
- * the indexed attribute and which is the compared-to
- * constant. It just assumes that the attribute is on the left
- * :-(
- *
- * I am leaving this code able to support both ways, even though
- * half of it is dead code, on the off chance that someone
- * will fix the scankey machinery someday --- tgl 8/11/99.
- */
-
- scanvar = NO_OP;
- run_keys[j] = NO_OP;
-
- /*
- * determine information in leftop
- */
- leftop = (Node *) get_leftop(clause);
-
- if (leftop && IsA(leftop, RelabelType))
- leftop = ((RelabelType *) leftop)->arg;
-
- Assert(leftop != NULL);
-
- if (IsA(leftop, Var) &&var_is_rel((Var *) leftop))
- {
- /*
- * if the leftop is a "rel-var", then it means that it is
- * a var node which tells us which attribute to use for
- * our scan key.
- */
- varattno = ((Var *) leftop)->varattno;
- scanvar = LEFT_OP;
- }
- else if (IsA(leftop, Const))
- {
- /*
- * if the leftop is a const node then it means it
- * identifies the value to place in our scan key.
- */
- scanvalue = ((Const *) leftop)->constvalue;
- if (((Const *) leftop)->constisnull)
- flags |= SK_ISNULL;
- }
- else if (IsA(leftop, Param))
- {
- bool isnull;
-
- /*
- * if the leftop is a Param node then it means it
- * identifies the value to place in our scan key.
- */
-
- /* Life was so easy before ... subselects */
- if (((Param *) leftop)->paramkind == PARAM_EXEC)
- {
- /* treat Param as runtime key */
- have_runtime_keys = true;
- run_keys[j] = LEFT_OP;
- }
- else
- {
- /* treat Param like a constant */
- scanvalue = ExecEvalParam((Param *) leftop,
- scanstate->cstate.cs_ExprContext,
- &isnull);
- if (isnull)
- flags |= SK_ISNULL;
- }
- }
- else
- {
- /*
- * otherwise, the leftop contains an expression evaluable
- * at runtime to figure out the value to place in our scan
- * key.
- */
- have_runtime_keys = true;
- run_keys[j] = LEFT_OP;
- }
-
- /*
- * now determine information in rightop
- */
- rightop = (Node *) get_rightop(clause);
-
- if (rightop && IsA(rightop, RelabelType))
- rightop = ((RelabelType *) rightop)->arg;
-
- Assert(rightop != NULL);
-
- if (IsA(rightop, Var) &&var_is_rel((Var *) rightop))
- {
- /*
- * here we make sure only one op identifies the
- * scan-attribute...
- */
- if (scanvar == LEFT_OP)
- elog(ERROR, "ExecInitIndexScan: %s",
- "both left and right op's are rel-vars");
-
- /*
- * if the rightop is a "rel-var", then it means that it is
- * a var node which tells us which attribute to use for
- * our scan key.
- */
- varattno = ((Var *) rightop)->varattno;
- scanvar = RIGHT_OP;
- }
- else if (IsA(rightop, Const))
- {
- /*
- * if the rightop is a const node then it means it
- * identifies the value to place in our scan key.
- */
- scanvalue = ((Const *) rightop)->constvalue;
- if (((Const *) rightop)->constisnull)
- flags |= SK_ISNULL;
- }
- else if (IsA(rightop, Param))
- {
- bool isnull;
-
- /*
- * if the rightop is a Param node then it means it
- * identifies the value to place in our scan key.
- */
-
- /* Life was so easy before ... subselects */
- if (((Param *) rightop)->paramkind == PARAM_EXEC)
- {
- /* treat Param as runtime key */
- have_runtime_keys = true;
- run_keys[j] = RIGHT_OP;
- }
- else
- {
- /* treat Param like a constant */
- scanvalue = ExecEvalParam((Param *) rightop,
- scanstate->cstate.cs_ExprContext,
- &isnull);
- if (isnull)
- flags |= SK_ISNULL;
- }
- }
- else
- {
- /*
- * otherwise, the rightop contains an expression evaluable
- * at runtime to figure out the value to place in our scan
- * key.
- */
- have_runtime_keys = true;
- run_keys[j] = RIGHT_OP;
- }
-
- /*
- * now check that at least one op tells us the scan
- * attribute...
- */
- if (scanvar == NO_OP)
- elog(ERROR, "ExecInitIndexScan: %s",
- "neither leftop nor rightop refer to scan relation");
-
- /*
- * initialize the scan key's fields appropriately
- */
- ScanKeyEntryInitialize(&scan_keys[j],
- flags,
- varattno, /* attribute number to
- * scan */
- (RegProcedure) opid, /* reg proc to use */
- scanvalue); /* constant */
- }
-
- /*
- * store the key information into our arrays.
- */
- numScanKeys[i] = n_keys;
- scanKeys[i] = scan_keys;
- runtimeKeyInfo[i] = run_keys;
- }
-
- indexstate->iss_NumIndices = numIndices;
- if (ScanDirectionIsBackward(node->indxorderdir))
- indexPtr = numIndices;
- indexstate->iss_IndexPtr = indexPtr;
- indexstate->iss_ScanKeys = scanKeys;
- indexstate->iss_NumScanKeys = numScanKeys;
-
- /*
- * If all of our keys have the form (op var const) , then we have no
- * runtime keys so we store NULL in the runtime key info. Otherwise
- * runtime key info contains an array of pointers (one for each index)
- * to arrays of flags (one for each key) which indicate that the qual
- * needs to be evaluated at runtime. -cim 10/24/89
- *
- * If we do have runtime keys, we need an ExprContext to evaluate them;
- * the node's standard context won't do because we want to reset that
- * context for every tuple. So, build another context just like the
- * other one... -tgl 7/11/00
- */
- if (have_runtime_keys)
- {
- ExprContext *stdecontext = scanstate->cstate.cs_ExprContext;
-
- ExecAssignExprContext(estate, &scanstate->cstate);
- indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo;
- indexstate->iss_RuntimeContext = scanstate->cstate.cs_ExprContext;
- scanstate->cstate.cs_ExprContext = stdecontext;
- }
- else
- {
- indexstate->iss_RuntimeKeyInfo = NULL;
- indexstate->iss_RuntimeContext = NULL;
- /* Get rid of the speculatively-allocated flag arrays, too */
- for (i = 0; i < numIndices; i++)
- {
- if (runtimeKeyInfo[i] != NULL)
- pfree(runtimeKeyInfo[i]);
- }
- pfree(runtimeKeyInfo);
- }
-
- /*
- * open the base relation and acquire AccessShareLock on it.
- */
- relid = node->scan.scanrelid;
- rtentry = rt_fetch(relid, estate->es_range_table);
- reloid = rtentry->relid;
-
- currentRelation = heap_open(reloid, AccessShareLock);
-
- if (!RelationGetForm(currentRelation)->relhasindex)
- elog(ERROR, "indexes of the relation %u was inactivated", reloid);
-
- scanstate->css_currentRelation = currentRelation;
- scanstate->css_currentScanDesc = NULL; /* no heap scan here */
-
- /*
- * get the scan type from the relation descriptor.
- */
- ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false);
- ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
-
- /*
- * open the index relations and initialize relation and scan
- * descriptors. Note we acquire no locks here; the index machinery
- * does its own locks and unlocks. (We rely on having AccessShareLock
- * on the parent table to ensure the index won't go away!)
- */
- listscan = indxid;
- for (i = 0; i < numIndices; i++)
- {
- Oid indexOid = (Oid) lfirsti(listscan);
-
- indexDescs[i] = index_open(indexOid);
- scanDescs[i] = index_beginscan(currentRelation,
- indexDescs[i],
- estate->es_snapshot,
- numScanKeys[i],
- scanKeys[i]);
- listscan = lnext(listscan);
- }
-
- indexstate->iss_RelationDescs = indexDescs;
- indexstate->iss_ScanDescs = scanDescs;
-
- /*
- * all done.
- */
- return TRUE;
-}
-
-int
-ExecCountSlotsIndexScan(IndexScan *node)
-{
- return ExecCountSlotsNode(outerPlan((Plan *) node)) +
- ExecCountSlotsNode(innerPlan((Plan *) node)) + INDEXSCAN_NSLOTS;
-}
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
deleted file mode 100644
index 2e8c444c500..00000000000
--- a/src/backend/executor/nodeLimit.c
+++ /dev/null
@@ -1,311 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeLimit.c
- * Routines to handle limiting of query results where appropriate
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.10 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecLimit - extract a limited range of tuples
- * ExecInitLimit - initialize node and subnodes..
- * ExecEndLimit - shutdown node and subnodes
- */
-
-#include "postgres.h"
-
-#include "executor/executor.h"
-#include "executor/nodeLimit.h"
-
-static void recompute_limits(Limit *node);
-
-
-/* ----------------------------------------------------------------
- * ExecLimit
- *
- * This is a very simple node which just performs LIMIT/OFFSET
- * filtering on the stream of tuples returned by a subplan.
- * ----------------------------------------------------------------
- */
-TupleTableSlot * /* return: a tuple or NULL */
-ExecLimit(Limit *node)
-{
- LimitState *limitstate;
- ScanDirection direction;
- TupleTableSlot *resultTupleSlot;
- TupleTableSlot *slot;
- Plan *outerPlan;
- long netlimit;
-
- /*
- * get information from the node
- */
- limitstate = node->limitstate;
- direction = node->plan.state->es_direction;
- outerPlan = outerPlan((Plan *) node);
- resultTupleSlot = limitstate->cstate.cs_ResultTupleSlot;
-
- /*
- * If first call for this scan, compute limit/offset. (We can't do
- * this any earlier, because parameters from upper nodes may not be
- * set until now.)
- */
- if (!limitstate->parmsSet)
- recompute_limits(node);
- netlimit = limitstate->offset + limitstate->count;
-
- /*
- * now loop, returning only desired tuples.
- */
- for (;;)
- {
- /*
- * If we have reached the subplan EOF or the limit, just quit.
- *
- * NOTE: when scanning forwards, we must fetch one tuple beyond the
- * COUNT limit before we can return NULL, else the subplan won't
- * be properly positioned to start going backwards. Hence test
- * here is for position > netlimit not position >= netlimit.
- *
- * Similarly, when scanning backwards, we must re-fetch the last
- * tuple in the offset region before we can return NULL. Otherwise
- * we won't be correctly aligned to start going forward again. So,
- * although you might think we can quit when position equals
- * offset + 1, we have to fetch a subplan tuple first, and then
- * exit when position = offset.
- */
- if (ScanDirectionIsForward(direction))
- {
- if (limitstate->atEnd)
- return NULL;
- if (!limitstate->noCount && limitstate->position > netlimit)
- return NULL;
- }
- else
- {
- if (limitstate->position <= limitstate->offset)
- return NULL;
- }
-
- /*
- * fetch a tuple from the outer subplan
- */
- slot = ExecProcNode(outerPlan, (Plan *) node);
- if (TupIsNull(slot))
- {
- /*
- * We are at start or end of the subplan. Update local state
- * appropriately, but always return NULL.
- */
- if (ScanDirectionIsForward(direction))
- {
- Assert(!limitstate->atEnd);
- /* must bump position to stay in sync for backwards fetch */
- limitstate->position++;
- limitstate->atEnd = true;
- }
- else
- {
- limitstate->position = 0;
- limitstate->atEnd = false;
- }
- return NULL;
- }
-
- /*
- * We got the next subplan tuple successfully, so adjust state.
- */
- if (ScanDirectionIsForward(direction))
- limitstate->position++;
- else
- {
- limitstate->position--;
- Assert(limitstate->position > 0);
- }
- limitstate->atEnd = false;
-
- /*
- * Now, is this a tuple we want? If not, loop around to fetch
- * another tuple from the subplan.
- */
- if (limitstate->position > limitstate->offset &&
- (limitstate->noCount || limitstate->position <= netlimit))
- break;
- }
-
- ExecStoreTuple(slot->val,
- resultTupleSlot,
- InvalidBuffer,
- false); /* tuple does not belong to slot */
-
- return resultTupleSlot;
-}
-
-/*
- * Evaluate the limit/offset expressions --- done at start of each scan.
- *
- * This is also a handy place to reset the current-position state info.
- */
-static void
-recompute_limits(Limit *node)
-{
- LimitState *limitstate = node->limitstate;
- ExprContext *econtext = limitstate->cstate.cs_ExprContext;
- bool isNull;
-
- if (node->limitOffset)
- {
- limitstate->offset =
- DatumGetInt32(ExecEvalExprSwitchContext(node->limitOffset,
- econtext,
- &isNull,
- NULL));
- /* Interpret NULL offset as no offset */
- if (isNull)
- limitstate->offset = 0;
- else if (limitstate->offset < 0)
- limitstate->offset = 0;
- }
- else
- {
- /* No OFFSET supplied */
- limitstate->offset = 0;
- }
-
- if (node->limitCount)
- {
- limitstate->count =
- DatumGetInt32(ExecEvalExprSwitchContext(node->limitCount,
- econtext,
- &isNull,
- NULL));
- /* Interpret NULL count as no count (LIMIT ALL) */
- if (isNull)
- limitstate->noCount = true;
- else if (limitstate->count < 0)
- limitstate->count = 0;
- }
- else
- {
- /* No COUNT supplied */
- limitstate->count = 0;
- limitstate->noCount = true;
- }
-
- /* Reset position data to start-of-scan */
- limitstate->position = 0;
- limitstate->atEnd = false;
-
- /* Set flag that params are computed */
- limitstate->parmsSet = true;
-}
-
-/* ----------------------------------------------------------------
- * ExecInitLimit
- *
- * This initializes the limit node state structures and
- * the node's subplan.
- * ----------------------------------------------------------------
- */
-bool /* return: initialization status */
-ExecInitLimit(Limit *node, EState *estate, Plan *parent)
-{
- LimitState *limitstate;
- Plan *outerPlan;
-
- /*
- * assign execution state to node
- */
- node->plan.state = estate;
-
- /*
- * create new LimitState for node
- */
- limitstate = makeNode(LimitState);
- node->limitstate = limitstate;
- limitstate->parmsSet = false;
-
- /*
- * Miscellaneous initialization
- *
- * Limit nodes never call ExecQual or ExecProject, but they need an
- * exprcontext anyway to evaluate the limit/offset parameters in.
- */
- ExecAssignExprContext(estate, &limitstate->cstate);
-
-#define LIMIT_NSLOTS 1
-
- /*
- * Tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &limitstate->cstate);
-
- /*
- * then initialize outer plan
- */
- outerPlan = outerPlan((Plan *) node);
- ExecInitNode(outerPlan, estate, (Plan *) node);
-
- /*
- * limit nodes do no projections, so initialize projection info for
- * this node appropriately
- */
- ExecAssignResultTypeFromOuterPlan((Plan *) node, &limitstate->cstate);
- limitstate->cstate.cs_ProjInfo = NULL;
-
- return TRUE;
-}
-
-int
-ExecCountSlotsLimit(Limit *node)
-{
- return ExecCountSlotsNode(outerPlan(node)) +
- ExecCountSlotsNode(innerPlan(node)) +
- LIMIT_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndLimit
- *
- * This shuts down the subplan and frees resources allocated
- * to this node.
- * ----------------------------------------------------------------
- */
-void
-ExecEndLimit(Limit *node)
-{
- LimitState *limitstate = node->limitstate;
-
- ExecFreeExprContext(&limitstate->cstate);
-
- ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
-
- /* clean up tuple table */
- ExecClearTuple(limitstate->cstate.cs_ResultTupleSlot);
-}
-
-
-void
-ExecReScanLimit(Limit *node, ExprContext *exprCtxt, Plan *parent)
-{
- LimitState *limitstate = node->limitstate;
-
- ExecClearTuple(limitstate->cstate.cs_ResultTupleSlot);
-
- /* force recalculation of limit expressions on first call */
- limitstate->parmsSet = false;
-
- /*
- * if chgParam of subnode is not null then plan will be re-scanned by
- * first ExecProcNode.
- */
- if (((Plan *) node)->lefttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-}
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
deleted file mode 100644
index 30eb9a285b6..00000000000
--- a/src/backend/executor/nodeMaterial.c
+++ /dev/null
@@ -1,303 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeMaterial.c
- * Routines to handle materialization nodes.
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.38 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecMaterial - materialize the result of a subplan
- * ExecInitMaterial - initialize node and subnodes
- * ExecEndMaterial - shutdown node and subnodes
- *
- */
-#include "postgres.h"
-
-#include "executor/executor.h"
-#include "executor/nodeMaterial.h"
-#include "miscadmin.h"
-#include "utils/tuplestore.h"
-
-/* ----------------------------------------------------------------
- * ExecMaterial
- *
- * The first time this is called, ExecMaterial retrieves tuples
- * from this node's outer subplan and inserts them into a tuplestore
- * (a temporary tuple storage structure). The first tuple is then
- * returned. Successive calls to ExecMaterial return successive
- * tuples from the tuplestore.
- *
- * Initial State:
- *
- * matstate->tuplestorestate is initially NULL, indicating we
- * haven't yet collected the results of the subplan.
- *
- * ----------------------------------------------------------------
- */
-TupleTableSlot * /* result tuple from subplan */
-ExecMaterial(Material *node)
-{
- EState *estate;
- MaterialState *matstate;
- ScanDirection dir;
- Tuplestorestate *tuplestorestate;
- HeapTuple heapTuple;
- TupleTableSlot *slot;
- bool should_free;
-
- /*
- * get state info from node
- */
- matstate = node->matstate;
- estate = node->plan.state;
- dir = estate->es_direction;
- tuplestorestate = (Tuplestorestate *) matstate->tuplestorestate;
-
- /*
- * If first time through, read all tuples from outer plan and pass
- * them to tuplestore.c. Subsequent calls just fetch tuples from
- * tuplestore.
- */
-
- if (tuplestorestate == NULL)
- {
- Plan *outerNode;
-
- /*
- * Want to scan subplan in the forward direction while creating
- * the stored data. (Does setting my direction actually affect
- * the subplan? I bet this is useless code...)
- */
- estate->es_direction = ForwardScanDirection;
-
- /*
- * Initialize tuplestore module.
- */
- tuplestorestate = tuplestore_begin_heap(true, /* randomAccess */
- SortMem);
-
- matstate->tuplestorestate = (void *) tuplestorestate;
-
- /*
- * Scan the subplan and feed all the tuples to tuplestore.
- */
- outerNode = outerPlan((Plan *) node);
-
- for (;;)
- {
- slot = ExecProcNode(outerNode, (Plan *) node);
-
- if (TupIsNull(slot))
- break;
-
- tuplestore_puttuple(tuplestorestate, (void *) slot->val);
- ExecClearTuple(slot);
- }
-
- /*
- * Complete the store.
- */
- tuplestore_donestoring(tuplestorestate);
-
- /*
- * restore to user specified direction
- */
- estate->es_direction = dir;
- }
-
- /*
- * Get the first or next tuple from tuplestore. Returns NULL if no
- * more tuples.
- */
- slot = (TupleTableSlot *) matstate->csstate.cstate.cs_ResultTupleSlot;
- heapTuple = tuplestore_getheaptuple(tuplestorestate,
- ScanDirectionIsForward(dir),
- &should_free);
-
- return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
-}
-
-/* ----------------------------------------------------------------
- * ExecInitMaterial
- * ----------------------------------------------------------------
- */
-bool /* initialization status */
-ExecInitMaterial(Material *node, EState *estate, Plan *parent)
-{
- MaterialState *matstate;
- Plan *outerPlan;
-
- /*
- * assign the node's execution state
- */
- node->plan.state = estate;
-
- /*
- * create state structure
- */
- matstate = makeNode(MaterialState);
- matstate->tuplestorestate = NULL;
- node->matstate = matstate;
-
- /*
- * Miscellaneous initialization
- *
- * Materialization nodes don't need ExprContexts because they never call
- * ExecQual or ExecProject.
- */
-
-#define MATERIAL_NSLOTS 2
-
- /*
- * tuple table initialization
- *
- * material nodes only return tuples from their materialized relation.
- */
- ExecInitResultTupleSlot(estate, &matstate->csstate.cstate);
- ExecInitScanTupleSlot(estate, &matstate->csstate);
-
- /*
- * initializes child nodes
- */
- outerPlan = outerPlan((Plan *) node);
- ExecInitNode(outerPlan, estate, (Plan *) node);
-
- /*
- * initialize tuple type. no need to initialize projection info
- * because this node doesn't do projections.
- */
- ExecAssignResultTypeFromOuterPlan((Plan *) node, &matstate->csstate.cstate);
- ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate);
- matstate->csstate.cstate.cs_ProjInfo = NULL;
-
- return TRUE;
-}
-
-int
-ExecCountSlotsMaterial(Material *node)
-{
- return ExecCountSlotsNode(outerPlan((Plan *) node)) +
- ExecCountSlotsNode(innerPlan((Plan *) node)) +
- MATERIAL_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndMaterial
- * ----------------------------------------------------------------
- */
-void
-ExecEndMaterial(Material *node)
-{
- MaterialState *matstate;
- Plan *outerPlan;
-
- /*
- * get info from the material state
- */
- matstate = node->matstate;
-
- /*
- * shut down the subplan
- */
- outerPlan = outerPlan((Plan *) node);
- ExecEndNode(outerPlan, (Plan *) node);
-
- /*
- * clean out the tuple table
- */
- ExecClearTuple(matstate->csstate.css_ScanTupleSlot);
-
- /*
- * Release tuplestore resources
- */
- if (matstate->tuplestorestate != NULL)
- tuplestore_end((Tuplestorestate *) matstate->tuplestorestate);
- matstate->tuplestorestate = NULL;
-}
-
-/* ----------------------------------------------------------------
- * ExecMaterialMarkPos
- *
- * Calls tuplestore to save the current position in the stored file.
- * ----------------------------------------------------------------
- */
-void
-ExecMaterialMarkPos(Material *node)
-{
- MaterialState *matstate = node->matstate;
-
- /*
- * if we haven't materialized yet, just return.
- */
- if (!matstate->tuplestorestate)
- return;
-
- tuplestore_markpos((Tuplestorestate *) matstate->tuplestorestate);
-}
-
-/* ----------------------------------------------------------------
- * ExecMaterialRestrPos
- *
- * Calls tuplestore to restore the last saved file position.
- * ----------------------------------------------------------------
- */
-void
-ExecMaterialRestrPos(Material *node)
-{
- MaterialState *matstate = node->matstate;
-
- /*
- * if we haven't materialized yet, just return.
- */
- if (!matstate->tuplestorestate)
- return;
-
- /*
- * restore the scan to the previously marked position
- */
- tuplestore_restorepos((Tuplestorestate *) matstate->tuplestorestate);
-}
-
-/* ----------------------------------------------------------------
- * ExecMaterialReScan
- *
- * Rescans the materialized relation.
- * ----------------------------------------------------------------
- */
-void
-ExecMaterialReScan(Material *node, ExprContext *exprCtxt, Plan *parent)
-{
- MaterialState *matstate = node->matstate;
-
- /*
- * If we haven't materialized yet, just return. If outerplan' chgParam
- * is not NULL then it will be re-scanned by ExecProcNode, else - no
- * reason to re-scan it at all.
- */
- if (!matstate->tuplestorestate)
- return;
-
- ExecClearTuple(matstate->csstate.cstate.cs_ResultTupleSlot);
-
- /*
- * If subnode is to be rescanned then we forget previous stored
- * results; we have to re-read the subplan and re-store.
- *
- * Otherwise we can just rewind and rescan the stored output.
- */
- if (((Plan *) node)->lefttree->chgParam != NULL)
- {
- tuplestore_end((Tuplestorestate *) matstate->tuplestorestate);
- matstate->tuplestorestate = NULL;
- }
- else
- tuplestore_rescan((Tuplestorestate *) matstate->tuplestorestate);
-}
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
deleted file mode 100644
index 4467fef9e12..00000000000
--- a/src/backend/executor/nodeMergejoin.c
+++ /dev/null
@@ -1,1595 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeMergejoin.c
- * routines supporting merge joins
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.50 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecMergeJoin mergejoin outer and inner relations.
- * ExecInitMergeJoin creates and initializes run time states
- * ExecEndMergeJoin cleans up the node.
- *
- * NOTES
- * Essential operation of the merge join algorithm is as follows:
- *
- * Join { -
- * get initial outer and inner tuples INITIALIZE
- * Skip Inner SKIPINNER
- * mark inner position JOINMARK
- * do forever { -
- * while (outer == inner) { JOINTEST
- * join tuples JOINTUPLES
- * advance inner position NEXTINNER
- * } -
- * advance outer position NEXTOUTER
- * if (outer == mark) { TESTOUTER
- * restore inner position to mark TESTOUTER
- * continue -
- * } else { -
- * Skip Outer SKIPOUTER
- * mark inner position JOINMARK
- * } -
- * } -
- * } -
- *
- * Skip Outer { SKIPOUTER_BEGIN
- * if (inner == outer) Join Tuples JOINTUPLES
- * while (outer < inner) SKIPOUTER_TEST
- * advance outer SKIPOUTER_ADVANCE
- * if (outer > inner) SKIPOUTER_TEST
- * Skip Inner SKIPINNER
- * } -
- *
- * Skip Inner { SKIPINNER_BEGIN
- * if (inner == outer) Join Tuples JOINTUPLES
- * while (outer > inner) SKIPINNER_TEST
- * advance inner SKIPINNER_ADVANCE
- * if (outer < inner) SKIPINNER_TEST
- * Skip Outer SKIPOUTER
- * } -
- *
- * The merge join operation is coded in the fashion
- * of a state machine. At each state, we do something and then
- * proceed to another state. This state is stored in the node's
- * execution state information and is preserved across calls to
- * ExecMergeJoin. -cim 10/31/89
- *
- */
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "access/printtup.h"
-#include "catalog/pg_operator.h"
-#include "executor/execdebug.h"
-#include "executor/execdefs.h"
-#include "executor/nodeMergejoin.h"
-#include "utils/lsyscache.h"
-#include "utils/syscache.h"
-
-
-static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext);
-
-#define MarkInnerTuple(innerTupleSlot, mergestate) \
-( \
- ExecStoreTuple(heap_copytuple((innerTupleSlot)->val), \
- (mergestate)->mj_MarkedTupleSlot, \
- InvalidBuffer, \
- true) \
-)
-
-
-/* ----------------------------------------------------------------
- * MJFormSkipQuals
- *
- * This takes the mergeclause which is a qualification of the
- * form ((= expr expr) (= expr expr) ...) and forms new lists
- * of the forms ((< expr expr) (< expr expr) ...) and
- * ((> expr expr) (> expr expr) ...). These lists will be used
- * by ExecMergeJoin() to determine if we should skip tuples.
- * (We expect there to be suitable operators because the "=" operators
- * were marked mergejoinable; however, there might be a different
- * one needed in each qual clause.)
- * ----------------------------------------------------------------
- */
-static void
-MJFormSkipQuals(List *qualList, List **ltQuals, List **gtQuals)
-{
- List *ltcdr,
- *gtcdr;
-
- /*
- * Make modifiable copies of the qualList.
- */
- *ltQuals = (List *) copyObject((Node *) qualList);
- *gtQuals = (List *) copyObject((Node *) qualList);
-
- /*
- * Scan both lists in parallel, so that we can update the operators
- * with the minimum number of syscache searches.
- */
- ltcdr = *ltQuals;
- foreach(gtcdr, *gtQuals)
- {
- Expr *ltqual = (Expr *) lfirst(ltcdr);
- Expr *gtqual = (Expr *) lfirst(gtcdr);
- Oper *ltop = (Oper *) ltqual->oper;
- Oper *gtop = (Oper *) gtqual->oper;
-
- /*
- * The two ops should be identical, so use either one for lookup.
- */
- if (!IsA(ltop, Oper))
- elog(ERROR, "MJFormSkipQuals: op not an Oper!");
-
- /*
- * Lookup the operators, and replace the data in the copied
- * operator nodes.
- */
- op_mergejoin_crossops(ltop->opno,
- &ltop->opno,
- &gtop->opno,
- &ltop->opid,
- &gtop->opid);
- ltop->op_fcache = NULL;
- gtop->op_fcache = NULL;
-
- ltcdr = lnext(ltcdr);
- }
-}
-
-/* ----------------------------------------------------------------
- * MergeCompare
- *
- * Compare the keys according to 'compareQual' which is of the
- * form: { (key1a > key2a) (key1b > key2b) ... }.
- *
- * (actually, it could also be of the form (key1a < key2a)...)
- *
- * This is different from calling ExecQual because ExecQual returns
- * true only if ALL the comparison clauses are satisfied.
- * However, there is an order of significance among the keys with
- * the first keys being most significant. Therefore, the clauses
- * are evaluated in order and the 'compareQual' is satisfied
- * if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
- * We use the original mergeclause items to detect equality.
- * ----------------------------------------------------------------
- */
-static bool
-MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
-{
- bool result;
- MemoryContext oldContext;
- List *clause;
- List *eqclause;
-
- /*
- * Do expression eval in short-lived context.
- */
- oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
- /*
- * for each pair of clauses, test them until our compare conditions
- * are satisfied. if we reach the end of the list, none of our key
- * greater-than conditions were satisfied so we return false.
- */
- result = false; /* assume 'false' result */
-
- eqclause = eqQual;
- foreach(clause, compareQual)
- {
- Datum const_value;
- bool isNull;
-
- /*
- * first test if our compare clause is satisfied. if so then
- * return true.
- *
- * A NULL result is considered false.
- */
- const_value = ExecEvalExpr((Node *) lfirst(clause), econtext,
- &isNull, NULL);
-
- if (DatumGetBool(const_value) && !isNull)
- {
- result = true;
- break;
- }
-
- /*-----------
- * ok, the compare clause failed so we test if the keys are
- * equal... if key1 != key2, we return false. otherwise
- * key1 = key2 so we move on to the next pair of keys.
- *-----------
- */
- const_value = ExecEvalExpr((Node *) lfirst(eqclause),
- econtext,
- &isNull,
- NULL);
-
- if (!DatumGetBool(const_value) || isNull)
- break; /* return false */
-
- eqclause = lnext(eqclause);
- }
-
- MemoryContextSwitchTo(oldContext);
-
- return result;
-}
-
-/* ----------------------------------------------------------------
- * ExecMergeTupleDump
- *
- * This function is called through the MJ_dump() macro
- * when EXEC_MERGEJOINDEBUG is defined
- * ----------------------------------------------------------------
- */
-#ifdef EXEC_MERGEJOINDEBUG
-
-static void
-ExecMergeTupleDumpOuter(MergeJoinState *mergestate)
-{
- TupleTableSlot *outerSlot = mergestate->mj_OuterTupleSlot;
-
- printf("==== outer tuple ====\n");
- if (TupIsNull(outerSlot))
- printf("(nil)\n");
- else
- MJ_debugtup(outerSlot->val,
- outerSlot->ttc_tupleDescriptor);
-}
-
-static void
-ExecMergeTupleDumpInner(MergeJoinState *mergestate)
-{
- TupleTableSlot *innerSlot = mergestate->mj_InnerTupleSlot;
-
- printf("==== inner tuple ====\n");
- if (TupIsNull(innerSlot))
- printf("(nil)\n");
- else
- MJ_debugtup(innerSlot->val,
- innerSlot->ttc_tupleDescriptor);
-}
-
-static void
-ExecMergeTupleDumpMarked(MergeJoinState *mergestate)
-{
- TupleTableSlot *markedSlot = mergestate->mj_MarkedTupleSlot;
-
- printf("==== marked tuple ====\n");
- if (TupIsNull(markedSlot))
- printf("(nil)\n");
- else
- MJ_debugtup(markedSlot->val,
- markedSlot->ttc_tupleDescriptor);
-}
-
-static void
-ExecMergeTupleDump(MergeJoinState *mergestate)
-{
- printf("******** ExecMergeTupleDump ********\n");
-
- ExecMergeTupleDumpOuter(mergestate);
- ExecMergeTupleDumpInner(mergestate);
- ExecMergeTupleDumpMarked(mergestate);
-
- printf("******** \n");
-}
-#endif
-
-/* ----------------------------------------------------------------
- * ExecMergeJoin
- *
- * old comments
- * Details of the merge-join routines:
- *
- * (1) ">" and "<" operators
- *
- * Merge-join is done by joining the inner and outer tuples satisfying
- * the join clauses of the form ((= outerKey innerKey) ...).
- * The join clauses is provided by the query planner and may contain
- * more than one (= outerKey innerKey) clauses (for composite key).
- *
- * However, the query executor needs to know whether an outer
- * tuple is "greater/smaller" than an inner tuple so that it can
- * "synchronize" the two relations. For e.g., consider the following
- * relations:
- *
- * outer: (0 ^1 1 2 5 5 5 6 6 7) current tuple: 1
- * inner: (1 ^3 5 5 5 5 6) current tuple: 3
- *
- * To continue the merge-join, the executor needs to scan both inner
- * and outer relations till the matching tuples 5. It needs to know
- * that currently inner tuple 3 is "greater" than outer tuple 1 and
- * therefore it should scan the outer relation first to find a
- * matching tuple and so on.
- *
- * Therefore, when initializing the merge-join node, the executor
- * creates the "greater/smaller" clause by substituting the "="
- * operator in the join clauses with the corresponding ">" operator.
- * The opposite "smaller/greater" clause is formed by substituting "<".
- *
- * Note: prior to v6.5, the relational clauses were formed using the
- * sort op used to sort the inner relation, which of course would fail
- * if the outer and inner keys were of different data types.
- * In the current code, we instead assume that operators named "<" and ">"
- * will do the right thing. This should be true since the mergejoin "="
- * operator's pg_operator entry will have told the planner to sort by
- * "<" for each of the left and right sides.
- *
- * (2) repositioning inner "cursor"
- *
- * Consider the above relations and suppose that the executor has
- * just joined the first outer "5" with the last inner "5". The
- * next step is of course to join the second outer "5" with all
- * the inner "5's". This requires repositioning the inner "cursor"
- * to point at the first inner "5". This is done by "marking" the
- * first inner 5 and restore the "cursor" to it before joining
- * with the second outer 5. The access method interface provides
- * routines to mark and restore to a tuple.
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecMergeJoin(MergeJoin *node)
-{
- EState *estate;
- MergeJoinState *mergestate;
- ScanDirection direction;
- List *innerSkipQual;
- List *outerSkipQual;
- List *mergeclauses;
- List *joinqual;
- List *otherqual;
- bool qualResult;
- bool compareResult;
- Plan *innerPlan;
- TupleTableSlot *innerTupleSlot;
- Plan *outerPlan;
- TupleTableSlot *outerTupleSlot;
- ExprContext *econtext;
- bool doFillOuter;
- bool doFillInner;
-
- /*
- * get information from node
- */
- mergestate = node->mergestate;
- estate = node->join.plan.state;
- direction = estate->es_direction;
- innerPlan = innerPlan((Plan *) node);
- outerPlan = outerPlan((Plan *) node);
- econtext = mergestate->jstate.cs_ExprContext;
- mergeclauses = node->mergeclauses;
- joinqual = node->join.joinqual;
- otherqual = node->join.plan.qual;
-
- switch (node->join.jointype)
- {
- case JOIN_INNER:
- doFillOuter = false;
- doFillInner = false;
- break;
- case JOIN_LEFT:
- doFillOuter = true;
- doFillInner = false;
- break;
- case JOIN_FULL:
- doFillOuter = true;
- doFillInner = true;
- break;
- case JOIN_RIGHT:
- doFillOuter = false;
- doFillInner = true;
- break;
- default:
- elog(ERROR, "ExecMergeJoin: unsupported join type %d",
- (int) node->join.jointype);
- doFillOuter = false; /* keep compiler quiet */
- doFillInner = false;
- break;
- }
-
- if (ScanDirectionIsForward(direction))
- {
- outerSkipQual = mergestate->mj_OuterSkipQual;
- innerSkipQual = mergestate->mj_InnerSkipQual;
- }
- else
- {
- outerSkipQual = mergestate->mj_InnerSkipQual;
- innerSkipQual = mergestate->mj_OuterSkipQual;
- }
-
- /*
- * Check to see if we're still projecting out tuples from a previous
- * join tuple (because there is a function-returning-set in the
- * projection expressions). If so, try to project another one.
- */
- if (mergestate->jstate.cs_TupFromTlist)
- {
- TupleTableSlot *result;
- ExprDoneCond isDone;
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone);
- if (isDone == ExprMultipleResult)
- return result;
- /* Done with that source tuple... */
- mergestate->jstate.cs_TupFromTlist = false;
- }
-
- /*
- * Reset per-tuple memory context to free any expression evaluation
- * storage allocated in the previous tuple cycle. Note this can't
- * happen until we're done projecting out tuples from a join tuple.
- */
- ResetExprContext(econtext);
-
- /*
- * ok, everything is setup.. let's go to work
- */
- for (;;)
- {
- /*
- * get the current state of the join and do things accordingly.
- * Note: The join states are highlighted with 32-* comments for
- * improved readability.
- */
- MJ_dump(mergestate);
-
- switch (mergestate->mj_JoinState)
- {
- /*
- * EXEC_MJ_INITIALIZE means that this is the first time
- * ExecMergeJoin() has been called and so we have to fetch
- * the first tuple for both outer and inner subplans. If
- * we fail to get a tuple here, then that subplan is
- * empty, and we either end the join or go to one of the
- * fill-remaining-tuples states.
- */
- case EXEC_MJ_INITIALIZE:
- MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
-
- outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
- mergestate->mj_OuterTupleSlot = outerTupleSlot;
- if (TupIsNull(outerTupleSlot))
- {
- MJ_printf("ExecMergeJoin: outer subplan is empty\n");
- if (doFillInner)
- {
- /*
- * Need to emit right-join tuples for remaining
- * inner tuples. We set MatchedInner = true to
- * force the ENDOUTER state to advance inner.
- */
- mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
- mergestate->mj_MatchedInner = true;
- break;
- }
- /* Otherwise we're done. */
- return NULL;
- }
-
- innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
- mergestate->mj_InnerTupleSlot = innerTupleSlot;
- if (TupIsNull(innerTupleSlot))
- {
- MJ_printf("ExecMergeJoin: inner subplan is empty\n");
- if (doFillOuter)
- {
- /*
- * Need to emit left-join tuples for all outer
- * tuples, including the one we just fetched. We
- * set MatchedOuter = false to force the ENDINNER
- * state to emit this tuple before advancing
- * outer.
- */
- mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
- mergestate->mj_MatchedOuter = false;
- break;
- }
- /* Otherwise we're done. */
- return NULL;
- }
-
- /*
- * OK, we have the initial tuples. Begin by skipping
- * unmatched inner tuples.
- */
- mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
- break;
-
- /*
- * EXEC_MJ_JOINMARK means we have just found a new outer
- * tuple and a possible matching inner tuple. This is the
- * case after the INITIALIZE, SKIPOUTER or SKIPINNER
- * states.
- */
- case EXEC_MJ_JOINMARK:
- MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
-
- ExecMarkPos(innerPlan);
-
- MarkInnerTuple(mergestate->mj_InnerTupleSlot, mergestate);
-
- mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
- break;
-
- /*
- * EXEC_MJ_JOINTEST means we have two tuples which might
- * satisfy the merge clause, so we test them.
- *
- * If they do satisfy, then we join them and move on to the
- * next inner tuple (EXEC_MJ_JOINTUPLES).
- *
- * If they do not satisfy then advance to next outer tuple.
- */
- case EXEC_MJ_JOINTEST:
- MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- qualResult = ExecQual(mergeclauses, econtext, false);
- MJ_DEBUG_QUAL(mergeclauses, qualResult);
-
- if (qualResult)
- mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
- else
- mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
- break;
-
- /*
- * EXEC_MJ_JOINTUPLES means we have two tuples which
- * satisfied the merge clause so we join them and then
- * proceed to get the next inner tuple (EXEC_NEXT_INNER).
- */
- case EXEC_MJ_JOINTUPLES:
- MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
-
- mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
-
- /*
- * Check the extra qual conditions to see if we actually
- * want to return this join tuple. If not, can proceed
- * with merge. We must distinguish the additional
- * joinquals (which must pass to consider the tuples
- * "matched" for outer-join logic) from the otherquals
- * (which must pass before we actually return the tuple).
- *
- * 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. Also,
- * the econtext's tuple pointers were set up before
- * checking the merge qual, so we needn't do it again.
- */
- qualResult = (joinqual == NIL ||
- ExecQual(joinqual, econtext, false));
- MJ_DEBUG_QUAL(joinqual, qualResult);
-
- if (qualResult)
- {
- mergestate->mj_MatchedOuter = true;
- mergestate->mj_MatchedInner = true;
-
- qualResult = (otherqual == NIL ||
- ExecQual(otherqual, econtext, false));
- MJ_DEBUG_QUAL(otherqual, qualResult);
-
- if (qualResult)
- {
- /*
- * qualification succeeded. now form the desired
- * projection tuple and return the slot containing
- * it.
- */
- TupleTableSlot *result;
- ExprDoneCond isDone;
-
- MJ_printf("ExecMergeJoin: returning tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
- break;
-
- /*
- * EXEC_MJ_NEXTINNER means advance the inner scan to the
- * next tuple. If the tuple is not nil, we then proceed to
- * test it against the join qualification.
- *
- * Before advancing, we check to see if we must emit an
- * outer-join fill tuple for this inner tuple.
- */
- case EXEC_MJ_NEXTINNER:
- MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
-
- if (doFillInner && !mergestate->mj_MatchedInner)
- {
- /*
- * Generate a fake join tuple with nulls for the outer
- * tuple, and return it if it passes the non-join
- * quals.
- */
- mergestate->mj_MatchedInner = true; /* do it only once */
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- if (ExecQual(otherqual, econtext, false))
- {
- /*
- * qualification succeeded. now form the desired
- * projection tuple and return the slot containing
- * it.
- */
- TupleTableSlot *result;
- ExprDoneCond isDone;
-
- MJ_printf("ExecMergeJoin: returning fill tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * now we get the next inner tuple, if any
- */
- innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
- mergestate->mj_InnerTupleSlot = innerTupleSlot;
- MJ_DEBUG_PROC_NODE(innerTupleSlot);
- mergestate->mj_MatchedInner = false;
-
- if (TupIsNull(innerTupleSlot))
- mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
- else
- mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
- break;
-
- /*-------------------------------------------
- * EXEC_MJ_NEXTOUTER means
- *
- * outer inner
- * outer tuple - 5 5 - marked tuple
- * 5 5
- * 6 6 - inner tuple
- * 7 7
- *
- * we know we just bumped into the
- * first inner tuple > current outer tuple
- * so get a new outer tuple and then
- * proceed to test it against the marked tuple
- * (EXEC_MJ_TESTOUTER)
- *
- * Before advancing, we check to see if we must emit an
- * outer-join fill tuple for this outer tuple.
- *------------------------------------------------
- */
- case EXEC_MJ_NEXTOUTER:
- MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
-
- if (doFillOuter && !mergestate->mj_MatchedOuter)
- {
- /*
- * Generate a fake join tuple with nulls for the inner
- * tuple, and return it if it passes the non-join
- * quals.
- */
- mergestate->mj_MatchedOuter = true; /* do it only once */
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- if (ExecQual(otherqual, econtext, false))
- {
- /*
- * qualification succeeded. now form the desired
- * projection tuple and return the slot containing
- * it.
- */
- TupleTableSlot *result;
- ExprDoneCond isDone;
-
- MJ_printf("ExecMergeJoin: returning fill tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * now we get the next outer tuple, if any
- */
- outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
- mergestate->mj_OuterTupleSlot = outerTupleSlot;
- MJ_DEBUG_PROC_NODE(outerTupleSlot);
- mergestate->mj_MatchedOuter = false;
-
- /*
- * if the outer tuple is null then we are done with the
- * join, unless we have inner tuples we need to null-fill.
- */
- if (TupIsNull(outerTupleSlot))
- {
- MJ_printf("ExecMergeJoin: end of outer subplan\n");
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- if (doFillInner && !TupIsNull(innerTupleSlot))
- {
- /*
- * Need to emit right-join tuples for remaining
- * inner tuples.
- */
- mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
- break;
- }
- /* Otherwise we're done. */
- return NULL;
- }
-
- mergestate->mj_JoinState = EXEC_MJ_TESTOUTER;
- break;
-
- /*--------------------------------------------------------
- * EXEC_MJ_TESTOUTER If the new outer tuple and the marked
- * tuple satisfy the merge clause then we know we have
- * duplicates in the outer scan so we have to restore the
- * inner scan to the marked tuple and proceed to join the
- * new outer tuples with the inner tuples (EXEC_MJ_JOINTEST)
- *
- * This is the case when
- * outer inner
- * 4 5 - marked tuple
- * outer tuple - 5 5
- * new outer tuple - 5 5
- * 6 8 - inner tuple
- * 7 12
- *
- * new outer tuple = marked tuple
- *
- * If the outer tuple fails the test, then we know we have
- * to proceed to skip outer tuples until outer >= inner
- * (EXEC_MJ_SKIPOUTER).
- *
- * This is the case when
- *
- * outer inner
- * 5 5 - marked tuple
- * outer tuple - 5 5
- * new outer tuple - 6 8 - inner tuple
- * 7 12
- *
- *
- * new outer tuple > marked tuple
- *
- *---------------------------------------------------------
- */
- case EXEC_MJ_TESTOUTER:
- MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
-
- /*
- * here we compare the outer tuple with the marked inner
- * tuple
- */
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_MarkedTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- qualResult = ExecQual(mergeclauses, econtext, false);
- MJ_DEBUG_QUAL(mergeclauses, qualResult);
-
- if (qualResult)
- {
- /*
- * the merge clause matched so now we restore the
- * inner scan position to the first mark, and loop
- * back to JOINTEST. Actually, since we know the
- * mergeclause matches, we can skip JOINTEST and go
- * straight to JOINTUPLES.
- *
- * NOTE: we do not need to worry about the MatchedInner
- * state for the rescanned inner tuples. We know all
- * of them will match this new outer tuple and
- * therefore won't be emitted as fill tuples. This
- * works *only* because we require the extra joinquals
- * to be nil when doing a right or full join ---
- * otherwise some of the rescanned tuples might fail
- * the extra joinquals.
- */
- ExecRestrPos(innerPlan);
- mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
- }
- else
- {
- /* ----------------
- * if the inner tuple was nil and the new outer
- * tuple didn't match the marked outer tuple then
- * we have the case:
- *
- * outer inner
- * 4 4 - marked tuple
- * new outer - 5 4
- * 6 nil - inner tuple
- * 7
- *
- * which means that all subsequent outer tuples will be
- * larger than our marked inner tuples. So we're done.
- * ----------------
- */
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- if (TupIsNull(innerTupleSlot))
- {
- if (doFillOuter)
- {
- /*
- * Need to emit left-join tuples for remaining
- * outer tuples.
- */
- mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
- break;
- }
- /* Otherwise we're done. */
- return NULL;
- }
-
- /* continue on to skip outer tuples */
- mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
- }
- break;
-
- /*----------------------------------------------------------
- * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
- * until we find an outer tuple >= current inner tuple.
- *
- * For example:
- *
- * outer inner
- * 5 5
- * 5 5
- * outer tuple - 6 8 - inner tuple
- * 7 12
- * 8 14
- *
- * we have to advance the outer scan
- * until we find the outer 8.
- *
- * To avoid redundant tests, we divide this into three
- * sub-states: BEGIN, TEST, ADVANCE.
- *----------------------------------------------------------
- */
- case EXEC_MJ_SKIPOUTER_BEGIN:
- MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_BEGIN\n");
-
- /*
- * before we advance, make sure the current tuples do not
- * satisfy the mergeclauses. If they do, then we update
- * the marked tuple and go join them.
- */
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- qualResult = ExecQual(mergeclauses, econtext, false);
- MJ_DEBUG_QUAL(mergeclauses, qualResult);
-
- if (qualResult)
- {
- ExecMarkPos(innerPlan);
-
- MarkInnerTuple(innerTupleSlot, mergestate);
-
- mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
- break;
- }
-
- mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
- break;
-
- case EXEC_MJ_SKIPOUTER_TEST:
- MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_TEST\n");
-
- /*
- * ok, now test the skip qualification
- */
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- compareResult = MergeCompare(mergeclauses,
- outerSkipQual,
- econtext);
-
- MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
-
- /*
- * compareResult is true as long as we should continue
- * skipping outer tuples.
- */
- if (compareResult)
- {
- mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
- break;
- }
-
- /*
- * now check the inner skip qual to see if we should now
- * skip inner tuples... if we fail the inner skip qual,
- * then we know we have a new pair of matching tuples.
- */
- compareResult = MergeCompare(mergeclauses,
- innerSkipQual,
- econtext);
-
- MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
-
- if (compareResult)
- mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
- else
- mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
- break;
-
- /*
- * Before advancing, we check to see if we must emit an
- * outer-join fill tuple for this outer tuple.
- */
- case EXEC_MJ_SKIPOUTER_ADVANCE:
- MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_ADVANCE\n");
-
- if (doFillOuter && !mergestate->mj_MatchedOuter)
- {
- /*
- * Generate a fake join tuple with nulls for the inner
- * tuple, and return it if it passes the non-join
- * quals.
- */
- mergestate->mj_MatchedOuter = true; /* do it only once */
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- if (ExecQual(otherqual, econtext, false))
- {
- /*
- * qualification succeeded. now form the desired
- * projection tuple and return the slot containing
- * it.
- */
- TupleTableSlot *result;
- ExprDoneCond isDone;
-
- MJ_printf("ExecMergeJoin: returning fill tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * now we get the next outer tuple, if any
- */
- outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
- mergestate->mj_OuterTupleSlot = outerTupleSlot;
- MJ_DEBUG_PROC_NODE(outerTupleSlot);
- mergestate->mj_MatchedOuter = false;
-
- /*
- * if the outer tuple is null then we are done with the
- * join, unless we have inner tuples we need to null-fill.
- */
- if (TupIsNull(outerTupleSlot))
- {
- MJ_printf("ExecMergeJoin: end of outer subplan\n");
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- if (doFillInner && !TupIsNull(innerTupleSlot))
- {
- /*
- * Need to emit right-join tuples for remaining
- * inner tuples.
- */
- mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
- break;
- }
- /* Otherwise we're done. */
- return NULL;
- }
-
- /*
- * otherwise test the new tuple against the skip qual.
- */
- mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
- break;
-
- /*-----------------------------------------------------------
- * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
- * until we find an inner tuple >= current outer tuple.
- *
- * For example:
- *
- * outer inner
- * 5 5
- * 5 5
- * outer tuple - 12 8 - inner tuple
- * 14 10
- * 17 12
- *
- * we have to advance the inner scan
- * until we find the inner 12.
- *
- * To avoid redundant tests, we divide this into three
- * sub-states: BEGIN, TEST, ADVANCE.
- *-------------------------------------------------------
- */
- case EXEC_MJ_SKIPINNER_BEGIN:
- MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_BEGIN\n");
-
- /*
- * before we advance, make sure the current tuples do not
- * satisfy the mergeclauses. If they do, then we update
- * the marked tuple and go join them.
- */
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- qualResult = ExecQual(mergeclauses, econtext, false);
- MJ_DEBUG_QUAL(mergeclauses, qualResult);
-
- if (qualResult)
- {
- ExecMarkPos(innerPlan);
-
- MarkInnerTuple(innerTupleSlot, mergestate);
-
- mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
- break;
- }
-
- mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
- break;
-
- case EXEC_MJ_SKIPINNER_TEST:
- MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_TEST\n");
-
- /*
- * ok, now test the skip qualification
- */
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- compareResult = MergeCompare(mergeclauses,
- innerSkipQual,
- econtext);
-
- MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
-
- /*
- * compareResult is true as long as we should continue
- * skipping inner tuples.
- */
- if (compareResult)
- {
- mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
- break;
- }
-
- /*
- * now check the outer skip qual to see if we should now
- * skip outer tuples... if we fail the outer skip qual,
- * then we know we have a new pair of matching tuples.
- */
- compareResult = MergeCompare(mergeclauses,
- outerSkipQual,
- econtext);
-
- MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
-
- if (compareResult)
- mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
- else
- mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
- break;
-
- /*
- * Before advancing, we check to see if we must emit an
- * outer-join fill tuple for this inner tuple.
- */
- case EXEC_MJ_SKIPINNER_ADVANCE:
- MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_ADVANCE\n");
-
- if (doFillInner && !mergestate->mj_MatchedInner)
- {
- /*
- * Generate a fake join tuple with nulls for the outer
- * tuple, and return it if it passes the non-join
- * quals.
- */
- mergestate->mj_MatchedInner = true; /* do it only once */
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- if (ExecQual(otherqual, econtext, false))
- {
- /*
- * qualification succeeded. now form the desired
- * projection tuple and return the slot containing
- * it.
- */
- TupleTableSlot *result;
- ExprDoneCond isDone;
-
- MJ_printf("ExecMergeJoin: returning fill tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * now we get the next inner tuple, if any
- */
- innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
- mergestate->mj_InnerTupleSlot = innerTupleSlot;
- MJ_DEBUG_PROC_NODE(innerTupleSlot);
- mergestate->mj_MatchedInner = false;
-
- /*
- * if the inner tuple is null then we are done with the
- * join, unless we have outer tuples we need to null-fill.
- */
- if (TupIsNull(innerTupleSlot))
- {
- MJ_printf("ExecMergeJoin: end of inner subplan\n");
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- if (doFillOuter && !TupIsNull(outerTupleSlot))
- {
- /*
- * Need to emit left-join tuples for remaining
- * outer tuples.
- */
- mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
- break;
- }
- /* Otherwise we're done. */
- return NULL;
- }
-
- /*
- * otherwise test the new tuple against the skip qual.
- */
- mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
- break;
-
- /*
- * EXEC_MJ_ENDOUTER means we have run out of outer tuples,
- * but are doing a right/full join and therefore must
- * null- fill any remaing unmatched inner tuples.
- */
- case EXEC_MJ_ENDOUTER:
- MJ_printf("ExecMergeJoin: EXEC_MJ_ENDOUTER\n");
-
- Assert(doFillInner);
-
- if (!mergestate->mj_MatchedInner)
- {
- /*
- * Generate a fake join tuple with nulls for the outer
- * tuple, and return it if it passes the non-join
- * quals.
- */
- mergestate->mj_MatchedInner = true; /* do it only once */
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_InnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- if (ExecQual(otherqual, econtext, false))
- {
- /*
- * qualification succeeded. now form the desired
- * projection tuple and return the slot containing
- * it.
- */
- TupleTableSlot *result;
- ExprDoneCond isDone;
-
- MJ_printf("ExecMergeJoin: returning fill tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * now we get the next inner tuple, if any
- */
- innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
- mergestate->mj_InnerTupleSlot = innerTupleSlot;
- MJ_DEBUG_PROC_NODE(innerTupleSlot);
- mergestate->mj_MatchedInner = false;
-
- if (TupIsNull(innerTupleSlot))
- {
- MJ_printf("ExecMergeJoin: end of inner subplan\n");
- return NULL;
- }
-
- /* Else remain in ENDOUTER state and process next tuple. */
- break;
-
- /*
- * EXEC_MJ_ENDINNER means we have run out of inner tuples,
- * but are doing a left/full join and therefore must null-
- * fill any remaing unmatched outer tuples.
- */
- case EXEC_MJ_ENDINNER:
- MJ_printf("ExecMergeJoin: EXEC_MJ_ENDINNER\n");
-
- Assert(doFillOuter);
-
- if (!mergestate->mj_MatchedOuter)
- {
- /*
- * Generate a fake join tuple with nulls for the inner
- * tuple, and return it if it passes the non-join
- * quals.
- */
- mergestate->mj_MatchedOuter = true; /* do it only once */
-
- ResetExprContext(econtext);
-
- outerTupleSlot = mergestate->mj_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
- econtext->ecxt_innertuple = innerTupleSlot;
-
- if (ExecQual(otherqual, econtext, false))
- {
- /*
- * qualification succeeded. now form the desired
- * projection tuple and return the slot containing
- * it.
- */
- TupleTableSlot *result;
- ExprDoneCond isDone;
-
- MJ_printf("ExecMergeJoin: returning fill tuple\n");
-
- result = ExecProject(mergestate->jstate.cs_ProjInfo,
- &isDone);
-
- if (isDone != ExprEndResult)
- {
- mergestate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * now we get the next outer tuple, if any
- */
- outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
- mergestate->mj_OuterTupleSlot = outerTupleSlot;
- MJ_DEBUG_PROC_NODE(outerTupleSlot);
- mergestate->mj_MatchedOuter = false;
-
- if (TupIsNull(outerTupleSlot))
- {
- MJ_printf("ExecMergeJoin: end of outer subplan\n");
- return NULL;
- }
-
- /* Else remain in ENDINNER state and process next tuple. */
- break;
-
- /*
- * if we get here it means our code is fouled up and so we
- * just end the join prematurely.
- */
- default:
- elog(WARNING, "ExecMergeJoin: invalid join state %d, aborting",
- mergestate->mj_JoinState);
- return NULL;
- }
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecInitMergeJoin
- *
- * old comments
- * Creates the run-time state information for the node and
- * sets the relation id to contain relevant decriptors.
- * ----------------------------------------------------------------
- */
-bool
-ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
-{
- MergeJoinState *mergestate;
-
- MJ1_printf("ExecInitMergeJoin: %s\n",
- "initializing node");
-
- /*
- * assign the node's execution state and get the range table and
- * direction from it
- */
- node->join.plan.state = estate;
-
- /*
- * create new merge state for node
- */
- mergestate = makeNode(MergeJoinState);
- node->mergestate = mergestate;
-
- /*
- * Miscellaneous initialization
- *
- * create expression context for node
- */
- ExecAssignExprContext(estate, &mergestate->jstate);
-
- /*
- * initialize subplans
- */
- ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
- ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
-
-#define MERGEJOIN_NSLOTS 4
-
- /*
- * tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &mergestate->jstate);
-
- mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
- ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
- ExecGetTupType(innerPlan((Plan *) node)),
- false);
-
- switch (node->join.jointype)
- {
- case JOIN_INNER:
- break;
- case JOIN_LEFT:
- mergestate->mj_NullInnerTupleSlot =
- ExecInitNullTupleSlot(estate,
- ExecGetTupType(innerPlan((Plan *) node)));
- break;
- case JOIN_RIGHT:
- mergestate->mj_NullOuterTupleSlot =
- ExecInitNullTupleSlot(estate,
- ExecGetTupType(outerPlan((Plan *) node)));
-
- /*
- * Can't handle right or full join with non-nil extra
- * joinclauses.
- */
- if (node->join.joinqual != NIL)
- elog(ERROR, "RIGHT JOIN is only supported with mergejoinable join conditions");
- break;
- case JOIN_FULL:
- mergestate->mj_NullOuterTupleSlot =
- ExecInitNullTupleSlot(estate,
- ExecGetTupType(outerPlan((Plan *) node)));
- mergestate->mj_NullInnerTupleSlot =
- ExecInitNullTupleSlot(estate,
- ExecGetTupType(innerPlan((Plan *) node)));
-
- /*
- * Can't handle right or full join with non-nil extra
- * joinclauses.
- */
- if (node->join.joinqual != NIL)
- elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions");
- break;
- default:
- elog(ERROR, "ExecInitMergeJoin: unsupported join type %d",
- (int) node->join.jointype);
- }
-
- /*
- * initialize tuple type and projection info
- */
- ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate);
- ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate);
-
- /*
- * form merge skip qualifications
- */
- MJFormSkipQuals(node->mergeclauses,
- &mergestate->mj_OuterSkipQual,
- &mergestate->mj_InnerSkipQual);
-
- MJ_printf("\nExecInitMergeJoin: OuterSkipQual is ");
- MJ_nodeDisplay(mergestate->mj_OuterSkipQual);
- MJ_printf("\nExecInitMergeJoin: InnerSkipQual is ");
- MJ_nodeDisplay(mergestate->mj_InnerSkipQual);
- MJ_printf("\n");
-
- /*
- * initialize join state
- */
- mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
- mergestate->jstate.cs_TupFromTlist = false;
- mergestate->mj_MatchedOuter = false;
- mergestate->mj_MatchedInner = false;
- mergestate->mj_OuterTupleSlot = NULL;
- mergestate->mj_InnerTupleSlot = NULL;
-
- /*
- * initialization successful
- */
- MJ1_printf("ExecInitMergeJoin: %s\n",
- "node initialized");
-
- return TRUE;
-}
-
-int
-ExecCountSlotsMergeJoin(MergeJoin *node)
-{
- return ExecCountSlotsNode(outerPlan((Plan *) node)) +
- ExecCountSlotsNode(innerPlan((Plan *) node)) +
- MERGEJOIN_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndMergeJoin
- *
- * old comments
- * frees storage allocated through C routines.
- * ----------------------------------------------------------------
- */
-void
-ExecEndMergeJoin(MergeJoin *node)
-{
- MergeJoinState *mergestate;
-
- MJ1_printf("ExecEndMergeJoin: %s\n",
- "ending node processing");
-
- /*
- * get state information from the node
- */
- mergestate = node->mergestate;
-
- /*
- * Free the projection info and the scan attribute info
- *
- * Note: we don't ExecFreeResultType(mergestate) because the rule manager
- * depends on the tupType returned by ExecMain(). So for now, this is
- * freed at end-transaction time. -cim 6/2/91
- */
- ExecFreeProjectionInfo(&mergestate->jstate);
- ExecFreeExprContext(&mergestate->jstate);
-
- /*
- * shut down the subplans
- */
- ExecEndNode((Plan *) innerPlan((Plan *) node), (Plan *) node);
- ExecEndNode((Plan *) outerPlan((Plan *) node), (Plan *) node);
-
- /*
- * clean out the tuple table
- */
- ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot);
- ExecClearTuple(mergestate->mj_MarkedTupleSlot);
-
- MJ1_printf("ExecEndMergeJoin: %s\n",
- "node processing ended");
-}
-
-void
-ExecReScanMergeJoin(MergeJoin *node, ExprContext *exprCtxt, Plan *parent)
-{
- MergeJoinState *mergestate = node->mergestate;
-
- ExecClearTuple(mergestate->mj_MarkedTupleSlot);
-
- mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
- mergestate->jstate.cs_TupFromTlist = false;
- mergestate->mj_MatchedOuter = false;
- mergestate->mj_MatchedInner = false;
- mergestate->mj_OuterTupleSlot = NULL;
- mergestate->mj_InnerTupleSlot = NULL;
-
- /*
- * if chgParam of subnodes is not null then plans will be re-scanned
- * by first ExecProcNode.
- */
- if (((Plan *) node)->lefttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
- if (((Plan *) node)->righttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->righttree, exprCtxt, (Plan *) node);
-
-}
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
deleted file mode 100644
index 2bd26938fcc..00000000000
--- a/src/backend/executor/nodeNestloop.c
+++ /dev/null
@@ -1,418 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeNestloop.c
- * routines to support nest-loop joins
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.26 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecNestLoop - process a nestloop join of two plans
- * ExecInitNestLoop - initialize the join
- * ExecEndNestLoop - shut down the join
- */
-
-#include "postgres.h"
-
-#include "executor/execdebug.h"
-#include "executor/nodeNestloop.h"
-#include "utils/memutils.h"
-
-
-/* ----------------------------------------------------------------
- * ExecNestLoop(node)
- *
- * old comments
- * Returns the tuple joined from inner and outer tuples which
- * satisfies the qualification clause.
- *
- * It scans the inner relation to join with current outer tuple.
- *
- * If none is found, next tuple from the outer relation is retrieved
- * and the inner relation is scanned from the beginning again to join
- * with the outer tuple.
- *
- * NULL is returned if all the remaining outer tuples are tried and
- * all fail to join with the inner tuples.
- *
- * NULL is also returned if there is no tuple from inner relation.
- *
- * Conditions:
- * -- outerTuple contains current tuple from outer relation and
- * the right son(inner relation) maintains "cursor" at the tuple
- * returned previously.
- * This is achieved by maintaining a scan position on the outer
- * relation.
- *
- * Initial States:
- * -- the outer child and the inner child
- * are prepared to return the first tuple.
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecNestLoop(NestLoop *node)
-{
- NestLoopState *nlstate;
- Plan *innerPlan;
- Plan *outerPlan;
- TupleTableSlot *outerTupleSlot;
- TupleTableSlot *innerTupleSlot;
- List *joinqual;
- List *otherqual;
- ExprContext *econtext;
-
- /*
- * get information from the node
- */
- ENL1_printf("getting info from node");
-
- nlstate = node->nlstate;
- joinqual = node->join.joinqual;
- otherqual = node->join.plan.qual;
- outerPlan = outerPlan((Plan *) node);
- innerPlan = innerPlan((Plan *) node);
- econtext = nlstate->jstate.cs_ExprContext;
-
- /*
- * get the current outer tuple
- */
- outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
-
- /*
- * Check to see if we're still projecting out tuples from a previous
- * join tuple (because there is a function-returning-set in the
- * projection expressions). If so, try to project another one.
- */
- if (nlstate->jstate.cs_TupFromTlist)
- {
- TupleTableSlot *result;
- ExprDoneCond isDone;
-
- result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
- if (isDone == ExprMultipleResult)
- return result;
- /* Done with that source tuple... */
- nlstate->jstate.cs_TupFromTlist = false;
- }
-
- /*
- * Reset per-tuple memory context to free any expression evaluation
- * storage allocated in the previous tuple cycle. Note this can't
- * happen until we're done projecting out tuples from a join tuple.
- */
- ResetExprContext(econtext);
-
- /*
- * Ok, everything is setup for the join so now loop until we return a
- * qualifying join tuple.
- */
- ENL1_printf("entering main loop");
-
- for (;;)
- {
- /*
- * If we don't have an outer tuple, get the next one and reset the
- * inner scan.
- */
- if (nlstate->nl_NeedNewOuter)
- {
- ENL1_printf("getting new outer tuple");
- outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
-
- /*
- * if there are no more outer tuples, then the join is
- * complete..
- */
- if (TupIsNull(outerTupleSlot))
- {
- ENL1_printf("no outer tuple, ending join");
- return NULL;
- }
-
- ENL1_printf("saving new outer tuple information");
- nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
- econtext->ecxt_outertuple = outerTupleSlot;
- nlstate->nl_NeedNewOuter = false;
- nlstate->nl_MatchedOuter = false;
-
- /*
- * now rescan the inner plan
- */
- ENL1_printf("rescanning inner plan");
-
- /*
- * The scan key of the inner plan might depend on the current
- * outer tuple (e.g. in index scans), that's why we pass our
- * expr context.
- */
- ExecReScan(innerPlan, econtext, (Plan *) node);
- }
-
- /*
- * we have an outerTuple, try to get the next inner tuple.
- */
- ENL1_printf("getting new inner tuple");
-
- innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
- econtext->ecxt_innertuple = innerTupleSlot;
-
- if (TupIsNull(innerTupleSlot))
- {
- ENL1_printf("no inner tuple, need new outer tuple");
-
- nlstate->nl_NeedNewOuter = true;
-
- if (!nlstate->nl_MatchedOuter &&
- node->join.jointype == JOIN_LEFT)
- {
- /*
- * We are doing an outer join and there were no join
- * matches for this outer tuple. Generate a fake join
- * tuple with nulls for the inner tuple, and return it if
- * it passes the non-join quals.
- */
- econtext->ecxt_innertuple = nlstate->nl_NullInnerTupleSlot;
-
- ENL1_printf("testing qualification for outer-join tuple");
-
- if (ExecQual(otherqual, econtext, false))
- {
- /*
- * qualification was satisfied so we project and
- * return the slot containing the result tuple using
- * ExecProject().
- */
- TupleTableSlot *result;
- ExprDoneCond isDone;
-
- ENL1_printf("qualification succeeded, projecting tuple");
-
- result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
-
- if (isDone != ExprEndResult)
- {
- nlstate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * Otherwise just return to top of loop for a new outer tuple.
- */
- continue;
- }
-
- /*
- * at this point we have a new pair of inner and outer tuples so
- * we test the inner and outer tuples to see if they satisfy the
- * node's qualification.
- *
- * Only the joinquals determine MatchedOuter status, but all quals
- * must pass to actually return the tuple.
- */
- ENL1_printf("testing qualification");
-
- if (ExecQual(joinqual, econtext, false))
- {
- nlstate->nl_MatchedOuter = true;
-
- if (otherqual == NIL || ExecQual(otherqual, econtext, false))
- {
- /*
- * qualification was satisfied so we project and return
- * the slot containing the result tuple using
- * ExecProject().
- */
- TupleTableSlot *result;
- ExprDoneCond isDone;
-
- ENL1_printf("qualification succeeded, projecting tuple");
-
- result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
-
- if (isDone != ExprEndResult)
- {
- nlstate->jstate.cs_TupFromTlist =
- (isDone == ExprMultipleResult);
- return result;
- }
- }
- }
-
- /*
- * Tuple fails qual, so free per-tuple memory and try again.
- */
- ResetExprContext(econtext);
-
- ENL1_printf("qualification failed, looping");
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecInitNestLoop
- *
- * Creates the run-time state information for the nestloop node
- * produced by the planner and initailizes inner and outer relations
- * (child nodes).
- * ----------------------------------------------------------------
- */
-bool
-ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
-{
- NestLoopState *nlstate;
-
- NL1_printf("ExecInitNestLoop: %s\n",
- "initializing node");
-
- /*
- * assign execution state to node
- */
- node->join.plan.state = estate;
-
- /*
- * create new nest loop state
- */
- nlstate = makeNode(NestLoopState);
- node->nlstate = nlstate;
-
- /*
- * Miscellaneous initialization
- *
- * create expression context for node
- */
- ExecAssignExprContext(estate, &nlstate->jstate);
-
- /*
- * now initialize children
- */
- ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
- ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
-
-#define NESTLOOP_NSLOTS 2
-
- /*
- * tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &nlstate->jstate);
-
- switch (node->join.jointype)
- {
- case JOIN_INNER:
- break;
- case JOIN_LEFT:
- nlstate->nl_NullInnerTupleSlot =
- ExecInitNullTupleSlot(estate,
- ExecGetTupType(innerPlan((Plan *) node)));
- break;
- default:
- elog(ERROR, "ExecInitNestLoop: unsupported join type %d",
- (int) node->join.jointype);
- }
-
- /*
- * initialize tuple type and projection info
- */
- ExecAssignResultTypeFromTL((Plan *) node, &nlstate->jstate);
- ExecAssignProjectionInfo((Plan *) node, &nlstate->jstate);
-
- /*
- * finally, wipe the current outer tuple clean.
- */
- nlstate->jstate.cs_OuterTupleSlot = NULL;
- nlstate->jstate.cs_TupFromTlist = false;
- nlstate->nl_NeedNewOuter = true;
- nlstate->nl_MatchedOuter = false;
-
- NL1_printf("ExecInitNestLoop: %s\n",
- "node initialized");
- return TRUE;
-}
-
-int
-ExecCountSlotsNestLoop(NestLoop *node)
-{
- return ExecCountSlotsNode(outerPlan(node)) +
- ExecCountSlotsNode(innerPlan(node)) +
- NESTLOOP_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndNestLoop
- *
- * closes down scans and frees allocated storage
- * ----------------------------------------------------------------
- */
-void
-ExecEndNestLoop(NestLoop *node)
-{
- NestLoopState *nlstate;
-
- NL1_printf("ExecEndNestLoop: %s\n",
- "ending node processing");
-
- /*
- * get info from the node
- */
- nlstate = node->nlstate;
-
- /*
- * Free the projection info
- *
- * Note: we don't ExecFreeResultType(nlstate) because the rule manager
- * depends on the tupType returned by ExecMain(). So for now, this is
- * freed at end-transaction time. -cim 6/2/91
- */
- ExecFreeProjectionInfo(&nlstate->jstate);
- ExecFreeExprContext(&nlstate->jstate);
-
- /*
- * close down subplans
- */
- ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
- ExecEndNode(innerPlan((Plan *) node), (Plan *) node);
-
- /*
- * clean out the tuple table
- */
- ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot);
-
- NL1_printf("ExecEndNestLoop: %s\n",
- "node processing ended");
-}
-
-/* ----------------------------------------------------------------
- * ExecReScanNestLoop
- * ----------------------------------------------------------------
- */
-void
-ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent)
-{
- NestLoopState *nlstate = node->nlstate;
- Plan *outerPlan = outerPlan((Plan *) node);
-
- /*
- * If outerPlan->chgParam is not null then plan will be automatically
- * re-scanned by first ExecProcNode. innerPlan is re-scanned for each
- * new outer tuple and MUST NOT be re-scanned from here or you'll get
- * troubles from inner index scans when outer Vars are used as
- * run-time keys...
- */
- if (outerPlan->chgParam == NULL)
- ExecReScan(outerPlan, exprCtxt, (Plan *) node);
-
- /* let outerPlan to free its result tuple ... */
- nlstate->jstate.cs_OuterTupleSlot = NULL;
- nlstate->jstate.cs_TupFromTlist = false;
- nlstate->nl_NeedNewOuter = true;
- nlstate->nl_MatchedOuter = false;
-}
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
deleted file mode 100644
index d9d1608a81d..00000000000
--- a/src/backend/executor/nodeResult.c
+++ /dev/null
@@ -1,289 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeResult.c
- * support for constant nodes needing special code.
- *
- * DESCRIPTION
- *
- * Result nodes are used in queries where no relations are scanned.
- * Examples of such queries are:
- *
- * retrieve (x = 1)
- * and
- * append emp (name = "mike", salary = 15000)
- *
- * Result nodes are also used to optimise queries with constant
- * qualifications (ie, quals that do not depend on the scanned data),
- * such as:
- *
- * retrieve (emp.all) where 2 > 1
- *
- * In this case, the plan generated is
- *
- * Result (with 2 > 1 qual)
- * /
- * 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-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.21 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "executor/executor.h"
-#include "executor/nodeResult.h"
-#include "utils/memutils.h"
-
-
-/* ----------------------------------------------------------------
- * ExecResult(node)
- *
- * returns the tuples from the outer plan which satisfy the
- * qualification clause. Since result nodes with right
- * subtrees are never planned, we ignore the right subtree
- * entirely (for now).. -cim 10/7/89
- *
- * The qualification containing only constant clauses are
- * checked first before any processing is done. It always returns
- * 'nil' if the constant qualification is not satisfied.
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecResult(Result *node)
-{
- ResultState *resstate;
- TupleTableSlot *outerTupleSlot;
- TupleTableSlot *resultSlot;
- Plan *outerPlan;
- ExprContext *econtext;
- ExprDoneCond isDone;
-
- /*
- * initialize the result node's state
- */
- resstate = node->resstate;
- econtext = resstate->cstate.cs_ExprContext;
-
- /*
- * check constant qualifications like (2 > 1), if not already done
- */
- if (resstate->rs_checkqual)
- {
- bool qualResult = ExecQual((List *) node->resconstantqual,
- econtext,
- false);
-
- resstate->rs_checkqual = false;
- if (qualResult == false)
- {
- resstate->rs_done = true;
- return NULL;
- }
- }
-
- /*
- * 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)
- {
- resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone);
- if (isDone == ExprMultipleResult)
- return resultSlot;
- /* Done with that source tuple... */
- resstate->cstate.cs_TupFromTlist = false;
- }
-
- /*
- * Reset per-tuple memory context to free any expression evaluation
- * storage allocated in the previous tuple cycle. Note this can't
- * happen until we're done projecting out tuples from a scan tuple.
- */
- ResetExprContext(econtext);
-
- /*
- * if rs_done is true then it means that we were asked to return a
- * constant tuple and we already did the last time ExecResult() was
- * called, OR that we failed the constant qual check. Either way, now
- * we are through.
- */
- while (!resstate->rs_done)
- {
- outerPlan = outerPlan(node);
-
- if (outerPlan != NULL)
- {
- /*
- * retrieve tuples from the outer plan until there are no
- * more.
- */
- outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
-
- if (TupIsNull(outerTupleSlot))
- return NULL;
-
- resstate->cstate.cs_OuterTupleSlot = outerTupleSlot;
-
- /*
- * XXX gross hack. use outer tuple as scan tuple for
- * projection
- */
- econtext->ecxt_outertuple = outerTupleSlot;
- econtext->ecxt_scantuple = outerTupleSlot;
- }
- else
- {
- /*
- * if we don't have an outer plan, then we are just generating
- * the results from a constant target list. Do it only once.
- */
- resstate->rs_done = true;
- }
-
- /*
- * form the result tuple using ExecProject(), and return it ---
- * unless the projection produces an empty set, in which case we
- * must loop back to see if there are more outerPlan tuples.
- */
- resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone);
-
- if (isDone != ExprEndResult)
- {
- resstate->cstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
- return resultSlot;
- }
- }
-
- return NULL;
-}
-
-/* ----------------------------------------------------------------
- * ExecInitResult
- *
- * Creates the run-time state information for the result node
- * produced by the planner and initailizes outer relations
- * (child nodes).
- * ----------------------------------------------------------------
- */
-bool
-ExecInitResult(Result *node, EState *estate, Plan *parent)
-{
- ResultState *resstate;
-
- /*
- * assign execution state to node
- */
- node->plan.state = estate;
-
- /*
- * create new ResultState for node
- */
- resstate = makeNode(ResultState);
- resstate->rs_done = false;
- resstate->rs_checkqual = (node->resconstantqual == NULL) ? false : true;
- node->resstate = resstate;
-
- /*
- * Miscellaneous initialization
- *
- * create expression context for node
- */
- ExecAssignExprContext(estate, &resstate->cstate);
-
-#define RESULT_NSLOTS 1
-
- /*
- * tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &resstate->cstate);
-
- /*
- * then initialize children
- */
- ExecInitNode(outerPlan(node), estate, (Plan *) node);
-
- /*
- * we don't use inner plan
- */
- Assert(innerPlan(node) == NULL);
-
- /*
- * initialize tuple type and projection info
- */
- ExecAssignResultTypeFromTL((Plan *) node, &resstate->cstate);
- ExecAssignProjectionInfo((Plan *) node, &resstate->cstate);
-
- return TRUE;
-}
-
-int
-ExecCountSlotsResult(Result *node)
-{
- return ExecCountSlotsNode(outerPlan(node)) + RESULT_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndResult
- *
- * frees up storage allocated through C routines
- * ----------------------------------------------------------------
- */
-void
-ExecEndResult(Result *node)
-{
- ResultState *resstate;
-
- resstate = node->resstate;
-
- /*
- * Free the projection info
- *
- * Note: we don't ExecFreeResultType(resstate) because the rule manager
- * depends on the tupType returned by ExecMain(). So for now, this is
- * freed at end-transaction time. -cim 6/2/91
- */
- ExecFreeProjectionInfo(&resstate->cstate);
- ExecFreeExprContext(&resstate->cstate);
-
- /*
- * shut down subplans
- */
- ExecEndNode(outerPlan(node), (Plan *) node);
-
- /*
- * clean out the tuple table
- */
- ExecClearTuple(resstate->cstate.cs_ResultTupleSlot);
- pfree(resstate);
- node->resstate = NULL; /* XXX - new for us - er1p */
-}
-
-void
-ExecReScanResult(Result *node, ExprContext *exprCtxt, Plan *parent)
-{
- ResultState *resstate = node->resstate;
-
- resstate->rs_done = false;
- resstate->cstate.cs_TupFromTlist = false;
- resstate->rs_checkqual = (node->resconstantqual == NULL) ? false : true;
-
- /*
- * if chgParam of subnode is not null then plan will be re-scanned by
- * first ExecProcNode.
- */
- if (((Plan *) node)->lefttree &&
- ((Plan *) node)->lefttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-}
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
deleted file mode 100644
index ddfcd3b8dad..00000000000
--- a/src/backend/executor/nodeSeqscan.c
+++ /dev/null
@@ -1,366 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeSeqscan.c
- * Support routines for sequential scans of relations.
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.36 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecSeqScan sequentially scans a relation.
- * ExecSeqNext retrieve next tuple in sequential order.
- * ExecInitSeqScan creates and initializes a seqscan node.
- * ExecEndSeqScan releases any storage allocated.
- * ExecSeqReScan rescans the relation
- * ExecMarkPos marks scan position
- * ExecRestrPos restores scan position
- *
- */
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "executor/execdebug.h"
-#include "executor/nodeSeqscan.h"
-#include "parser/parsetree.h"
-
-static Oid InitScanRelation(SeqScan *node, EState *estate,
- CommonScanState *scanstate);
-static TupleTableSlot *SeqNext(SeqScan *node);
-
-/* ----------------------------------------------------------------
- * Scan Support
- * ----------------------------------------------------------------
- */
-/* ----------------------------------------------------------------
- * SeqNext
- *
- * This is a workhorse for ExecSeqScan
- * ----------------------------------------------------------------
- */
-static TupleTableSlot *
-SeqNext(SeqScan *node)
-{
- HeapTuple tuple;
- HeapScanDesc scandesc;
- CommonScanState *scanstate;
- EState *estate;
- ScanDirection direction;
- TupleTableSlot *slot;
-
- /*
- * get information from the estate and scan state
- */
- estate = node->plan.state;
- scanstate = node->scanstate;
- scandesc = scanstate->css_currentScanDesc;
- direction = estate->es_direction;
- slot = scanstate->css_ScanTupleSlot;
-
- /*
- * Check if we are evaluating PlanQual for tuple of this relation.
- * Additional checking is not good, but no other way for now. We could
- * introduce new nodes for this case and handle SeqScan --> NewNode
- * switching in Init/ReScan plan...
- */
- if (estate->es_evTuple != NULL &&
- estate->es_evTuple[node->scanrelid - 1] != NULL)
- {
- ExecClearTuple(slot);
- if (estate->es_evTupleNull[node->scanrelid - 1])
- return slot; /* return empty slot */
-
- ExecStoreTuple(estate->es_evTuple[node->scanrelid - 1],
- slot, InvalidBuffer, false);
-
- /*
- * Note that unlike IndexScan, SeqScan never use keys in
- * heap_beginscan (and this is very bad) - so, here we do not
- * check are keys ok or not.
- */
-
- /* Flag for the next call that no more tuples */
- estate->es_evTupleNull[node->scanrelid - 1] = true;
- return (slot);
- }
-
- /*
- * get the next tuple from the access methods
- */
- tuple = heap_getnext(scandesc, direction);
-
- /*
- * save the tuple and the buffer returned to us by the access methods
- * in our scan tuple slot and return the slot. Note: we pass 'false'
- * because tuples returned by heap_getnext() are pointers onto disk
- * pages and were not created with palloc() and so should not be
- * pfree()'d. Note also that ExecStoreTuple will increment the
- * refcount of the buffer; the refcount will not be dropped until the
- * tuple table slot is cleared.
- */
-
- slot = ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- scandesc->rs_cbuf, /* buffer associated with
- * this tuple */
- false); /* don't pfree this pointer */
-
- return slot;
-}
-
-/* ----------------------------------------------------------------
- * ExecSeqScan(node)
- *
- * Scans the relation sequentially and returns the next qualifying
- * tuple.
- * It calls the ExecScan() routine and passes it the access method
- * which retrieve tuples sequentially.
- *
- */
-
-TupleTableSlot *
-ExecSeqScan(SeqScan *node)
-{
- /*
- * use SeqNext as access method
- */
- return ExecScan(node, (ExecScanAccessMtd) SeqNext);
-}
-
-/* ----------------------------------------------------------------
- * InitScanRelation
- *
- * This does the initialization for scan relations and
- * subplans of scans.
- * ----------------------------------------------------------------
- */
-static Oid
-InitScanRelation(SeqScan *node, EState *estate,
- CommonScanState *scanstate)
-{
- Index relid;
- List *rangeTable;
- RangeTblEntry *rtentry;
- Oid reloid;
- Relation currentRelation;
- HeapScanDesc currentScanDesc;
-
- /*
- * get the relation object id from the relid'th entry in the range
- * table, open that relation and initialize the scan state.
- *
- * We acquire AccessShareLock for the duration of the scan.
- */
- relid = node->scanrelid;
- rangeTable = estate->es_range_table;
- rtentry = rt_fetch(relid, rangeTable);
- reloid = rtentry->relid;
-
- currentRelation = heap_open(reloid, AccessShareLock);
-
- currentScanDesc = heap_beginscan(currentRelation,
- estate->es_snapshot,
- 0,
- NULL);
-
- scanstate->css_currentRelation = currentRelation;
- scanstate->css_currentScanDesc = currentScanDesc;
-
- ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false);
-
- return reloid;
-}
-
-
-/* ----------------------------------------------------------------
- * ExecInitSeqScan
- * ----------------------------------------------------------------
- */
-bool
-ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
-{
- CommonScanState *scanstate;
- Oid reloid;
-
- /*
- * Once upon a time it was possible to have an outerPlan of a SeqScan,
- * but not any more.
- */
- Assert(outerPlan((Plan *) node) == NULL);
- Assert(innerPlan((Plan *) node) == NULL);
-
- /*
- * assign the node's execution state
- */
- node->plan.state = estate;
-
- /*
- * create new CommonScanState for node
- */
- scanstate = makeNode(CommonScanState);
- node->scanstate = scanstate;
-
- /*
- * Miscellaneous initialization
- *
- * create expression context for node
- */
- ExecAssignExprContext(estate, &scanstate->cstate);
-
-#define SEQSCAN_NSLOTS 2
-
- /*
- * tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &scanstate->cstate);
- ExecInitScanTupleSlot(estate, scanstate);
-
- /*
- * initialize scan relation
- */
- reloid = InitScanRelation(node, estate, scanstate);
-
- scanstate->cstate.cs_TupFromTlist = false;
-
- /*
- * initialize tuple type
- */
- ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
- ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
-
- return TRUE;
-}
-
-int
-ExecCountSlotsSeqScan(SeqScan *node)
-{
- return ExecCountSlotsNode(outerPlan(node)) +
- ExecCountSlotsNode(innerPlan(node)) +
- SEQSCAN_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndSeqScan
- *
- * frees any storage allocated through C routines.
- * ----------------------------------------------------------------
- */
-void
-ExecEndSeqScan(SeqScan *node)
-{
- CommonScanState *scanstate;
- Relation relation;
- HeapScanDesc scanDesc;
-
- /*
- * get information from node
- */
- scanstate = node->scanstate;
- relation = scanstate->css_currentRelation;
- scanDesc = scanstate->css_currentScanDesc;
-
- /*
- * Free the projection info and the scan attribute info
- *
- * Note: we don't ExecFreeResultType(scanstate) because the rule manager
- * depends on the tupType returned by ExecMain(). So for now, this is
- * freed at end-transaction time. -cim 6/2/91
- */
- ExecFreeProjectionInfo(&scanstate->cstate);
- ExecFreeExprContext(&scanstate->cstate);
-
- /*
- * close heap scan
- */
- heap_endscan(scanDesc);
-
- /*
- * close the heap relation.
- *
- * Currently, we do not release the AccessShareLock acquired by
- * InitScanRelation. This lock should be held till end of transaction.
- * (There is a faction that considers this too much locking, however.)
- */
- heap_close(relation, NoLock);
-
- /*
- * clean out the tuple table
- */
- ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
- ExecClearTuple(scanstate->css_ScanTupleSlot);
-}
-
-/* ----------------------------------------------------------------
- * Join Support
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- * ExecSeqReScan
- *
- * Rescans the relation.
- * ----------------------------------------------------------------
- */
-void
-ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent)
-{
- CommonScanState *scanstate;
- EState *estate;
- HeapScanDesc scan;
-
- scanstate = node->scanstate;
- estate = node->plan.state;
-
- /* 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;
- }
-
- scan = scanstate->css_currentScanDesc;
-
- heap_rescan(scan, /* scan desc */
- NULL); /* new scan keys */
-}
-
-/* ----------------------------------------------------------------
- * ExecSeqMarkPos(node)
- *
- * Marks scan position.
- * ----------------------------------------------------------------
- */
-void
-ExecSeqMarkPos(SeqScan *node)
-{
- CommonScanState *scanstate;
- HeapScanDesc scan;
-
- scanstate = node->scanstate;
- scan = scanstate->css_currentScanDesc;
- heap_markpos(scan);
-}
-
-/* ----------------------------------------------------------------
- * ExecSeqRestrPos
- *
- * Restores scan position.
- * ----------------------------------------------------------------
- */
-void
-ExecSeqRestrPos(SeqScan *node)
-{
- CommonScanState *scanstate;
- HeapScanDesc scan;
-
- scanstate = node->scanstate;
- scan = scanstate->css_currentScanDesc;
- heap_restrpos(scan);
-}
diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c
deleted file mode 100644
index 3d1cf2c8efa..00000000000
--- a/src/backend/executor/nodeSetOp.c
+++ /dev/null
@@ -1,334 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeSetOp.c
- * Routines to handle INTERSECT and EXCEPT selection
- *
- * The input of a SetOp node consists of tuples from two relations,
- * which have been combined into one dataset and sorted on all the nonjunk
- * attributes. In addition there is a junk attribute that shows which
- * relation each tuple came from. The SetOp node scans each group of
- * identical tuples to determine how many came from each input relation.
- * Then it is a simple matter to emit the output demanded by the SQL spec
- * for INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL.
- *
- * This node type is not used for UNION or UNION ALL, since those can be
- * implemented more cheaply (there's no need for the junk attribute to
- * identify the source relation).
- *
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSetOp.c,v 1.6 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecSetOp - filter input to generate INTERSECT/EXCEPT results
- * ExecInitSetOp - initialize node and subnodes..
- * ExecEndSetOp - shutdown node and subnodes
- */
-
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "executor/executor.h"
-#include "executor/nodeGroup.h"
-#include "executor/nodeSetOp.h"
-
-/* ----------------------------------------------------------------
- * ExecSetOp
- * ----------------------------------------------------------------
- */
-TupleTableSlot * /* return: a tuple or NULL */
-ExecSetOp(SetOp *node)
-{
- SetOpState *setopstate;
- TupleTableSlot *resultTupleSlot;
- Plan *outerPlan;
- TupleDesc tupDesc;
-
- /*
- * get information from the node
- */
- setopstate = node->setopstate;
- outerPlan = outerPlan((Plan *) node);
- resultTupleSlot = setopstate->cstate.cs_ResultTupleSlot;
- tupDesc = ExecGetResultType(&setopstate->cstate);
-
- /*
- * If the previously-returned tuple needs to be returned more than
- * once, keep returning it.
- */
- if (setopstate->numOutput > 0)
- {
- setopstate->numOutput--;
- return resultTupleSlot;
- }
-
- /* Flag that we have no current tuple */
- ExecClearTuple(resultTupleSlot);
-
- /*
- * Absorb groups of duplicate tuples, counting them, and saving the
- * first of each group as a possible return value. At the end of each
- * group, decide whether to return anything.
- *
- * We assume that the tuples arrive in sorted order so we can detect
- * duplicates easily.
- */
- for (;;)
- {
- TupleTableSlot *inputTupleSlot;
- bool endOfGroup;
-
- /*
- * fetch a tuple from the outer subplan, unless we already did.
- */
- if (setopstate->cstate.cs_OuterTupleSlot == NULL &&
- !setopstate->subplan_done)
- {
- setopstate->cstate.cs_OuterTupleSlot =
- ExecProcNode(outerPlan, (Plan *) node);
- if (TupIsNull(setopstate->cstate.cs_OuterTupleSlot))
- setopstate->subplan_done = true;
- }
- inputTupleSlot = setopstate->cstate.cs_OuterTupleSlot;
-
- if (TupIsNull(resultTupleSlot))
- {
- /*
- * First of group: save a copy in result slot, and reset
- * duplicate-counters for new group.
- */
- if (setopstate->subplan_done)
- return NULL; /* no more tuples */
- ExecStoreTuple(heap_copytuple(inputTupleSlot->val),
- resultTupleSlot,
- InvalidBuffer,
- true); /* free copied tuple at
- * ExecClearTuple */
- setopstate->numLeft = 0;
- setopstate->numRight = 0;
- endOfGroup = false;
- }
- else if (setopstate->subplan_done)
- {
- /*
- * Reached end of input, so finish processing final group
- */
- endOfGroup = true;
- }
- else
- {
- /*
- * Else test if the new tuple and the previously saved tuple
- * match.
- */
- if (execTuplesMatch(inputTupleSlot->val,
- resultTupleSlot->val,
- tupDesc,
- node->numCols, node->dupColIdx,
- setopstate->eqfunctions,
- setopstate->tempContext))
- endOfGroup = false;
- else
- endOfGroup = true;
- }
-
- if (endOfGroup)
- {
- /*
- * We've reached the end of the group containing resultTuple.
- * Decide how many copies (if any) to emit. This logic is
- * straight from the SQL92 specification.
- */
- switch (node->cmd)
- {
- case SETOPCMD_INTERSECT:
- if (setopstate->numLeft > 0 && setopstate->numRight > 0)
- setopstate->numOutput = 1;
- else
- setopstate->numOutput = 0;
- break;
- case SETOPCMD_INTERSECT_ALL:
- setopstate->numOutput =
- (setopstate->numLeft < setopstate->numRight) ?
- setopstate->numLeft : setopstate->numRight;
- break;
- case SETOPCMD_EXCEPT:
- if (setopstate->numLeft > 0 && setopstate->numRight == 0)
- setopstate->numOutput = 1;
- else
- setopstate->numOutput = 0;
- break;
- case SETOPCMD_EXCEPT_ALL:
- setopstate->numOutput =
- (setopstate->numLeft < setopstate->numRight) ?
- 0 : (setopstate->numLeft - setopstate->numRight);
- break;
- default:
- elog(ERROR, "ExecSetOp: bogus command code %d",
- (int) node->cmd);
- break;
- }
- /* Fall out of for-loop if we have tuples to emit */
- if (setopstate->numOutput > 0)
- break;
- /* Else flag that we have no current tuple, and loop around */
- ExecClearTuple(resultTupleSlot);
- }
- else
- {
- /*
- * Current tuple is member of same group as resultTuple. Count
- * it in the appropriate counter.
- */
- int flag;
- bool isNull;
-
- flag = DatumGetInt32(heap_getattr(inputTupleSlot->val,
- node->flagColIdx,
- tupDesc,
- &isNull));
- Assert(!isNull);
- if (flag)
- setopstate->numRight++;
- else
- setopstate->numLeft++;
- /* Set flag to fetch a new input tuple, and loop around */
- setopstate->cstate.cs_OuterTupleSlot = NULL;
- }
- }
-
- /*
- * If we fall out of loop, then we need to emit at least one copy of
- * resultTuple.
- */
- Assert(setopstate->numOutput > 0);
- setopstate->numOutput--;
- return resultTupleSlot;
-}
-
-/* ----------------------------------------------------------------
- * ExecInitSetOp
- *
- * This initializes the setop node state structures and
- * the node's subplan.
- * ----------------------------------------------------------------
- */
-bool /* return: initialization status */
-ExecInitSetOp(SetOp *node, EState *estate, Plan *parent)
-{
- SetOpState *setopstate;
- Plan *outerPlan;
-
- /*
- * assign execution state to node
- */
- node->plan.state = estate;
-
- /*
- * create new SetOpState for node
- */
- setopstate = makeNode(SetOpState);
- node->setopstate = setopstate;
- setopstate->cstate.cs_OuterTupleSlot = NULL;
- setopstate->subplan_done = false;
- setopstate->numOutput = 0;
-
- /*
- * Miscellaneous initialization
- *
- * SetOp nodes have no ExprContext initialization because they never call
- * ExecQual or ExecProject. But they do need a per-tuple memory
- * context anyway for calling execTuplesMatch.
- */
- setopstate->tempContext =
- AllocSetContextCreate(CurrentMemoryContext,
- "SetOp",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
-
-#define SETOP_NSLOTS 1
-
- /*
- * Tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &setopstate->cstate);
-
- /*
- * then initialize outer plan
- */
- outerPlan = outerPlan((Plan *) node);
- ExecInitNode(outerPlan, estate, (Plan *) node);
-
- /*
- * setop nodes do no projections, so initialize projection info for
- * this node appropriately
- */
- ExecAssignResultTypeFromOuterPlan((Plan *) node, &setopstate->cstate);
- setopstate->cstate.cs_ProjInfo = NULL;
-
- /*
- * Precompute fmgr lookup data for inner loop
- */
- setopstate->eqfunctions =
- execTuplesMatchPrepare(ExecGetResultType(&setopstate->cstate),
- node->numCols,
- node->dupColIdx);
-
- return TRUE;
-}
-
-int
-ExecCountSlotsSetOp(SetOp *node)
-{
- return ExecCountSlotsNode(outerPlan(node)) +
- ExecCountSlotsNode(innerPlan(node)) +
- SETOP_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndSetOp
- *
- * This shuts down the subplan and frees resources allocated
- * to this node.
- * ----------------------------------------------------------------
- */
-void
-ExecEndSetOp(SetOp *node)
-{
- SetOpState *setopstate = node->setopstate;
-
- ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
-
- MemoryContextDelete(setopstate->tempContext);
-
- /* clean up tuple table */
- ExecClearTuple(setopstate->cstate.cs_ResultTupleSlot);
- setopstate->cstate.cs_OuterTupleSlot = NULL;
-}
-
-
-void
-ExecReScanSetOp(SetOp *node, ExprContext *exprCtxt, Plan *parent)
-{
- SetOpState *setopstate = node->setopstate;
-
- ExecClearTuple(setopstate->cstate.cs_ResultTupleSlot);
- setopstate->cstate.cs_OuterTupleSlot = NULL;
- setopstate->subplan_done = false;
- setopstate->numOutput = 0;
-
- /*
- * if chgParam of subnode is not null then plan will be re-scanned by
- * first ExecProcNode.
- */
- if (((Plan *) node)->lefttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-}
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
deleted file mode 100644
index 6ce648e2b24..00000000000
--- a/src/backend/executor/nodeSort.c
+++ /dev/null
@@ -1,397 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeSort.c
- * Routines to handle sorting of relations.
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.36 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres.h"
-
-#include "executor/execdebug.h"
-#include "executor/nodeSort.h"
-#include "utils/tuplesort.h"
-
-/* ----------------------------------------------------------------
- * ExtractSortKeys
- *
- * Extract the sorting key information from the plan node.
- *
- * Returns two palloc'd arrays, one of sort operator OIDs and
- * one of attribute numbers.
- * ----------------------------------------------------------------
- */
-static void
-ExtractSortKeys(Sort *sortnode,
- Oid **sortOperators,
- AttrNumber **attNums)
-{
- List *targetList;
- int keycount;
- Oid *sortOps;
- AttrNumber *attNos;
- List *tl;
-
- /*
- * get information from the node
- */
- targetList = sortnode->plan.targetlist;
- keycount = sortnode->keycount;
-
- /*
- * first allocate space for results
- */
- if (keycount <= 0)
- elog(ERROR, "ExtractSortKeys: keycount <= 0");
- sortOps = (Oid *) palloc(keycount * sizeof(Oid));
- MemSet(sortOps, 0, keycount * sizeof(Oid));
- *sortOperators = sortOps;
- attNos = (AttrNumber *) palloc(keycount * sizeof(AttrNumber));
- MemSet(attNos, 0, keycount * sizeof(AttrNumber));
- *attNums = attNos;
-
- /*
- * extract info from the resdom nodes in the target list
- */
- foreach(tl, targetList)
- {
- TargetEntry *target = (TargetEntry *) lfirst(tl);
- Resdom *resdom = target->resdom;
- Index reskey = resdom->reskey;
-
- if (reskey > 0) /* ignore TLEs that are not sort keys */
- {
- Assert(reskey <= keycount);
- sortOps[reskey - 1] = resdom->reskeyop;
- attNos[reskey - 1] = resdom->resno;
- }
- }
-}
-
-/* ----------------------------------------------------------------
- * ExecSort
- *
- * Sorts tuples from the outer subtree of the node using tuplesort,
- * which saves the results in a temporary file or memory. After the
- * initial call, returns a tuple from the file with each call.
- *
- * Conditions:
- * -- none.
- *
- * Initial States:
- * -- the outer child is prepared to return the first tuple.
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecSort(Sort *node)
-{
- EState *estate;
- SortState *sortstate;
- ScanDirection dir;
- Tuplesortstate *tuplesortstate;
- HeapTuple heapTuple;
- TupleTableSlot *slot;
- bool should_free;
-
- /*
- * get state info from node
- */
- SO1_printf("ExecSort: %s\n",
- "entering routine");
-
- sortstate = node->sortstate;
- estate = node->plan.state;
- dir = estate->es_direction;
- tuplesortstate = (Tuplesortstate *) sortstate->tuplesortstate;
-
- /*
- * If first time through, read all tuples from outer plan and pass
- * them to tuplesort.c. Subsequent calls just fetch tuples from
- * tuplesort.
- */
-
- if (!sortstate->sort_Done)
- {
- Plan *outerNode;
- TupleDesc tupDesc;
- Oid *sortOperators;
- AttrNumber *attNums;
-
- SO1_printf("ExecSort: %s\n",
- "sorting subplan");
-
- /*
- * Want to scan subplan in the forward direction while creating
- * the sorted data. (Does setting my direction actually affect
- * the subplan? I bet this is useless code...)
- */
- estate->es_direction = ForwardScanDirection;
-
- /*
- * Initialize tuplesort module.
- */
- SO1_printf("ExecSort: %s\n",
- "calling tuplesort_begin");
-
- outerNode = outerPlan((Plan *) node);
- tupDesc = ExecGetTupType(outerNode);
-
- ExtractSortKeys(node, &sortOperators, &attNums);
-
- tuplesortstate = tuplesort_begin_heap(tupDesc, node->keycount,
- sortOperators, attNums,
- true /* randomAccess */ );
- sortstate->tuplesortstate = (void *) tuplesortstate;
-
- pfree(sortOperators);
- pfree(attNums);
-
- /*
- * Scan the subplan and feed all the tuples to tuplesort.
- */
-
- for (;;)
- {
- slot = ExecProcNode(outerNode, (Plan *) node);
-
- if (TupIsNull(slot))
- break;
-
- tuplesort_puttuple(tuplesortstate, (void *) slot->val);
- }
-
- /*
- * Complete the sort.
- */
- tuplesort_performsort(tuplesortstate);
-
- /*
- * restore to user specified direction
- */
- estate->es_direction = dir;
-
- /*
- * make sure the tuple descriptor is up to date (is this needed?)
- */
- ExecAssignResultType(&sortstate->csstate.cstate, tupDesc, false);
-
- /*
- * finally set the sorted flag to true
- */
- sortstate->sort_Done = true;
- SO1_printf(stderr, "ExecSort: sorting done.\n");
- }
-
- SO1_printf("ExecSort: %s\n",
- "retrieving tuple from tuplesort");
-
- /*
- * Get the first or next tuple from tuplesort. Returns NULL if no more
- * tuples.
- */
- heapTuple = tuplesort_getheaptuple(tuplesortstate,
- ScanDirectionIsForward(dir),
- &should_free);
-
- slot = sortstate->csstate.cstate.cs_ResultTupleSlot;
- return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
-}
-
-/* ----------------------------------------------------------------
- * ExecInitSort
- *
- * Creates the run-time state information for the sort node
- * produced by the planner and initailizes its outer subtree.
- * ----------------------------------------------------------------
- */
-bool
-ExecInitSort(Sort *node, EState *estate, Plan *parent)
-{
- SortState *sortstate;
- Plan *outerPlan;
-
- SO1_printf("ExecInitSort: %s\n",
- "initializing sort node");
-
- /*
- * assign the node's execution state
- */
- node->plan.state = estate;
-
- /*
- * create state structure
- */
- sortstate = makeNode(SortState);
- sortstate->sort_Done = false;
- sortstate->tuplesortstate = NULL;
-
- node->sortstate = sortstate;
-
- /*
- * Miscellaneous initialization
- *
- * Sort nodes don't initialize their ExprContexts because they never call
- * ExecQual or ExecProject.
- */
-
-#define SORT_NSLOTS 2
-
- /*
- * tuple table initialization
- *
- * sort nodes only return scan tuples from their sorted relation.
- */
- ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate);
- ExecInitScanTupleSlot(estate, &sortstate->csstate);
-
- /*
- * initializes child nodes
- */
- outerPlan = outerPlan((Plan *) node);
- ExecInitNode(outerPlan, estate, (Plan *) node);
-
- /*
- * initialize tuple type. no need to initialize projection info
- * because this node doesn't do projections.
- */
- ExecAssignResultTypeFromOuterPlan((Plan *) node, &sortstate->csstate.cstate);
- ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate);
- sortstate->csstate.cstate.cs_ProjInfo = NULL;
-
- SO1_printf("ExecInitSort: %s\n",
- "sort node initialized");
-
- return TRUE;
-}
-
-int
-ExecCountSlotsSort(Sort *node)
-{
- return ExecCountSlotsNode(outerPlan((Plan *) node)) +
- ExecCountSlotsNode(innerPlan((Plan *) node)) +
- SORT_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndSort(node)
- * ----------------------------------------------------------------
- */
-void
-ExecEndSort(Sort *node)
-{
- SortState *sortstate;
- Plan *outerPlan;
-
- /*
- * get info from the sort state
- */
- SO1_printf("ExecEndSort: %s\n",
- "shutting down sort node");
-
- sortstate = node->sortstate;
-
- /*
- * shut down the subplan
- */
- outerPlan = outerPlan((Plan *) node);
- ExecEndNode(outerPlan, (Plan *) node);
-
- /*
- * clean out the tuple table
- */
- ExecClearTuple(sortstate->csstate.css_ScanTupleSlot);
-
- /*
- * Release tuplesort resources
- */
- if (sortstate->tuplesortstate != NULL)
- tuplesort_end((Tuplesortstate *) sortstate->tuplesortstate);
- sortstate->tuplesortstate = NULL;
-
- pfree(sortstate);
- node->sortstate = NULL;
-
- SO1_printf("ExecEndSort: %s\n",
- "sort node shutdown");
-}
-
-/* ----------------------------------------------------------------
- * ExecSortMarkPos
- *
- * Calls tuplesort to save the current position in the sorted file.
- * ----------------------------------------------------------------
- */
-void
-ExecSortMarkPos(Sort *node)
-{
- SortState *sortstate = node->sortstate;
-
- /*
- * if we haven't sorted yet, just return
- */
- if (!sortstate->sort_Done)
- return;
-
- tuplesort_markpos((Tuplesortstate *) sortstate->tuplesortstate);
-}
-
-/* ----------------------------------------------------------------
- * ExecSortRestrPos
- *
- * Calls tuplesort to restore the last saved sort file position.
- * ----------------------------------------------------------------
- */
-void
-ExecSortRestrPos(Sort *node)
-{
- SortState *sortstate = node->sortstate;
-
- /*
- * if we haven't sorted yet, just return.
- */
- if (!sortstate->sort_Done)
- return;
-
- /*
- * restore the scan to the previously marked position
- */
- tuplesort_restorepos((Tuplesortstate *) sortstate->tuplesortstate);
-}
-
-void
-ExecReScanSort(Sort *node, ExprContext *exprCtxt, Plan *parent)
-{
- SortState *sortstate = node->sortstate;
-
- /*
- * If we haven't sorted yet, just return. If outerplan' chgParam is
- * not NULL then it will be re-scanned by ExecProcNode, else - no
- * reason to re-scan it at all.
- */
- if (!sortstate->sort_Done)
- return;
-
- ExecClearTuple(sortstate->csstate.cstate.cs_ResultTupleSlot);
-
- /*
- * If subnode is to be rescanned then we forget previous sort results;
- * we have to re-read the subplan and re-sort.
- *
- * Otherwise we can just rewind and rescan the sorted output.
- */
- if (((Plan *) node)->lefttree->chgParam != NULL)
- {
- sortstate->sort_Done = false;
- tuplesort_end((Tuplesortstate *) sortstate->tuplesortstate);
- sortstate->tuplesortstate = NULL;
- }
- else
- tuplesort_rescan((Tuplesortstate *) sortstate->tuplesortstate);
-}
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
deleted file mode 100644
index 0c7b3557cff..00000000000
--- a/src/backend/executor/nodeSubplan.c
+++ /dev/null
@@ -1,491 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeSubplan.c
- * routines to support subselects
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.33 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecSubPlan - process a subselect
- * ExecInitSubPlan - initialize a subselect
- * ExecEndSubPlan - shut down a subselect
- */
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "executor/executor.h"
-#include "executor/nodeSubplan.h"
-#include "tcop/pquery.h"
-
-
-/* ----------------------------------------------------------------
- * ExecSubPlan(node)
- *
- * ----------------------------------------------------------------
- */
-Datum
-ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
-{
- Plan *plan = node->plan;
- SubLink *sublink = node->sublink;
- SubLinkType subLinkType = sublink->subLinkType;
- bool useor = sublink->useor;
- MemoryContext oldcontext;
- TupleTableSlot *slot;
- Datum result;
- bool found = false; /* TRUE if got at least one subplan tuple */
- List *lst;
-
- /*
- * We are probably in a short-lived expression-evaluation context.
- * Switch to longer-lived per-query context.
- */
- oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-
- if (node->setParam != NIL)
- elog(ERROR, "ExecSubPlan: can't set parent params from subquery");
-
- /*
- * Set Params of this plan from parent plan correlation Vars
- */
- if (node->parParam != NIL)
- {
- foreach(lst, node->parParam)
- {
- ParamExecData *prm;
-
- prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
- Assert(pvar != NIL);
- prm->value = ExecEvalExprSwitchContext((Node *) lfirst(pvar),
- econtext,
- &(prm->isnull),
- NULL);
- pvar = lnext(pvar);
- }
- plan->chgParam = nconc(plan->chgParam, listCopy(node->parParam));
- }
- Assert(pvar == NIL);
-
- ExecReScan(plan, NULL, NULL);
-
- /*
- * For all sublink types except EXPR_SUBLINK, the result is boolean as
- * are the results of the combining operators. We combine results
- * within a tuple (if there are multiple columns) using OR semantics
- * if "useor" is true, AND semantics if not. We then combine results
- * across tuples (if the subplan produces more than one) using OR
- * semantics for ANY_SUBLINK or AND semantics for ALL_SUBLINK.
- * (MULTIEXPR_SUBLINK doesn't allow multiple tuples from the subplan.)
- * NULL results from the combining operators are handled according to
- * the usual SQL semantics for OR and AND. The result for no input
- * tuples is FALSE for ANY_SUBLINK, TRUE for ALL_SUBLINK, NULL for
- * MULTIEXPR_SUBLINK.
- *
- * For EXPR_SUBLINK we require the subplan to produce no more than one
- * tuple, else an error is raised. If zero tuples are produced, we
- * return NULL. Assuming we get a tuple, we just return its first
- * column (there can be only one non-junk column in this case).
- */
- result = BoolGetDatum(subLinkType == ALL_SUBLINK);
- *isNull = false;
-
- for (slot = ExecProcNode(plan, NULL);
- !TupIsNull(slot);
- slot = ExecProcNode(plan, NULL))
- {
- HeapTuple tup = slot->val;
- TupleDesc tdesc = slot->ttc_tupleDescriptor;
- Datum rowresult = BoolGetDatum(!useor);
- bool rownull = false;
- int col = 1;
-
- if (subLinkType == EXISTS_SUBLINK)
- {
- found = true;
- result = BoolGetDatum(true);
- break;
- }
-
- if (subLinkType == EXPR_SUBLINK)
- {
- /* cannot allow multiple input tuples for EXPR sublink */
- if (found)
- elog(ERROR, "More than one tuple returned by a subselect used as an expression.");
- found = true;
-
- /*
- * We need to copy the subplan's tuple in case the result is
- * of pass-by-ref type --- our return value will point into
- * this copied tuple! Can't use the subplan's instance of the
- * tuple since it won't still be valid after next
- * ExecProcNode() call. node->curTuple keeps track of the
- * copied tuple for eventual freeing.
- */
- tup = heap_copytuple(tup);
- if (node->curTuple)
- heap_freetuple(node->curTuple);
- node->curTuple = tup;
- result = heap_getattr(tup, col, tdesc, isNull);
- /* keep scanning subplan to make sure there's only one tuple */
- continue;
- }
-
- /* cannot allow multiple input tuples for MULTIEXPR sublink either */
- if (subLinkType == MULTIEXPR_SUBLINK && found)
- elog(ERROR, "More than one tuple returned by a subselect used as an expression.");
-
- found = true;
-
- /*
- * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
- * operators for columns of tuple.
- */
- foreach(lst, sublink->oper)
- {
- Expr *expr = (Expr *) lfirst(lst);
- Const *con = lsecond(expr->args);
- Datum expresult;
- bool expnull;
-
- /*
- * The righthand side of the expression should be either a
- * Const or a function call or RelabelType node taking a Const
- * as arg (these nodes represent run-time type coercions
- * inserted by the parser to get to the input type needed by
- * the operator). Find the Const node and insert the actual
- * righthand-side value into it.
- */
- if (!IsA(con, Const))
- {
- switch (con->type)
- {
- case T_Expr:
- con = lfirst(((Expr *) con)->args);
- break;
- case T_RelabelType:
- con = (Const *) (((RelabelType *) con)->arg);
- break;
- default:
- /* will fail below */
- break;
- }
- if (!IsA(con, Const))
- elog(ERROR, "ExecSubPlan: failed to find placeholder for subplan result");
- }
- con->constvalue = heap_getattr(tup, col, tdesc,
- &(con->constisnull));
-
- /*
- * Now we can eval the combining operator for this column.
- */
- expresult = ExecEvalExprSwitchContext((Node *) expr, econtext,
- &expnull, NULL);
-
- /*
- * Combine the result into the row result as appropriate.
- */
- if (col == 1)
- {
- rowresult = expresult;
- rownull = expnull;
- }
- else if (useor)
- {
- /* combine within row per OR semantics */
- if (expnull)
- rownull = true;
- else if (DatumGetBool(expresult))
- {
- rowresult = BoolGetDatum(true);
- rownull = false;
- break; /* needn't look at any more columns */
- }
- }
- else
- {
- /* combine within row per AND semantics */
- if (expnull)
- rownull = true;
- else if (!DatumGetBool(expresult))
- {
- rowresult = BoolGetDatum(false);
- rownull = false;
- break; /* needn't look at any more columns */
- }
- }
- col++;
- }
-
- if (subLinkType == ANY_SUBLINK)
- {
- /* combine across rows per OR semantics */
- if (rownull)
- *isNull = true;
- else if (DatumGetBool(rowresult))
- {
- result = BoolGetDatum(true);
- *isNull = false;
- break; /* needn't look at any more rows */
- }
- }
- else if (subLinkType == ALL_SUBLINK)
- {
- /* combine across rows per AND semantics */
- if (rownull)
- *isNull = true;
- else if (!DatumGetBool(rowresult))
- {
- result = BoolGetDatum(false);
- *isNull = false;
- break; /* needn't look at any more rows */
- }
- }
- else
- {
- /* must be MULTIEXPR_SUBLINK */
- result = rowresult;
- *isNull = rownull;
- }
- }
-
- if (!found)
- {
- /*
- * deal with empty subplan result. result/isNull were previously
- * initialized correctly for all sublink types except EXPR and
- * MULTIEXPR; for those, return NULL.
- */
- if (subLinkType == EXPR_SUBLINK || subLinkType == MULTIEXPR_SUBLINK)
- {
- result = (Datum) 0;
- *isNull = true;
- }
- }
-
- MemoryContextSwitchTo(oldcontext);
-
- return result;
-}
-
-/* ----------------------------------------------------------------
- * ExecInitSubPlan
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
-{
- EState *sp_estate = CreateExecutorState();
-
- sp_estate->es_range_table = node->rtable;
- sp_estate->es_param_list_info = estate->es_param_list_info;
- sp_estate->es_param_exec_vals = estate->es_param_exec_vals;
- sp_estate->es_tupleTable =
- ExecCreateTupleTable(ExecCountSlotsNode(node->plan) + 10);
- sp_estate->es_snapshot = estate->es_snapshot;
-
- node->needShutdown = false;
- node->curTuple = NULL;
-
- if (!ExecInitNode(node->plan, sp_estate, parent))
- return false;
-
- node->needShutdown = true; /* now we need to shutdown the subplan */
-
- /*
- * If this plan is un-correlated or undirect correlated one and want
- * to set params for parent plan then prepare parameters.
- */
- if (node->setParam != NIL)
- {
- List *lst;
-
- foreach(lst, node->setParam)
- {
- ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(lst)]);
-
- prm->execPlan = node;
- }
-
- /*
- * Note that in the case of un-correlated subqueries we don't care
- * about setting parent->chgParam here: indices take care about
- * it, for others - it doesn't matter...
- */
- }
-
- return true;
-}
-
-/* ----------------------------------------------------------------
- * ExecSetParamPlan
- *
- * Executes an InitPlan subplan and sets its output parameters.
- *
- * This is called from ExecEvalParam() when the value of a PARAM_EXEC
- * parameter is requested and the param's execPlan field is set (indicating
- * that the param has not yet been evaluated). This allows lazy evaluation
- * of initplans: we don't run the subplan until/unless we need its output.
- * Note that this routine MUST clear the execPlan fields of the plan's
- * output parameters after evaluating them!
- * ----------------------------------------------------------------
- */
-void
-ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
-{
- Plan *plan = node->plan;
- SubLink *sublink = node->sublink;
- MemoryContext oldcontext;
- TupleTableSlot *slot;
- List *lst;
- bool found = false;
-
- /*
- * We are probably in a short-lived expression-evaluation context.
- * Switch to longer-lived per-query context.
- */
- oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-
- if (sublink->subLinkType == ANY_SUBLINK ||
- sublink->subLinkType == ALL_SUBLINK)
- elog(ERROR, "ExecSetParamPlan: ANY/ALL subselect unsupported");
-
- if (plan->chgParam != NULL)
- ExecReScan(plan, NULL, NULL);
-
- for (slot = ExecProcNode(plan, NULL);
- !TupIsNull(slot);
- slot = ExecProcNode(plan, NULL))
- {
- HeapTuple tup = slot->val;
- TupleDesc tdesc = slot->ttc_tupleDescriptor;
- int i = 1;
-
- if (sublink->subLinkType == EXISTS_SUBLINK)
- {
- ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
-
- prm->execPlan = NULL;
- prm->value = BoolGetDatum(true);
- prm->isnull = false;
- found = true;
- break;
- }
-
- if (found &&
- (sublink->subLinkType == EXPR_SUBLINK ||
- sublink->subLinkType == MULTIEXPR_SUBLINK))
- elog(ERROR, "More than one tuple returned by a subselect used as an expression.");
-
- found = true;
-
- /*
- * We need to copy the subplan's tuple in case any of the params
- * are pass-by-ref type --- the pointers stored in the param
- * structs will point at this copied tuple! node->curTuple keeps
- * track of the copied tuple for eventual freeing.
- */
- tup = heap_copytuple(tup);
- if (node->curTuple)
- heap_freetuple(node->curTuple);
- node->curTuple = tup;
-
- foreach(lst, node->setParam)
- {
- ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
-
- prm->execPlan = NULL;
- prm->value = heap_getattr(tup, i, tdesc, &(prm->isnull));
- i++;
- }
- }
-
- if (!found)
- {
- if (sublink->subLinkType == EXISTS_SUBLINK)
- {
- ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
-
- prm->execPlan = NULL;
- prm->value = BoolGetDatum(false);
- prm->isnull = false;
- }
- else
- {
- foreach(lst, node->setParam)
- {
- ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
-
- prm->execPlan = NULL;
- prm->value = (Datum) 0;
- prm->isnull = true;
- }
- }
- }
-
- if (plan->extParam == NULL) /* un-correlated ... */
- {
- ExecEndNode(plan, NULL);
- node->needShutdown = false;
- }
-
- MemoryContextSwitchTo(oldcontext);
-}
-
-/* ----------------------------------------------------------------
- * ExecEndSubPlan
- * ----------------------------------------------------------------
- */
-void
-ExecEndSubPlan(SubPlan *node)
-{
- if (node->needShutdown)
- {
- ExecEndNode(node->plan, NULL);
- node->needShutdown = false;
- }
- if (node->curTuple)
- {
- heap_freetuple(node->curTuple);
- node->curTuple = NULL;
- }
-}
-
-void
-ExecReScanSetParamPlan(SubPlan *node, Plan *parent)
-{
- Plan *plan = node->plan;
- List *lst;
-
- if (node->parParam != NULL)
- elog(ERROR, "ExecReScanSetParamPlan: direct correlated subquery unsupported, yet");
- if (node->setParam == NULL)
- elog(ERROR, "ExecReScanSetParamPlan: setParam list is NULL");
- if (plan->extParam == NULL)
- elog(ERROR, "ExecReScanSetParamPlan: extParam list of plan is NULL");
-
- /*
- * Don't actual re-scan: ExecSetParamPlan does re-scan if
- * node->plan->chgParam is not NULL... ExecReScan (plan, NULL, NULL);
- */
-
- /*
- * Mark this subplan's output parameters as needing recalculation
- */
- foreach(lst, node->setParam)
- {
- ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
-
- prm->execPlan = node;
- }
-
- parent->chgParam = nconc(parent->chgParam, listCopy(node->setParam));
-
-}
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
deleted file mode 100644
index 982dd0236ca..00000000000
--- a/src/backend/executor/nodeSubqueryscan.c
+++ /dev/null
@@ -1,262 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeSubqueryscan.c
- * Support routines for scanning subqueries (subselects in rangetable).
- *
- * This is just enough different from sublinks (nodeSubplan.c) to mean that
- * we need two sets of code. Ought to look at trying to unify the cases.
- *
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.13 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecSubqueryScan scans a subquery.
- * ExecSubqueryNext retrieve next tuple in sequential order.
- * ExecInitSubqueryScan creates and initializes a subqueryscan node.
- * ExecEndSubqueryScan releases any storage allocated.
- * ExecSubqueryReScan rescans the relation
- *
- */
-#include "postgres.h"
-
-#include "catalog/pg_type.h"
-#include "executor/execdebug.h"
-#include "executor/execdefs.h"
-#include "executor/execdesc.h"
-#include "executor/nodeSubqueryscan.h"
-#include "parser/parsetree.h"
-#include "tcop/pquery.h"
-
-static TupleTableSlot *SubqueryNext(SubqueryScan *node);
-
-/* ----------------------------------------------------------------
- * Scan Support
- * ----------------------------------------------------------------
- */
-/* ----------------------------------------------------------------
- * SubqueryNext
- *
- * This is a workhorse for ExecSubqueryScan
- * ----------------------------------------------------------------
- */
-static TupleTableSlot *
-SubqueryNext(SubqueryScan *node)
-{
- SubqueryScanState *subquerystate;
- EState *estate;
- ScanDirection direction;
- TupleTableSlot *slot;
-
- /*
- * get information from the estate and scan state
- */
- estate = node->scan.plan.state;
- subquerystate = (SubqueryScanState *) node->scan.scanstate;
- direction = estate->es_direction;
-
- /*
- * We need not support EvalPlanQual here, since we are not scanning a
- * real relation.
- */
-
- /*
- * get the next tuple from the sub-query
- */
- subquerystate->sss_SubEState->es_direction = direction;
-
- slot = ExecProcNode(node->subplan, (Plan *) node);
-
- subquerystate->csstate.css_ScanTupleSlot = slot;
-
- return slot;
-}
-
-/* ----------------------------------------------------------------
- * ExecSubqueryScan(node)
- *
- * Scans the subquery sequentially and returns the next qualifying
- * tuple.
- * It calls the ExecScan() routine and passes it the access method
- * which retrieve tuples sequentially.
- *
- */
-
-TupleTableSlot *
-ExecSubqueryScan(SubqueryScan *node)
-{
- /*
- * use SubqueryNext as access method
- */
- return ExecScan(&node->scan, (ExecScanAccessMtd) SubqueryNext);
-}
-
-/* ----------------------------------------------------------------
- * ExecInitSubqueryScan
- * ----------------------------------------------------------------
- */
-bool
-ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent)
-{
- SubqueryScanState *subquerystate;
- RangeTblEntry *rte;
- EState *sp_estate;
-
- /*
- * SubqueryScan should not have any "normal" children.
- */
- Assert(outerPlan((Plan *) node) == NULL);
- Assert(innerPlan((Plan *) node) == NULL);
-
- /*
- * assign the node's execution state
- */
- node->scan.plan.state = estate;
-
- /*
- * create new SubqueryScanState for node
- */
- subquerystate = makeNode(SubqueryScanState);
- node->scan.scanstate = (CommonScanState *) subquerystate;
-
- /*
- * Miscellaneous initialization
- *
- * create expression context for node
- */
- ExecAssignExprContext(estate, &subquerystate->csstate.cstate);
-
-#define SUBQUERYSCAN_NSLOTS 1
-
- /*
- * tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &subquerystate->csstate.cstate);
-
- /*
- * initialize subquery
- *
- * This should agree with ExecInitSubPlan
- */
- rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
- Assert(rte->rtekind == RTE_SUBQUERY);
-
- sp_estate = CreateExecutorState();
- subquerystate->sss_SubEState = sp_estate;
-
- sp_estate->es_range_table = rte->subquery->rtable;
- sp_estate->es_param_list_info = estate->es_param_list_info;
- sp_estate->es_param_exec_vals = estate->es_param_exec_vals;
- sp_estate->es_tupleTable =
- ExecCreateTupleTable(ExecCountSlotsNode(node->subplan) + 10);
- sp_estate->es_snapshot = estate->es_snapshot;
-
- if (!ExecInitNode(node->subplan, sp_estate, (Plan *) node))
- return false;
-
- subquerystate->csstate.css_ScanTupleSlot = NULL;
- subquerystate->csstate.cstate.cs_TupFromTlist = false;
-
- /*
- * initialize tuple type
- */
- ExecAssignResultTypeFromTL((Plan *) node, &subquerystate->csstate.cstate);
- ExecAssignProjectionInfo((Plan *) node, &subquerystate->csstate.cstate);
-
- return TRUE;
-}
-
-int
-ExecCountSlotsSubqueryScan(SubqueryScan *node)
-{
- /*
- * The subplan has its own tuple table and must not be counted here!
- */
- return ExecCountSlotsNode(outerPlan(node)) +
- ExecCountSlotsNode(innerPlan(node)) +
- SUBQUERYSCAN_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndSubqueryScan
- *
- * frees any storage allocated through C routines.
- * ----------------------------------------------------------------
- */
-void
-ExecEndSubqueryScan(SubqueryScan *node)
-{
- SubqueryScanState *subquerystate;
-
- /*
- * get information from node
- */
- subquerystate = (SubqueryScanState *) node->scan.scanstate;
-
- /*
- * Free the projection info and the scan attribute info
- *
- * Note: we don't ExecFreeResultType(subquerystate) because the rule
- * manager depends on the tupType returned by ExecMain(). So for now,
- * this is freed at end-transaction time. -cim 6/2/91
- */
- ExecFreeProjectionInfo(&subquerystate->csstate.cstate);
- ExecFreeExprContext(&subquerystate->csstate.cstate);
-
- /*
- * close down subquery
- */
- ExecEndNode(node->subplan, (Plan *) node);
-
- /*
- * clean up subquery's tuple table
- */
- subquerystate->csstate.css_ScanTupleSlot = NULL;
- ExecDropTupleTable(subquerystate->sss_SubEState->es_tupleTable, true);
-
- /* XXX we seem to be leaking the sub-EState... */
-
- /*
- * clean out the upper tuple table
- */
- ExecClearTuple(subquerystate->csstate.cstate.cs_ResultTupleSlot);
-}
-
-/* ----------------------------------------------------------------
- * ExecSubqueryReScan
- *
- * Rescans the relation.
- * ----------------------------------------------------------------
- */
-void
-ExecSubqueryReScan(SubqueryScan *node, ExprContext *exprCtxt, Plan *parent)
-{
- SubqueryScanState *subquerystate;
- EState *estate;
-
- subquerystate = (SubqueryScanState *) node->scan.scanstate;
- estate = node->scan.plan.state;
-
- /*
- * ExecReScan doesn't know about my subplan, so I have to do
- * changed-parameter signaling myself.
- */
- if (node->scan.plan.chgParam != NULL)
- SetChangedParamList(node->subplan, node->scan.plan.chgParam);
-
- /*
- * if chgParam of subnode is not null then plan will be re-scanned by
- * first ExecProcNode.
- */
- if (node->subplan->chgParam == NULL)
- ExecReScan(node->subplan, NULL, (Plan *) node);
-
- subquerystate->csstate.css_ScanTupleSlot = NULL;
-}
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
deleted file mode 100644
index 300735fff06..00000000000
--- a/src/backend/executor/nodeTidscan.c
+++ /dev/null
@@ -1,496 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeTidscan.c
- * Routines to support direct tid scans of relations
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.25 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- *
- * ExecTidScan scans a relation using tids
- * ExecInitTidScan creates and initializes state info.
- * ExecTidReScan rescans the tid relation.
- * ExecEndTidScan releases all storage.
- * ExecTidMarkPos marks scan position.
- *
- */
-#include "postgres.h"
-
-#include "executor/execdebug.h"
-#include "executor/nodeTidscan.h"
-#include "access/heapam.h"
-#include "parser/parsetree.h"
-
-static int TidListCreate(List *, ExprContext *, ItemPointerData[]);
-static TupleTableSlot *TidNext(TidScan *node);
-
-static int
-TidListCreate(List *evalList, ExprContext *econtext, ItemPointerData tidList[])
-{
- List *lst;
- ItemPointer itemptr;
- bool isNull;
- int numTids = 0;
-
- foreach(lst, evalList)
- {
- itemptr = (ItemPointer)
- DatumGetPointer(ExecEvalExprSwitchContext(lfirst(lst),
- econtext,
- &isNull,
- NULL));
- if (!isNull && itemptr && ItemPointerIsValid(itemptr))
- {
- tidList[numTids] = *itemptr;
- numTids++;
- }
- }
- return numTids;
-}
-
-/* ----------------------------------------------------------------
- * TidNext
- *
- * Retrieve a tuple from the TidScan node's currentRelation
- * using the tids in the TidScanState information.
- *
- * ----------------------------------------------------------------
- */
-static TupleTableSlot *
-TidNext(TidScan *node)
-{
- EState *estate;
- CommonScanState *scanstate;
- TidScanState *tidstate;
- ScanDirection direction;
- Snapshot snapshot;
- Relation heapRelation;
- HeapTuple tuple;
- TupleTableSlot *slot;
- Buffer buffer = InvalidBuffer;
- int numTids;
-
- bool bBackward;
- int tidNumber;
- ItemPointerData *tidList;
-
- /*
- * extract necessary information from tid scan node
- */
- estate = node->scan.plan.state;
- direction = estate->es_direction;
- snapshot = estate->es_snapshot;
- scanstate = node->scan.scanstate;
- tidstate = node->tidstate;
- heapRelation = scanstate->css_currentRelation;
- numTids = tidstate->tss_NumTids;
- tidList = tidstate->tss_TidList;
- slot = scanstate->css_ScanTupleSlot;
-
- /*
- * Check if we are evaluating PlanQual for tuple of this relation.
- * Additional checking is not good, but no other way for now. We could
- * introduce new nodes for this case and handle TidScan --> NewNode
- * switching in Init/ReScan plan...
- */
- if (estate->es_evTuple != NULL &&
- estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
- {
- ExecClearTuple(slot);
- if (estate->es_evTupleNull[node->scan.scanrelid - 1])
- return slot; /* return empty slot */
-
- /*
- * XXX shouldn't we check here to make sure tuple matches TID list?
- * In runtime-key case this is not certain, is it?
- */
-
- ExecStoreTuple(estate->es_evTuple[node->scan.scanrelid - 1],
- slot, InvalidBuffer, false);
-
- /* Flag for the next call that no more tuples */
- estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
- return (slot);
- }
-
- tuple = &(tidstate->tss_htup);
-
- /*
- * ok, now that we have what we need, fetch an tid tuple. if scanning
- * this tid succeeded then return the appropriate heap tuple.. else
- * return NULL.
- */
- bBackward = ScanDirectionIsBackward(direction);
- if (bBackward)
- {
- tidNumber = numTids - tidstate->tss_TidPtr - 1;
- if (tidNumber < 0)
- {
- tidNumber = 0;
- tidstate->tss_TidPtr = numTids - 1;
- }
- }
- else
- {
- if ((tidNumber = tidstate->tss_TidPtr) < 0)
- {
- tidNumber = 0;
- tidstate->tss_TidPtr = 0;
- }
- }
- while (tidNumber < numTids)
- {
- bool slot_is_valid = false;
-
- tuple->t_self = tidList[tidstate->tss_TidPtr];
- if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
- {
- bool prev_matches = false;
- int prev_tid;
-
- /*
- * store the scanned tuple in the scan tuple slot of the scan
- * state. Eventually we will only do this and not return a
- * tuple. Note: we pass 'false' because tuples returned by
- * amgetnext are pointers onto disk pages and were not created
- * with palloc() and so should not be pfree()'d.
- */
- ExecStoreTuple(tuple, /* tuple to store */
- slot, /* slot to store in */
- buffer, /* buffer associated with tuple */
- false); /* don't pfree */
-
- /*
- * At this point we have an extra pin on the buffer, because
- * ExecStoreTuple incremented the pin count. Drop our local
- * pin.
- */
- ReleaseBuffer(buffer);
-
- /*
- * We must check to see if the current tuple would have been
- * matched by an earlier tid, so we don't double report it. We
- * do this by passing the tuple through ExecQual and look for
- * failure with all previous qualifications.
- */
- for (prev_tid = 0; prev_tid < tidstate->tss_TidPtr;
- prev_tid++)
- {
- if (ItemPointerEquals(&tidList[prev_tid], &tuple->t_self))
- {
- prev_matches = true;
- break;
- }
- }
- if (!prev_matches)
- slot_is_valid = true;
- else
- ExecClearTuple(slot);
- }
- tidNumber++;
- if (bBackward)
- tidstate->tss_TidPtr--;
- else
- tidstate->tss_TidPtr++;
- if (slot_is_valid)
- return slot;
- }
-
- /*
- * if we get here it means the tid scan failed so we are at the end of
- * the scan..
- */
- return ExecClearTuple(slot);
-}
-
-/* ----------------------------------------------------------------
- * ExecTidScan(node)
- *
- * Scans the relation using tids and returns
- * the next qualifying tuple in the direction specified.
- * It calls ExecScan() and passes it the access methods which returns
- * the next tuple using the tids.
- *
- * Conditions:
- * -- the "cursor" maintained by the AMI is positioned at the tuple
- * returned previously.
- *
- * Initial States:
- * -- the relation indicated is opened for scanning so that the
- * "cursor" is positioned before the first qualifying tuple.
- * -- tidPtr points to the first tid.
- * -- state variable ruleFlag = nil.
- * ----------------------------------------------------------------
- */
-TupleTableSlot *
-ExecTidScan(TidScan *node)
-{
- /*
- * use TidNext as access method
- */
- return ExecScan(&node->scan, (ExecScanAccessMtd) TidNext);
-}
-
-/* ----------------------------------------------------------------
- * ExecTidReScan(node)
- * ----------------------------------------------------------------
- */
-void
-ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent)
-{
- EState *estate;
- TidScanState *tidstate;
- ItemPointerData *tidList;
-
- estate = node->scan.plan.state;
- tidstate = node->tidstate;
- tidList = tidstate->tss_TidList;
-
- /* If we are being passed an outer tuple, save it for runtime key calc */
- if (exprCtxt != NULL)
- node->scan.scanstate->cstate.cs_ExprContext->ecxt_outertuple =
- exprCtxt->ecxt_outertuple;
-
- /* do runtime calc of target TIDs, if needed */
- if (node->needRescan)
- tidstate->tss_NumTids =
- TidListCreate(node->tideval,
- node->scan.scanstate->cstate.cs_ExprContext,
- tidList);
-
- /* If this is re-scanning of PlanQual ... */
- if (estate->es_evTuple != NULL &&
- estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
- {
- estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
- return;
- }
-
- tidstate->tss_TidPtr = -1;
-
- /*
- * perhaps return something meaningful
- */
- return;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndTidScan
- *
- * Releases any storage allocated through C routines.
- * Returns nothing.
- * ----------------------------------------------------------------
- */
-void
-ExecEndTidScan(TidScan *node)
-{
- CommonScanState *scanstate;
- TidScanState *tidstate;
-
- /*
- * extract information from the node
- */
- scanstate = node->scan.scanstate;
- tidstate = node->tidstate;
- if (tidstate && tidstate->tss_TidList)
- pfree(tidstate->tss_TidList);
-
- /*
- * Free the projection info and the scan attribute info
- *
- * Note: we don't ExecFreeResultType(scanstate) because the rule manager
- * depends on the tupType returned by ExecMain(). So for now, this is
- * freed at end-transaction time. -cim 6/2/91
- */
- ExecFreeProjectionInfo(&scanstate->cstate);
- ExecFreeExprContext(&scanstate->cstate);
-
- /*
- * close the heap relation.
- *
- * Currently, we do not release the AccessShareLock acquired by
- * ExecInitTidScan. This lock should be held till end of transaction.
- * (There is a faction that considers this too much locking, however.)
- */
- heap_close(scanstate->css_currentRelation, NoLock);
-
- /*
- * clear out tuple table slots
- */
- ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
- ExecClearTuple(scanstate->css_ScanTupleSlot);
-}
-
-/* ----------------------------------------------------------------
- * ExecTidMarkPos
- *
- * Marks scan position by marking the current tid.
- * Returns nothing.
- * ----------------------------------------------------------------
- */
-void
-ExecTidMarkPos(TidScan *node)
-{
- TidScanState *tidstate;
-
- tidstate = node->tidstate;
- tidstate->tss_MarkTidPtr = tidstate->tss_TidPtr;
-}
-
-#ifdef NOT_USED
-/* ----------------------------------------------------------------
- * ExecTidRestrPos
- *
- * Restores scan position by restoring the current tid.
- * Returns nothing.
- *
- * XXX Assumes previously marked scan position belongs to current tid
- * ----------------------------------------------------------------
- */
-void
-ExecTidRestrPos(TidScan *node)
-{
- TidScanState *tidstate;
-
- tidstate = node->tidstate;
- tidstate->tss_TidPtr = tidstate->tss_MarkTidPtr;
-}
-#endif
-
-/* ----------------------------------------------------------------
- * ExecInitTidScan
- *
- * Initializes the tid scan's state information, creates
- * scan keys, and opens the base and tid relations.
- *
- * Parameters:
- * node: TidNode node produced by the planner.
- * estate: the execution state initialized in InitPlan.
- * ----------------------------------------------------------------
- */
-bool
-ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
-{
- TidScanState *tidstate;
- CommonScanState *scanstate;
- ItemPointerData *tidList;
- int numTids;
- int tidPtr;
- List *rangeTable;
- RangeTblEntry *rtentry;
- Oid relid;
- Oid reloid;
- Relation currentRelation;
- List *execParam = NIL;
-
- /*
- * assign execution state to node
- */
- node->scan.plan.state = estate;
-
- /*
- * Part 1) initialize scan state
- *
- * create new CommonScanState for node
- */
- scanstate = makeNode(CommonScanState);
- node->scan.scanstate = scanstate;
-
- /*
- * Miscellaneous initialization
- *
- * create expression context for node
- */
- ExecAssignExprContext(estate, &scanstate->cstate);
-
-#define TIDSCAN_NSLOTS 2
-
- /*
- * tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &scanstate->cstate);
- ExecInitScanTupleSlot(estate, scanstate);
-
- /*
- * initialize projection info. result type comes from scan desc
- * below..
- */
- ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
-
- /*
- * Part 2) initialize tid scan state
- *
- * create new TidScanState for node
- */
- tidstate = makeNode(TidScanState);
- node->tidstate = tidstate;
-
- /*
- * get the tid node information
- */
- tidList = (ItemPointerData *) palloc(length(node->tideval) * sizeof(ItemPointerData));
- numTids = 0;
- if (!node->needRescan)
- numTids = TidListCreate(node->tideval,
- scanstate->cstate.cs_ExprContext,
- tidList);
- tidPtr = -1;
-
- CXT1_printf("ExecInitTidScan: context is %d\n", CurrentMemoryContext);
-
- tidstate->tss_NumTids = numTids;
- tidstate->tss_TidPtr = tidPtr;
- tidstate->tss_TidList = tidList;
-
- /*
- * get the range table and direction information from the execution
- * state (these are needed to open the relations).
- */
- rangeTable = estate->es_range_table;
-
- /*
- * open the base relation
- *
- * We acquire AccessShareLock for the duration of the scan.
- */
- relid = node->scan.scanrelid;
- rtentry = rt_fetch(relid, rangeTable);
- reloid = rtentry->relid;
-
- currentRelation = heap_open(reloid, AccessShareLock);
-
- scanstate->css_currentRelation = currentRelation;
- scanstate->css_currentScanDesc = NULL; /* no heap scan here */
-
- /*
- * get the scan type from the relation descriptor.
- */
- ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false);
- ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
-
- /*
- * if there are some PARAM_EXEC in skankeys then force tid rescan on
- * first scan.
- */
- ((Plan *) node)->chgParam = execParam;
-
- /*
- * all done.
- */
- return TRUE;
-}
-
-int
-ExecCountSlotsTidScan(TidScan *node)
-{
- return ExecCountSlotsNode(outerPlan((Plan *) node)) +
- ExecCountSlotsNode(innerPlan((Plan *) node)) + TIDSCAN_NSLOTS;
-}
diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c
deleted file mode 100644
index b71403de0b8..00000000000
--- a/src/backend/executor/nodeUnique.c
+++ /dev/null
@@ -1,237 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * nodeUnique.c
- * Routines to handle unique'ing of queries where appropriate
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.34 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecUnique - generate a unique'd temporary relation
- * ExecInitUnique - initialize node and subnodes..
- * ExecEndUnique - shutdown node and subnodes
- *
- * NOTES
- * Assumes tuples returned from subplan arrive in
- * sorted order.
- *
- */
-
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "executor/executor.h"
-#include "executor/nodeGroup.h"
-#include "executor/nodeUnique.h"
-
-/* ----------------------------------------------------------------
- * ExecUnique
- *
- * This is a very simple node which filters out duplicate
- * tuples from a stream of sorted tuples from a subplan.
- * ----------------------------------------------------------------
- */
-TupleTableSlot * /* return: a tuple or NULL */
-ExecUnique(Unique *node)
-{
- UniqueState *uniquestate;
- TupleTableSlot *resultTupleSlot;
- TupleTableSlot *slot;
- Plan *outerPlan;
- TupleDesc tupDesc;
-
- /*
- * get information from the node
- */
- uniquestate = node->uniquestate;
- outerPlan = outerPlan((Plan *) node);
- resultTupleSlot = uniquestate->cstate.cs_ResultTupleSlot;
- tupDesc = ExecGetResultType(&uniquestate->cstate);
-
- /*
- * now loop, returning only non-duplicate tuples. We assume that the
- * tuples arrive in sorted order so we can detect duplicates easily.
- */
- for (;;)
- {
- /*
- * fetch a tuple from the outer subplan
- */
- slot = ExecProcNode(outerPlan, (Plan *) node);
- if (TupIsNull(slot))
- return NULL;
-
- /*
- * Always return the first tuple from the subplan.
- */
- if (uniquestate->priorTuple == NULL)
- break;
-
- /*
- * Else test if the new tuple and the previously returned tuple
- * match. If so then we loop back and fetch another new tuple
- * from the subplan.
- */
- if (!execTuplesMatch(slot->val, uniquestate->priorTuple,
- tupDesc,
- node->numCols, node->uniqColIdx,
- uniquestate->eqfunctions,
- uniquestate->tempContext))
- break;
- }
-
- /*
- * We have a new tuple different from the previous saved tuple (if
- * any). Save it and return it. We must copy it because the source
- * subplan won't guarantee that this source tuple is still accessible
- * after fetching the next source tuple.
- *
- * Note that we manage the copy ourselves. We can't rely on the result
- * tuple slot to maintain the tuple reference because our caller may
- * replace the slot contents with a different tuple (see junk filter
- * handling in execMain.c). We assume that the caller will no longer
- * be interested in the current tuple after he next calls us.
- */
- if (uniquestate->priorTuple != NULL)
- heap_freetuple(uniquestate->priorTuple);
- uniquestate->priorTuple = heap_copytuple(slot->val);
-
- ExecStoreTuple(uniquestate->priorTuple,
- resultTupleSlot,
- InvalidBuffer,
- false); /* tuple does not belong to slot */
-
- return resultTupleSlot;
-}
-
-/* ----------------------------------------------------------------
- * ExecInitUnique
- *
- * This initializes the unique node state structures and
- * the node's subplan.
- * ----------------------------------------------------------------
- */
-bool /* return: initialization status */
-ExecInitUnique(Unique *node, EState *estate, Plan *parent)
-{
- UniqueState *uniquestate;
- Plan *outerPlan;
-
- /*
- * assign execution state to node
- */
- node->plan.state = estate;
-
- /*
- * create new UniqueState for node
- */
- uniquestate = makeNode(UniqueState);
- node->uniquestate = uniquestate;
- uniquestate->priorTuple = NULL;
-
- /*
- * Miscellaneous initialization
- *
- * Unique nodes have no ExprContext initialization because they never
- * call ExecQual or ExecProject. But they do need a per-tuple memory
- * context anyway for calling execTuplesMatch.
- */
- uniquestate->tempContext =
- AllocSetContextCreate(CurrentMemoryContext,
- "Unique",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
-
-#define UNIQUE_NSLOTS 1
-
- /*
- * Tuple table initialization
- */
- ExecInitResultTupleSlot(estate, &uniquestate->cstate);
-
- /*
- * then initialize outer plan
- */
- outerPlan = outerPlan((Plan *) node);
- ExecInitNode(outerPlan, estate, (Plan *) node);
-
- /*
- * unique nodes do no projections, so initialize projection info for
- * this node appropriately
- */
- ExecAssignResultTypeFromOuterPlan((Plan *) node, &uniquestate->cstate);
- uniquestate->cstate.cs_ProjInfo = NULL;
-
- /*
- * Precompute fmgr lookup data for inner loop
- */
- uniquestate->eqfunctions =
- execTuplesMatchPrepare(ExecGetResultType(&uniquestate->cstate),
- node->numCols,
- node->uniqColIdx);
-
- return TRUE;
-}
-
-int
-ExecCountSlotsUnique(Unique *node)
-{
- return ExecCountSlotsNode(outerPlan(node)) +
- ExecCountSlotsNode(innerPlan(node)) +
- UNIQUE_NSLOTS;
-}
-
-/* ----------------------------------------------------------------
- * ExecEndUnique
- *
- * This shuts down the subplan and frees resources allocated
- * to this node.
- * ----------------------------------------------------------------
- */
-void
-ExecEndUnique(Unique *node)
-{
- UniqueState *uniquestate = node->uniquestate;
-
- ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
-
- MemoryContextDelete(uniquestate->tempContext);
-
- /* clean up tuple table */
- ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot);
- if (uniquestate->priorTuple != NULL)
- {
- heap_freetuple(uniquestate->priorTuple);
- uniquestate->priorTuple = NULL;
- }
-}
-
-
-void
-ExecReScanUnique(Unique *node, ExprContext *exprCtxt, Plan *parent)
-{
- UniqueState *uniquestate = node->uniquestate;
-
- ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot);
- if (uniquestate->priorTuple != NULL)
- {
- heap_freetuple(uniquestate->priorTuple);
- uniquestate->priorTuple = NULL;
- }
-
- /*
- * if chgParam of subnode is not null then plan will be re-scanned by
- * first ExecProcNode.
- */
- if (((Plan *) node)->lefttree->chgParam == NULL)
- ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-
-}
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
deleted file mode 100644
index 60b39768d53..00000000000
--- a/src/backend/executor/spi.c
+++ /dev/null
@@ -1,1424 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * spi.c
- * Server Programming Interface
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.71 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "access/printtup.h"
-#include "catalog/heap.h"
-#include "commands/portalcmds.h"
-#include "executor/spi_priv.h"
-#include "tcop/tcopprot.h"
-#include "utils/lsyscache.h"
-
-
-uint32 SPI_processed = 0;
-Oid SPI_lastoid = InvalidOid;
-SPITupleTable *SPI_tuptable = NULL;
-int SPI_result;
-
-static _SPI_connection *_SPI_stack = NULL;
-static _SPI_connection *_SPI_current = NULL;
-static int _SPI_connected = -1;
-static int _SPI_curid = -1;
-
-static int _SPI_execute(char *src, int tcount, _SPI_plan *plan);
-static int _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount);
-
-static int _SPI_execute_plan(_SPI_plan *plan,
- Datum *Values, char *Nulls, int tcount);
-
-static void _SPI_cursor_operation(Portal portal, bool forward, int count,
- CommandDest dest);
-
-static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location);
-
-static int _SPI_begin_call(bool execmem);
-static int _SPI_end_call(bool procmem);
-static MemoryContext _SPI_execmem(void);
-static MemoryContext _SPI_procmem(void);
-static bool _SPI_checktuples(void);
-
-
-/* =================== interface functions =================== */
-
-int
-SPI_connect(void)
-{
- _SPI_connection *new_SPI_stack;
-
- /*
- * When procedure called by Executor _SPI_curid expected to be equal
- * to _SPI_connected
- */
- if (_SPI_curid != _SPI_connected)
- return SPI_ERROR_CONNECT;
-
- if (_SPI_stack == NULL)
- {
- if (_SPI_connected != -1)
- elog(FATAL, "SPI_connect: no connection(s) expected");
- new_SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection));
- }
- else
- {
- if (_SPI_connected <= -1)
- elog(FATAL, "SPI_connect: some connection(s) expected");
- new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
- (_SPI_connected + 2) * sizeof(_SPI_connection));
- }
-
- if (new_SPI_stack == NULL)
- elog(ERROR, "Memory exhausted in SPI_connect");
-
- /*
- * We' returning to procedure where _SPI_curid == _SPI_connected - 1
- */
- _SPI_stack = new_SPI_stack;
- _SPI_connected++;
-
- _SPI_current = &(_SPI_stack[_SPI_connected]);
- _SPI_current->qtlist = NULL;
- _SPI_current->processed = 0;
- _SPI_current->tuptable = NULL;
-
- /* Create memory contexts for this procedure */
- _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
- "SPI Proc",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext,
- "SPI Exec",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- /* ... and switch to procedure's context */
- _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
-
- return SPI_OK_CONNECT;
-}
-
-int
-SPI_finish(void)
-{
- int res;
-
- res = _SPI_begin_call(false); /* live in procedure memory */
- if (res < 0)
- return res;
-
- /* Restore memory context as it was before procedure call */
- MemoryContextSwitchTo(_SPI_current->savedcxt);
-
- /* Release memory used in procedure call */
- MemoryContextDelete(_SPI_current->execCxt);
- MemoryContextDelete(_SPI_current->procCxt);
-
- /*
- * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are
- * closing connection to SPI and returning to upper Executor and so
- * _SPI_connected must be equal to _SPI_curid.
- */
- _SPI_connected--;
- _SPI_curid--;
- if (_SPI_connected == -1)
- {
- free(_SPI_stack);
- _SPI_stack = NULL;
- _SPI_current = NULL;
- }
- else
- {
- _SPI_connection *new_SPI_stack;
-
- new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
- (_SPI_connected + 1) * sizeof(_SPI_connection));
- /* This could only fail with a pretty stupid malloc package ... */
- if (new_SPI_stack == NULL)
- elog(ERROR, "Memory exhausted in SPI_finish");
- _SPI_stack = new_SPI_stack;
- _SPI_current = &(_SPI_stack[_SPI_connected]);
- }
-
- return SPI_OK_FINISH;
-
-}
-
-/*
- * Clean up SPI state at transaction commit or abort (we don't care which).
- */
-void
-AtEOXact_SPI(void)
-{
- /*
- * Note that memory contexts belonging to SPI stack entries will be
- * freed automatically, so we can ignore them here. We just need to
- * restore our static variables to initial state.
- */
- if (_SPI_stack != NULL) /* there was abort */
- free(_SPI_stack);
- _SPI_current = _SPI_stack = NULL;
- _SPI_connected = _SPI_curid = -1;
- SPI_processed = 0;
- SPI_lastoid = InvalidOid;
- SPI_tuptable = NULL;
-}
-
-void
-SPI_push(void)
-{
- _SPI_curid++;
-}
-
-void
-SPI_pop(void)
-{
- _SPI_curid--;
-}
-
-int
-SPI_exec(char *src, int tcount)
-{
- int res;
-
- if (src == NULL || tcount < 0)
- return SPI_ERROR_ARGUMENT;
-
- res = _SPI_begin_call(true);
- if (res < 0)
- return res;
-
- res = _SPI_execute(src, tcount, NULL);
-
- _SPI_end_call(true);
- return res;
-}
-
-int
-SPI_execp(void *plan, Datum *Values, char *Nulls, int tcount)
-{
- int res;
-
- if (plan == NULL || tcount < 0)
- return SPI_ERROR_ARGUMENT;
-
- if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL)
- return SPI_ERROR_PARAM;
-
- res = _SPI_begin_call(true);
- if (res < 0)
- return res;
-
- /* copy plan to current (executor) context */
- plan = (void *) _SPI_copy_plan(plan, _SPI_CPLAN_CURCXT);
-
- res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount);
-
- _SPI_end_call(true);
- return res;
-}
-
-void *
-SPI_prepare(char *src, int nargs, Oid *argtypes)
-{
- _SPI_plan *plan;
-
- if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL))
- {
- SPI_result = SPI_ERROR_ARGUMENT;
- return NULL;
- }
-
- SPI_result = _SPI_begin_call(true);
- if (SPI_result < 0)
- return NULL;
-
- plan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); /* Executor context */
- plan->argtypes = argtypes;
- plan->nargs = nargs;
-
- SPI_result = _SPI_execute(src, 0, plan);
-
- if (SPI_result >= 0) /* copy plan to procedure context */
- plan = _SPI_copy_plan(plan, _SPI_CPLAN_PROCXT);
- else
- plan = NULL;
-
- _SPI_end_call(true);
-
- return (void *) plan;
-
-}
-
-void *
-SPI_saveplan(void *plan)
-{
- _SPI_plan *newplan;
-
- if (plan == NULL)
- {
- SPI_result = SPI_ERROR_ARGUMENT;
- return NULL;
- }
-
- SPI_result = _SPI_begin_call(false); /* don't change context */
- if (SPI_result < 0)
- return NULL;
-
- newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT);
-
- _SPI_curid--;
- SPI_result = 0;
-
- return (void *) newplan;
-
-}
-
-int
-SPI_freeplan(void *plan)
-{
- _SPI_plan *spiplan = (_SPI_plan *) plan;
-
- if (plan == NULL)
- return SPI_ERROR_ARGUMENT;
-
- MemoryContextDelete(spiplan->plancxt);
- return 0;
-}
-
-HeapTuple
-SPI_copytuple(HeapTuple tuple)
-{
- MemoryContext oldcxt = NULL;
- HeapTuple ctuple;
-
- if (tuple == NULL)
- {
- SPI_result = SPI_ERROR_ARGUMENT;
- return NULL;
- }
-
- if (_SPI_curid + 1 == _SPI_connected) /* connected */
- {
- if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
- elog(FATAL, "SPI: stack corrupted");
- oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
- }
-
- ctuple = heap_copytuple(tuple);
-
- if (oldcxt)
- MemoryContextSwitchTo(oldcxt);
-
- return ctuple;
-}
-
-TupleDesc
-SPI_copytupledesc(TupleDesc tupdesc)
-{
- MemoryContext oldcxt = NULL;
- TupleDesc ctupdesc;
-
- if (tupdesc == NULL)
- {
- SPI_result = SPI_ERROR_ARGUMENT;
- return NULL;
- }
-
- if (_SPI_curid + 1 == _SPI_connected) /* connected */
- {
- if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
- elog(FATAL, "SPI: stack corrupted");
- oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
- }
-
- ctupdesc = CreateTupleDescCopy(tupdesc);
-
- if (oldcxt)
- MemoryContextSwitchTo(oldcxt);
-
- return ctupdesc;
-}
-
-TupleTableSlot *
-SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc)
-{
- MemoryContext oldcxt = NULL;
- TupleTableSlot *cslot;
- HeapTuple ctuple;
- TupleDesc ctupdesc;
-
- if (tuple == NULL || tupdesc == NULL)
- {
- SPI_result = SPI_ERROR_ARGUMENT;
- return NULL;
- }
-
- if (_SPI_curid + 1 == _SPI_connected) /* connected */
- {
- if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
- elog(FATAL, "SPI: stack corrupted");
- oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
- }
-
- ctuple = heap_copytuple(tuple);
- ctupdesc = CreateTupleDescCopy(tupdesc);
-
- cslot = MakeTupleTableSlot();
- ExecSetSlotDescriptor(cslot, ctupdesc, true);
- cslot = ExecStoreTuple(ctuple, cslot, InvalidBuffer, true);
-
- if (oldcxt)
- MemoryContextSwitchTo(oldcxt);
-
- return cslot;
-}
-
-HeapTuple
-SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
- Datum *Values, char *Nulls)
-{
- MemoryContext oldcxt = NULL;
- HeapTuple mtuple;
- int numberOfAttributes;
- uint8 infomask;
- Datum *v;
- char *n;
- bool isnull;
- int i;
-
- if (rel == NULL || tuple == NULL || natts <= 0 || attnum == NULL || Values == NULL)
- {
- SPI_result = SPI_ERROR_ARGUMENT;
- return NULL;
- }
-
- if (_SPI_curid + 1 == _SPI_connected) /* connected */
- {
- if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
- elog(FATAL, "SPI: stack corrupted");
- oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
- }
- SPI_result = 0;
- numberOfAttributes = rel->rd_att->natts;
- v = (Datum *) palloc(numberOfAttributes * sizeof(Datum));
- n = (char *) palloc(numberOfAttributes * sizeof(char));
-
- /* fetch old values and nulls */
- for (i = 0; i < numberOfAttributes; i++)
- {
- v[i] = heap_getattr(tuple, i + 1, rel->rd_att, &isnull);
- n[i] = (isnull) ? 'n' : ' ';
- }
-
- /* replace values and nulls */
- for (i = 0; i < natts; i++)
- {
- if (attnum[i] <= 0 || attnum[i] > numberOfAttributes)
- break;
- v[attnum[i] - 1] = Values[i];
- n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' ';
- }
-
- if (i == natts) /* no errors in *attnum */
- {
- mtuple = heap_formtuple(rel->rd_att, v, n);
- infomask = mtuple->t_data->t_infomask;
- memmove(&(mtuple->t_data->t_oid), &(tuple->t_data->t_oid),
- ((char *) &(tuple->t_data->t_hoff) -
- (char *) &(tuple->t_data->t_oid)));
- mtuple->t_data->t_infomask = infomask;
- mtuple->t_data->t_natts = numberOfAttributes;
- }
- else
- {
- mtuple = NULL;
- SPI_result = SPI_ERROR_NOATTRIBUTE;
- }
-
- pfree(v);
- pfree(n);
-
- if (oldcxt)
- MemoryContextSwitchTo(oldcxt);
-
- return mtuple;
-}
-
-int
-SPI_fnumber(TupleDesc tupdesc, char *fname)
-{
- int res;
- Form_pg_attribute sysatt;
-
- for (res = 0; res < tupdesc->natts; res++)
- {
- if (namestrcmp(&tupdesc->attrs[res]->attname, fname) == 0)
- return res + 1;
- }
-
- sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ );
- if (sysatt != NULL)
- return sysatt->attnum;
-
- /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */
- return SPI_ERROR_NOATTRIBUTE;
-}
-
-char *
-SPI_fname(TupleDesc tupdesc, int fnumber)
-{
- Form_pg_attribute att;
-
- SPI_result = 0;
-
- if (fnumber > tupdesc->natts || fnumber == 0 ||
- fnumber <= FirstLowInvalidHeapAttributeNumber)
- {
- SPI_result = SPI_ERROR_NOATTRIBUTE;
- return NULL;
- }
-
- if (fnumber > 0)
- att = tupdesc->attrs[fnumber - 1];
- else
- att = SystemAttributeDefinition(fnumber, true);
-
- return pstrdup(NameStr(att->attname));
-}
-
-char *
-SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
-{
- Datum origval,
- val,
- result;
- bool isnull;
- Oid typoid,
- foutoid,
- typelem;
- int32 typmod;
- bool typisvarlena;
-
- SPI_result = 0;
-
- if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
- fnumber <= FirstLowInvalidHeapAttributeNumber)
- {
- SPI_result = SPI_ERROR_NOATTRIBUTE;
- return NULL;
- }
-
- origval = heap_getattr(tuple, fnumber, tupdesc, &isnull);
- if (isnull)
- return NULL;
-
- if (fnumber > 0)
- {
- typoid = tupdesc->attrs[fnumber - 1]->atttypid;
- typmod = tupdesc->attrs[fnumber - 1]->atttypmod;
- }
- else
- {
- typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
- typmod = -1;
- }
-
- if (!getTypeOutputInfo(typoid, &foutoid, &typelem, &typisvarlena))
- {
- SPI_result = SPI_ERROR_NOOUTFUNC;
- return NULL;
- }
-
- /*
- * If we have a toasted datum, forcibly detoast it here to avoid
- * memory leakage inside the type's output routine.
- */
- if (typisvarlena)
- val = PointerGetDatum(PG_DETOAST_DATUM(origval));
- else
- val = origval;
-
- result = OidFunctionCall3(foutoid,
- val,
- ObjectIdGetDatum(typelem),
- Int32GetDatum(typmod));
-
- /* Clean up detoasted copy, if any */
- if (val != origval)
- pfree(DatumGetPointer(val));
-
- return DatumGetCString(result);
-}
-
-Datum
-SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
-{
- SPI_result = 0;
-
- if (fnumber > tuple->t_data->t_natts || fnumber == 0 ||
- fnumber <= FirstLowInvalidHeapAttributeNumber)
- {
- SPI_result = SPI_ERROR_NOATTRIBUTE;
- *isnull = true;
- return (Datum) NULL;
- }
-
- return heap_getattr(tuple, fnumber, tupdesc, isnull);
-}
-
-char *
-SPI_gettype(TupleDesc tupdesc, int fnumber)
-{
- Oid typoid;
- HeapTuple typeTuple;
- char *result;
-
- SPI_result = 0;
-
- if (fnumber > tupdesc->natts || fnumber == 0 ||
- fnumber <= FirstLowInvalidHeapAttributeNumber)
- {
- SPI_result = SPI_ERROR_NOATTRIBUTE;
- return NULL;
- }
-
- if (fnumber > 0)
- typoid = tupdesc->attrs[fnumber - 1]->atttypid;
- else
- typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
-
- typeTuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typoid),
- 0, 0, 0);
-
- if (!HeapTupleIsValid(typeTuple))
- {
- SPI_result = SPI_ERROR_TYPUNKNOWN;
- return NULL;
- }
-
- result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname));
- ReleaseSysCache(typeTuple);
- return result;
-}
-
-Oid
-SPI_gettypeid(TupleDesc tupdesc, int fnumber)
-{
- SPI_result = 0;
-
- if (fnumber > tupdesc->natts || fnumber == 0 ||
- fnumber <= FirstLowInvalidHeapAttributeNumber)
- {
- SPI_result = SPI_ERROR_NOATTRIBUTE;
- return InvalidOid;
- }
-
- if (fnumber > 0)
- return tupdesc->attrs[fnumber - 1]->atttypid;
- else
- return (SystemAttributeDefinition(fnumber, true))->atttypid;
-}
-
-char *
-SPI_getrelname(Relation rel)
-{
- return pstrdup(RelationGetRelationName(rel));
-}
-
-void *
-SPI_palloc(Size size)
-{
- MemoryContext oldcxt = NULL;
- void *pointer;
-
- if (_SPI_curid + 1 == _SPI_connected) /* connected */
- {
- if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
- elog(FATAL, "SPI: stack corrupted");
- oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
- }
-
- pointer = palloc(size);
-
- if (oldcxt)
- MemoryContextSwitchTo(oldcxt);
-
- return pointer;
-}
-
-void *
-SPI_repalloc(void *pointer, Size size)
-{
- /* No longer need to worry which context chunk was in... */
- return repalloc(pointer, size);
-}
-
-void
-SPI_pfree(void *pointer)
-{
- /* No longer need to worry which context chunk was in... */
- pfree(pointer);
-}
-
-void
-SPI_freetuple(HeapTuple tuple)
-{
- /* No longer need to worry which context tuple was in... */
- heap_freetuple(tuple);
-}
-
-void
-SPI_freetuptable(SPITupleTable *tuptable)
-{
- if (tuptable != NULL)
- MemoryContextDelete(tuptable->tuptabcxt);
-}
-
-
-
-/*
- * SPI_cursor_open()
- *
- * Open a prepared SPI plan as a portal
- */
-Portal
-SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls)
-{
- static int unnamed_portal_count = 0;
-
- _SPI_plan *spiplan = (_SPI_plan *) plan;
- List *qtlist = spiplan->qtlist;
- List *ptlist = spiplan->ptlist;
- Query *queryTree;
- Plan *planTree;
- QueryDesc *queryDesc;
- EState *eState;
- TupleDesc attinfo;
- MemoryContext oldcontext;
- Portal portal;
- char portalname[64];
- int k;
-
- /* Ensure that the plan contains only one regular SELECT query */
- if (length(ptlist) != 1)
- elog(ERROR, "cannot open multi-query plan as cursor");
- queryTree = (Query *) lfirst(qtlist);
- planTree = (Plan *) lfirst(ptlist);
-
- if (queryTree->commandType != CMD_SELECT)
- elog(ERROR, "plan in SPI_cursor_open() is not a SELECT");
- if (queryTree->isPortal)
- elog(ERROR, "plan in SPI_cursor_open() must NOT be a DECLARE already");
- else if (queryTree->into != NULL)
- elog(ERROR, "plan in SPI_cursor_open() must NOT be a SELECT INTO");
-
- /* Increment CommandCounter to see changes made by now */
- CommandCounterIncrement();
-
- /* Reset SPI result */
- SPI_processed = 0;
- SPI_tuptable = NULL;
- _SPI_current->processed = 0;
- _SPI_current->tuptable = NULL;
-
- if (name == NULL)
- {
- /* Make up a portal name if none given */
- for (;;)
- {
- unnamed_portal_count++;
- if (unnamed_portal_count < 0)
- unnamed_portal_count = 0;
- sprintf(portalname, "<unnamed cursor %d>", unnamed_portal_count);
- if (GetPortalByName(portalname) == NULL)
- break;
- }
-
- name = portalname;
- }
- else
- {
- /* Ensure the portal doesn't exist already */
- portal = GetPortalByName(name);
- if (portal != NULL)
- elog(ERROR, "cursor \"%s\" already in use", name);
- }
-
- /* Create the portal */
- portal = CreatePortal(name);
- if (portal == NULL)
- elog(ERROR, "failed to create portal \"%s\"", name);
-
- /* Switch to portals memory and copy the parsetree and plan to there */
- oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
- queryTree = copyObject(queryTree);
- planTree = copyObject(planTree);
-
- /* Modify the parsetree to be a cursor */
- queryTree->isPortal = true;
- queryTree->into = makeNode(RangeVar);
- queryTree->into->relname = pstrdup(name);
- queryTree->isBinary = false;
-
- /* Create the QueryDesc object and the executor state */
- queryDesc = CreateQueryDesc(queryTree, planTree, SPI, NULL);
- eState = CreateExecutorState();
-
- /* If the plan has parameters, put them into the executor state */
- if (spiplan->nargs > 0)
- {
- ParamListInfo paramLI;
-
- paramLI = (ParamListInfo) palloc((spiplan->nargs + 1) *
- sizeof(ParamListInfoData));
- MemSet(paramLI, 0, (spiplan->nargs + 1) * sizeof(ParamListInfoData));
-
- eState->es_param_list_info = paramLI;
- for (k = 0; k < spiplan->nargs; paramLI++, k++)
- {
- paramLI->kind = PARAM_NUM;
- paramLI->id = k + 1;
- paramLI->isnull = (Nulls && Nulls[k] == 'n');
- if (paramLI->isnull)
- {
- /* nulls just copy */
- paramLI->value = Values[k];
- }
- else
- {
- /* pass-by-ref values must be copied into portal context */
- int16 paramTypLen;
- bool paramTypByVal;
-
- get_typlenbyval(spiplan->argtypes[k],
- &paramTypLen, &paramTypByVal);
- paramLI->value = datumCopy(Values[k],
- paramTypByVal, paramTypLen);
- }
- }
- paramLI->kind = PARAM_INVALID;
- }
- else
- eState->es_param_list_info = NULL;
-
- /* Start the executor */
- attinfo = ExecutorStart(queryDesc, eState);
-
- /* Put all the objects into the portal */
- PortalSetQuery(portal, queryDesc, attinfo, eState, PortalCleanup);
-
- /* Switch back to the callers memory context */
- MemoryContextSwitchTo(oldcontext);
-
- /* Return the created portal */
- return portal;
-}
-
-
-/*
- * SPI_cursor_find()
- *
- * Find the portal of an existing open cursor
- */
-Portal
-SPI_cursor_find(char *name)
-{
- return GetPortalByName(name);
-}
-
-
-/*
- * SPI_cursor_fetch()
- *
- * Fetch rows in a cursor
- */
-void
-SPI_cursor_fetch(Portal portal, bool forward, int count)
-{
- _SPI_cursor_operation(portal, forward, count, SPI);
-}
-
-
-/*
- * SPI_cursor_move()
- *
- * Move in a cursor
- */
-void
-SPI_cursor_move(Portal portal, bool forward, int count)
-{
- _SPI_cursor_operation(portal, forward, count, None);
-}
-
-
-/*
- * SPI_cursor_close()
- *
- * Close a cursor
- */
-void
-SPI_cursor_close(Portal portal)
-{
- if (!PortalIsValid(portal))
- elog(ERROR, "invalid portal in SPI cursor operation");
-
- PortalDrop(portal);
-}
-
-/* =================== private functions =================== */
-
-/*
- * spi_printtup
- * store tuple retrieved by Executor into SPITupleTable
- * of current SPI procedure
- *
- */
-void
-spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
-{
- SPITupleTable *tuptable;
- MemoryContext oldcxt;
- MemoryContext tuptabcxt;
-
- /*
- * When called by Executor _SPI_curid expected to be equal to
- * _SPI_connected
- */
- if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
- elog(FATAL, "SPI: improper call to spi_printtup");
- if (_SPI_current != &(_SPI_stack[_SPI_curid]))
- elog(FATAL, "SPI: stack corrupted in spi_printtup");
-
- oldcxt = _SPI_procmem(); /* switch to procedure memory context */
-
- tuptable = _SPI_current->tuptable;
- if (tuptable == NULL)
- {
- tuptabcxt = AllocSetContextCreate(CurrentMemoryContext,
- "SPI TupTable",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- MemoryContextSwitchTo(tuptabcxt);
-
- _SPI_current->tuptable = tuptable = (SPITupleTable *)
- palloc(sizeof(SPITupleTable));
- tuptable->tuptabcxt = tuptabcxt;
- tuptable->alloced = tuptable->free = 128;
- tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
- tuptable->tupdesc = CreateTupleDescCopy(tupdesc);
- }
- else
- {
- MemoryContextSwitchTo(tuptable->tuptabcxt);
-
- if (tuptable->free == 0)
- {
- tuptable->free = 256;
- tuptable->alloced += tuptable->free;
- tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
- tuptable->alloced * sizeof(HeapTuple));
- }
- }
-
- tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
- (tuptable->free)--;
-
- MemoryContextSwitchTo(oldcxt);
- return;
-}
-
-/*
- * Static functions
- */
-
-static int
-_SPI_execute(char *src, int tcount, _SPI_plan *plan)
-{
- List *queryTree_list;
- List *planTree_list;
- List *queryTree_list_item;
- QueryDesc *qdesc;
- Query *queryTree;
- Plan *planTree;
- EState *state;
- int nargs = 0;
- Oid *argtypes = NULL;
- int res = 0;
- bool islastquery;
-
- /* Increment CommandCounter to see changes made by now */
- CommandCounterIncrement();
-
- SPI_processed = 0;
- SPI_lastoid = InvalidOid;
- SPI_tuptable = NULL;
- _SPI_current->tuptable = NULL;
- _SPI_current->qtlist = NULL;
-
- if (plan)
- {
- nargs = plan->nargs;
- argtypes = plan->argtypes;
- }
-
- queryTree_list = pg_parse_and_rewrite(src, argtypes, nargs);
-
- _SPI_current->qtlist = queryTree_list;
-
- planTree_list = NIL;
-
- foreach(queryTree_list_item, queryTree_list)
- {
- queryTree = (Query *) lfirst(queryTree_list_item);
- islastquery = (lnext(queryTree_list_item) == NIL);
-
- planTree = pg_plan_query(queryTree);
- planTree_list = lappend(planTree_list, planTree);
-
- if (queryTree->commandType == CMD_UTILITY)
- {
- if (nodeTag(queryTree->utilityStmt) == T_CopyStmt)
- {
- CopyStmt *stmt = (CopyStmt *) (queryTree->utilityStmt);
-
- if (stmt->filename == NULL)
- return SPI_ERROR_COPY;
- }
- else if (nodeTag(queryTree->utilityStmt) == T_ClosePortalStmt ||
- nodeTag(queryTree->utilityStmt) == T_FetchStmt)
- return SPI_ERROR_CURSOR;
- else if (nodeTag(queryTree->utilityStmt) == T_TransactionStmt)
- return SPI_ERROR_TRANSACTION;
- res = SPI_OK_UTILITY;
- if (plan == NULL)
- {
- ProcessUtility(queryTree->utilityStmt, None, NULL);
- if (!islastquery)
- CommandCounterIncrement();
- else
- return res;
- }
- else if (islastquery)
- break;
- }
- else if (plan == NULL)
- {
- qdesc = CreateQueryDesc(queryTree, planTree,
- islastquery ? SPI : None, NULL);
- state = CreateExecutorState();
- res = _SPI_pquery(qdesc, state, islastquery ? tcount : 0);
- if (res < 0 || islastquery)
- return res;
- CommandCounterIncrement();
- }
- else
- {
- qdesc = CreateQueryDesc(queryTree, planTree,
- islastquery ? SPI : None, NULL);
- res = _SPI_pquery(qdesc, NULL, islastquery ? tcount : 0);
- if (res < 0)
- return res;
- if (islastquery)
- break;
- }
- }
-
- if (plan)
- {
- plan->qtlist = queryTree_list;
- plan->ptlist = planTree_list;
- }
-
- return res;
-}
-
-static int
-_SPI_execute_plan(_SPI_plan *plan, Datum *Values, char *Nulls, int tcount)
-{
- List *queryTree_list = plan->qtlist;
- List *planTree_list = plan->ptlist;
- List *queryTree_list_item;
- QueryDesc *qdesc;
- Query *queryTree;
- Plan *planTree;
- EState *state;
- int nargs = plan->nargs;
- int res = 0;
- bool islastquery;
- int k;
-
- /* Increment CommandCounter to see changes made by now */
- CommandCounterIncrement();
-
- SPI_processed = 0;
- SPI_lastoid = InvalidOid;
- SPI_tuptable = NULL;
- _SPI_current->tuptable = NULL;
- _SPI_current->qtlist = NULL;
-
- foreach(queryTree_list_item, queryTree_list)
- {
- queryTree = (Query *) lfirst(queryTree_list_item);
- planTree = lfirst(planTree_list);
- planTree_list = lnext(planTree_list);
- islastquery = (planTree_list == NIL); /* assume lists are same
- * len */
-
- if (queryTree->commandType == CMD_UTILITY)
- {
- ProcessUtility(queryTree->utilityStmt, None, NULL);
- if (!islastquery)
- CommandCounterIncrement();
- else
- return SPI_OK_UTILITY;
- }
- else
- {
- qdesc = CreateQueryDesc(queryTree, planTree,
- islastquery ? SPI : None, NULL);
- state = CreateExecutorState();
- if (nargs > 0)
- {
- ParamListInfo paramLI;
-
- paramLI = (ParamListInfo) palloc((nargs + 1) *
- sizeof(ParamListInfoData));
- MemSet(paramLI, 0, (nargs + 1) * sizeof(ParamListInfoData));
-
- state->es_param_list_info = paramLI;
- for (k = 0; k < plan->nargs; paramLI++, k++)
- {
- paramLI->kind = PARAM_NUM;
- paramLI->id = k + 1;
- paramLI->isnull = (Nulls && Nulls[k] == 'n');
- paramLI->value = Values[k];
- }
- paramLI->kind = PARAM_INVALID;
- }
- else
- state->es_param_list_info = NULL;
- res = _SPI_pquery(qdesc, state, islastquery ? tcount : 0);
- if (res < 0 || islastquery)
- return res;
- CommandCounterIncrement();
- }
- }
-
- return res;
-}
-
-static int
-_SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
-{
- Query *parseTree = queryDesc->parsetree;
- int operation = queryDesc->operation;
- CommandDest dest = queryDesc->dest;
- bool isRetrieveIntoPortal = false;
- bool isRetrieveIntoRelation = false;
- char *intoName = NULL;
- int res;
- Oid save_lastoid;
-
- switch (operation)
- {
- case CMD_SELECT:
- res = SPI_OK_SELECT;
- if (parseTree->isPortal)
- {
- isRetrieveIntoPortal = true;
- intoName = parseTree->into->relname;
- parseTree->isBinary = false; /* */
-
- return SPI_ERROR_CURSOR;
-
- }
- else if (parseTree->into != NULL) /* select into table */
- {
- res = SPI_OK_SELINTO;
- isRetrieveIntoRelation = true;
- queryDesc->dest = None; /* */
- }
- break;
- case CMD_INSERT:
- res = SPI_OK_INSERT;
- break;
- case CMD_DELETE:
- res = SPI_OK_DELETE;
- break;
- case CMD_UPDATE:
- res = SPI_OK_UPDATE;
- break;
- default:
- return SPI_ERROR_OPUNKNOWN;
- }
-
- if (state == NULL) /* plan preparation */
- return res;
-
-#ifdef SPI_EXECUTOR_STATS
- if (ShowExecutorStats)
- ResetUsage();
-#endif
-
- ExecutorStart(queryDesc, state);
-
- /*
- * Don't work currently --- need to rearrange callers so that we
- * prepare the portal before doing CreateExecutorState() etc. See
- * pquery.c for the correct order of operations.
- */
- if (isRetrieveIntoPortal)
- elog(FATAL, "SPI_select: retrieve into portal not implemented");
-
- ExecutorRun(queryDesc, state, ForwardScanDirection, (long) tcount);
-
- _SPI_current->processed = state->es_processed;
- save_lastoid = state->es_lastoid;
-
- if (operation == CMD_SELECT && queryDesc->dest == SPI)
- {
- if (_SPI_checktuples())
- elog(FATAL, "SPI_select: # of processed tuples check failed");
- }
-
- ExecutorEnd(queryDesc, state);
-
-#ifdef SPI_EXECUTOR_STATS
- if (ShowExecutorStats)
- ShowUsage("SPI EXECUTOR STATS");
-#endif
-
- if (dest == SPI)
- {
- SPI_processed = _SPI_current->processed;
- SPI_lastoid = save_lastoid;
- SPI_tuptable = _SPI_current->tuptable;
- }
- queryDesc->dest = dest;
-
- return res;
-
-}
-
-/*
- * _SPI_cursor_operation()
- *
- * Do a FETCH or MOVE in a cursor
- */
-static void
-_SPI_cursor_operation(Portal portal, bool forward, int count,
- CommandDest dest)
-{
- QueryDesc *querydesc;
- EState *estate;
- MemoryContext oldcontext;
- ScanDirection direction;
- CommandDest olddest;
-
- /* Check that the portal is valid */
- if (!PortalIsValid(portal))
- elog(ERROR, "invalid portal in SPI cursor operation");
-
- /* Push the SPI stack */
- _SPI_begin_call(true);
-
- /* Reset the SPI result */
- SPI_processed = 0;
- SPI_tuptable = NULL;
- _SPI_current->processed = 0;
- _SPI_current->tuptable = NULL;
-
- /* Switch to the portals memory context */
- oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
-
- querydesc = PortalGetQueryDesc(portal);
- estate = PortalGetState(portal);
-
- /* Save the queries command destination and set it to SPI (for fetch) */
- /* or None (for move) */
- olddest = querydesc->dest;
- querydesc->dest = dest;
-
- /* Run the executor like PerformPortalFetch and remember states */
- if (forward)
- {
- if (portal->atEnd)
- direction = NoMovementScanDirection;
- else
- direction = ForwardScanDirection;
-
- ExecutorRun(querydesc, estate, direction, (long) count);
-
- if (estate->es_processed > 0)
- portal->atStart = false; /* OK to back up now */
- if (count <= 0 || (int) estate->es_processed < count)
- portal->atEnd = true; /* we retrieved 'em all */
- }
- else
- {
- if (portal->atStart)
- direction = NoMovementScanDirection;
- else
- direction = BackwardScanDirection;
-
- ExecutorRun(querydesc, estate, direction, (long) count);
-
- if (estate->es_processed > 0)
- portal->atEnd = false; /* OK to go forward now */
- if (count <= 0 || (int) estate->es_processed < count)
- portal->atStart = true; /* we retrieved 'em all */
- }
-
- _SPI_current->processed = estate->es_processed;
-
- /* Restore the old command destination and switch back to callers */
- /* memory context */
- querydesc->dest = olddest;
- MemoryContextSwitchTo(oldcontext);
-
- if (dest == SPI && _SPI_checktuples())
- elog(FATAL, "SPI_fetch: # of processed tuples check failed");
-
- /* Put the result into place for access by caller */
- SPI_processed = _SPI_current->processed;
- SPI_tuptable = _SPI_current->tuptable;
-
- /* Pop the SPI stack */
- _SPI_end_call(true);
-}
-
-
-static MemoryContext
-_SPI_execmem()
-{
- return MemoryContextSwitchTo(_SPI_current->execCxt);
-}
-
-static MemoryContext
-_SPI_procmem()
-{
- return MemoryContextSwitchTo(_SPI_current->procCxt);
-}
-
-/*
- * _SPI_begin_call
- *
- */
-static int
-_SPI_begin_call(bool execmem)
-{
- if (_SPI_curid + 1 != _SPI_connected)
- return SPI_ERROR_UNCONNECTED;
- _SPI_curid++;
- if (_SPI_current != &(_SPI_stack[_SPI_curid]))
- elog(FATAL, "SPI: stack corrupted");
-
- if (execmem) /* switch to the Executor memory context */
- _SPI_execmem();
-
- return 0;
-}
-
-static int
-_SPI_end_call(bool procmem)
-{
- /*
- * We' returning to procedure where _SPI_curid == _SPI_connected - 1
- */
- _SPI_curid--;
-
- _SPI_current->qtlist = NULL;
-
- if (procmem) /* switch to the procedure memory context */
- {
- _SPI_procmem();
- /* and free Executor memory */
- MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
- }
-
- return 0;
-}
-
-static bool
-_SPI_checktuples(void)
-{
- uint32 processed = _SPI_current->processed;
- SPITupleTable *tuptable = _SPI_current->tuptable;
- bool failed = false;
-
- if (processed == 0)
- {
- if (tuptable != NULL)
- failed = true;
- }
- else
- {
- /* some tuples were processed */
- if (tuptable == NULL) /* spi_printtup was not called */
- failed = true;
- else if (processed != (tuptable->alloced - tuptable->free))
- failed = true;
- }
-
- return failed;
-}
-
-static _SPI_plan *
-_SPI_copy_plan(_SPI_plan *plan, int location)
-{
- _SPI_plan *newplan;
- MemoryContext oldcxt;
- MemoryContext plancxt;
- MemoryContext parentcxt;
-
- /* Determine correct parent for the plan's memory context */
- if (location == _SPI_CPLAN_PROCXT)
- parentcxt = _SPI_current->procCxt;
- else if (location == _SPI_CPLAN_TOPCXT)
- parentcxt = TopMemoryContext;
- else
- parentcxt = CurrentMemoryContext;
-
- /*
- * Create a memory context for the plan. We don't expect the plan to
- * be very large, so use smaller-than-default alloc parameters.
- */
- plancxt = AllocSetContextCreate(parentcxt,
- "SPI Plan",
- 1024,
- 1024,
- ALLOCSET_DEFAULT_MAXSIZE);
- oldcxt = MemoryContextSwitchTo(plancxt);
-
- /* Copy the SPI plan into its own context */
- newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
- newplan->plancxt = plancxt;
- newplan->qtlist = (List *) copyObject(plan->qtlist);
- newplan->ptlist = (List *) copyObject(plan->ptlist);
- newplan->nargs = plan->nargs;
- if (plan->nargs > 0)
- {
- newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
- memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
- }
- else
- newplan->argtypes = NULL;
-
- MemoryContextSwitchTo(oldcxt);
-
- return newplan;
-}