mirror of https://github.com/postgres/postgres
Remove _deadcode.
This commit is contained in:
parent
739adf32ee
commit
43515ba3f8
File diff suppressed because it is too large
Load Diff
|
@ -1,346 +0,0 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* version.c
|
||||
* This file contains all the rules that govern all version semantics.
|
||||
*
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* The version stuff has not been tested under postgres95 and probably
|
||||
* doesn't work! - jolly 8/19/95
|
||||
*
|
||||
*
|
||||
* $Id: version.c,v 1.30 2002/06/20 20:29:27 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* At the point the version is defined, 2 physical relations are created
|
||||
* <vname>_added and <vname>_deleted.
|
||||
*
|
||||
* In addition, 4 rules are defined which govern the semantics of
|
||||
* versions w.r.t retrieves, appends, replaces and deletes.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
|
||||
#define MAX_QUERY_LEN 1024
|
||||
|
||||
char rule_buf[MAX_QUERY_LEN];
|
||||
|
||||
/*
|
||||
* problem: the version system assumes that the rules it declares will
|
||||
* be fired in the order of declaration, it also assumes
|
||||
* goh's silly instead semantics. Unfortunately, it is a pain
|
||||
* to make the version system work with the new semantics.
|
||||
* However the whole problem can be solved, and some nice
|
||||
* functionality can be achieved if we get multiple action rules
|
||||
* to work. So thats what I did -- glass
|
||||
*
|
||||
* Well, at least they've been working for about 20 minutes.
|
||||
*
|
||||
* So any comments in this code about 1 rule per transction are false...:)
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is needed because the rule system only allows
|
||||
* *1* rule to be defined per transaction.
|
||||
*
|
||||
* NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||
* OOOOOOOOOOOOOOOOOOO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
*
|
||||
* DONT DO THAT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
*
|
||||
* If you commit the current Xact all the palloced memory GOES AWAY
|
||||
* and could be re-palloced in the new Xact and the whole hell breaks
|
||||
* loose and poor people like me spend 2 hours of their live chassing
|
||||
* a strange memory bug instead of watching the "Get Smart" marathon
|
||||
* in NICK !
|
||||
* DO NOT COMMIT THE XACT, just increase the Cid counter!
|
||||
* _sp.
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
static void
|
||||
eval_as_new_xact(char *query)
|
||||
{
|
||||
|
||||
/*------
|
||||
* WARNING! do not uncomment the following lines WARNING!
|
||||
*
|
||||
* CommitTransactionCommand();
|
||||
* StartTransactionCommand();
|
||||
*------
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
pg_exec_query(query);
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Define a version.
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
void
|
||||
DefineVersion(char *name, char *fromRelname, char *date)
|
||||
{
|
||||
char *bname;
|
||||
static char saved_basename[512];
|
||||
static char saved_snapshot[512];
|
||||
|
||||
if (date == NULL)
|
||||
{
|
||||
/* no time ranges */
|
||||
bname = fromRelname;
|
||||
strcpy(saved_basename, (char *) bname);
|
||||
*saved_snapshot = (char) NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* version is a snapshot */
|
||||
bname = fromRelname;
|
||||
strcpy(saved_basename, (char *) bname);
|
||||
sprintf(saved_snapshot, "['%s']", date);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Calls the routine ``GetAttrList'' get the list of attributes from
|
||||
* the base relation. Code is put here so that we only need to look up
|
||||
* the attribute once for both appends and replaces.
|
||||
*/
|
||||
setAttrList(bname);
|
||||
|
||||
VersionCreate(name, saved_basename);
|
||||
VersionAppend(name, saved_basename);
|
||||
VersionDelete(name, saved_basename, saved_snapshot);
|
||||
VersionReplace(name, saved_basename, saved_snapshot);
|
||||
VersionRetrieve(name, saved_basename, saved_snapshot);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Creates the deltas.
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
void
|
||||
VersionCreate(char *vname, char *bname)
|
||||
{
|
||||
static char query_buf[MAX_QUERY_LEN];
|
||||
|
||||
/*
|
||||
* Creating the dummy version relation for triggering rules.
|
||||
*/
|
||||
sprintf(query_buf, "SELECT * INTO TABLE %s from %s where 1 =2",
|
||||
vname, bname);
|
||||
|
||||
pg_exec_query(query_buf);
|
||||
|
||||
/*
|
||||
* Creating the ``v_added'' relation
|
||||
*/
|
||||
sprintf(query_buf, "SELECT * INTO TABLE %s_added from %s where 1 = 2",
|
||||
vname, bname);
|
||||
eval_as_new_xact(query_buf);
|
||||
|
||||
/*
|
||||
* Creating the ``v_deleted'' relation.
|
||||
*/
|
||||
sprintf(query_buf, "CREATE TABLE %s_del (DOID oid)", vname);
|
||||
eval_as_new_xact(query_buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Given the relation name, does a catalog lookup for that relation and
|
||||
* sets the global variable 'attr_list' with the list of attributes (names)
|
||||
* for that relation.
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
static void
|
||||
setAttrList(char *bname)
|
||||
{
|
||||
Relation rel;
|
||||
int i = 0;
|
||||
int maxattrs = 0;
|
||||
char *attrname;
|
||||
char temp_buf[512];
|
||||
int notfirst = 0;
|
||||
|
||||
rel = heap_openr(bname);
|
||||
if (rel == NULL)
|
||||
{
|
||||
elog(ERROR, "Unable to expand all -- amopenr failed ");
|
||||
return;
|
||||
}
|
||||
maxattrs = RelationGetNumberOfAttributes(rel);
|
||||
|
||||
attr_list[0] = '\0';
|
||||
|
||||
for (i = maxattrs - 1; i > -1; --i)
|
||||
{
|
||||
attrname = NameStr(rel->rd_att->attrs[i]->attname);
|
||||
|
||||
if (notfirst == 1)
|
||||
sprintf(temp_buf, ", %s = new.%s", attrname, attrname);
|
||||
else
|
||||
{
|
||||
sprintf(temp_buf, "%s = new.%s", attrname, attrname);
|
||||
notfirst = 1;
|
||||
}
|
||||
strcat(attr_list, temp_buf);
|
||||
}
|
||||
|
||||
heap_close(rel);
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This routine defines the rule governing the append semantics of
|
||||
* versions. All tuples appended to a version gets appended to the
|
||||
* <vname>_added relation.
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
static void
|
||||
VersionAppend(char *vname, char *bname)
|
||||
{
|
||||
sprintf(rule_buf,
|
||||
"define rewrite rule %s_append is on INSERT to %s do instead append %s_added(%s)",
|
||||
vname, vname, vname, attr_list);
|
||||
|
||||
eval_as_new_xact(rule_buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This routine defines the rule governing the retrieval semantics of
|
||||
* versions. To retrieve tuples from a version , we need to:
|
||||
*
|
||||
* 1. Retrieve all tuples in the <vname>_added relation.
|
||||
* 2. Retrieve all tuples in the base relation which are not in
|
||||
* the <vname>_del relation.
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
void
|
||||
VersionRetrieve(char *vname, char *bname, char *snapshot)
|
||||
{
|
||||
|
||||
sprintf(rule_buf,
|
||||
"define rewrite rule %s_retrieve is on SELECT to %s do instead\n\
|
||||
SELECT %s_1.oid, %s_1.* from _%s in %s%s, %s_1 in (%s_added | _%s) \
|
||||
where _%s.oid !!= '%s_del.DOID'",
|
||||
vname, vname, vname, vname, bname,
|
||||
bname, snapshot,
|
||||
vname, vname, bname, bname, vname);
|
||||
|
||||
eval_as_new_xact(rule_buf);
|
||||
|
||||
/* printf("%s\n",rule_buf); */
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This routine defines the rules that govern the delete semantics of
|
||||
* versions. Two things happens when we delete a tuple from a version:
|
||||
*
|
||||
* 1. If the tuple to be deleted was added to the version *after*
|
||||
* the version was created, then we simply delete the tuple
|
||||
* from the <vname>_added relation.
|
||||
* 2. If the tuple to be deleted is actually in the base relation,
|
||||
* then we have to mark that tuple as being deleted by adding
|
||||
* it to the <vname>_del relation.
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
void
|
||||
VersionDelete(char *vname, char *bname, char *snapshot)
|
||||
{
|
||||
|
||||
sprintf(rule_buf,
|
||||
"define rewrite rule %s_delete1 is on delete to %s do instead\n \
|
||||
[delete %s_added where current.oid = %s_added.oid\n \
|
||||
append %s_del(DOID = current.oid) from _%s in %s%s \
|
||||
where current.oid = _%s.oid] \n",
|
||||
vname, vname, vname, vname, vname,
|
||||
bname, bname, snapshot, bname);
|
||||
|
||||
eval_as_new_xact(rule_buf);
|
||||
#ifdef OLD_REWRITE
|
||||
sprintf(rule_buf,
|
||||
"define rewrite rule %s_delete2 is on delete to %s do instead \n \
|
||||
append %s_del(DOID = current.oid) from _%s in %s%s \
|
||||
where current.oid = _%s.oid \n",
|
||||
vname, vname, vname, bname, bname, snapshot, bname);
|
||||
|
||||
eval_as_new_xact(rule_buf);
|
||||
#endif /* OLD_REWRITE */
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This routine defines the rules that govern the update semantics
|
||||
* of versions. To update a tuple in a version:
|
||||
*
|
||||
* 1. If the tuple is in <vname>_added, we simply ``replace''
|
||||
* the tuple (as per postgres style).
|
||||
* 2. if the tuple is in the base relation, then two things have to
|
||||
* happen:
|
||||
* 2.1 The tuple is marked ``deleted'' from the base relation by
|
||||
* adding the tuple to the <vname>_del relation.
|
||||
* 2.2 A copy of the tuple is appended to the <vname>_added relation
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
void
|
||||
VersionReplace(char *vname, char *bname, char *snapshot)
|
||||
{
|
||||
sprintf(rule_buf,
|
||||
"define rewrite rule %s_replace1 is on replace to %s do instead \n\
|
||||
[replace %s_added(%s) where current.oid = %s_added.oid \n\
|
||||
append %s_del(DOID = current.oid) from _%s in %s%s \
|
||||
where current.oid = _%s.oid\n\
|
||||
append %s_added(%s) from _%s in %s%s \
|
||||
where current.oid !!= '%s_added.oid' and current.oid = _%s.oid]\n",
|
||||
vname, vname, vname, attr_list, vname,
|
||||
vname, bname, bname, snapshot, bname,
|
||||
vname, attr_list, bname, bname, snapshot, vname, bname);
|
||||
|
||||
eval_as_new_xact(rule_buf);
|
||||
|
||||
/* printf("%s\n",rule_buf); */
|
||||
#ifdef OLD_REWRITE
|
||||
sprintf(rule_buf,
|
||||
"define rewrite rule %s_replace2 is on replace to %s do \n\
|
||||
append %s_del(DOID = current.oid) from _%s in %s%s \
|
||||
where current.oid = _%s.oid\n",
|
||||
vname, vname, vname, bname, bname, snapshot, bname);
|
||||
|
||||
eval_as_new_xact(rule_buf);
|
||||
|
||||
sprintf(rule_buf,
|
||||
"define rewrite rule %s_replace3 is on replace to %s do instead\n\
|
||||
append %s_added(%s) from _%s in %s%s \
|
||||
where current.oid !!= '%s_added.oid' and current.oid = \
|
||||
_%s.oid\n",
|
||||
vname, vname, vname, attr_list, bname, bname, snapshot, vname, bname);
|
||||
|
||||
eval_as_new_xact(rule_buf);
|
||||
#endif /* OLD_REWRITE */
|
||||
/* printf("%s\n",rule_buf); */
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,810 +0,0 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* predmig.c
|
||||
*
|
||||
*
|
||||
* 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/optimizer/path/_deadcode/Attic/predmig.c,v 1.15 2002/06/20 20:29:30 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
** DESCRIPTION
|
||||
** Main Routines to handle Predicate Migration (i.e. correct optimization
|
||||
** of queries with expensive functions.)
|
||||
**
|
||||
** The reasoning behind some of these algorithms is rather detailed.
|
||||
** Have a look at Sequoia Tech Report 92/13 for more info. Also
|
||||
** see Monma and Sidney's paper "Sequencing with Series-Parallel
|
||||
** Precedence Constraints", in "Mathematics of Operations Research",
|
||||
** volume 4 (1979), pp. 215-224.
|
||||
**
|
||||
** The main thing that this code does that wasn't handled in xfunc.c is
|
||||
** it considers the possibility that two joins in a stream may not
|
||||
** be ordered by ascending rank -- in such a scenario, it may be optimal
|
||||
** to pullup more restrictions than we did via xfunc_try_pullup.
|
||||
**
|
||||
** This code in some sense generalizes xfunc_try_pullup; if you
|
||||
** run postgres -x noprune, you'll turn off xfunc_try_pullup, and this
|
||||
** code will do everything that xfunc_try_pullup would have, and maybe
|
||||
** more. However, this results in no pruning, which may slow down the
|
||||
** optimizer and/or cause the system to run out of memory.
|
||||
** -- JMH, 11/13/92
|
||||
*/
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/nodes.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/keys.h"
|
||||
#include "optimizer/tlist.h"
|
||||
|
||||
#define is_clause(node) (get_cinfo(node)) /* a stream node
|
||||
* represents a clause
|
||||
* (not a join) iff it has
|
||||
* a non-NULL cinfo field */
|
||||
|
||||
static void xfunc_predmig(JoinPath pathnode, Stream streamroot,
|
||||
Stream laststream, bool *progressp);
|
||||
static bool xfunc_series_llel(Stream stream);
|
||||
static bool xfunc_llel_chains(Stream root, Stream bottom);
|
||||
static Stream xfunc_complete_stream(Stream stream);
|
||||
static bool xfunc_prdmig_pullup(Stream origstream, Stream pullme,
|
||||
JoinPath joinpath);
|
||||
static void xfunc_form_groups(Stream root, Stream bottom);
|
||||
static void xfunc_free_stream(Stream root);
|
||||
static Stream xfunc_add_clauses(Stream current);
|
||||
static void xfunc_setup_group(Stream node, Stream bottom);
|
||||
static Stream xfunc_streaminsert(RestrictInfo restrictinfo, Stream current,
|
||||
int clausetype);
|
||||
static int xfunc_num_relids(Stream node);
|
||||
static StreamPtr xfunc_get_downjoin(Stream node);
|
||||
static StreamPtr xfunc_get_upjoin(Stream node);
|
||||
static Stream xfunc_stream_qsort(Stream root, Stream bottom);
|
||||
static int xfunc_stream_compare(void *arg1, void *arg2);
|
||||
static bool xfunc_check_stream(Stream node);
|
||||
static bool xfunc_in_stream(Stream node, Stream stream);
|
||||
|
||||
/* ----------------- MAIN FUNCTIONS ------------------------ */
|
||||
/*
|
||||
** xfunc_do_predmig
|
||||
** wrapper for Predicate Migration. It calls xfunc_predmig until no
|
||||
** more progress is made.
|
||||
** return value says if any changes were ever made.
|
||||
*/
|
||||
bool
|
||||
xfunc_do_predmig(Path root)
|
||||
{
|
||||
bool progress,
|
||||
changed = false;
|
||||
|
||||
if (is_join(root))
|
||||
do
|
||||
{
|
||||
progress = false;
|
||||
Assert(IsA(root, JoinPath));
|
||||
xfunc_predmig((JoinPath) root, (Stream) NULL, (Stream) NULL,
|
||||
&progress);
|
||||
if (changed && progress)
|
||||
elog(DEBUG, "Needed to do a second round of predmig!\n");
|
||||
if (progress)
|
||||
changed = true;
|
||||
} while (progress);
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** xfunc_predmig
|
||||
** The main routine for Predicate Migration. It traverses a join tree,
|
||||
** and for each root-to-leaf path in the plan tree it constructs a
|
||||
** "Stream", which it passes to xfunc_series_llel for optimization.
|
||||
** Destructively modifies the join tree (via predicate pullup).
|
||||
*/
|
||||
static void
|
||||
xfunc_predmig(JoinPath pathnode, /* root of the join tree */
|
||||
Stream streamroot,
|
||||
Stream laststream,/* for recursive calls -- these are the
|
||||
* root of the stream under construction,
|
||||
* and the lowest node created so far */
|
||||
bool *progressp)
|
||||
{
|
||||
Stream newstream;
|
||||
|
||||
/*
|
||||
* * traverse the join tree dfs-style, constructing a stream as you
|
||||
* go. * When you hit a scan node, pass the stream off to
|
||||
* xfunc_series_llel.
|
||||
*/
|
||||
|
||||
/* sanity check */
|
||||
if ((!streamroot && laststream) ||
|
||||
(streamroot && !laststream))
|
||||
elog(ERROR, "called xfunc_predmig with bad inputs");
|
||||
if (streamroot)
|
||||
Assert(xfunc_check_stream(streamroot));
|
||||
|
||||
/* add path node to stream */
|
||||
newstream = RMakeStream();
|
||||
if (!streamroot)
|
||||
streamroot = newstream;
|
||||
set_upstream(newstream, (StreamPtr) laststream);
|
||||
if (laststream)
|
||||
set_downstream(laststream, (StreamPtr) newstream);
|
||||
set_downstream(newstream, (StreamPtr) NULL);
|
||||
set_pathptr(newstream, (pathPtr) pathnode);
|
||||
set_cinfo(newstream, (RestrictInfo) NULL);
|
||||
set_clausetype(newstream, XFUNC_UNKNOWN);
|
||||
|
||||
/* base case: we're at a leaf, call xfunc_series_llel */
|
||||
if (!is_join(pathnode))
|
||||
{
|
||||
/* form a fleshed-out copy of the stream */
|
||||
Stream fullstream = xfunc_complete_stream(streamroot);
|
||||
|
||||
/* sort it via series-llel */
|
||||
if (xfunc_series_llel(fullstream))
|
||||
*progressp = true;
|
||||
|
||||
/* free up the copy */
|
||||
xfunc_free_stream(fullstream);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* visit left child */
|
||||
xfunc_predmig((JoinPath) get_outerjoinpath(pathnode),
|
||||
streamroot, newstream, progressp);
|
||||
|
||||
/* visit right child */
|
||||
xfunc_predmig((JoinPath) get_innerjoinpath(pathnode),
|
||||
streamroot, newstream, progressp);
|
||||
}
|
||||
|
||||
/* remove this node */
|
||||
if (get_upstream(newstream))
|
||||
set_downstream((Stream) get_upstream(newstream), (StreamPtr) NULL);
|
||||
pfree(newstream);
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_series_llel
|
||||
** A flavor of Monma and Sidney's Series-Parallel algorithm.
|
||||
** Traverse stream downwards. When you find a node with restrictions on it,
|
||||
** call xfunc_llel_chains on the substream from root to that node.
|
||||
*/
|
||||
static bool
|
||||
xfunc_series_llel(Stream stream)
|
||||
{
|
||||
Stream temp,
|
||||
next;
|
||||
bool progress = false;
|
||||
|
||||
for (temp = stream; temp != (Stream) NULL; temp = next)
|
||||
{
|
||||
next = (Stream) xfunc_get_downjoin(temp);
|
||||
|
||||
/*
|
||||
* * if there are restrictions/secondary join clauses above this *
|
||||
* node, call xfunc_llel_chains
|
||||
*/
|
||||
if (get_upstream(temp) && is_clause((Stream) get_upstream(temp)))
|
||||
if (xfunc_llel_chains(stream, temp))
|
||||
progress = true;
|
||||
}
|
||||
return progress;
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_llel_chains
|
||||
** A flavor of Monma and Sidney's Parallel Chains algorithm.
|
||||
** Given a stream which has been well-ordered except for its lowermost
|
||||
** restrictions/2-ary joins, pull up the restrictions/2-arys as appropriate.
|
||||
** What that means here is to form groups in the chain above the lowest
|
||||
** join node above bottom inclusive, and then take all the restrictions
|
||||
** following bottom, and try to pull them up as far as possible.
|
||||
*/
|
||||
static bool
|
||||
xfunc_llel_chains(Stream root, Stream bottom)
|
||||
{
|
||||
bool progress = false;
|
||||
Stream origstream;
|
||||
Stream tmpstream,
|
||||
pathstream;
|
||||
Stream rootcopy = root;
|
||||
|
||||
Assert(xfunc_check_stream(root));
|
||||
|
||||
/* xfunc_prdmig_pullup will need an unmodified copy of the stream */
|
||||
origstream = (Stream) copyObject((Node) root);
|
||||
|
||||
/* form groups among ill-ordered nodes */
|
||||
xfunc_form_groups(root, bottom);
|
||||
|
||||
/* sort chain by rank */
|
||||
Assert(xfunc_in_stream(bottom, root));
|
||||
rootcopy = xfunc_stream_qsort(root, bottom);
|
||||
|
||||
/*
|
||||
* * traverse sorted stream -- if any restriction has moved above a
|
||||
* join, * we must pull it up in the plan. That is, make plan tree *
|
||||
* reflect order of sorted stream.
|
||||
*/
|
||||
for (tmpstream = rootcopy,
|
||||
pathstream = (Stream) xfunc_get_downjoin(rootcopy);
|
||||
tmpstream != (Stream) NULL && pathstream != (Stream) NULL;
|
||||
tmpstream = (Stream) get_downstream(tmpstream))
|
||||
{
|
||||
if (is_clause(tmpstream)
|
||||
&& get_pathptr(pathstream) != get_pathptr(tmpstream))
|
||||
{
|
||||
/*
|
||||
* * If restriction moved above a Join after sort, we pull it *
|
||||
* up in the join plan. * If restriction moved down, we
|
||||
* ignore it. * This is because Joey's Sequoia paper proves
|
||||
* that * restrictions should never move down. If this * one
|
||||
* were moved down, it would violate "semantic correctness", *
|
||||
* i.e. it would be lower than the attributes it references.
|
||||
*/
|
||||
Assert(xfunc_num_relids(pathstream) > xfunc_num_relids(tmpstream));
|
||||
progress = xfunc_prdmig_pullup(origstream, tmpstream,
|
||||
(JoinPath) get_pathptr(pathstream));
|
||||
}
|
||||
if (get_downstream(tmpstream))
|
||||
pathstream = (Stream) xfunc_get_downjoin((Stream) get_downstream(tmpstream));
|
||||
}
|
||||
|
||||
/* free up origstream */
|
||||
xfunc_free_stream(origstream);
|
||||
return progress;
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_complete_stream
|
||||
** Given a stream composed of join nodes only, make a copy containing the
|
||||
** join nodes along with the associated restriction nodes.
|
||||
*/
|
||||
static Stream
|
||||
xfunc_complete_stream(Stream stream)
|
||||
{
|
||||
Stream tmpstream,
|
||||
copystream,
|
||||
curstream = (Stream) NULL;
|
||||
|
||||
copystream = (Stream) copyObject((Node) stream);
|
||||
Assert(xfunc_check_stream(copystream));
|
||||
|
||||
curstream = copystream;
|
||||
Assert(!is_clause(curstream));
|
||||
|
||||
/* curstream = (Stream)xfunc_get_downjoin(curstream); */
|
||||
|
||||
while (curstream != (Stream) NULL)
|
||||
{
|
||||
xfunc_add_clauses(curstream);
|
||||
curstream = (Stream) xfunc_get_downjoin(curstream);
|
||||
}
|
||||
|
||||
/* find top of stream and return it */
|
||||
for (tmpstream = copystream; get_upstream(tmpstream) != (StreamPtr) NULL;
|
||||
tmpstream = (Stream) get_upstream(tmpstream))
|
||||
/* no body in for loop */ ;
|
||||
|
||||
return tmpstream;
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_prdmig_pullup
|
||||
** pullup a clause in a path above joinpath. Since the JoinPath tree
|
||||
** doesn't have upward pointers, it's difficult to deal with. Thus we
|
||||
** require the original stream, which maintains pointers to all the path
|
||||
** nodes. We use the original stream to find out what joins are
|
||||
** above the clause.
|
||||
*/
|
||||
static bool
|
||||
xfunc_prdmig_pullup(Stream origstream, Stream pullme, JoinPath joinpath)
|
||||
{
|
||||
RestrictInfo restrictinfo = get_cinfo(pullme);
|
||||
bool progress = false;
|
||||
Stream upjoin,
|
||||
orignode,
|
||||
temp;
|
||||
int whichchild;
|
||||
|
||||
/* find node in origstream that contains clause */
|
||||
for (orignode = origstream;
|
||||
orignode != (Stream) NULL
|
||||
&& get_cinfo(orignode) != restrictinfo;
|
||||
orignode = (Stream) get_downstream(orignode))
|
||||
/* empty body in for loop */ ;
|
||||
if (!orignode)
|
||||
elog(ERROR, "Didn't find matching node in original stream");
|
||||
|
||||
|
||||
/* pull up this node as far as it should go */
|
||||
for (upjoin = (Stream) xfunc_get_upjoin(orignode);
|
||||
upjoin != (Stream) NULL
|
||||
&& (JoinPath) get_pathptr((Stream) xfunc_get_downjoin(upjoin))
|
||||
!= joinpath;
|
||||
upjoin = (Stream) xfunc_get_upjoin(upjoin))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
elog(DEBUG, "pulling up in xfunc_predmig_pullup!");
|
||||
#endif
|
||||
/* move clause up in path */
|
||||
if (get_pathptr((Stream) get_downstream(upjoin))
|
||||
== (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(upjoin)))
|
||||
whichchild = OUTER;
|
||||
else
|
||||
whichchild = INNER;
|
||||
restrictinfo = xfunc_pullup((Path) get_pathptr((Stream) get_downstream(upjoin)),
|
||||
(JoinPath) get_pathptr(upjoin),
|
||||
restrictinfo,
|
||||
whichchild,
|
||||
get_clausetype(orignode));
|
||||
set_pathptr(pullme, get_pathptr(upjoin));
|
||||
/* pullme has been moved into locrestrictinfo */
|
||||
set_clausetype(pullme, XFUNC_LOCPRD);
|
||||
|
||||
/*
|
||||
* * xfunc_pullup makes new path nodes for children of *
|
||||
* get_pathptr(current). We must modify the stream nodes to point *
|
||||
* to these path nodes
|
||||
*/
|
||||
if (whichchild == OUTER)
|
||||
{
|
||||
for (temp = (Stream) get_downstream(upjoin); is_clause(temp);
|
||||
temp = (Stream) get_downstream(temp))
|
||||
set_pathptr
|
||||
(temp, (pathPtr)
|
||||
get_outerjoinpath((JoinPath) get_pathptr(upjoin)));
|
||||
set_pathptr
|
||||
(temp,
|
||||
(pathPtr) get_outerjoinpath((JoinPath) get_pathptr(upjoin)));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (temp = (Stream) get_downstream(upjoin); is_clause(temp);
|
||||
temp = (Stream) get_downstream(temp))
|
||||
set_pathptr
|
||||
(temp, (pathPtr)
|
||||
get_innerjoinpath((JoinPath) get_pathptr(upjoin)));
|
||||
set_pathptr
|
||||
(temp, (pathPtr)
|
||||
get_innerjoinpath((JoinPath) get_pathptr(upjoin)));
|
||||
}
|
||||
progress = true;
|
||||
}
|
||||
if (!progress)
|
||||
elog(DEBUG, "didn't succeed in pulling up in xfunc_prdmig_pullup");
|
||||
return progress;
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_form_groups
|
||||
** A group is a pair of stream nodes a,b such that a is constrained to
|
||||
** precede b (for instance if a and b are both joins), but rank(a) > rank(b).
|
||||
** In such a situation, Monma and Sidney prove that no clauses should end
|
||||
** up between a and b, and therefore we may treat them as a group, with
|
||||
** selectivity equal to the product of their selectivities, and cost
|
||||
** equal to the cost of the first plus the selectivity of the first times the
|
||||
** cost of the second. We define each node to be in a group by itself,
|
||||
** and then repeatedly find adjacent groups which are ordered by descending
|
||||
** rank, and make larger groups. You know that two adjacent nodes are in a
|
||||
** group together if the lower has groupup set to true. They will both have
|
||||
** the same groupcost and groupsel (since they're in the same group!)
|
||||
*/
|
||||
static void
|
||||
xfunc_form_groups(Query *queryInfo, Stream root, Stream bottom)
|
||||
{
|
||||
Stream temp,
|
||||
parent;
|
||||
int lowest = xfunc_num_relids((Stream) xfunc_get_upjoin(bottom));
|
||||
bool progress;
|
||||
LispValue primjoin;
|
||||
int whichchild;
|
||||
|
||||
if (!lowest)
|
||||
return; /* no joins in stream, so no groups */
|
||||
|
||||
/* initialize groups to be single nodes */
|
||||
for (temp = root;
|
||||
temp != (Stream) NULL && temp != bottom;
|
||||
temp = (Stream) get_downstream(temp))
|
||||
{
|
||||
/* if a Join node */
|
||||
if (!is_clause(temp))
|
||||
{
|
||||
if (get_pathptr((Stream) get_downstream(temp))
|
||||
== (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(temp)))
|
||||
whichchild = OUTER;
|
||||
else
|
||||
whichchild = INNER;
|
||||
set_groupcost(temp,
|
||||
xfunc_join_expense((JoinPath) get_pathptr(temp),
|
||||
whichchild));
|
||||
if (primjoin = xfunc_primary_join((JoinPath) get_pathptr(temp)))
|
||||
{
|
||||
set_groupsel(temp,
|
||||
compute_clause_selec(queryInfo,
|
||||
primjoin, NIL));
|
||||
}
|
||||
else
|
||||
set_groupsel(temp, 1.0);
|
||||
}
|
||||
else
|
||||
/* a restriction, or 2-ary join pred */
|
||||
{
|
||||
set_groupcost(temp,
|
||||
xfunc_expense(queryInfo,
|
||||
get_clause(get_cinfo(temp))));
|
||||
set_groupsel(temp,
|
||||
compute_clause_selec(queryInfo,
|
||||
get_clause(get_cinfo(temp)),
|
||||
NIL));
|
||||
}
|
||||
set_groupup(temp, false);
|
||||
}
|
||||
|
||||
/* make passes upwards, forming groups */
|
||||
do
|
||||
{
|
||||
progress = false;
|
||||
for (temp = (Stream) get_upstream(bottom);
|
||||
temp != (Stream) NULL;
|
||||
temp = (Stream) get_upstream(temp))
|
||||
{
|
||||
/* check for grouping with node upstream */
|
||||
if (!get_groupup(temp) && /* not already grouped */
|
||||
(parent = (Stream) get_upstream(temp)) != (Stream) NULL &&
|
||||
/* temp is a join or temp is the top of a group */
|
||||
(is_join((Path) get_pathptr(temp)) ||
|
||||
get_downstream(temp) &&
|
||||
get_groupup((Stream) get_downstream(temp))) &&
|
||||
get_grouprank(parent) < get_grouprank(temp))
|
||||
{
|
||||
progress = true; /* we formed a new group */
|
||||
set_groupup(temp, true);
|
||||
set_groupcost(temp,
|
||||
get_groupcost(temp) +
|
||||
get_groupsel(temp) * get_groupcost(parent));
|
||||
set_groupsel(temp, get_groupsel(temp) * get_groupsel(parent));
|
||||
|
||||
/* fix costs and sels of all members of group */
|
||||
xfunc_setup_group(temp, bottom);
|
||||
}
|
||||
}
|
||||
} while (progress);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------- UTILITY FUNCTIONS ------------------------- */
|
||||
|
||||
/*
|
||||
** xfunc_free_stream
|
||||
** walk down a stream and pfree it
|
||||
*/
|
||||
static void
|
||||
xfunc_free_stream(Stream root)
|
||||
{
|
||||
Stream cur,
|
||||
next;
|
||||
|
||||
Assert(xfunc_check_stream(root));
|
||||
|
||||
if (root != (Stream) NULL)
|
||||
for (cur = root; cur != (Stream) NULL; cur = next)
|
||||
{
|
||||
next = (Stream) get_downstream(cur);
|
||||
pfree(cur);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_add<_clauses
|
||||
** find any clauses above current, and insert them into stream as
|
||||
** appropriate. Return uppermost clause inserted, or current if none.
|
||||
*/
|
||||
static Stream
|
||||
xfunc_add_clauses(Stream current)
|
||||
{
|
||||
Stream topnode = current;
|
||||
LispValue temp;
|
||||
LispValue primjoin;
|
||||
|
||||
/* first add in the local clauses */
|
||||
foreach(temp, get_loc_restrictinfo((Path) get_pathptr(current)))
|
||||
{
|
||||
topnode = xfunc_streaminsert((RestrictInfo) lfirst(temp), topnode,
|
||||
XFUNC_LOCPRD);
|
||||
}
|
||||
|
||||
/* and add in the join clauses */
|
||||
if (IsA(get_pathptr(current), JoinPath))
|
||||
{
|
||||
primjoin = xfunc_primary_join((JoinPath) get_pathptr(current));
|
||||
foreach(temp, get_pathrestrictinfo((JoinPath) get_pathptr(current)))
|
||||
{
|
||||
if (!equal(get_clause((RestrictInfo) lfirst(temp)), primjoin))
|
||||
topnode = xfunc_streaminsert((RestrictInfo) lfirst(temp), topnode,
|
||||
XFUNC_JOINPRD);
|
||||
}
|
||||
}
|
||||
return topnode;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** xfunc_setup_group
|
||||
** find all elements of stream that are grouped with node and are above
|
||||
** bottom, and set their groupcost and groupsel to be the same as node's.
|
||||
*/
|
||||
static void
|
||||
xfunc_setup_group(Stream node, Stream bottom)
|
||||
{
|
||||
Stream temp;
|
||||
|
||||
if (node != bottom)
|
||||
/* traverse downwards */
|
||||
for (temp = (Stream) get_downstream(node);
|
||||
temp != (Stream) NULL && temp != bottom;
|
||||
temp = (Stream) get_downstream(temp))
|
||||
{
|
||||
if (!get_groupup(temp))
|
||||
break;
|
||||
else
|
||||
{
|
||||
set_groupcost(temp, get_groupcost(node));
|
||||
set_groupsel(temp, get_groupsel(node));
|
||||
}
|
||||
}
|
||||
|
||||
/* traverse upwards */
|
||||
for (temp = (Stream) get_upstream(node); temp != (Stream) NULL;
|
||||
temp = (Stream) get_upstream(temp))
|
||||
{
|
||||
if (!get_groupup((Stream) get_downstream(temp)))
|
||||
break;
|
||||
else
|
||||
{
|
||||
set_groupcost(temp, get_groupcost(node));
|
||||
set_groupsel(temp, get_groupsel(node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** xfunc_streaminsert
|
||||
** Make a new Stream node to hold clause, and insert it above current.
|
||||
** Return new node.
|
||||
*/
|
||||
static Stream
|
||||
xfunc_streaminsert(RestrictInfo restrictinfo,
|
||||
Stream current,
|
||||
int clausetype) /* XFUNC_LOCPRD or XFUNC_JOINPRD */
|
||||
{
|
||||
Stream newstream = RMakeStream();
|
||||
|
||||
set_upstream(newstream, get_upstream(current));
|
||||
if (get_upstream(current))
|
||||
set_downstream((Stream) (get_upstream(current)), (StreamPtr) newstream);
|
||||
set_upstream(current, (StreamPtr) newstream);
|
||||
set_downstream(newstream, (StreamPtr) current);
|
||||
set_pathptr(newstream, get_pathptr(current));
|
||||
set_cinfo(newstream, restrictinfo);
|
||||
set_clausetype(newstream, clausetype);
|
||||
return newstream;
|
||||
}
|
||||
|
||||
/*
|
||||
** Given a Stream node, find the number of relids referenced in the pathnode
|
||||
** associated with the stream node. The number of relids gives a unique
|
||||
** ordering on the joins in a stream, which we use to compare the height of
|
||||
** join nodes.
|
||||
*/
|
||||
static int
|
||||
xfunc_num_relids(Stream node)
|
||||
{
|
||||
if (!node || !IsA(get_pathptr(node), JoinPath))
|
||||
return 0;
|
||||
else
|
||||
return (length
|
||||
(get_relids(get_parent((JoinPath) get_pathptr(node)))));
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_get_downjoin
|
||||
** Given a stream node, find the next lowest node which points to a
|
||||
** join predicate or a scan node.
|
||||
*/
|
||||
static StreamPtr
|
||||
xfunc_get_downjoin(Stream node)
|
||||
{
|
||||
Stream temp;
|
||||
|
||||
if (!is_clause(node)) /* if this is a join */
|
||||
node = (Stream) get_downstream(node);
|
||||
for (temp = node; temp && is_clause(temp);
|
||||
temp = (Stream) get_downstream(temp))
|
||||
/* empty body in for loop */ ;
|
||||
|
||||
return (StreamPtr) temp;
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_get_upjoin
|
||||
** same as above, but upwards.
|
||||
*/
|
||||
static StreamPtr
|
||||
xfunc_get_upjoin(Stream node)
|
||||
{
|
||||
Stream temp;
|
||||
|
||||
if (!is_clause(node)) /* if this is a join */
|
||||
node = (Stream) get_upstream(node);
|
||||
for (temp = node; temp && is_clause(temp);
|
||||
temp = (Stream) get_upstream(temp))
|
||||
/* empty body in for loop */ ;
|
||||
|
||||
return (StreamPtr) temp;
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_stream_qsort
|
||||
** Given a stream, sort by group rank the elements in the stream from the
|
||||
** node "bottom" up. DESTRUCTIVELY MODIFIES STREAM! Returns new root.
|
||||
*/
|
||||
static Stream
|
||||
xfunc_stream_qsort(Stream root, Stream bottom)
|
||||
{
|
||||
int i;
|
||||
size_t num;
|
||||
Stream *nodearray,
|
||||
output;
|
||||
Stream tmp;
|
||||
|
||||
/* find size of list */
|
||||
for (num = 0, tmp = root; tmp != bottom;
|
||||
tmp = (Stream) get_downstream(tmp))
|
||||
num++;
|
||||
if (num <= 1)
|
||||
return root;
|
||||
|
||||
/* copy elements of the list into an array */
|
||||
nodearray = (Stream *) palloc(num * sizeof(Stream));
|
||||
|
||||
for (tmp = root, i = 0; tmp != bottom;
|
||||
tmp = (Stream) get_downstream(tmp), i++)
|
||||
nodearray[i] = tmp;
|
||||
|
||||
/* sort the array */
|
||||
qsort(nodearray, num, sizeof(LispValue), xfunc_stream_compare);
|
||||
|
||||
/* paste together the array elements */
|
||||
output = nodearray[num - 1];
|
||||
set_upstream(output, (StreamPtr) NULL);
|
||||
for (i = num - 2; i >= 0; i--)
|
||||
{
|
||||
set_downstream(nodearray[i + 1], (StreamPtr) nodearray[i]);
|
||||
set_upstream(nodearray[i], (StreamPtr) nodearray[i + 1]);
|
||||
}
|
||||
set_downstream(nodearray[0], (StreamPtr) bottom);
|
||||
if (bottom)
|
||||
set_upstream(bottom, (StreamPtr) nodearray[0]);
|
||||
|
||||
Assert(xfunc_check_stream(output));
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_stream_compare
|
||||
** comparison function for xfunc_stream_qsort.
|
||||
** Compare nodes by group rank. If group ranks are equal, ensure that
|
||||
** join nodes appear in same order as in plan tree.
|
||||
*/
|
||||
static int
|
||||
xfunc_stream_compare(void *arg1, void *arg2)
|
||||
{
|
||||
Stream stream1 = *(Stream *) arg1;
|
||||
Stream stream2 = *(Stream *) arg2;
|
||||
Cost rank1,
|
||||
rank2;
|
||||
|
||||
rank1 = get_grouprank(stream1);
|
||||
rank2 = get_grouprank(stream2);
|
||||
|
||||
if (rank1 > rank2)
|
||||
return 1;
|
||||
else if (rank1 < rank2)
|
||||
return -1;
|
||||
else
|
||||
{
|
||||
if (is_clause(stream1) && is_clause(stream2))
|
||||
return 0; /* doesn't matter what order if both are
|
||||
* restrictions */
|
||||
else if (!is_clause(stream1) && !is_clause(stream2))
|
||||
{
|
||||
if (xfunc_num_relids(stream1) < xfunc_num_relids(stream2))
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
else if (is_clause(stream1) && !is_clause(stream2))
|
||||
{
|
||||
if (xfunc_num_relids(stream1) == xfunc_num_relids(stream2))
|
||||
/* stream1 is a restriction over stream2 */
|
||||
return 1;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
else if (!is_clause(stream1) && is_clause(stream2))
|
||||
{
|
||||
/* stream2 is a restriction over stream1: never push down */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------ DEBUGGING ROUTINES ---------------------------- */
|
||||
|
||||
/*
|
||||
** Make sure all pointers in stream make sense. Make sure no joins are
|
||||
** out of order.
|
||||
*/
|
||||
static bool
|
||||
xfunc_check_stream(Stream node)
|
||||
{
|
||||
Stream temp;
|
||||
int numrelids,
|
||||
tmp;
|
||||
|
||||
/* set numrelids higher than max */
|
||||
if (!is_clause(node))
|
||||
numrelids = xfunc_num_relids(node) + 1;
|
||||
else if (xfunc_get_downjoin(node))
|
||||
numrelids = xfunc_num_relids((Stream) xfunc_get_downjoin(node)) + 1;
|
||||
else
|
||||
numrelids = 1;
|
||||
|
||||
for (temp = node; get_downstream(temp); temp = (Stream) get_downstream(temp))
|
||||
{
|
||||
if ((Stream) get_upstream((Stream) get_downstream(temp)) != temp)
|
||||
{
|
||||
elog(ERROR, "bad pointers in stream");
|
||||
return false;
|
||||
}
|
||||
if (!is_clause(temp))
|
||||
{
|
||||
if ((tmp = xfunc_num_relids(temp)) >= numrelids)
|
||||
{
|
||||
elog(ERROR, "Joins got reordered!");
|
||||
return false;
|
||||
}
|
||||
numrelids = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_in_stream
|
||||
** check if node is in stream
|
||||
*/
|
||||
static bool
|
||||
xfunc_in_stream(Stream node, Stream stream)
|
||||
{
|
||||
Stream temp;
|
||||
|
||||
for (temp = stream; temp; temp = (Stream) get_downstream(temp))
|
||||
if (temp == node)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,83 +0,0 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* xfunc.h
|
||||
* prototypes for xfunc.c and predmig.c.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: xfunc.h,v 1.9 2002/06/20 20:29:51 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef XFUNC_H
|
||||
#define XFUNC_H
|
||||
|
||||
#include "nodes/relation.h"
|
||||
#include "utils/rel.h"
|
||||
|
||||
/* command line arg flags */
|
||||
#define XFUNC_OFF -1 /* do no optimization of expensive preds */
|
||||
#define XFUNC_NOR 2 /* do no optimization of OR clauses */
|
||||
#define XFUNC_NOPULL 4 /* never pull restrictions above joins */
|
||||
#define XFUNC_NOPM 8 /* don't do predicate migration */
|
||||
#define XFUNC_WAIT 16 /* don't do pullup until predicate
|
||||
* migration */
|
||||
#define XFUNC_PULLALL 32 /* pull all expensive restrictions up,
|
||||
* always */
|
||||
|
||||
/* constants for local and join predicates */
|
||||
#define XFUNC_LOCPRD 1
|
||||
#define XFUNC_JOINPRD 2
|
||||
#define XFUNC_UNKNOWN 0
|
||||
|
||||
extern int XfuncMode; /* defined in tcop/postgres.c */
|
||||
|
||||
/* default width assumed for variable length attributes */
|
||||
#define VARLEN_DEFAULT 128;
|
||||
|
||||
/* Macro to get group rank out of group cost and group sel */
|
||||
#define get_grouprank(a) ((get_groupsel(a) - 1) / get_groupcost(a))
|
||||
|
||||
/* Macro to see if a path node is actually a Join */
|
||||
#define is_join(pathnode) (length(get_relids(get_parent(pathnode))) > 1 ? 1 : 0)
|
||||
|
||||
/* function prototypes from planner/path/xfunc.c */
|
||||
extern void xfunc_trypullup(RelOptInfo *rel);
|
||||
extern int xfunc_shouldpull(Path *childpath, JoinPath *parentpath,
|
||||
int whichchild, RestrictInfo *maxcinfopt);
|
||||
extern RestrictInfo *xfunc_pullup(Path *childpath, JoinPath *parentpath, RestrictInfo *cinfo,
|
||||
int whichchild, int clausetype);
|
||||
extern Cost xfunc_rank(Expr *clause);
|
||||
extern Cost xfunc_expense(Query *queryInfo, Expr *clause);
|
||||
extern Cost xfunc_join_expense(JoinPath *path, int whichchild);
|
||||
extern Cost xfunc_local_expense(Expr *clause);
|
||||
extern Cost xfunc_func_expense(Expr *node, List *args);
|
||||
extern int xfunc_width(Expr *clause);
|
||||
|
||||
/* static, moved to xfunc.c */
|
||||
/* extern int xfunc_card_unreferenced(Expr *clause, Relids referenced); */
|
||||
extern int xfunc_card_product(Relids relids);
|
||||
extern List *xfunc_find_references(List *clause);
|
||||
extern List *xfunc_primary_join(JoinPath *pathnode);
|
||||
extern Cost xfunc_get_path_cost(Path *pathnode);
|
||||
extern Cost xfunc_total_path_cost(JoinPath *pathnode);
|
||||
extern Cost xfunc_expense_per_tuple(JoinPath *joinnode, int whichchild);
|
||||
extern void xfunc_fixvars(Expr *clause, RelOptInfo *rel, int varno);
|
||||
extern int xfunc_cinfo_compare(void *arg1, void *arg2);
|
||||
extern int xfunc_clause_compare(void *arg1, void *arg2);
|
||||
extern void xfunc_disjunct_sort(List *clause_list);
|
||||
extern int xfunc_disjunct_compare(void *arg1, void *arg2);
|
||||
extern int xfunc_func_width(RegProcedure funcid, List *args);
|
||||
extern int xfunc_tuple_width(Relation rd);
|
||||
extern int xfunc_num_join_clauses(JoinPath *path);
|
||||
extern List *xfunc_LispRemove(List *foo, List *bar);
|
||||
extern bool xfunc_copyrel(RelOptInfo *from, RelOptInfo **to);
|
||||
|
||||
/*
|
||||
* function prototypes for path/predmig.c
|
||||
*/
|
||||
extern bool xfunc_do_predmig(Path root);
|
||||
|
||||
#endif /* XFUNC_H */
|
Loading…
Reference in New Issue