+++ /dev/null
-/*-------------------------------------------------------------------------
- *
- * recipe.c
- * routines for handling execution of Tioga recipes
- *
- * 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/commands/_deadcode/Attic/recipe.c,v 1.17 2002/06/20 20:29:27 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "catalog/pg_type.h"
-#include "commands/recipe.h"
-#include "executor/executor.h"
-#include "libpq/libpq-be.h"
-#include "nodes/execnodes.h"
-#include "nodes/makefuncs.h"
-#include "nodes/parsenodes.h"
-#include "nodes/plannodes.h"
-#include "optimizer/planner.h"
-#include "parser/parse_node.h"
-#include "rewrite/rewriteHandler.h"
-#include "rewrite/rewriteManip.h"
-#include "tcop/dest.h"
-#include "tcop/pquery.h"
-#include "utils/builtins.h"
-#include "utils/relcache.h"
-
-/* from tcop/postgres.c */
-extern CommandDest whereToSendOutput;
-
-#ifndef TIOGA
-
-void
-beginRecipe(RecipeStmt *stmt)
-{
- elog(WARNING, "You must compile with TIOGA defined in order to use recipes\n");
-}
-
-#else
-
-#include "tioga/tgRecipe.h"
-
-#define DEBUG_RECIPE 1
-
-/* structure to keep track of the tee node plans */
-typedef struct _teePlanInfo
-{
- char *tpi_relName;
- Query *tpi_parsetree;
- Plan *tpi_plan;
-} TeePlanInfo;
-
-typedef struct _teeInfo
-{
- int num;
- TeePlanInfo *val;
-} TeeInfo;
-
-QueryTreeList *appendQlist(QueryTreeList * q1, QueryTreeList * q2);
-void OffsetVarAttno(Node *node, int varno, int offset);
-
-static void appendTeeQuery(TeeInfo * teeInfo,
- QueryTreeList * q,
- char *teeNodeName);
-
-static Plan *replaceTeeScans(Plan *plan,
- Query *parsetree,
- TeeInfo * teeInfo);
-static void replaceSeqScan(Plan *plan,
- Plan *parent,
- int rt_ind,
- Plan *tplan);
-
-static void tg_rewriteQuery(TgRecipe * r, TgNode * n,
- QueryTreeList * q,
- QueryTreeList * inputQlist);
-static Node *tg_replaceNumberedParam(Node *expression,
- int pnum,
- int rt_ind,
- char *teeRelName);
-static Node *tg_rewriteParamsInExpr(Node *expression,
- QueryTreeList * inputQlist);
-static QueryTreeList *tg_parseSubQuery(TgRecipe * r,
- TgNode * n,
- TeeInfo * teeInfo);
-static QueryTreeList *tg_parseTeeNode(TgRecipe * r,
- TgNode * n,
- int i,
- QueryTreeList * qList,
- TeeInfo * teeInfo);
-
-
-/*
- The Tioga recipe rewrite algorithm:
-
- To parse a Tioga recipe, we start from an eye node and go backwards through
- its input nodes. To rewrite a Tioga node, we do the following:
-
- 1) parse the node we're at in the standard way (calling parser() )
- 2) rewrite its input nodes recursively using Tioga rewrite
- 3) now, with the rewritten input parse trees and the original parse tree
- of the node, we rewrite the the node.
- To do the rewrite, we use the target lists, range tables, and
- qualifications of the input parse trees
-*/
-
-/*
- * beginRecipe:
- * this is the main function to recipe execution
- * this function is invoked for EXECUTE RECIPE ... statements
- *
- * takes in a RecipeStmt structure from the parser
- * and returns a list of cursor names
- */
-
-void
-beginRecipe(RecipeStmt *stmt)
-{
- TgRecipe *r;
- int i,
- numTees;
- QueryTreeList *qList;
- char portalName[1024];
-
- Plan *plan;
- TupleDesc attinfo;
- QueryDesc *queryDesc;
- Query *parsetree;
-
- TeeInfo *teeInfo;
-
- /*
- * retrieveRecipe() reads the recipe from the database and returns a
- * TgRecipe* structure we can work with
- */
-
- r = retrieveRecipe(stmt->recipeName);
-
- if (r == NULL)
- return;
-
- /* find the number of tees in the recipe */
- numTees = r->tees->num;
-
- if (numTees > 0)
- {
- /* allocate a teePlan structure */
- teeInfo = (TeeInfo *) malloc(sizeof(TeeInfo));
- teeInfo->num = numTees;
- teeInfo->val = (TeePlanInfo *) malloc(numTees * sizeof(TeePlanInfo));
- for (i = 0; i < numTees; i++)
- {
- teeInfo->val[i].tpi_relName = r->tees->val[i]->nodeName;
- teeInfo->val[i].tpi_parsetree = NULL;
- teeInfo->val[i].tpi_plan = NULL;
- }
- }
- else
- teeInfo = NULL;
-
- /*
- * for each viewer in the recipe, go backwards from each viewer input
- * and generate a plan. Attach the plan to cursors.
- */
- for (i = 0; i < r->eyes->num; i++)
- {
- TgNodePtr e;
-
- e = r->eyes->val[i];
- if (e->inNodes->num > 1)
- {
- elog(WARNING,
- "beginRecipe: Currently eyes cannot have more than one input");
- }
- if (e->inNodes->num == 0)
- {
- /* no input to this eye, skip it */
- continue;
- }
-
-#ifdef DEBUG_RECIPE
- elog(WARNING, "beginRecipe: eyes[%d] = %s\n", i, e->nodeName);
-#endif /* DEBUG_RECIPE */
-
- qList = tg_parseSubQuery(r, e->inNodes->val[0], teeInfo);
-
- if (qList == NULL)
- {
- /* eye is directly connected to a tee node */
- /* XXX TODO: handle this case */
- }
-
- /* now, plan the queries */
-
- /*
- * should really do everything pg_plan() does, but for now, we
- * skip the rule rewrite and time qual stuff
- */
-
- /*
- * 1) plan the main query, everything from an eye node back to a
- * Tee
- */
- parsetree = qList->qtrees[0];
-
- /*
- * before we plan, we want to see all the changes we did, during
- * the rewrite phase, such as creating the tee tables,
- * CommandCounterIncrement() allows us to see the changes
- */
- CommandCounterIncrement();
-
- plan = planner(parsetree);
-
- /*
- * 2) plan the tee queries, (subgraphs rooted from a Tee) by the
- * time the eye is processed, all tees that contribute to that eye
- * will have been included in the teeInfo list
- */
- if (teeInfo)
- {
- int t;
- Plan *tplan;
- Tee *newplan;
-
- for (t = 0; t < teeInfo->num; t++)
- {
- if (teeInfo->val[t].tpi_plan == NULL)
- {
- /* plan it in the usual fashion */
- tplan = planner(teeInfo->val[t].tpi_parsetree);
-
- /* now add a tee node to the root of the plan */
- elog(WARNING, "adding tee plan node to the root of the %s\n",
- teeInfo->val[t].tpi_relName);
- newplan = (Tee *) makeNode(Tee);
- newplan->plan.targetlist = tplan->targetlist;
- newplan->plan.qual = NULL; /* tplan->qual; */
- newplan->plan.lefttree = tplan;
- newplan->plan.righttree = NULL;
- newplan->leftParent = NULL;
- newplan->rightParent = NULL;
-
- /*
- * the range table of the tee is the range table of
- * the tplan
- */
- newplan->rtentries = teeInfo->val[t].tpi_parsetree->rtable;
- strcpy(newplan->teeTableName,
- teeInfo->val[t].tpi_relName);
- teeInfo->val[t].tpi_plan = (Plan *) newplan;
- }
- }
-
- /*
- * 3) replace the tee table scans in the main plan with actual
- * tee plannodes
- */
-
- plan = replaceTeeScans(plan, parsetree, teeInfo);
-
- } /* if (teeInfo) */
-
- /* define a portal for this viewer input */
- /* for now, eyes can only have one input */
- snprintf(portalName, 1024, "%s%d", e->nodeName, 0);
-
- queryDesc = CreateQueryDesc(parsetree,
- plan,
- whereToSendOutput);
-
- /*
- * call ExecStart to prepare the plan for execution
- */
- attinfo = ExecutorStart(queryDesc, NULL);
-
- ProcessPortal(portalName,
- parsetree,
- plan,
- attinfo,
- whereToSendOutput);
- elog(WARNING, "beginRecipe: cursor named %s is now available", portalName);
- }
-
-}
-
-
-
-/*
- * tg_rewriteQuery -
- * r - the recipe being rewritten
- * n - the node that we're current at
- * q - a QueryTree List containing the parse tree of the node
- * inputQlist - the parsetrees of its input nodes,
- * the size of inputQlist must be the same as the
- * number of input nodes. Some elements in the inpuQlist
- * may be null if the inputs to those nodes are unconnected
- *
- * this is the main routine for rewriting the recipe queries
- * the original query tree 'q' is modified
- */
-
-static void
-tg_rewriteQuery(TgRecipe * r,
- TgNode * n,
- QueryTreeList * q,
- QueryTreeList * inputQlist)
-{
- Query *orig;
- Query *inputQ;
- int i;
- List *rtable;
- List *input_rtable;
- int rt_length;
-
- /* orig is the original parse tree of the node */
- orig = q->qtrees[0];
-
-
- /*
- * step 1:
- *
- * form a combined range table from all the range tables in the original
- * query as well as the input nodes
- *
- * form a combined qualification from the qual in the original plus the
- * quals of the input nodes
- */
-
- /* start with the original range table */
- rtable = orig->rtable;
- rt_length = length(rtable);
-
- for (i = 0; i < n->inNodes->num; i++)
- {
- if (n->inNodes->val[i] != NULL &&
- n->inNodes->val[i]->nodeType != TG_TEE_NODE)
- {
- inputQ = inputQlist->qtrees[i];
- input_rtable = inputQ->rtable;
-
- /*
- * need to offset the var nodes in the qual and targetlist
- * because they are indexed off the original rtable
- */
- OffsetVarNodes((Node *) inputQ->qual, rt_length, 0);
- OffsetVarNodes((Node *) inputQ->targetList, rt_length, 0);
-
- /* append the range tables from the children nodes */
- rtable = nconc(rtable, input_rtable);
-
- /*
- * append the qualifications of the child node into the
- * original qual list
- */
- AddQual(orig, inputQ->qual);
- }
- }
- orig->rtable = rtable;
-
- /*
- * step 2: rewrite the target list of the original parse tree if there
- * are any references to params, replace them with the appropriate
- * target list entry of the children node
- */
- if (orig->targetList != NIL)
- {
- List *tl;
- TargetEntry *tle;
-
- foreach(tl, orig->targetList)
- {
- tle = lfirst(tl);
- if (tle->resdom != NULL)
- tle->expr = tg_rewriteParamsInExpr(tle->expr, inputQlist);
- }
- }
-
- /*
- * step 3: rewrite the qual of the original parse tree if there are
- * any references to params, replace them with the appropriate target
- * list entry of the children node
- */
- if (orig->qual)
- {
- if (nodeTag(orig->qual) == T_List)
- elog(ERROR, "tg_rewriteQuery: Whoa! why is my qual a List???");
- orig->qual = tg_rewriteParamsInExpr(orig->qual, inputQlist);
- }
-
- /*
- * at this point, we're done with the rewrite, the querytreelist q has
- * been modified
- */
-
-}
-
-
-/* tg_replaceNumberedParam:
-
- this procedure replaces the specified numbered param with a
- reference to a range table
-
- this procedure recursively calls itself
-
- it returns a (possibly modified) Node*.
-
-*/
-static Node *
-tg_replaceNumberedParam(Node *expression,
- int pnum, /* the number of the parameter */
- int rt_ind, /* the range table index */
- char *teeRelName) /* the relname of the tee
- * table */
-{
- TargetEntry *param_tle;
- Param *p;
- Var *newVar,
- *oldVar;
-
- if (expression == NULL)
- return NULL;
-
- switch (nodeTag(expression))
- {
- case T_Param:
- {
- /*
- * the node is a parameter, substitute the entry from the
- * target list of the child that corresponds to the
- * parameter number
- */
- p = (Param *) expression;
-
- /* we only deal with the case of numbered parameters */
- if (p->paramkind == PARAM_NUM && p->paramid == pnum)
- {
-
- if (p->param_tlist)
- {
- /*
- * we have a parameter with an attribute like
- * $N.foo so replace it with a new var node
- */
-
- /* param tlist can only have one entry in them! */
- param_tle = (TargetEntry *) (lfirst(p->param_tlist));
- oldVar = (Var *) param_tle->expr;
- oldVar->varno = rt_ind;
- oldVar->varnoold = rt_ind;
- return (Node *) oldVar;
- }
- else
- {
- /* we have $N without the .foo */
- bool defined;
- bool isRel;
-
- /*
- * TODO here, we need to check to see whether the
- * type of the tee is a complex type (relation) or
- * a simple type
- */
-
- /*
- * if it is a simple type, then we need to get the
- * "result" attribute from the tee relation
- */
-
- isRel = (typeidTypeRelid(p->paramtype) != 0);
- if (isRel)
- {
- newVar = makeVar(rt_ind,
- 0, /* the whole tuple */
- TypeGet(teeRelName, &defined),
- -1,
- 0,
- rt_ind,
- 0);
- return (Node *) newVar;
- }
- else
- newVar = makeVar(rt_ind,
- 1, /* just the first field,
- * which is 'result' */
- TypeGet(teeRelName, &defined),
- -1,
- 0,
- rt_ind,
- 0);
- return (Node *) newVar;
-
- }
- }
- else
- elog(WARNING, "tg_replaceNumberedParam: unexpected paramkind value of %d", p->paramkind);
- }
- break;
- case T_Expr:
- {
- /*
- * the node is an expression, we need to recursively call
- * ourselves until we find parameter nodes
- */
- List *l;
- Expr *expr = (Expr *) expression;
- List *newArgs;
-
- /*
- * we have to make a new args lists because Params can be
- * replaced by Var nodes in tg_replaceNumberedParam()
- */
- newArgs = NIL;
-
- /*
- * we only care about argument to expressions, it doesn't
- * matter when the opType is
- */
- /* recursively rewrite the arguments of this expression */
- foreach(l, expr->args)
- {
- newArgs = lappend(newArgs,
- tg_replaceNumberedParam(lfirst(l),
- pnum,
- rt_ind,
- teeRelName));
- }
- /* change the arguments of the expression */
- expr->args = newArgs;
- }
- break;
- default:
- {
- /* ignore other expr types */
- }
- }
-
- return expression;
-}
-
-
-
-
-
-/* tg_rewriteParamsInExpr:
-
- rewrite the params in expressions by using the targetlist entries
- from the input parsetrees
-
- this procedure recursively calls itself
-
- it returns a (possibly modified) Node*.
-
-*/
-static Node *
-tg_rewriteParamsInExpr(Node *expression, QueryTreeList * inputQlist)
-{
- List *tl;
- TargetEntry *param_tle,
- *tle;
- Param *p;
- int childno;
- char *resname;
-
- if (expression == NULL)
- return NULL;
-
- switch (nodeTag(expression))
- {
- case T_Param:
- {
- /*
- * the node is a parameter, substitute the entry from the
- * target list of the child that corresponds to the
- * parameter number
- */
- p = (Param *) expression;
-
- /* we only deal with the case of numbered parameters */
- if (p->paramkind == PARAM_NUM)
- {
- /* paramid's start from 1 */
- childno = p->paramid - 1;
-
- if (p->param_tlist)
- {
- /*
- * we have a parameter with an attribute like
- * $N.foo so match the resname "foo" against the
- * target list of the (N-1)th inputQlist
- */
-
- /* param tlist can only have one entry in them! */
- param_tle = (TargetEntry *) (lfirst(p->param_tlist));
- resname = param_tle->resdom->resname;
-
- if (inputQlist->qtrees[childno])
- {
- foreach(tl, inputQlist->qtrees[childno]->targetList)
- {
- tle = lfirst(tl);
- if (strcmp(resname, tle->resdom->resname) == 0)
- return tle->expr;
- }
- }
- else
- elog(ERROR, "tg_rewriteParamsInExpr:can't substitute for parameter %d when that input is unconnected", p->paramid);
-
- }
- else
- {
- /* we have $N without the .foo */
- /* use the first resdom in the targetlist of the */
- /* appropriate child query */
- tl = inputQlist->qtrees[childno]->targetList;
- tle = lfirst(tl);
- return tle->expr;
- }
- }
- else
- elog(WARNING, "tg_rewriteParamsInExpr: unexpected paramkind value of %d", p->paramkind);
- }
- break;
- case T_Expr:
- {
- /*
- * the node is an expression, we need to recursively call
- * ourselves until we find parameter nodes
- */
- List *l;
- Expr *expr = (Expr *) expression;
- List *newArgs;
-
- /*
- * we have to make a new args lists because Params can be
- * replaced by Var nodes in tg_rewriteParamsInExpr()
- */
- newArgs = NIL;
-
- /*
- * we only care about argument to expressions, it doesn't
- * matter when the opType is
- */
- /* recursively rewrite the arguments of this expression */
- foreach(l, expr->args)
- {
- newArgs = lappend(newArgs,
- tg_rewriteParamsInExpr(lfirst(l), inputQlist));
- }
- /* change the arguments of the expression */
- expr->args = newArgs;
- }
- break;
- default:
- {
- /* ignore other expr types */
- }
- }
-
- return expression;
-}
-
-
-
-/*
- getParamTypes:
- given an element, finds its parameter types.
- the typev array argument is set to the parameter types.
- the parameterCount is returned
-
- this code is very similar to ProcedureDefine() in pg_proc.c
-*/
-static int
-getParamTypes(TgElement * elem, Oid *typev)
-{
- /* this code is similar to ProcedureDefine() */
- int16 parameterCount;
- bool defined;
- Oid toid;
- char *t;
- int i,
- j;
-
- parameterCount = 0;
- for (i = 0; i < FUNC_MAX_ARGS; i++)
- typev[i] = 0;
- for (j = 0; j < elem->inTypes->num; j++)
- {
- if (parameterCount == FUNC_MAX_ARGS)
- {
- elog(ERROR,
- "getParamTypes: Ingredients cannot take > %d arguments", FUNC_MAX_ARGS);
- }
- t = elem->inTypes->val[j];
- if (strcmp(t, "opaque") == 0)
- {
- elog(ERROR,
- "getParamTypes: Ingredient functions cannot take type 'opaque'");
- }
- else
- {
- toid = TypeGet(elem->inTypes->val[j], &defined);
- if (!OidIsValid(toid))
- elog(ERROR, "getParamTypes: arg type '%s' is not defined", t);
- if (!defined)
- elog(WARNING, "getParamTypes: arg type '%s' is only a shell", t);
- }
- typev[parameterCount++] = toid;
- }
-
- return parameterCount;
-}
-
-
-/*
- * tg_parseTeeNode
- *
- * handles the parsing of the tee node
- *
- *
- */
-
-static QueryTreeList *
-tg_parseTeeNode(TgRecipe * r,
- TgNode * n, /* the tee node */
- int i, /* which input this node is to its parent */
- QueryTreeList * qList,
- TeeInfo * teeInfo)
-
-{
- QueryTreeList *q;
- char *tt;
- int rt_ind;
- Query *orig;
-
- /*
- * the input Node is a tee node, so we need to do the following: we
- * need to parse the child of the tee node, we add that to our query
- * tree list we need the name of the tee node table the tee node table
- * is the table into which the tee node may materialize results. Call
- * it TT we add a range table to our existing query with TT in it we
- * need to replace the parameter $i with TT (otherwise the optimizer
- * won't know to use the table on expression containining $i) After
- * that rewrite, the optimizer will generate sequential scans of TT
- *
- * Later, in the glue phase, we replace all instances of TT sequential
- * scans with the actual Tee node
- */
- q = tg_parseSubQuery(r, n, teeInfo);
-
- /* tt is the name of the tee node table */
- tt = n->nodeName;
-
- if (q)
- appendTeeQuery(teeInfo, q, tt);
-
- orig = qList->qtrees[0];
- rt_ind = RangeTablePosn(orig->rtable, tt);
-
- /*
- * check to see that this table is not part of the range table
- * already. This usually only happens if multiple inputs are
- * connected to the same Tee.
- */
- if (rt_ind == 0)
- {
- orig->rtable = lappend(orig->rtable,
- addRangeTableEntry(NULL,
- tt,
- tt,
- FALSE,
- FALSE));
- rt_ind = length(orig->rtable);
- }
-
- orig->qual = tg_replaceNumberedParam(orig->qual,
- i + 1, /* params start at 1 */
- rt_ind,
- tt);
- return qList;
-}
-
-
-/*
- * tg_parseSubQuery:
- * go backwards from a node and parse the query
- *
- * the result parse tree is passed back
- *
- * could return NULL if trying to parse a teeNode
- * that's already been processed by another parent
- *
- */
-
-static QueryTreeList *
-tg_parseSubQuery(TgRecipe * r, TgNode * n, TeeInfo * teeInfo)
-{
- TgElement *elem;
- char *funcName;
- Oid typev[FUNC_MAX_ARGS], /* eight arguments maximum */
- relid;
- int i,
- parameterCount;
-
- QueryTreeList *qList; /* the parse tree of the nodeElement */
- QueryTreeList *inputQlist; /* the list of parse trees for the inputs
- * to this node */
- QueryTreeList *q;
- TgNode *child;
- Relation rel;
- unsigned int len;
- TupleDesc tupdesc;
-
- qList = NULL;
-
- if (n->nodeType == TG_INGRED_NODE)
- {
- /* parse each ingredient node in turn */
-
- elem = n->nodeElem;
- switch (elem->srcLang)
- {
- case TG_SQL:
- {
- /*
- * for SQL ingredients, the SQL query is contained in
- * the 'src' field
- */
-
-#ifdef DEBUG_RECIPE
- elog(WARNING, "calling parser with %s", elem->src);
-#endif /* DEBUG_RECIPE */
-
- parameterCount = getParamTypes(elem, typev);
-
- qList = parser(elem->src, typev, parameterCount);
-
- if (qList->len > 1)
- {
- elog(WARNING,
- "tg_parseSubQuery: parser produced > 1 query tree");
- }
- }
- break;
- case TG_C:
- {
- /* C ingredients are registered functions in postgres */
-
- /*
- * we create a new query string by using the function
- * name (found in the 'src' field) and adding
- * parameters to it so if the function was FOOBAR and
- * took in two arguments, we would create a string
- * select FOOBAR($1,$2)
- */
- char newquery[1000];
-
- funcName = elem->src;
- parameterCount = getParamTypes(elem, typev);
-
- if (parameterCount > 0)
- {
- int i;
-
- snprintf(newquery, 1000, "select %s($1", funcName);
- for (i = 1; i < parameterCount; i++)
- snprintf(newquery, 1000, "%s,$%d", pstrdup(newquery), i);
- snprintf(newquery, 1000, "%s)", pstrdup(newquery));
- }
- else
- snprintf(newquery, 1000, "select %s()", funcName);
-
-#ifdef DEBUG_RECIPE
- elog(WARNING, "calling parser with %s", newquery);
-#endif /* DEBUG_RECIPE */
-
- qList = parser(newquery, typev, parameterCount);
- if (qList->len > 1)
- {
- elog(WARNING,
- "tg_parseSubQuery: parser produced > 1 query tree");
- }
- }
- break;
- case TG_RECIPE_GRAPH:
- elog(WARNING, "tg_parseSubQuery: can't parse recipe graph ingredients yet!");
- break;
- case TG_COMPILED:
- elog(WARNING, "tg_parseSubQuery: can't parse compiled ingredients yet!");
- break;
- default:
- elog(WARNING, "tg_parseSubQuery: unknown srcLang: %d", elem->srcLang);
- }
-
- /* parse each of the subrecipes that are input to this node */
-
- if (n->inNodes->num > 0)
- {
- inputQlist = malloc(sizeof(QueryTreeList));
- inputQlist->len = n->inNodes->num + 1;
- inputQlist->qtrees = (Query **) malloc(inputQlist->len * sizeof(Query *));
- for (i = 0; i < n->inNodes->num; i++)
- {
-
- inputQlist->qtrees[i] = NULL;
- if (n->inNodes->val[i])
- {
- if (n->inNodes->val[i]->nodeType == TG_TEE_NODE)
- {
- qList = tg_parseTeeNode(r, n->inNodes->val[i],
- i, qList, teeInfo);
- }
- else
- { /* input node is not a Tee */
- q = tg_parseSubQuery(r, n->inNodes->val[i],
- teeInfo);
- Assert(q->len == 1);
- inputQlist->qtrees[i] = q->qtrees[0];
- }
- }
- }
-
- /* now, we have all the query trees from our input nodes */
- /* transform the original parse tree appropriately */
- tg_rewriteQuery(r, n, qList, inputQlist);
- }
- }
- else if (n->nodeType == TG_EYE_NODE)
- {
- /*
- * if we hit an eye, we need to stop and make what we have into a
- * subrecipe query block
- */
- elog(WARNING, "tg_parseSubQuery: can't handle eye nodes yet");
- }
- else if (n->nodeType == TG_TEE_NODE)
- {
- /*
- * if we hit a tee, check to see if the parsing has been done for
- * this tee already by the other parent
- */
-
- rel = RelationNameGetRelation(n->nodeName);
- if (RelationIsValid(rel))
- {
- /*
- * this tee has already been visited, no need to do any
- * further processing
- */
- return NULL;
- }
- else
- {
- /* we need to process the child of the tee first, */
- child = n->inNodes->val[0];
-
- if (child->nodeType == TG_TEE_NODE)
- {
- /* nested Tee nodes */
- qList = tg_parseTeeNode(r, child, 0, qList, teeInfo);
- return qList;
- }
-
- Assert(child != NULL);
-
- /* parse the input node */
- q = tg_parseSubQuery(r, child, teeInfo);
- Assert(q->len == 1);
-
- /* add the parsed query to the main list of queries */
- qList = appendQlist(qList, q);
-
- /* need to create the tee table here */
-
- /*
- * the tee table created is used both for materializing the
- * values at the tee node, and for parsing and optimization.
- * The optimization needs to have a real table before it will
- * consider scans on it
- */
-
- /*
- * first, find the type of the tuples being produced by the
- * tee. The type is the same as the output type of the child
- * node.
- *
- * NOTE: we are assuming that the child node only has a single
- * output here!
- */
- getParamTypes(child->nodeElem, typev);
-
- /*
- * the output type is either a complex type, (and is thus a
- * relation) or is a simple type
- */
-
- rel = RelationNameGetRelation(child->nodeElem->outTypes->val[0]);
-
- if (RelationIsValid(rel))
- {
- /*
- * for complex types, create new relation with the same
- * tuple descriptor as the output table type
- */
- len = length(q->qtrees[0]->targetList);
- tupdesc = rel->rd_att;
-
- relid = heap_create_with_catalog(
- child->nodeElem->outTypes->val[0],
- tupdesc, RELKIND_RELATION, false);
- }
- else
- {
- /*
- * we have to create a relation with one attribute of the
- * simple base type. That attribute will have an attr
- * name of "result"
- */
- /* NOTE: ignore array types for the time being */
-
- len = 1;
- tupdesc = CreateTemplateTupleDesc(len);
-
- if (!TupleDescInitEntry(tupdesc, 1,
- "result",
- InvalidOid,
- -1, 0, false))
- elog(WARNING, "tg_parseSubQuery: unexpected result from TupleDescInitEntry");
- else
- {
- relid = heap_create_with_catalog(
- child->nodeElem->outTypes->val[0],
- tupdesc, RELKIND_RELATION, false);
- }
- }
- }
- }
- else if (n->nodeType == TG_RECIPE_NODE)
- elog(WARNING, "tg_parseSubQuery: can't handle embedded recipes yet!");
- else
- elog(WARNING, "unknown nodeType: %d", n->nodeType);
-
- return qList;
-}
-
-/*
- * OffsetVarAttno -
- * recursively find all the var nodes with the specified varno
- * and offset their varattno with the offset
- *
- * code is similar to OffsetVarNodes in rewriteManip.c
- */
-
-void
-OffsetVarAttno(Node *node, int varno, int offset)
-{
- if (node == NULL)
- return;
- switch (nodeTag(node))
- {
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
-
- OffsetVarAttno(tle->expr, varno, offset);
- }
- break;
- case T_Expr:
- {
- Expr *expr = (Expr *) node;
-
- OffsetVarAttno((Node *) expr->args, varno, offset);
- }
- break;
- case T_Var:
- {
- Var *var = (Var *) node;
-
- if (var->varno == varno)
- var->varattno += offset;
- }
- break;
- case T_List:
- {
- List *l;
-
- foreach(l, (List *) node)
- OffsetVarAttno(lfirst(l), varno, offset);
- }
- break;
- default:
- /* ignore the others */
- break;
- }
-}
-
-/*
- * appendQlist
- * add the contents of a QueryTreeList q2 to the end of the QueryTreeList
- * q1
- *
- * returns a new querytree list
- */
-
-QueryTreeList *
-appendQlist(QueryTreeList * q1, QueryTreeList * q2)
-{
- QueryTreeList *newq;
- int i,
- j;
- int newlen;
-
- if (q1 == NULL)
- return q2;
-
- if (q2 == NULL)
- return q1;
-
- newlen = q1->len + q2->len;
- newq = (QueryTreeList *) malloc(sizeof(QueryTreeList));
- newq->len = newlen;
- newq->qtrees = (Query **) malloc(newlen * sizeof(Query *));
- for (i = 0; i < q1->len; i++)
- newq->qtrees[i] = q1->qtrees[i];
- for (j = 0; j < q2->len; j++)
- newq->qtrees[i + j] = q2->qtrees[j];
- return newq;
-}
-
-/*
- * appendTeeQuery
- *
- * modify the query field of the teeInfo list of the particular tee node
- */
-static void
-appendTeeQuery(TeeInfo * teeInfo, QueryTreeList * q, char *teeNodeName)
-{
- int i;
-
- Assert(teeInfo);
-
- for (i = 0; i < teeInfo->num; i++)
- {
- if (strcmp(teeInfo->val[i].tpi_relName, teeNodeName) == 0)
- {
-
- Assert(q->len == 1);
- teeInfo->val[i].tpi_parsetree = q->qtrees[0];
- return;
- }
- }
- elog(WARNING, "appendTeeQuery: teeNodeName '%s' not found in teeInfo");
-}
-
-
-
-/*
- * replaceSeqScan
- * replaces sequential scans of a specified relation with the tee plan
- * the relation is specified by its index in the range table, rt_ind
- *
- * returns the modified plan
- * the offset_attno is the offset that needs to be added to the parent's
- * qual or targetlist because the child plan has been replaced with a tee node
- */
-static void
-replaceSeqScan(Plan *plan, Plan *parent,
- int rt_ind, Plan *tplan)
-{
- Scan *snode;
- Tee *teePlan;
- Result *newPlan;
-
- if (plan == NULL)
- return;
-
- if (plan->type == T_SeqScan)
- {
- snode = (Scan *) plan;
- if (snode->scanrelid == rt_ind)
- {
- /*
- * found the sequential scan that should be replaced with the
- * tplan.
- */
- /* we replace the plan, but we also need to modify its parent */
-
- /*
- * replace the sequential scan with a Result node the reason
- * we use a result node is so that we get the proper
- * projection behavior. The Result node is simply (ab)used as
- * a projection node
- */
-
- newPlan = makeNode(Result);
- newPlan->plan.cost = 0.0;
- newPlan->plan.state = (EState *) NULL;
- newPlan->plan.targetlist = plan->targetlist;
- newPlan->plan.lefttree = tplan;
- newPlan->plan.righttree = NULL;
- newPlan->resconstantqual = NULL;
- newPlan->resstate = NULL;
-
- /* change all the varno's to 1 */
- ChangeVarNodes((Node *) newPlan->plan.targetlist,
- snode->scanrelid, 1);
-
- if (parent)
- {
- teePlan = (Tee *) tplan;
-
- if (parent->lefttree == plan)
- parent->lefttree = (Plan *) newPlan;
- else
- parent->righttree = (Plan *) newPlan;
-
-
- if (teePlan->leftParent == NULL)
- teePlan->leftParent = (Plan *) newPlan;
- else
- teePlan->rightParent = (Plan *) newPlan;
-
-/* comment for now to test out executor-stuff
- if (parent->state) {
- ExecInitNode((Plan*)newPlan, parent->state, (Plan*)newPlan);
- }
-*/
- }
- }
-
- }
- else
- {
- if (plan->lefttree)
- replaceSeqScan(plan->lefttree, plan, rt_ind, tplan);
- if (plan->righttree)
- replaceSeqScan(plan->righttree, plan, rt_ind, tplan);
- }
-}
-
-/*
- * replaceTeeScans
- * places the sequential scans of the Tee table with
- * a connection to the actual tee plan node
- */
-static Plan *
-replaceTeeScans(Plan *plan, Query *parsetree, TeeInfo * teeInfo)
-{
-
- int i;
- List *rtable;
- RangeTblEntry *rte;
- char prefix[5];
- int rt_ind;
- Plan *tplan;
-
- rtable = parsetree->rtable;
- if (rtable == NULL)
- return plan;
-
- /*
- * look through the range table for the tee relation entry, that will
- * give use the varno we need to detect which sequential scans need to
- * be replaced with tee nodes
- */
-
- rt_ind = 0;
- while (rtable != NIL)
- {
- rte = lfirst(rtable);
- rtable = lnext(rtable);
- rt_ind++; /* range table references in varno fields
- * start w/ 1 */
-
- /*
- * look for the "tee_" prefix in the refname, also check to see
- * that the relname and the refname are the same this should
- * eliminate any user-specified table and leave us with the tee
- * table entries only
- */
- if ((strlen(rte->refname) < 4) ||
- (strcmp(rte->relname, rte->refname) != 0))
- continue;
- StrNCpy(prefix, rte->refname, 5);
- if (strcmp(prefix, "tee_") == 0)
- {
- /* okay, we found a tee node entry in the range table */
-
- /* find the appropriate plan in the teeInfo list */
- tplan = NULL;
- for (i = 0; i < teeInfo->num; i++)
- {
- if (strcmp(teeInfo->val[i].tpi_relName,
- rte->refname) == 0)
- tplan = teeInfo->val[i].tpi_plan;
- }
- if (tplan == NULL)
- elog(WARNING, "replaceTeeScans didn't find the corresponding tee plan");
-
- /*
- * replace the sequential scan node with that var number with
- * the tee plan node
- */
- replaceSeqScan(plan, NULL, rt_ind, tplan);
- }
- }
-
- return plan;
-}
-
-
-#endif /* TIOGA */
+++ /dev/null
-/*-------------------------------------------------------------------------
- *
- * 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
+++ /dev/null
-/*-------------------------------------------------------------------------
- *
- * 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;
- }
- }
- }
-
-}
+++ /dev/null
-/*-------------------------------------------------------------------------
- *
- * 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;
-}
+++ /dev/null
-/*-------------------------------------------------------------------------
- *
- * xfunc.c
- * Utility routines to handle expensive function optimization.
- * Includes xfunc_trypullup(), which attempts early pullup of predicates
- * to allow for maximal pruning.
- *
- * 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/xfunc.c,v 1.19 2002/06/20 20:29:30 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include <math.h>
-
-#ifdef HAVE_VALUES_H
-#include <values.h>
-#endif
-
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "access/htup.h"
-#include "catalog/pg_language.h"
-#include "catalog/pg_proc.h"
-#include "catalog/pg_type.h"
-#include "lib/lispsort.h"
-#include "nodes/nodes.h"
-#include "nodes/pg_list.h"
-#include "nodes/primnodes.h"
-#include "nodes/relation.h"
-#include "optimizer/clauses.h"
-#include "optimizer/cost.h"
-#include "optimizer/internal.h"
-#include "optimizer/keys.h"
-#include "optimizer/pathnode.h"
-#include "optimizer/tlist.h"
-#include "storage/buf_internals.h"
-#include "tcop/dest.h"
-#include "utils/syscache.h"
-
-#define ever ; 1 ;
-
-/* local funcs */
-static int xfunc_card_unreferenced(Query *queryInfo,
- Expr *clause, Relids referenced);
-
-*/
-
-/*
-** xfunc_trypullup
-** Preliminary pullup of predicates, to allow for maximal pruning.
-** Given a relation, check each of its paths and see if you can
-** pullup clauses from its inner and outer.
-*/
-
-void
-xfunc_trypullup(RelOptInfo rel)
-{
- LispValue y; /* list ptr */
- RestrictInfo maxcinfo; /* The RestrictInfo to pull up, as
- * calculated by xfunc_shouldpull() */
- JoinPath curpath; /* current path in list */
- int progress; /* has progress been made this time
- * through? */
- int clausetype;
-
- do
- {
- progress = false; /* no progress yet in this iteration */
- foreach(y, get_pathlist(rel))
- {
- curpath = (JoinPath) lfirst(y);
-
- /*
- * * for each operand, attempt to pullup predicates until
- * first * failure.
- */
- for (ever)
- {
- /* No, the following should NOT be '==' !! */
- if (clausetype = xfunc_shouldpull((Path) get_innerjoinpath(curpath),
- curpath, INNER, &maxcinfo))
- {
-
- xfunc_pullup((Path) get_innerjoinpath(curpath),
- curpath, maxcinfo, INNER, clausetype);
- progress = true;
- }
- else
- break;
- }
- for (ever)
- {
-
- /* No, the following should NOT be '==' !! */
- if (clausetype = xfunc_shouldpull((Path) get_outerjoinpath(curpath),
- curpath, OUTER, &maxcinfo))
- {
-
- xfunc_pullup((Path) get_outerjoinpath(curpath),
- curpath, maxcinfo, OUTER, clausetype);
- progress = true;
- }
- else
- break;
- }
-
- /*
- * * make sure the unpruneable flag bubbles up, i.e. * if
- * anywhere below us in the path pruneable is false, * then
- * pruneable should be false here
- */
- if (get_pruneable(get_parent(curpath)) &&
- (!get_pruneable(get_parent
- ((Path) get_innerjoinpath(curpath))) ||
- !get_pruneable(get_parent((Path)
- get_outerjoinpath(curpath)))))
- {
-
- set_pruneable(get_parent(curpath), false);
- progress = true;
- }
- }
- } while (progress);
-}
-
-/*
- ** xfunc_shouldpull
- ** find clause with highest rank, and decide whether to pull it up
- ** from child to parent. Currently we only pullup secondary join clauses
- ** that are in the pathrestrictinfo. Secondary hash and sort clauses are
- ** left where they are.
- ** If we find an expensive function but decide *not* to pull it up,
- ** we'd better set the unpruneable flag. -- JMH, 11/11/92
- **
- ** Returns: 0 if nothing left to pullup
- ** XFUNC_LOCPRD if a local predicate is to be pulled up
- ** XFUNC_JOINPRD if a secondary join predicate is to be pulled up
- */
-int
-xfunc_shouldpull(Query *queryInfo,
- Path childpath,
- JoinPath parentpath,
- int whichchild,
- RestrictInfo *maxcinfopt) /* Out: pointer to clause
- * to pullup */
-{
- LispValue clauselist,
- tmplist; /* lists of clauses */
- RestrictInfo maxcinfo; /* clause to pullup */
- LispValue primjoinclause /* primary join clause */
- = xfunc_primary_join(parentpath);
- Cost tmprank,
- maxrank = (-1 * MAXFLOAT); /* ranks of clauses */
- Cost joinselec = 0; /* selectivity of the join predicate */
- Cost joincost = 0; /* join cost + primjoinclause cost */
- int retval = XFUNC_LOCPRD;
-
- clauselist = get_loc_restrictinfo(childpath);
-
- if (clauselist != LispNil)
- {
- /* find local predicate with maximum rank */
- for (tmplist = clauselist,
- maxcinfo = (RestrictInfo) lfirst(tmplist),
- maxrank = xfunc_rank(get_clause(maxcinfo));
- tmplist != LispNil;
- tmplist = lnext(tmplist))
- {
-
- if ((tmprank = xfunc_rank(get_clause((RestrictInfo) lfirst(tmplist))))
- > maxrank)
- {
- maxcinfo = (RestrictInfo) lfirst(tmplist);
- maxrank = tmprank;
- }
- }
- }
-
- /*
- * * If child is a join path, and there are multiple join clauses, *
- * see if any join clause has even higher rank than the highest *
- * local predicate
- */
- if (is_join(childpath) && xfunc_num_join_clauses((JoinPath) childpath) > 1)
- for (tmplist = get_pathrestrictinfo((JoinPath) childpath);
- tmplist != LispNil;
- tmplist = lnext(tmplist))
- {
-
- if (tmplist != LispNil &&
- (tmprank = xfunc_rank(get_clause((RestrictInfo) lfirst(tmplist))))
- > maxrank)
- {
- maxcinfo = (RestrictInfo) lfirst(tmplist);
- maxrank = tmprank;
- retval = XFUNC_JOINPRD;
- }
- }
- if (maxrank == (-1 * MAXFLOAT)) /* no expensive clauses */
- return 0;
-
- /*
- * * Pullup over join if clause is higher rank than join, or if * join
- * is nested loop and current path is inner child (note that *
- * restrictions on the inner of a nested loop don't buy you anything
- * -- * you still have to scan the entire inner relation each time). *
- * Note that the cost of a secondary join clause is only what's *
- * calculated by xfunc_expense(), since the actual joining * (i.e. the
- * usual path_cost) is paid for by the primary join clause.
- */
- if (primjoinclause != LispNil)
- {
- joinselec = compute_clause_selec(queryInfo, primjoinclause, LispNil);
- joincost = xfunc_join_expense(parentpath, whichchild);
-
- if (XfuncMode == XFUNC_PULLALL ||
- (XfuncMode != XFUNC_WAIT &&
- ((joincost != 0 &&
- (maxrank = xfunc_rank(get_clause(maxcinfo))) >
- ((joinselec - 1.0) / joincost))
- || (joincost == 0 && joinselec < 1)
- || (!is_join(childpath)
- && (whichchild == INNER)
- && IsA(parentpath, NestPath)
- &&!IsA(parentpath, HashPath)
- &&!IsA(parentpath, MergePath)))))
- {
-
- *maxcinfopt = maxcinfo;
- return retval;
-
- }
- else if (maxrank != -(MAXFLOAT))
- {
- /*
- * * we've left an expensive restriction below a join. Since *
- * we may pullup this restriction in predmig.c, we'd best *
- * set the RelOptInfo of this join to be unpruneable
- */
- set_pruneable(get_parent(parentpath), false);
- /* and fall through */
- }
- }
- return 0;
-}
-
-
-/*
- ** xfunc_pullup
- ** move clause from child pathnode to parent pathnode. This operation
- ** makes the child pathnode produce a larger relation than it used to.
- ** This means that we must construct a new RelOptInfo just for the childpath,
- ** although this RelOptInfo will not be added to the list of Rels to be joined up
- ** in the query; it's merely a parent for the new childpath.
- ** We also have to fix up the path costs of the child and parent.
- **
- ** Now returns a pointer to the new pulled-up RestrictInfo. -- JMH, 11/18/92
- */
-RestrictInfo
-xfunc_pullup(Query *queryInfo,
- Path childpath,
- JoinPath parentpath,
- RestrictInfo cinfo, /* clause to pull up */
- int whichchild, /* whether child is INNER or OUTER of join */
- int clausetype) /* whether clause to pull is join or local */
-{
- Path newkid;
- RelOptInfo newrel;
- Cost pulled_selec;
- Cost cost;
- RestrictInfo newinfo;
-
- /* remove clause from childpath */
- newkid = (Path) copyObject((Node) childpath);
- if (clausetype == XFUNC_LOCPRD)
- {
- set_locrestrictinfo(newkid,
- xfunc_LispRemove((LispValue) cinfo,
- (List) get_loc_restrictinfo(newkid)));
- }
- else
- {
- set_pathrestrictinfo
- ((JoinPath) newkid,
- xfunc_LispRemove((LispValue) cinfo,
- (List) get_pathrestrictinfo((JoinPath) newkid)));
- }
-
- /*
- * * give the new child path its own RelOptInfo node that reflects the *
- * lack of the pulled-up predicate
- */
- pulled_selec = compute_clause_selec(queryInfo,
- get_clause(cinfo), LispNil);
- xfunc_copyrel(get_parent(newkid), &newrel);
- set_parent(newkid, newrel);
- set_pathlist(newrel, makeList1(newkid));
- set_unorderedpath(newrel, (PathPtr) newkid);
- set_cheapestpath(newrel, (PathPtr) newkid);
- set_size(newrel,
- (Count) ((Cost) get_size(get_parent(childpath)) / pulled_selec));
-
- /*
- * * fix up path cost of newkid. To do this we subtract away all the *
- * xfunc_costs of childpath, then recompute the xfunc_costs of newkid
- */
- cost = get_path_cost(newkid) - xfunc_get_path_cost(childpath);
- Assert(cost >= 0);
- set_path_cost(newkid, cost);
- cost = get_path_cost(newkid) + xfunc_get_path_cost(newkid);
- set_path_cost(newkid, cost);
-
- /*
- * * We copy the cinfo, since it may appear in other plans, and we're
- * going * to munge it. -- JMH, 7/22/92
- */
- newinfo = (RestrictInfo) copyObject((Node) cinfo);
-
- /*
- * * Fix all vars in the clause * to point to the right varno and
- * varattno in parentpath
- */
- xfunc_fixvars(get_clause(newinfo), newrel, whichchild);
-
- /* add clause to parentpath, and fix up its cost. */
- set_locrestrictinfo(parentpath,
- lispCons((LispValue) newinfo,
- (LispValue) get_loc_restrictinfo(parentpath)));
- /* put new childpath into the path tree */
- if (whichchild == INNER)
- set_innerjoinpath(parentpath, (pathPtr) newkid);
- else
- set_outerjoinpath(parentpath, (pathPtr) newkid);
-
- /*
- * * recompute parentpath cost from scratch -- the cost * of the join
- * method has changed
- */
- cost = xfunc_total_path_cost(parentpath);
- set_path_cost(parentpath, cost);
-
- return newinfo;
-}
-
-/*
- ** calculate (selectivity-1)/cost.
- */
-Cost
-xfunc_rank(Query *queryInfo, LispValue clause)
-{
- Cost selec = compute_clause_selec(queryInfo, clause, LispNil);
- Cost cost = xfunc_expense(queryInfo, clause);
-
- if (cost == 0)
- if (selec > 1)
- return MAXFLOAT;
- else
- return -(MAXFLOAT);
- return (selec - 1) / cost;
-}
-
-/*
- ** Find the "global" expense of a clause; i.e. the local expense divided
- ** by the cardinalities of all the base relations of the query that are *not*
- ** referenced in the clause.
- */
-Cost
-xfunc_expense(Query *queryInfo, clause)
-LispValue clause;
-{
- Cost cost = xfunc_local_expense(clause);
-
- if (cost)
- {
- Count card = xfunc_card_unreferenced(queryInfo, clause, LispNil);
-
- if (card)
- cost /= card;
- }
-
- return cost;
-}
-
-/*
- ** xfunc_join_expense
- ** Find global expense of a join clause
- */
-Cost
-xfunc_join_expense(Query *queryInfo, JoinPath path, int whichchild)
-{
- LispValue primjoinclause = xfunc_primary_join(path);
-
- /*
- * * the second argument to xfunc_card_unreferenced reflects all the *
- * relations involved in the join clause, i.e. all the relids in the
- * RelOptInfo * of the join clause
- */
- Count card = 0;
- Cost cost = xfunc_expense_per_tuple(path, whichchild);
-
- card = xfunc_card_unreferenced(queryInfo,
- primjoinclause,
- get_relids(get_parent(path)));
- if (primjoinclause)
- cost += xfunc_local_expense(primjoinclause);
-
- if (card)
- cost /= card;
-
- return cost;
-}
-
-/*
- ** Recursively find the per-tuple expense of a clause. See
- ** xfunc_func_expense for more discussion.
- */
-Cost
-xfunc_local_expense(LispValue clause)
-{
- Cost cost = 0; /* running expense */
- LispValue tmpclause;
-
- /* First handle the base case */
- if (IsA(clause, Const) ||IsA(clause, Var) ||IsA(clause, Param))
- return 0;
- /* now other stuff */
- else if (IsA(clause, Iter))
- /* Too low. Should multiply by the expected number of iterations. */
- return xfunc_local_expense(get_iterexpr((Iter) clause));
- else if (IsA(clause, ArrayRef))
- return xfunc_local_expense(get_refexpr((ArrayRef) clause));
- else if (fast_is_clause(clause))
- return (xfunc_func_expense((LispValue) get_op(clause),
- (LispValue) get_opargs(clause)));
- else if (fast_is_funcclause(clause))
- return (xfunc_func_expense((LispValue) get_function(clause),
- (LispValue) get_funcargs(clause)));
- else if (fast_not_clause(clause))
- return xfunc_local_expense(lsecond(clause));
- else if (fast_or_clause(clause) || fast_and_clause(clause))
- {
- /* find cost of evaluating each disjunct */
- for (tmpclause = lnext(clause); tmpclause != LispNil;
- tmpclause = lnext(tmpclause))
- cost += xfunc_local_expense(lfirst(tmpclause));
- return cost;
- }
- else
- {
- elog(ERROR, "Clause node of undetermined type");
- return -1;
- }
-}
-
-/*
- ** xfunc_func_expense
- ** given a Func or Oper and its args, find its expense.
- ** Note: in Stonebraker's SIGMOD '91 paper, he uses a more complicated metric
- ** than the one here. We can ignore the expected number of tuples for
- ** our calculations; we just need the per-tuple expense. But he also
- ** proposes components to take into account the costs of accessing disk and
- ** archive. We didn't adopt that scheme here; eventually the vacuum
- ** cleaner should be able to tell us what percentage of bytes to find on
- ** which storage level, and that should be multiplied in appropriately
- ** in the cost function below. Right now we don't model the cost of
- ** accessing secondary or tertiary storage, since we don't have sufficient
- ** stats to do it right.
- */
-Cost
-xfunc_func_expense(LispValue node, LispValue args)
-{
- HeapTuple tupl; /* the pg_proc tuple for each function */
- Form_pg_proc proc; /* a data structure to hold the pg_proc
- * tuple */
- int width = 0; /* byte width of the field referenced by
- * each clause */
- RegProcedure funcid; /* ID of function associate with node */
- Cost cost = 0; /* running expense */
- LispValue tmpclause;
- LispValue operand; /* one operand of an operator */
-
- if (IsA(node, Oper))
- {
- /* don't trust the opid in the Oper node. Use the opno. */
- if (!(funcid = get_opcode(get_opno((Oper) node))))
- elog(ERROR, "Oper's function is undefined");
- }
- else
- funcid = get_funcid((Func) node);
-
- /* look up tuple in cache */
- tupl = SearchSysCacheTuple(PROCOID,
- ObjectIdGetDatum(funcid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tupl))
- elog(ERROR, "Cache lookup failed for procedure %u", funcid);
- proc = (Form_pg_proc) GETSTRUCT(tupl);
-
- /*
- * * if it's a Postquel function, its cost is stored in the *
- * associated plan.
- */
- if (proc->prolang == SQLlanguageId)
- {
- LispValue tmpplan;
- List planlist;
-
- if (IsA(node, Oper) ||get_func_planlist((Func) node) == LispNil)
- {
- Oid *argOidVect; /* vector of argtypes */
- char *pq_src; /* text of PQ function */
- int nargs; /* num args to PQ function */
- QueryTreeList *queryTree_list; /* dummy variable */
-
- /*
- * * plan the function, storing it in the Func node for later *
- * use by the executor.
- */
- pq_src = (char *) textout(&(proc->prosrc));
- nargs = proc->pronargs;
- if (nargs > 0)
- argOidVect = proc->proargtypes;
- planlist = (List) pg_parse_and_plan(pq_src, argOidVect, nargs,
- &parseTree_list, None, FALSE);
- if (IsA(node, Func))
- set_func_planlist((Func) node, planlist);
-
- }
- else
- { /* plan has been cached inside the Func
- * node already */
- planlist = get_func_planlist((Func) node);
- }
-
- /*
- * * Return the sum of the costs of the plans (the PQ function *
- * may have many queries in its body).
- */
- foreach(tmpplan, planlist)
- cost += get_cost((Plan) lfirst(tmpplan));
- return cost;
- }
- else
- { /* it's a C function */
-
- /*
- * * find the cost of evaluating the function's arguments * and
- * the width of the operands
- */
- for (tmpclause = args; tmpclause != LispNil;
- tmpclause = lnext(tmpclause))
- {
-
- if ((operand = lfirst(tmpclause)) != LispNil)
- {
- cost += xfunc_local_expense(operand);
- width += xfunc_width(operand);
- }
- }
-
- /*
- * * when stats become available, add in cost of accessing
- * secondary * and tertiary storage here.
- */
- return (cost +
- (Cost) proc->propercall_cpu +
- (Cost) proc->properbyte_cpu * (Cost) proc->probyte_pct / 100.00 *
- (Cost) width
-
- /*
- * Pct_of_obj_in_mem DISK_COST * proc->probyte_pct/100.00 * width
- * Pct_of_obj_on_disk + ARCH_COST * proc->probyte_pct/100.00 *
- * width Pct_of_obj_on_arch
- */
- );
- }
-}
-
-/*
- ** xfunc_width
- ** recursively find the width of a expression
- */
-
-int
-xfunc_width(LispValue clause)
-{
- Relation rd; /* Relation Descriptor */
- HeapTuple tupl; /* structure to hold a cached tuple */
- Form_pg_type type; /* structure to hold a type tuple */
- int retval = 0;
-
- if (IsA(clause, Const))
- {
- /* base case: width is the width of this constant */
- retval = get_constlen((Const) clause);
- goto exit;
- }
- else if (IsA(clause, ArrayRef))
- {
- /* base case: width is width of the refelem within the array */
- retval = get_refelemlength((ArrayRef) clause);
- goto exit;
- }
- else if (IsA(clause, Var))
- {
- /* base case: width is width of this attribute */
- tupl = SearchSysCacheTuple(TYPEOID,
- ObjectIdGetDatum(get_vartype((Var) clause)),
- 0, 0, 0);
- if (!HeapTupleIsValid(tupl))
- elog(ERROR, "Cache lookup failed for type %u",
- get_vartype((Var) clause));
- type = (Form_pg_type) GETSTRUCT(tupl);
- if (get_varattno((Var) clause) == 0)
- {
- /* clause is a tuple. Get its width */
- rd = heap_open(type->typrelid);
- retval = xfunc_tuple_width(rd);
- heap_close(rd);
- }
- else
- {
- /* attribute is a base type */
- retval = type->typlen;
- }
- goto exit;
- }
- else if (IsA(clause, Param))
- {
- if (typeidTypeRelids(get_paramtype((Param) clause)))
- {
- /* Param node returns a tuple. Find its width */
- rd = heap_open(typeidTypeRelids(get_paramtype((Param) clause)));
- retval = xfunc_tuple_width(rd);
- heap_close(rd);
- }
- else if (get_param_tlist((Param) clause) != LispNil)
- {
- /* Param node projects a complex type */
- Assert(length(get_param_tlist((Param) clause)) == 1); /* sanity */
- retval = xfunc_width((LispValue)
- get_expr(lfirst(get_param_tlist((Param) clause))));
- }
- else
- {
- /* Param node returns a base type */
- retval = typeLen(typeidType(get_paramtype((Param) clause)));
- }
- goto exit;
- }
- else if (IsA(clause, Iter))
- {
- /*
- * * An Iter returns a setof things, so return the width of a
- * single * thing. * Note: THIS MAY NOT WORK RIGHT WHEN AGGS GET
- * FIXED, * SINCE AGG FUNCTIONS CHEW ON THE WHOLE SETOF THINGS!!!! *
- * This whole Iter business is bogus, anyway.
- */
- retval = xfunc_width(get_iterexpr((Iter) clause));
- goto exit;
- }
- else if (fast_is_clause(clause))
- {
- /*
- * * get function associated with this Oper, and treat this as * a
- * Func
- */
- tupl = SearchSysCacheTuple(OPEROID,
- ObjectIdGetDatum(get_opno((Oper) get_op(clause))),
- 0, 0, 0);
- if (!HeapTupleIsValid(tupl))
- elog(ERROR, "Cache lookup failed for procedure %u",
- get_opno((Oper) get_op(clause)));
- return (xfunc_func_width
- ((RegProcedure) (((Form_pg_operator) (GETSTRUCT(tupl)))->oprcode),
- (LispValue) get_opargs(clause)));
- }
- else if (fast_is_funcclause(clause))
- {
- Func func = (Func) get_function(clause);
-
- if (get_func_tlist(func) != LispNil)
- {
- /*
- * this function has a projection on it. Get the length of
- * the projected attribute
- */
- Assert(length(get_func_tlist(func)) == 1); /* sanity */
- retval = xfunc_width((LispValue)
- get_expr(lfirst(get_func_tlist(func))));
- goto exit;
- }
- else
- {
- return (xfunc_func_width((RegProcedure) get_funcid(func),
- (LispValue) get_funcargs(clause)));
- }
- }
- else
- {
- elog(ERROR, "Clause node of undetermined type");
- return -1;
- }
-
-exit:
- if (retval == -1)
- retval = VARLEN_DEFAULT;
- return retval;
-}
-
-/*
- ** xfunc_card_unreferenced:
- ** find all relations not referenced in clause, and multiply their
- ** cardinalities. Ignore relation of cardinality 0.
- ** User may pass in referenced list, if they know it (useful
- ** for joins).
- */
-static Count
-xfunc_card_unreferenced(Query *queryInfo,
- LispValue clause, Relids referenced)
-{
- Relids unreferenced,
- allrelids = LispNil;
- LispValue temp;
-
- /* find all relids of base relations referenced in query */
- foreach(temp, queryInfo->base_rel_list)
- {
- Assert(lnext(get_relids((RelOptInfo) lfirst(temp))) == LispNil);
- allrelids = lappend(allrelids,
- lfirst(get_relids((RelOptInfo) lfirst(temp))));
- }
-
- /* find all relids referenced in query but not in clause */
- if (!referenced)
- referenced = xfunc_find_references(clause);
- unreferenced = set_difference(allrelids, referenced);
-
- return xfunc_card_product(unreferenced);
-}
-
-/*
- ** xfunc_card_product
- ** multiple together cardinalities of a list relations.
- */
-Count
-xfunc_card_product(Query *queryInfo, Relids relids)
-{
- LispValue cinfonode;
- LispValue temp;
- RelOptInfo currel;
- Cost tuples;
- Count retval = 0;
-
- foreach(temp, relids)
- {
- currel = get_rel(lfirst(temp));
- tuples = get_tuples(currel);
-
- if (tuples)
- { /* not of cardinality 0 */
- /* factor in the selectivity of all zero-cost clauses */
- foreach(cinfonode, get_restrictinfo(currel))
- {
- if (!xfunc_expense(queryInfo, get_clause((RestrictInfo) lfirst(cinfonode))))
- tuples *= compute_clause_selec(queryInfo,
- get_clause((RestrictInfo) lfirst(cinfonode)),
- LispNil);
- }
-
- if (retval == 0)
- retval = tuples;
- else
- retval *= tuples;
- }
- }
- if (retval == 0)
- retval = 1; /* saves caller from dividing by zero */
- return retval;
-}
-
-
-/*
- ** xfunc_find_references:
- ** Traverse a clause and find all relids referenced in the clause.
- */
-List
-xfunc_find_references(LispValue clause)
-{
- List retval = (List) LispNil;
- LispValue tmpclause;
-
- /* Base cases */
- if (IsA(clause, Var))
- return lispCons(lfirst(get_varid((Var) clause)), LispNil);
- else if (IsA(clause, Const) ||IsA(clause, Param))
- return (List) LispNil;
-
- /* recursion */
- else if (IsA(clause, Iter))
-
- /*
- * Too low. Should multiply by the expected number of iterations.
- * maybe
- */
- return xfunc_find_references(get_iterexpr((Iter) clause));
- else if (IsA(clause, ArrayRef))
- return xfunc_find_references(get_refexpr((ArrayRef) clause));
- else if (fast_is_clause(clause))
- {
- /* string together result of all operands of Oper */
- for (tmpclause = (LispValue) get_opargs(clause); tmpclause != LispNil;
- tmpclause = lnext(tmpclause))
- retval = nconc(retval, xfunc_find_references(lfirst(tmpclause)));
- return retval;
- }
- else if (fast_is_funcclause(clause))
- {
- /* string together result of all args of Func */
- for (tmpclause = (LispValue) get_funcargs(clause);
- tmpclause != LispNil;
- tmpclause = lnext(tmpclause))
- retval = nconc(retval, xfunc_find_references(lfirst(tmpclause)));
- return retval;
- }
- else if (fast_not_clause(clause))
- return xfunc_find_references(lsecond(clause));
- else if (fast_or_clause(clause) || fast_and_clause(clause))
- {
- /* string together result of all operands of OR */
- for (tmpclause = lnext(clause); tmpclause != LispNil;
- tmpclause = lnext(tmpclause))
- retval = nconc(retval, xfunc_find_references(lfirst(tmpclause)));
- return retval;
- }
- else
- {
- elog(ERROR, "Clause node of undetermined type");
- return (List) LispNil;
- }
-}
-
-/*
- ** xfunc_primary_join:
- ** Find the primary join clause: for Hash and Merge Joins, this is the
- ** min rank Hash or Merge clause, while for Nested Loop it's the
- ** min rank pathclause
- */
-LispValue
-xfunc_primary_join(JoinPath pathnode)
-{
- LispValue joinclauselist = get_pathrestrictinfo(pathnode);
- RestrictInfo mincinfo;
- LispValue tmplist;
- LispValue minclause = LispNil;
- Cost minrank,
- tmprank;
-
- if (IsA(pathnode, MergePath))
- {
- for (tmplist = get_path_mergeclauses((MergePath) pathnode),
- minclause = lfirst(tmplist),
- minrank = xfunc_rank(minclause);
- tmplist != LispNil;
- tmplist = lnext(tmplist))
- if ((tmprank = xfunc_rank(lfirst(tmplist)))
- < minrank)
- {
- minrank = tmprank;
- minclause = lfirst(tmplist);
- }
- return minclause;
- }
- else if (IsA(pathnode, HashPath))
- {
- for (tmplist = get_path_hashclauses((HashPath) pathnode),
- minclause = lfirst(tmplist),
- minrank = xfunc_rank(minclause);
- tmplist != LispNil;
- tmplist = lnext(tmplist))
- if ((tmprank = xfunc_rank(lfirst(tmplist)))
- < minrank)
- {
- minrank = tmprank;
- minclause = lfirst(tmplist);
- }
- return minclause;
- }
-
- /* if we drop through, it's nested loop join */
- if (joinclauselist == LispNil)
- return LispNil;
-
- for (tmplist = joinclauselist, mincinfo = (RestrictInfo) lfirst(joinclauselist),
- minrank = xfunc_rank(get_clause((RestrictInfo) lfirst(tmplist)));
- tmplist != LispNil;
- tmplist = lnext(tmplist))
- if ((tmprank = xfunc_rank(get_clause((RestrictInfo) lfirst(tmplist))))
- < minrank)
- {
- minrank = tmprank;
- mincinfo = (RestrictInfo) lfirst(tmplist);
- }
- return (LispValue) get_clause(mincinfo);
-}
-
-/*
- ** xfunc_get_path_cost
- ** get the expensive function costs of the path
- */
-Cost
-xfunc_get_path_cost(Query *queryInfo, Path pathnode)
-{
- Cost cost = 0;
- LispValue tmplist;
- Cost selec = 1.0;
-
- /*
- * * first add in the expensive local function costs. * We ensure that
- * the clauses are sorted by rank, so that we * know (via
- * selectivities) the number of tuples that will be checked * by each
- * function. If we're not doing any optimization of expensive *
- * functions, we don't sort.
- */
- if (XfuncMode != XFUNC_OFF)
- set_locrestrictinfo(pathnode, lisp_qsort(get_loc_restrictinfo(pathnode),
- xfunc_cinfo_compare));
- for (tmplist = get_loc_restrictinfo(pathnode), selec = 1.0;
- tmplist != LispNil;
- tmplist = lnext(tmplist))
- {
- cost += (Cost) (xfunc_local_expense(get_clause((RestrictInfo) lfirst(tmplist)))
- * (Cost) get_tuples(get_parent(pathnode)) * selec);
- selec *= compute_clause_selec(queryInfo,
- get_clause((RestrictInfo) lfirst(tmplist)),
- LispNil);
- }
-
- /*
- * * Now add in any node-specific expensive function costs. * Again,
- * we must ensure that the clauses are sorted by rank.
- */
- if (IsA(pathnode, JoinPath))
- {
- if (XfuncMode != XFUNC_OFF)
- set_pathrestrictinfo((JoinPath) pathnode, lisp_qsort
- (get_pathrestrictinfo((JoinPath) pathnode),
- xfunc_cinfo_compare));
- for (tmplist = get_pathrestrictinfo((JoinPath) pathnode), selec = 1.0;
- tmplist != LispNil;
- tmplist = lnext(tmplist))
- {
- cost += (Cost) (xfunc_local_expense(get_clause((RestrictInfo) lfirst(tmplist)))
- * (Cost) get_tuples(get_parent(pathnode)) * selec);
- selec *= compute_clause_selec(queryInfo,
- get_clause((RestrictInfo) lfirst(tmplist)),
- LispNil);
- }
- }
- if (IsA(pathnode, HashPath))
- {
- if (XfuncMode != XFUNC_OFF)
- set_path_hashclauses
- ((HashPath) pathnode,
- lisp_qsort(get_path_hashclauses((HashPath) pathnode),
- xfunc_clause_compare));
- for (tmplist = get_path_hashclauses((HashPath) pathnode), selec = 1.0;
- tmplist != LispNil;
- tmplist = lnext(tmplist))
- {
- cost += (Cost) (xfunc_local_expense(lfirst(tmplist))
- * (Cost) get_tuples(get_parent(pathnode)) * selec);
- selec *= compute_clause_selec(queryInfo,
- lfirst(tmplist), LispNil);
- }
- }
- if (IsA(pathnode, MergePath))
- {
- if (XfuncMode != XFUNC_OFF)
- set_path_mergeclauses
- ((MergePath) pathnode,
- lisp_qsort(get_path_mergeclauses((MergePath) pathnode),
- xfunc_clause_compare));
- for (tmplist = get_path_mergeclauses((MergePath) pathnode), selec = 1.0;
- tmplist != LispNil;
- tmplist = lnext(tmplist))
- {
- cost += (Cost) (xfunc_local_expense(lfirst(tmplist))
- * (Cost) get_tuples(get_parent(pathnode)) * selec);
- selec *= compute_clause_selec(queryInfo,
- lfirst(tmplist), LispNil);
- }
- }
- Assert(cost >= 0);
- return cost;
-}
-
-/*
- ** Recalculate the cost of a path node. This includes the basic cost of the
- ** node, as well as the cost of its expensive functions.
- ** We need to do this to the parent after pulling a clause from a child into a
- ** parent. Thus we should only be calling this function on JoinPaths.
- */
-Cost
-xfunc_total_path_cost(JoinPath pathnode)
-{
- Cost cost = xfunc_get_path_cost((Path) pathnode);
-
- Assert(IsA(pathnode, JoinPath));
- if (IsA(pathnode, MergePath))
- {
- MergePath mrgnode = (MergePath) pathnode;
-
- cost += cost_mergejoin(get_path_cost((Path) get_outerjoinpath(mrgnode)),
- get_path_cost((Path) get_innerjoinpath(mrgnode)),
- get_outersortkeys(mrgnode),
- get_innersortkeys(mrgnode),
- get_tuples(get_parent((Path) get_outerjoinpath
- (mrgnode))),
- get_tuples(get_parent((Path) get_innerjoinpath
- (mrgnode))),
- get_width(get_parent((Path) get_outerjoinpath
- (mrgnode))),
- get_width(get_parent((Path) get_innerjoinpath
- (mrgnode))));
- Assert(cost >= 0);
- return cost;
- }
- else if (IsA(pathnode, HashPath))
- {
- HashPath hashnode = (HashPath) pathnode;
-
- cost += cost_hashjoin(get_path_cost((Path) get_outerjoinpath(hashnode)),
- get_path_cost((Path) get_innerjoinpath(hashnode)),
- get_outerhashkeys(hashnode),
- get_innerhashkeys(hashnode),
- get_tuples(get_parent((Path) get_outerjoinpath
- (hashnode))),
- get_tuples(get_parent((Path) get_innerjoinpath
- (hashnode))),
- get_width(get_parent((Path) get_outerjoinpath
- (hashnode))),
- get_width(get_parent((Path) get_innerjoinpath
- (hashnode))));
- Assert(cost >= 0);
- return cost;
- }
- else
-/* Nested Loop Join */
- {
- cost += cost_nestloop(get_path_cost((Path) get_outerjoinpath(pathnode)),
- get_path_cost((Path) get_innerjoinpath(pathnode)),
- get_tuples(get_parent((Path) get_outerjoinpath
- (pathnode))),
- get_tuples(get_parent((Path) get_innerjoinpath
- (pathnode))),
- get_pages(get_parent((Path) get_outerjoinpath
- (pathnode))),
- IsA(get_innerjoinpath(pathnode), IndexPath));
- Assert(cost >= 0);
- return cost;
- }
-}
-
-
-/*
- ** xfunc_expense_per_tuple
- ** return the expense of the join *per-tuple* of the input relation.
- ** The cost model here is that a join costs
- ** k*card(outer)*card(inner) + l*card(outer) + m*card(inner) + n
- **
- ** We treat the l and m terms by considering them to be like restrictions
- ** constrained to be right under the join. Thus the cost per inner and
- ** cost per outer of the join is different, reflecting these virtual nodes.
- **
- ** The cost per tuple of outer is k + l/referenced(inner). Cost per tuple
- ** of inner is k + m/referenced(outer).
- ** The constants k, l, m and n depend on the join method. Measures here are
- ** based on the costs in costsize.c, with fudging for HashJoin and Sorts to
- ** make it fit our model (the 'q' in HashJoin results in a
- ** card(outer)/card(inner) term, and sorting results in a log term.
-
- */
-Cost
-xfunc_expense_per_tuple(JoinPath joinnode, int whichchild)
-{
- RelOptInfo outerrel = get_parent((Path) get_outerjoinpath(joinnode));
- RelOptInfo innerrel = get_parent((Path) get_innerjoinpath(joinnode));
- Count outerwidth = get_width(outerrel);
- Count outers_per_page = ceil(BLCKSZ / (outerwidth + MinTupleSize));
-
- if (IsA(joinnode, HashPath))
- {
- if (whichchild == INNER)
- return (1 + cpu_page_weight) * outers_per_page / NBuffers;
- else
- return (((1 + cpu_page_weight) * outers_per_page / NBuffers)
- + cpu_page_weight
- / xfunc_card_product(get_relids(innerrel)));
- }
- else if (IsA(joinnode, MergePath))
- {
- /* assumes sort exists, and costs one (I/O + CPU) per tuple */
- if (whichchild == INNER)
- return ((2 * cpu_page_weight + 1)
- / xfunc_card_product(get_relids(outerrel)));
- else
- return ((2 * cpu_page_weight + 1)
- / xfunc_card_product(get_relids(innerrel)));
- }
- else
-/* nestloop */
- {
- Assert(IsA(joinnode, JoinPath));
- return cpu_page_weight;
- }
-}
-
-/*
- ** xfunc_fixvars
- ** After pulling up a clause, we must walk its expression tree, fixing Var
- ** nodes to point to the correct varno (either INNER or OUTER, depending
- ** on which child the clause was pulled from), and the right varattno in the
- ** target list of the child's former relation. If the target list of the
- ** child RelOptInfo does not contain the attribute we need, we add it.
- */
-void
-xfunc_fixvars(LispValue clause, /* clause being pulled up */
- RelOptInfo rel, /* rel it's being pulled from */
- int varno) /* whether rel is INNER or OUTER of join */
-{
- LispValue tmpclause; /* temporary variable */
- TargetEntry *tle; /* tlist member corresponding to var */
-
-
- if (IsA(clause, Const) ||IsA(clause, Param))
- return;
- else if (IsA(clause, Var))
- {
- /* here's the meat */
- tle = tlistentry_member((Var) clause, get_targetlist(rel));
- if (tle == LispNil)
- {
- /*
- * * The attribute we need is not in the target list, * so we
- * have to add it. *
- *
- */
- add_var_to_tlist(rel, (Var) clause);
- tle = tlistentry_member((Var) clause, get_targetlist(rel));
- }
- set_varno(((Var) clause), varno);
- set_varattno(((Var) clause), get_resno(get_resdom(get_entry(tle))));
- }
- else if (IsA(clause, Iter))
- xfunc_fixvars(get_iterexpr((Iter) clause), rel, varno);
- else if (fast_is_clause(clause))
- {
- xfunc_fixvars(lfirst(lnext(clause)), rel, varno);
- xfunc_fixvars(lfirst(lnext(lnext(clause))), rel, varno);
- }
- else if (fast_is_funcclause(clause))
- for (tmpclause = lnext(clause); tmpclause != LispNil;
- tmpclause = lnext(tmpclause))
- xfunc_fixvars(lfirst(tmpclause), rel, varno);
- else if (fast_not_clause(clause))
- xfunc_fixvars(lsecond(clause), rel, varno);
- else if (fast_or_clause(clause) || fast_and_clause(clause))
- for (tmpclause = lnext(clause); tmpclause != LispNil;
- tmpclause = lnext(tmpclause))
- xfunc_fixvars(lfirst(tmpclause), rel, varno);
- else
- elog(ERROR, "Clause node of undetermined type");
-}
-
-
-/*
- ** Comparison function for lisp_qsort() on a list of RestrictInfo's.
- ** arg1 and arg2 should really be of type (RestrictInfo *).
- */
-int
-xfunc_cinfo_compare(void *arg1, void *arg2)
-{
- RestrictInfo info1 = *(RestrictInfo *) arg1;
- RestrictInfo info2 = *(RestrictInfo *) arg2;
-
- LispValue clause1 = (LispValue) get_clause(info1),
- clause2 = (LispValue) get_clause(info2);
-
- return xfunc_clause_compare((void *) &clause1, (void *) &clause2);
-}
-
-/*
- ** xfunc_clause_compare: comparison function for lisp_qsort() that compares two
- ** clauses based on expense/(1 - selectivity)
- ** arg1 and arg2 are really pointers to clauses.
- */
-int
-xfunc_clause_compare(void *arg1, void *arg2)
-{
- LispValue clause1 = *(LispValue *) arg1;
- LispValue clause2 = *(LispValue *) arg2;
- Cost rank1, /* total xfunc rank of clause1 */
- rank2; /* total xfunc rank of clause2 */
-
- rank1 = xfunc_rank(clause1);
- rank2 = xfunc_rank(clause2);
-
- if (rank1 < rank2)
- return -1;
- else if (rank1 == rank2)
- return 0;
- else
- return 1;
-}
-
-/*
- ** xfunc_disjunct_sort
- ** given a list of clauses, for each clause sort the disjuncts by cost
- ** (this assumes the predicates have been converted to Conjunctive NF)
- ** Modifies the clause list!
- */
-void
-xfunc_disjunct_sort(LispValue clause_list)
-{
- LispValue temp;
-
- foreach(temp, clause_list)
- if (or_clause(lfirst(temp)))
- lnext(lfirst(temp)) = lisp_qsort(lnext(lfirst(temp)), xfunc_disjunct_compare);
-}
-
-
-/*
- ** xfunc_disjunct_compare: comparison function for qsort() that compares two
- ** disjuncts based on cost/selec.
- ** arg1 and arg2 are really pointers to disjuncts
- */
-int
-xfunc_disjunct_compare(Query *queryInfo, void *arg1, void *arg2)
-{
- LispValue disjunct1 = *(LispValue *) arg1;
- LispValue disjunct2 = *(LispValue *) arg2;
- Cost cost1, /* total cost of disjunct1 */
- cost2, /* total cost of disjunct2 */
- selec1,
- selec2;
- Cost rank1,
- rank2;
-
- cost1 = xfunc_expense(queryInfo, disjunct1);
- cost2 = xfunc_expense(queryInfo, disjunct2);
- selec1 = compute_clause_selec(queryInfo,
- disjunct1, LispNil);
- selec2 = compute_clause_selec(queryInfo,
- disjunct2, LispNil);
-
- if (selec1 == 0)
- rank1 = MAXFLOAT;
- else if (cost1 == 0)
- rank1 = 0;
- else
- rank1 = cost1 / selec1;
-
- if (selec2 == 0)
- rank2 = MAXFLOAT;
- else if (cost2 == 0)
- rank2 = 0;
- else
- rank2 = cost2 / selec2;
-
- if (rank1 < rank2)
- return -1;
- else if (rank1 == rank2)
- return 0;
- else
- return 1;
-}
-
-/* ------------------------ UTILITY FUNCTIONS ------------------------------- */
-/*
- ** xfunc_func_width
- ** Given a function OID and operands, find the width of the return value.
- */
-int
-xfunc_func_width(RegProcedure funcid, LispValue args)
-{
- Relation rd; /* Relation Descriptor */
- HeapTuple tupl; /* structure to hold a cached tuple */
- Form_pg_proc proc; /* structure to hold the pg_proc tuple */
- Form_pg_type type; /* structure to hold the pg_type tuple */
- LispValue tmpclause;
- int retval;
-
- /* lookup function and find its return type */
- Assert(RegProcedureIsValid(funcid));
- tupl = SearchSysCacheTuple(PROCOID,
- ObjectIdGetDatum(funcid),
- 0, 0, 0);
- if (!HeapTupleIsValid(tupl))
- elog(ERROR, "Cache lookup failed for procedure %u", funcid);
- proc = (Form_pg_proc) GETSTRUCT(tupl);
-
- /* if function returns a tuple, get the width of that */
- if (typeidTypeRelids(proc->prorettype))
- {
- rd = heap_open(typeidTypeRelids(proc->prorettype));
- retval = xfunc_tuple_width(rd);
- heap_close(rd);
- goto exit;
- }
- else
-/* function returns a base type */
- {
- tupl = SearchSysCacheTuple(TYPEOID,
- ObjectIdGetDatum(proc->prorettype),
- 0, 0, 0);
- if (!HeapTupleIsValid(tupl))
- elog(ERROR, "Cache lookup failed for type %u", proc->prorettype);
- type = (Form_pg_type) GETSTRUCT(tupl);
- /* if the type length is known, return that */
- if (type->typlen != -1)
- {
- retval = type->typlen;
- goto exit;
- }
- else
-/* estimate the return size */
- {
- /* find width of the function's arguments */
- for (tmpclause = args; tmpclause != LispNil;
- tmpclause = lnext(tmpclause))
- retval += xfunc_width(lfirst(tmpclause));
- /* multiply by outin_ratio */
- retval = (int) (proc->prooutin_ratio / 100.0 * retval);
- goto exit;
- }
- }
-exit:
- return retval;
-}
-
-/*
- ** xfunc_tuple_width
- ** Return the sum of the lengths of all the attributes of a given relation
- */
-int
-xfunc_tuple_width(Relation rd)
-{
- int i;
- int retval = 0;
- TupleDesc tdesc = RelationGetDescr(rd);
-
- for (i = 0; i < tdesc->natts; i++)
- {
- if (tdesc->attrs[i]->attlen != -1)
- retval += tdesc->attrs[i]->attlen;
- else
- retval += VARLEN_DEFAULT;
- }
-
- return retval;
-}
-
-/*
- ** xfunc_num_join_clauses
- ** Find the number of join clauses associated with this join path
- */
-int
-xfunc_num_join_clauses(JoinPath path)
-{
- int num = length(get_pathrestrictinfo(path));
-
- if (IsA(path, MergePath))
- return num + length(get_path_mergeclauses((MergePath) path));
- else if (IsA(path, HashPath))
- return num + length(get_path_hashclauses((HashPath) path));
- else
- return num;
-}
-
-/*
- ** xfunc_LispRemove
- ** Just like LispRemove, but it whines if the item to be removed ain't there
- */
-LispValue
-xfunc_LispRemove(LispValue foo, List bar)
-{
- LispValue temp = LispNil;
- LispValue result = LispNil;
- int sanity = false;
-
- for (temp = bar; !null(temp); temp = lnext(temp))
- if (!equal((Node) (foo), (Node) (lfirst(temp))))
- result = lappend(result, lfirst(temp));
- else
- sanity = true; /* found a matching item to remove! */
-
- if (!sanity)
- elog(ERROR, "xfunc_LispRemove: didn't find a match!");
-
- return result;
-}
-
-#define Node_Copy(a, b, c, d) \
-do { \
- if (NodeCopy((Node)((a)->d), (Node*)&((b)->d), c) != true) \
- { \
- return false; \
- } \
-} while(0)
-
-/*
- ** xfunc_copyrel
- ** Just like _copyRel, but doesn't copy the paths
- */
-bool
-xfunc_copyrel(RelOptInfo from, RelOptInfo *to)
-{
- RelOptInfo newnode;
-
- Pointer (*alloc) () = palloc;
-
- /* COPY_CHECKARGS() */
- if (to == NULL)
- return false;
-
- /* COPY_CHECKNULL() */
- if (from == NULL)
- {
- (*to) = NULL;
- return true;
- }
-
- /* COPY_NEW(c) */
- newnode = (RelOptInfo) (*alloc) (classSize(RelOptInfo));
- if (newnode == NULL)
- return false;
-
- /*
- * copy node superclass fields
- */
- CopyNodeFields((Node) from, (Node) newnode, alloc);
-
- /*
- * copy remainder of node
- */
- Node_Copy(from, newnode, alloc, relids);
-
- newnode->indexed = from->indexed;
- newnode->pages = from->pages;
- newnode->tuples = from->tuples;
- newnode->size = from->size;
- newnode->width = from->width;
-
- Node_Copy(from, newnode, alloc, targetlist);
-
- /*
- * No!!!! Node_Copy(from, newnode, alloc, pathlist);
- * Node_Copy(from, newnode, alloc, unorderedpath); Node_Copy(from,
- * newnode, alloc, cheapestpath);
- */
-#if 0 /* can't use Node_copy now. 2/95 -ay */
- Node_Copy(from, newnode, alloc, classlist);
- Node_Copy(from, newnode, alloc, indexkeys);
- Node_Copy(from, newnode, alloc, ordering);
-#endif
- Node_Copy(from, newnode, alloc, restrictinfo);
- Node_Copy(from, newnode, alloc, joininfo);
- Node_Copy(from, newnode, alloc, innerjoin);
-
- (*to) = newnode;
- return true;
-}
+++ /dev/null
-/*-------------------------------------------------------------------------
- *
- * 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 */