mirror of https://github.com/postgres/postgres
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