From 43515ba3f8523e492143e2a1a1159022b5424431 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Wed, 24 Jul 2002 19:16:43 +0000 Subject: [PATCH] Remove _deadcode. --- src/backend/commands/_deadcode/recipe.c | 1318 --------------- src/backend/commands/_deadcode/version.c | 346 ---- src/backend/executor/_deadcode/nodeTee.c | 499 ------ .../optimizer/path/_deadcode/predmig.c | 810 --------- src/backend/optimizer/path/_deadcode/xfunc.c | 1479 ----------------- src/include/optimizer/_deadcode/xfunc.h | 83 - 6 files changed, 4535 deletions(-) delete mode 100644 src/backend/commands/_deadcode/recipe.c delete mode 100644 src/backend/commands/_deadcode/version.c delete mode 100644 src/backend/executor/_deadcode/nodeTee.c delete mode 100644 src/backend/optimizer/path/_deadcode/predmig.c delete mode 100644 src/backend/optimizer/path/_deadcode/xfunc.c delete mode 100644 src/include/optimizer/_deadcode/xfunc.h diff --git a/src/backend/commands/_deadcode/recipe.c b/src/backend/commands/_deadcode/recipe.c deleted file mode 100644 index f1b3d84ab4..0000000000 --- a/src/backend/commands/_deadcode/recipe.c +++ /dev/null @@ -1,1318 +0,0 @@ -/*------------------------------------------------------------------------- - * - * 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 */ diff --git a/src/backend/commands/_deadcode/version.c b/src/backend/commands/_deadcode/version.c deleted file mode 100644 index a88247e684..0000000000 --- a/src/backend/commands/_deadcode/version.c +++ /dev/null @@ -1,346 +0,0 @@ -/*------------------------------------------------------------------------- - * - * version.c - * This file contains all the rules that govern all version semantics. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * The version stuff has not been tested under postgres95 and probably - * doesn't work! - jolly 8/19/95 - * - * - * $Id: version.c,v 1.30 2002/06/20 20:29:27 momjian Exp $ - * - * NOTES - * At the point the version is defined, 2 physical relations are created - * _added and _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 - * _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 _added relation. - * 2. Retrieve all tuples in the base relation which are not in - * the _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 _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 _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 _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 _del relation. - * 2.2 A copy of the tuple is appended to the _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 diff --git a/src/backend/executor/_deadcode/nodeTee.c b/src/backend/executor/_deadcode/nodeTee.c deleted file mode 100644 index e02f8cb59f..0000000000 --- a/src/backend/executor/_deadcode/nodeTee.c +++ /dev/null @@ -1,499 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeTee.c - * - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * DESCRIPTION - * This code provides support for a tee node, which allows - * multiple parent in a megaplan. - * - * INTERFACE ROUTINES - * ExecTee - * ExecInitTee - * ExecEndTee - * - * $Id: nodeTee.c,v 1.12 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ - -#include -#include -#include "postgres.h" - -#include "access/heapam.h" -#include "catalog/catalog.h" -#include "catalog/heap.h" -#include "executor/executor.h" -#include "executor/nodeTee.h" -#include "optimizer/internal.h" -#include "storage/bufmgr.h" -#include "storage/smgr.h" -#include "tcop/pquery.h" -#include "utils/relcache.h" - -/* ------------------------------------------------------------------ - * ExecInitTee - * - * Create tee state - * - * ------------------------------------------------------------------ - */ -bool -ExecInitTee(Tee * node, EState *currentEstate, Plan *parent) -{ - TeeState *teeState; - Plan *outerPlan; - int len; - Relation bufferRel; - TupleDesc tupType; - EState *estate; - - /* - * it is possible that the Tee has already been initialized since it - * can be reached by multiple parents. If it is already initialized, - * simply return and do not initialize the children nodes again - */ - if (node->plan.state) - return TRUE; - - /* - * assign the node's execution state - */ - - /* - * make a new executor state, because we have a different - * es_range_table - */ - -/* node->plan.state = estate;*/ - - estate = CreateExecutorState(); - estate->es_direction = currentEstate->es_direction; - estate->es_BaseId = currentEstate->es_BaseId; - estate->es_BaseId = currentEstate->es_BaseId; - estate->es_tupleTable = currentEstate->es_tupleTable; - estate->es_refcount = currentEstate->es_refcount; - estate->es_junkFilter = currentEstate->es_junkFilter; - estate->es_snapshot = currentEstate->es_snapshot; - - /* - * use the range table for Tee subplan since the range tables for the - * two parents may be different - */ - if (node->rtentries) - estate->es_range_table = node->rtentries; - else - estate->es_range_table = currentEstate->es_range_table; - - node->plan.state = estate; - - - /* - * create teeState structure - */ - teeState = makeNode(TeeState); - teeState->tee_leftPlace = 0; - teeState->tee_rightPlace = 0; - teeState->tee_lastPlace = 0; - teeState->tee_bufferRel = NULL; - teeState->tee_leftScanDesc = NULL; - teeState->tee_rightScanDesc = NULL; - - - node->teestate = teeState; - - /* ---------------- - * Miscellanious initialization - * - * + assign node's base_id - * + assign debugging hooks and - * + create expression context for node - * ---------------- - */ - ExecAssignNodeBaseInfo(estate, &(teeState->cstate), parent); - ExecAssignExprContext(estate, &(teeState->cstate)); - -#define TEE_NSLOTS 2 - - /* - * initialize tuple slots - */ - ExecInitResultTupleSlot(estate, &(teeState->cstate)); - - /* initialize child nodes */ - outerPlan = outerPlan((Plan *) node); - ExecInitNode(outerPlan, estate, (Plan *) node); - - /* - * the tuple type info is from the outer plan of this node the result - * type is also the same as the outerplan - */ - ExecAssignResultTypeFromOuterPlan((Plan *) node, &(teeState->cstate)); - ExecAssignProjectionInfo((Plan *) node, &teeState->cstate); - - /* - * initialize temporary relation to buffer tuples - */ - tupType = ExecGetResultType(&(teeState->cstate)); - len = ExecTargetListLength(((Plan *) node)->targetlist); - - /* - * create a catalogued relation even though this is a temporary - * relation - */ - /* cleanup of catalogued relations is easier to do */ - - if (node->teeTableName[0] != '\0') - { - Relation r; - - teeState->tee_bufferRelname = pstrdup(node->teeTableName); - - /* - * we are given an tee table name, if a relation by that name - * exists, then we open it, else we create it and then open it - */ - r = RelationNameGetRelation(teeState->tee_bufferRelname); - - if (RelationIsValid(r)) - bufferRel = heap_openr(teeState->tee_bufferRelname); - else - bufferRel = heap_open( - heap_create_with_catalog(teeState->tee_bufferRelname, - tupType, RELKIND_RELATION, false)); - } - else - { - sprintf(teeState->tee_bufferRelname, - "ttemp_%d", /* 'ttemp' for 'tee' temporary */ - newoid()); - bufferRel = heap_open( - heap_create_with_catalog(teeState->tee_bufferRelname, - tupType, RELKIND_RELATION, false)); - } - - teeState->tee_bufferRel = bufferRel; - - /* - * initialize a memory context for allocating thing like scan - * descriptors - */ - - /* - * we do this so that on cleanup of the tee, we can free things. if we - * didn't have our own memory context, we would be in the memory - * context of the portal that we happen to be using at the moment - */ - - teeState->tee_mcxt = (MemoryContext) CreateGlobalMemory(teeState->tee_bufferRelname); - - /* - * don't initialize the scan descriptors here because it's not good to - * initialize scan descriptors on empty rels. Wait until the scan - * descriptors are needed before initializing them. - */ - - teeState->tee_leftScanDesc = NULL; - teeState->tee_rightScanDesc = NULL; - - return TRUE; -} - -int -ExecCountSlotsTee(Tee * node) -{ - /* Tee nodes can't have innerPlans */ - return ExecCountSlotsNode(outerPlan(node)) + TEE_NSLOTS; -} - -/* ---------------------------------------------------------------- - initTeeScanDescs - initializes the left and right scandescs on the temporary - relation of a Tee node - - must open two separate scan descriptors, - because the left and right scans may be at different points -* ---------------------------------------------------------------- -*/ -static void -initTeeScanDescs(Tee * node) -{ - TeeState *teeState; - Relation bufferRel; - ScanDirection dir; - Snapshot snapshot; - MemoryContext orig; - - teeState = node->teestate; - if (teeState->tee_leftScanDesc && teeState->tee_rightScanDesc) - return; - - orig = CurrentMemoryContext; - MemoryContextSwitchTo(teeState->tee_mcxt); - - bufferRel = teeState->tee_bufferRel; - dir = ((Plan *) node)->state->es_direction; /* backwards not handled - * yet XXX */ - snapshot = ((Plan *) node)->state->es_snapshot; - - if (teeState->tee_leftScanDesc == NULL) - { - teeState->tee_leftScanDesc = heap_beginscan(bufferRel, - ScanDirectionIsBackward(dir), - snapshot, - 0, /* num scan keys */ - NULL /* scan keys */ - ); - } - if (teeState->tee_rightScanDesc == NULL) - { - teeState->tee_rightScanDesc = heap_beginscan(bufferRel, - ScanDirectionIsBackward(dir), - snapshot, - 0, /* num scan keys */ - NULL /* scan keys */ - ); - } - - MemoryContextSwitchTo(orig); -} - -/* ---------------------------------------------------------------- - * ExecTee(node) - * - * - * A Tee serves to connect a subplan to multiple parents. - * the subplan is always the outplan of the Tee node. - * - * The Tee gets requests from either leftParent or rightParent, - * fetches the result tuple from the child, and then - * stored the result into a temporary relation (serving as a queue). - * leftPlace and rightPlace keep track of where the left and rightParents - * are. - * If a parent requests a tuple and that parent is not at the end - * of the temporary relation, then the request is satisfied from - * the queue instead of by executing the child plan - * - * ---------------------------------------------------------------- - */ - -TupleTableSlot * -ExecTee(Tee * node, Plan *parent) -{ - EState *estate; - TeeState *teeState; - int leftPlace, - rightPlace, - lastPlace; - int branch; - TupleTableSlot *result; - TupleTableSlot *slot; - Plan *childNode; - ScanDirection dir; - HeapTuple heapTuple; - Relation bufferRel; - HeapScanDesc scanDesc; - - estate = ((Plan *) node)->state; - teeState = node->teestate; - leftPlace = teeState->tee_leftPlace; - rightPlace = teeState->tee_rightPlace; - lastPlace = teeState->tee_lastPlace; - bufferRel = teeState->tee_bufferRel; - - childNode = outerPlan(node); - - dir = estate->es_direction; - - /* XXX doesn't handle backwards direction yet */ - - if (parent == node->leftParent) - branch = leftPlace; - else if ((parent == node->rightParent) || (parent == (Plan *) node)) - - /* - * the tee node could be the root node of the plan, in which case, - * we treat it like a right-parent pull - */ - branch = rightPlace; - else - { - elog(ERROR, "A Tee node can only be executed from its left or right parent\n"); - return NULL; - } - - if (branch == lastPlace) - { /* we're at the end of the queue already, - * - get a new tuple from the child plan, - * - store it in the queue, - increment - * lastPlace, - increment leftPlace or - * rightPlace as appropriate, - and return - * result */ - slot = ExecProcNode(childNode, (Plan *) node); - if (!TupIsNull(slot)) - { - /* - * heap_insert changes something... - */ - if (slot->ttc_buffer != InvalidBuffer) - heapTuple = heap_copytuple(slot->val); - else - heapTuple = slot->val; - - /* insert into temporary relation */ - heap_insert(bufferRel, heapTuple); - - if (slot->ttc_buffer != InvalidBuffer) - heap_freetuple(heapTuple); - - /* - * once there is data in the temporary relation, ensure that - * the left and right scandescs are initialized - */ - initTeeScanDescs(node); - - scanDesc = (parent == node->leftParent) ? - teeState->tee_leftScanDesc : teeState->tee_rightScanDesc; - - { - /* - * move the scandesc forward so we don't re-read this - * tuple later - */ - HeapTuple throwAway; - - /* Buffer buffer; */ - throwAway = heap_getnext(scanDesc, ScanDirectionIsBackward(dir)); - } - - /* - * set the shouldFree field of the child's slot so that when - * the child's slot is free'd, this tuple isn't free'd also - */ - - /* - * does this mean this tuple has to be garbage collected - * later?? - */ - slot->ttc_shouldFree = false; - - teeState->tee_lastPlace = lastPlace + 1; - } - result = slot; - } - else - { /* the desired data already exists in the - * temporary relation */ - scanDesc = (parent == node->leftParent) ? - teeState->tee_leftScanDesc : teeState->tee_rightScanDesc; - - heapTuple = heap_getnext(scanDesc, ScanDirectionIsBackward(dir)); - - /* - * Increase the pin count on the buffer page, because the tuple - * stored in the slot also points to it (as well as the scan - * descriptor). If we don't, ExecStoreTuple will decrease the pin - * count on the next iteration. - */ - - if (scanDesc->rs_cbuf != InvalidBuffer) - IncrBufferRefCount(scanDesc->rs_cbuf); - - slot = teeState->cstate.cs_ResultTupleSlot; - slot->ttc_tupleDescriptor = RelationGetDescr(bufferRel); - - result = ExecStoreTuple(heapTuple, /* tuple to store */ - slot, /* slot to store in */ - scanDesc->rs_cbuf, /* this tuple's buffer */ - false); /* don't free stuff from - * heap_getnext */ - - } - - if (parent == node->leftParent) - teeState->tee_leftPlace = leftPlace + 1; - else - teeState->tee_rightPlace = rightPlace + 1; - - return result; -} - -/* --------------------------------------------------------------------- - * ExecEndTee - * - * End the Tee node, and free up any storage - * since a Tee node can be downstream of multiple parent nodes, - * only free when both parents are done - * -------------------------------------------------------------------- - */ - -void -ExecEndTee(Tee * node, Plan *parent) -{ - EState *estate; - TeeState *teeState; - int leftPlace, - rightPlace, - lastPlace; - Relation bufferRel; - MemoryContext orig; - - estate = ((Plan *) node)->state; - teeState = node->teestate; - leftPlace = teeState->tee_leftPlace; - rightPlace = teeState->tee_rightPlace; - lastPlace = teeState->tee_lastPlace; - - if (!node->leftParent || parent == node->leftParent) - leftPlace = -1; - - if (!node->rightParent || parent == node->rightParent) - rightPlace = -1; - - if (parent == (Plan *) node) - rightPlace = leftPlace = -1; - - teeState->tee_leftPlace = leftPlace; - teeState->tee_rightPlace = rightPlace; - if ((leftPlace == -1) && (rightPlace == -1)) - { - /* remove the temporary relations */ - /* and close the scan descriptors */ - - bufferRel = teeState->tee_bufferRel; - if (bufferRel) - { - heap_drop(bufferRel); - teeState->tee_bufferRel = NULL; - if (teeState->tee_mcxt) - { - orig = CurrentMemoryContext; - MemoryContextSwitchTo(teeState->tee_mcxt); - } - else - orig = 0; - - if (teeState->tee_leftScanDesc) - { - heap_endscan(teeState->tee_leftScanDesc); - teeState->tee_leftScanDesc = NULL; - } - if (teeState->tee_rightScanDesc) - { - heap_endscan(teeState->tee_rightScanDesc); - teeState->tee_rightScanDesc = NULL; - } - - if (teeState->tee_mcxt) - { - MemoryContextSwitchTo(orig); - teeState->tee_mcxt = NULL; - } - } - } - -} diff --git a/src/backend/optimizer/path/_deadcode/predmig.c b/src/backend/optimizer/path/_deadcode/predmig.c deleted file mode 100644 index 5ce4083ab5..0000000000 --- a/src/backend/optimizer/path/_deadcode/predmig.c +++ /dev/null @@ -1,810 +0,0 @@ -/*------------------------------------------------------------------------- - * - * predmig.c - * - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/_deadcode/Attic/predmig.c,v 1.15 2002/06/20 20:29:30 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* -** DESCRIPTION -** Main Routines to handle Predicate Migration (i.e. correct optimization -** of queries with expensive functions.) -** -** The reasoning behind some of these algorithms is rather detailed. -** Have a look at Sequoia Tech Report 92/13 for more info. Also -** see Monma and Sidney's paper "Sequencing with Series-Parallel -** Precedence Constraints", in "Mathematics of Operations Research", -** volume 4 (1979), pp. 215-224. -** -** The main thing that this code does that wasn't handled in xfunc.c is -** it considers the possibility that two joins in a stream may not -** be ordered by ascending rank -- in such a scenario, it may be optimal -** to pullup more restrictions than we did via xfunc_try_pullup. -** -** This code in some sense generalizes xfunc_try_pullup; if you -** run postgres -x noprune, you'll turn off xfunc_try_pullup, and this -** code will do everything that xfunc_try_pullup would have, and maybe -** more. However, this results in no pruning, which may slow down the -** optimizer and/or cause the system to run out of memory. -** -- JMH, 11/13/92 -*/ - -#include "nodes/pg_list.h" -#include "nodes/nodes.h" -#include "nodes/primnodes.h" -#include "nodes/relation.h" -#include "optimizer/pathnode.h" -#include "optimizer/internal.h" -#include "optimizer/cost.h" -#include "optimizer/keys.h" -#include "optimizer/tlist.h" - -#define is_clause(node) (get_cinfo(node)) /* a stream node - * represents a clause - * (not a join) iff it has - * a non-NULL cinfo field */ - -static void xfunc_predmig(JoinPath pathnode, Stream streamroot, - Stream laststream, bool *progressp); -static bool xfunc_series_llel(Stream stream); -static bool xfunc_llel_chains(Stream root, Stream bottom); -static Stream xfunc_complete_stream(Stream stream); -static bool xfunc_prdmig_pullup(Stream origstream, Stream pullme, - JoinPath joinpath); -static void xfunc_form_groups(Stream root, Stream bottom); -static void xfunc_free_stream(Stream root); -static Stream xfunc_add_clauses(Stream current); -static void xfunc_setup_group(Stream node, Stream bottom); -static Stream xfunc_streaminsert(RestrictInfo restrictinfo, Stream current, - int clausetype); -static int xfunc_num_relids(Stream node); -static StreamPtr xfunc_get_downjoin(Stream node); -static StreamPtr xfunc_get_upjoin(Stream node); -static Stream xfunc_stream_qsort(Stream root, Stream bottom); -static int xfunc_stream_compare(void *arg1, void *arg2); -static bool xfunc_check_stream(Stream node); -static bool xfunc_in_stream(Stream node, Stream stream); - -/* ----------------- MAIN FUNCTIONS ------------------------ */ -/* -** xfunc_do_predmig -** wrapper for Predicate Migration. It calls xfunc_predmig until no -** more progress is made. -** return value says if any changes were ever made. -*/ -bool -xfunc_do_predmig(Path root) -{ - bool progress, - changed = false; - - if (is_join(root)) - do - { - progress = false; - Assert(IsA(root, JoinPath)); - xfunc_predmig((JoinPath) root, (Stream) NULL, (Stream) NULL, - &progress); - if (changed && progress) - elog(DEBUG, "Needed to do a second round of predmig!\n"); - if (progress) - changed = true; - } while (progress); - return changed; -} - - -/* - ** xfunc_predmig - ** The main routine for Predicate Migration. It traverses a join tree, - ** and for each root-to-leaf path in the plan tree it constructs a - ** "Stream", which it passes to xfunc_series_llel for optimization. - ** Destructively modifies the join tree (via predicate pullup). - */ -static void -xfunc_predmig(JoinPath pathnode, /* root of the join tree */ - Stream streamroot, - Stream laststream,/* for recursive calls -- these are the - * root of the stream under construction, - * and the lowest node created so far */ - bool *progressp) -{ - Stream newstream; - - /* - * * traverse the join tree dfs-style, constructing a stream as you - * go. * When you hit a scan node, pass the stream off to - * xfunc_series_llel. - */ - - /* sanity check */ - if ((!streamroot && laststream) || - (streamroot && !laststream)) - elog(ERROR, "called xfunc_predmig with bad inputs"); - if (streamroot) - Assert(xfunc_check_stream(streamroot)); - - /* add path node to stream */ - newstream = RMakeStream(); - if (!streamroot) - streamroot = newstream; - set_upstream(newstream, (StreamPtr) laststream); - if (laststream) - set_downstream(laststream, (StreamPtr) newstream); - set_downstream(newstream, (StreamPtr) NULL); - set_pathptr(newstream, (pathPtr) pathnode); - set_cinfo(newstream, (RestrictInfo) NULL); - set_clausetype(newstream, XFUNC_UNKNOWN); - - /* base case: we're at a leaf, call xfunc_series_llel */ - if (!is_join(pathnode)) - { - /* form a fleshed-out copy of the stream */ - Stream fullstream = xfunc_complete_stream(streamroot); - - /* sort it via series-llel */ - if (xfunc_series_llel(fullstream)) - *progressp = true; - - /* free up the copy */ - xfunc_free_stream(fullstream); - } - else - { - /* visit left child */ - xfunc_predmig((JoinPath) get_outerjoinpath(pathnode), - streamroot, newstream, progressp); - - /* visit right child */ - xfunc_predmig((JoinPath) get_innerjoinpath(pathnode), - streamroot, newstream, progressp); - } - - /* remove this node */ - if (get_upstream(newstream)) - set_downstream((Stream) get_upstream(newstream), (StreamPtr) NULL); - pfree(newstream); -} - -/* - ** xfunc_series_llel - ** A flavor of Monma and Sidney's Series-Parallel algorithm. - ** Traverse stream downwards. When you find a node with restrictions on it, - ** call xfunc_llel_chains on the substream from root to that node. - */ -static bool -xfunc_series_llel(Stream stream) -{ - Stream temp, - next; - bool progress = false; - - for (temp = stream; temp != (Stream) NULL; temp = next) - { - next = (Stream) xfunc_get_downjoin(temp); - - /* - * * if there are restrictions/secondary join clauses above this * - * node, call xfunc_llel_chains - */ - if (get_upstream(temp) && is_clause((Stream) get_upstream(temp))) - if (xfunc_llel_chains(stream, temp)) - progress = true; - } - return progress; -} - -/* - ** xfunc_llel_chains - ** A flavor of Monma and Sidney's Parallel Chains algorithm. - ** Given a stream which has been well-ordered except for its lowermost - ** restrictions/2-ary joins, pull up the restrictions/2-arys as appropriate. - ** What that means here is to form groups in the chain above the lowest - ** join node above bottom inclusive, and then take all the restrictions - ** following bottom, and try to pull them up as far as possible. - */ -static bool -xfunc_llel_chains(Stream root, Stream bottom) -{ - bool progress = false; - Stream origstream; - Stream tmpstream, - pathstream; - Stream rootcopy = root; - - Assert(xfunc_check_stream(root)); - - /* xfunc_prdmig_pullup will need an unmodified copy of the stream */ - origstream = (Stream) copyObject((Node) root); - - /* form groups among ill-ordered nodes */ - xfunc_form_groups(root, bottom); - - /* sort chain by rank */ - Assert(xfunc_in_stream(bottom, root)); - rootcopy = xfunc_stream_qsort(root, bottom); - - /* - * * traverse sorted stream -- if any restriction has moved above a - * join, * we must pull it up in the plan. That is, make plan tree * - * reflect order of sorted stream. - */ - for (tmpstream = rootcopy, - pathstream = (Stream) xfunc_get_downjoin(rootcopy); - tmpstream != (Stream) NULL && pathstream != (Stream) NULL; - tmpstream = (Stream) get_downstream(tmpstream)) - { - if (is_clause(tmpstream) - && get_pathptr(pathstream) != get_pathptr(tmpstream)) - { - /* - * * If restriction moved above a Join after sort, we pull it * - * up in the join plan. * If restriction moved down, we - * ignore it. * This is because Joey's Sequoia paper proves - * that * restrictions should never move down. If this * one - * were moved down, it would violate "semantic correctness", * - * i.e. it would be lower than the attributes it references. - */ - Assert(xfunc_num_relids(pathstream) > xfunc_num_relids(tmpstream)); - progress = xfunc_prdmig_pullup(origstream, tmpstream, - (JoinPath) get_pathptr(pathstream)); - } - if (get_downstream(tmpstream)) - pathstream = (Stream) xfunc_get_downjoin((Stream) get_downstream(tmpstream)); - } - - /* free up origstream */ - xfunc_free_stream(origstream); - return progress; -} - -/* - ** xfunc_complete_stream - ** Given a stream composed of join nodes only, make a copy containing the - ** join nodes along with the associated restriction nodes. - */ -static Stream -xfunc_complete_stream(Stream stream) -{ - Stream tmpstream, - copystream, - curstream = (Stream) NULL; - - copystream = (Stream) copyObject((Node) stream); - Assert(xfunc_check_stream(copystream)); - - curstream = copystream; - Assert(!is_clause(curstream)); - - /* curstream = (Stream)xfunc_get_downjoin(curstream); */ - - while (curstream != (Stream) NULL) - { - xfunc_add_clauses(curstream); - curstream = (Stream) xfunc_get_downjoin(curstream); - } - - /* find top of stream and return it */ - for (tmpstream = copystream; get_upstream(tmpstream) != (StreamPtr) NULL; - tmpstream = (Stream) get_upstream(tmpstream)) - /* no body in for loop */ ; - - return tmpstream; -} - -/* - ** xfunc_prdmig_pullup - ** pullup a clause in a path above joinpath. Since the JoinPath tree - ** doesn't have upward pointers, it's difficult to deal with. Thus we - ** require the original stream, which maintains pointers to all the path - ** nodes. We use the original stream to find out what joins are - ** above the clause. - */ -static bool -xfunc_prdmig_pullup(Stream origstream, Stream pullme, JoinPath joinpath) -{ - RestrictInfo restrictinfo = get_cinfo(pullme); - bool progress = false; - Stream upjoin, - orignode, - temp; - int whichchild; - - /* find node in origstream that contains clause */ - for (orignode = origstream; - orignode != (Stream) NULL - && get_cinfo(orignode) != restrictinfo; - orignode = (Stream) get_downstream(orignode)) - /* empty body in for loop */ ; - if (!orignode) - elog(ERROR, "Didn't find matching node in original stream"); - - - /* pull up this node as far as it should go */ - for (upjoin = (Stream) xfunc_get_upjoin(orignode); - upjoin != (Stream) NULL - && (JoinPath) get_pathptr((Stream) xfunc_get_downjoin(upjoin)) - != joinpath; - upjoin = (Stream) xfunc_get_upjoin(upjoin)) - { -#ifdef DEBUG - elog(DEBUG, "pulling up in xfunc_predmig_pullup!"); -#endif - /* move clause up in path */ - if (get_pathptr((Stream) get_downstream(upjoin)) - == (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(upjoin))) - whichchild = OUTER; - else - whichchild = INNER; - restrictinfo = xfunc_pullup((Path) get_pathptr((Stream) get_downstream(upjoin)), - (JoinPath) get_pathptr(upjoin), - restrictinfo, - whichchild, - get_clausetype(orignode)); - set_pathptr(pullme, get_pathptr(upjoin)); - /* pullme has been moved into locrestrictinfo */ - set_clausetype(pullme, XFUNC_LOCPRD); - - /* - * * xfunc_pullup makes new path nodes for children of * - * get_pathptr(current). We must modify the stream nodes to point * - * to these path nodes - */ - if (whichchild == OUTER) - { - for (temp = (Stream) get_downstream(upjoin); is_clause(temp); - temp = (Stream) get_downstream(temp)) - set_pathptr - (temp, (pathPtr) - get_outerjoinpath((JoinPath) get_pathptr(upjoin))); - set_pathptr - (temp, - (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(upjoin))); - } - else - { - for (temp = (Stream) get_downstream(upjoin); is_clause(temp); - temp = (Stream) get_downstream(temp)) - set_pathptr - (temp, (pathPtr) - get_innerjoinpath((JoinPath) get_pathptr(upjoin))); - set_pathptr - (temp, (pathPtr) - get_innerjoinpath((JoinPath) get_pathptr(upjoin))); - } - progress = true; - } - if (!progress) - elog(DEBUG, "didn't succeed in pulling up in xfunc_prdmig_pullup"); - return progress; -} - -/* - ** xfunc_form_groups - ** A group is a pair of stream nodes a,b such that a is constrained to - ** precede b (for instance if a and b are both joins), but rank(a) > rank(b). - ** In such a situation, Monma and Sidney prove that no clauses should end - ** up between a and b, and therefore we may treat them as a group, with - ** selectivity equal to the product of their selectivities, and cost - ** equal to the cost of the first plus the selectivity of the first times the - ** cost of the second. We define each node to be in a group by itself, - ** and then repeatedly find adjacent groups which are ordered by descending - ** rank, and make larger groups. You know that two adjacent nodes are in a - ** group together if the lower has groupup set to true. They will both have - ** the same groupcost and groupsel (since they're in the same group!) - */ -static void -xfunc_form_groups(Query *queryInfo, Stream root, Stream bottom) -{ - Stream temp, - parent; - int lowest = xfunc_num_relids((Stream) xfunc_get_upjoin(bottom)); - bool progress; - LispValue primjoin; - int whichchild; - - if (!lowest) - return; /* no joins in stream, so no groups */ - - /* initialize groups to be single nodes */ - for (temp = root; - temp != (Stream) NULL && temp != bottom; - temp = (Stream) get_downstream(temp)) - { - /* if a Join node */ - if (!is_clause(temp)) - { - if (get_pathptr((Stream) get_downstream(temp)) - == (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(temp))) - whichchild = OUTER; - else - whichchild = INNER; - set_groupcost(temp, - xfunc_join_expense((JoinPath) get_pathptr(temp), - whichchild)); - if (primjoin = xfunc_primary_join((JoinPath) get_pathptr(temp))) - { - set_groupsel(temp, - compute_clause_selec(queryInfo, - primjoin, NIL)); - } - else - set_groupsel(temp, 1.0); - } - else -/* a restriction, or 2-ary join pred */ - { - set_groupcost(temp, - xfunc_expense(queryInfo, - get_clause(get_cinfo(temp)))); - set_groupsel(temp, - compute_clause_selec(queryInfo, - get_clause(get_cinfo(temp)), - NIL)); - } - set_groupup(temp, false); - } - - /* make passes upwards, forming groups */ - do - { - progress = false; - for (temp = (Stream) get_upstream(bottom); - temp != (Stream) NULL; - temp = (Stream) get_upstream(temp)) - { - /* check for grouping with node upstream */ - if (!get_groupup(temp) && /* not already grouped */ - (parent = (Stream) get_upstream(temp)) != (Stream) NULL && - /* temp is a join or temp is the top of a group */ - (is_join((Path) get_pathptr(temp)) || - get_downstream(temp) && - get_groupup((Stream) get_downstream(temp))) && - get_grouprank(parent) < get_grouprank(temp)) - { - progress = true; /* we formed a new group */ - set_groupup(temp, true); - set_groupcost(temp, - get_groupcost(temp) + - get_groupsel(temp) * get_groupcost(parent)); - set_groupsel(temp, get_groupsel(temp) * get_groupsel(parent)); - - /* fix costs and sels of all members of group */ - xfunc_setup_group(temp, bottom); - } - } - } while (progress); -} - - -/* ------------------- UTILITY FUNCTIONS ------------------------- */ - -/* - ** xfunc_free_stream - ** walk down a stream and pfree it - */ -static void -xfunc_free_stream(Stream root) -{ - Stream cur, - next; - - Assert(xfunc_check_stream(root)); - - if (root != (Stream) NULL) - for (cur = root; cur != (Stream) NULL; cur = next) - { - next = (Stream) get_downstream(cur); - pfree(cur); - } -} - -/* - ** xfunc_add<_clauses - ** find any clauses above current, and insert them into stream as - ** appropriate. Return uppermost clause inserted, or current if none. - */ -static Stream -xfunc_add_clauses(Stream current) -{ - Stream topnode = current; - LispValue temp; - LispValue primjoin; - - /* first add in the local clauses */ - foreach(temp, get_loc_restrictinfo((Path) get_pathptr(current))) - { - topnode = xfunc_streaminsert((RestrictInfo) lfirst(temp), topnode, - XFUNC_LOCPRD); - } - - /* and add in the join clauses */ - if (IsA(get_pathptr(current), JoinPath)) - { - primjoin = xfunc_primary_join((JoinPath) get_pathptr(current)); - foreach(temp, get_pathrestrictinfo((JoinPath) get_pathptr(current))) - { - if (!equal(get_clause((RestrictInfo) lfirst(temp)), primjoin)) - topnode = xfunc_streaminsert((RestrictInfo) lfirst(temp), topnode, - XFUNC_JOINPRD); - } - } - return topnode; -} - - -/* - ** xfunc_setup_group - ** find all elements of stream that are grouped with node and are above - ** bottom, and set their groupcost and groupsel to be the same as node's. - */ -static void -xfunc_setup_group(Stream node, Stream bottom) -{ - Stream temp; - - if (node != bottom) - /* traverse downwards */ - for (temp = (Stream) get_downstream(node); - temp != (Stream) NULL && temp != bottom; - temp = (Stream) get_downstream(temp)) - { - if (!get_groupup(temp)) - break; - else - { - set_groupcost(temp, get_groupcost(node)); - set_groupsel(temp, get_groupsel(node)); - } - } - - /* traverse upwards */ - for (temp = (Stream) get_upstream(node); temp != (Stream) NULL; - temp = (Stream) get_upstream(temp)) - { - if (!get_groupup((Stream) get_downstream(temp))) - break; - else - { - set_groupcost(temp, get_groupcost(node)); - set_groupsel(temp, get_groupsel(node)); - } - } -} - - -/* - ** xfunc_streaminsert - ** Make a new Stream node to hold clause, and insert it above current. - ** Return new node. - */ -static Stream -xfunc_streaminsert(RestrictInfo restrictinfo, - Stream current, - int clausetype) /* XFUNC_LOCPRD or XFUNC_JOINPRD */ -{ - Stream newstream = RMakeStream(); - - set_upstream(newstream, get_upstream(current)); - if (get_upstream(current)) - set_downstream((Stream) (get_upstream(current)), (StreamPtr) newstream); - set_upstream(current, (StreamPtr) newstream); - set_downstream(newstream, (StreamPtr) current); - set_pathptr(newstream, get_pathptr(current)); - set_cinfo(newstream, restrictinfo); - set_clausetype(newstream, clausetype); - return newstream; -} - -/* - ** Given a Stream node, find the number of relids referenced in the pathnode - ** associated with the stream node. The number of relids gives a unique - ** ordering on the joins in a stream, which we use to compare the height of - ** join nodes. - */ -static int -xfunc_num_relids(Stream node) -{ - if (!node || !IsA(get_pathptr(node), JoinPath)) - return 0; - else - return (length - (get_relids(get_parent((JoinPath) get_pathptr(node))))); -} - -/* - ** xfunc_get_downjoin - ** Given a stream node, find the next lowest node which points to a - ** join predicate or a scan node. - */ -static StreamPtr -xfunc_get_downjoin(Stream node) -{ - Stream temp; - - if (!is_clause(node)) /* if this is a join */ - node = (Stream) get_downstream(node); - for (temp = node; temp && is_clause(temp); - temp = (Stream) get_downstream(temp)) - /* empty body in for loop */ ; - - return (StreamPtr) temp; -} - -/* - ** xfunc_get_upjoin - ** same as above, but upwards. - */ -static StreamPtr -xfunc_get_upjoin(Stream node) -{ - Stream temp; - - if (!is_clause(node)) /* if this is a join */ - node = (Stream) get_upstream(node); - for (temp = node; temp && is_clause(temp); - temp = (Stream) get_upstream(temp)) - /* empty body in for loop */ ; - - return (StreamPtr) temp; -} - -/* - ** xfunc_stream_qsort - ** Given a stream, sort by group rank the elements in the stream from the - ** node "bottom" up. DESTRUCTIVELY MODIFIES STREAM! Returns new root. - */ -static Stream -xfunc_stream_qsort(Stream root, Stream bottom) -{ - int i; - size_t num; - Stream *nodearray, - output; - Stream tmp; - - /* find size of list */ - for (num = 0, tmp = root; tmp != bottom; - tmp = (Stream) get_downstream(tmp)) - num++; - if (num <= 1) - return root; - - /* copy elements of the list into an array */ - nodearray = (Stream *) palloc(num * sizeof(Stream)); - - for (tmp = root, i = 0; tmp != bottom; - tmp = (Stream) get_downstream(tmp), i++) - nodearray[i] = tmp; - - /* sort the array */ - qsort(nodearray, num, sizeof(LispValue), xfunc_stream_compare); - - /* paste together the array elements */ - output = nodearray[num - 1]; - set_upstream(output, (StreamPtr) NULL); - for (i = num - 2; i >= 0; i--) - { - set_downstream(nodearray[i + 1], (StreamPtr) nodearray[i]); - set_upstream(nodearray[i], (StreamPtr) nodearray[i + 1]); - } - set_downstream(nodearray[0], (StreamPtr) bottom); - if (bottom) - set_upstream(bottom, (StreamPtr) nodearray[0]); - - Assert(xfunc_check_stream(output)); - return output; -} - -/* - ** xfunc_stream_compare - ** comparison function for xfunc_stream_qsort. - ** Compare nodes by group rank. If group ranks are equal, ensure that - ** join nodes appear in same order as in plan tree. - */ -static int -xfunc_stream_compare(void *arg1, void *arg2) -{ - Stream stream1 = *(Stream *) arg1; - Stream stream2 = *(Stream *) arg2; - Cost rank1, - rank2; - - rank1 = get_grouprank(stream1); - rank2 = get_grouprank(stream2); - - if (rank1 > rank2) - return 1; - else if (rank1 < rank2) - return -1; - else - { - if (is_clause(stream1) && is_clause(stream2)) - return 0; /* doesn't matter what order if both are - * restrictions */ - else if (!is_clause(stream1) && !is_clause(stream2)) - { - if (xfunc_num_relids(stream1) < xfunc_num_relids(stream2)) - return -1; - else - return 1; - } - else if (is_clause(stream1) && !is_clause(stream2)) - { - if (xfunc_num_relids(stream1) == xfunc_num_relids(stream2)) - /* stream1 is a restriction over stream2 */ - return 1; - else - return -1; - } - else if (!is_clause(stream1) && is_clause(stream2)) - { - /* stream2 is a restriction over stream1: never push down */ - return -1; - } - } -} - -/* ------------------ DEBUGGING ROUTINES ---------------------------- */ - -/* - ** Make sure all pointers in stream make sense. Make sure no joins are - ** out of order. - */ -static bool -xfunc_check_stream(Stream node) -{ - Stream temp; - int numrelids, - tmp; - - /* set numrelids higher than max */ - if (!is_clause(node)) - numrelids = xfunc_num_relids(node) + 1; - else if (xfunc_get_downjoin(node)) - numrelids = xfunc_num_relids((Stream) xfunc_get_downjoin(node)) + 1; - else - numrelids = 1; - - for (temp = node; get_downstream(temp); temp = (Stream) get_downstream(temp)) - { - if ((Stream) get_upstream((Stream) get_downstream(temp)) != temp) - { - elog(ERROR, "bad pointers in stream"); - return false; - } - if (!is_clause(temp)) - { - if ((tmp = xfunc_num_relids(temp)) >= numrelids) - { - elog(ERROR, "Joins got reordered!"); - return false; - } - numrelids = tmp; - } - } - - return true; -} - -/* - ** xfunc_in_stream - ** check if node is in stream - */ -static bool -xfunc_in_stream(Stream node, Stream stream) -{ - Stream temp; - - for (temp = stream; temp; temp = (Stream) get_downstream(temp)) - if (temp == node) - return 1; - return 0; -} diff --git a/src/backend/optimizer/path/_deadcode/xfunc.c b/src/backend/optimizer/path/_deadcode/xfunc.c deleted file mode 100644 index 80087652c7..0000000000 --- a/src/backend/optimizer/path/_deadcode/xfunc.c +++ /dev/null @@ -1,1479 +0,0 @@ -/*------------------------------------------------------------------------- - * - * 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 - -#ifdef HAVE_VALUES_H -#include -#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; -} diff --git a/src/include/optimizer/_deadcode/xfunc.h b/src/include/optimizer/_deadcode/xfunc.h deleted file mode 100644 index 3bd5645f1a..0000000000 --- a/src/include/optimizer/_deadcode/xfunc.h +++ /dev/null @@ -1,83 +0,0 @@ -/*------------------------------------------------------------------------- - * - * xfunc.h - * prototypes for xfunc.c and predmig.c. - * - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * $Id: xfunc.h,v 1.9 2002/06/20 20:29:51 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#ifndef XFUNC_H -#define XFUNC_H - -#include "nodes/relation.h" -#include "utils/rel.h" - -/* command line arg flags */ -#define XFUNC_OFF -1 /* do no optimization of expensive preds */ -#define XFUNC_NOR 2 /* do no optimization of OR clauses */ -#define XFUNC_NOPULL 4 /* never pull restrictions above joins */ -#define XFUNC_NOPM 8 /* don't do predicate migration */ -#define XFUNC_WAIT 16 /* don't do pullup until predicate - * migration */ -#define XFUNC_PULLALL 32 /* pull all expensive restrictions up, - * always */ - -/* constants for local and join predicates */ -#define XFUNC_LOCPRD 1 -#define XFUNC_JOINPRD 2 -#define XFUNC_UNKNOWN 0 - -extern int XfuncMode; /* defined in tcop/postgres.c */ - -/* default width assumed for variable length attributes */ -#define VARLEN_DEFAULT 128; - -/* Macro to get group rank out of group cost and group sel */ -#define get_grouprank(a) ((get_groupsel(a) - 1) / get_groupcost(a)) - -/* Macro to see if a path node is actually a Join */ -#define is_join(pathnode) (length(get_relids(get_parent(pathnode))) > 1 ? 1 : 0) - -/* function prototypes from planner/path/xfunc.c */ -extern void xfunc_trypullup(RelOptInfo *rel); -extern int xfunc_shouldpull(Path *childpath, JoinPath *parentpath, - int whichchild, RestrictInfo *maxcinfopt); -extern RestrictInfo *xfunc_pullup(Path *childpath, JoinPath *parentpath, RestrictInfo *cinfo, - int whichchild, int clausetype); -extern Cost xfunc_rank(Expr *clause); -extern Cost xfunc_expense(Query *queryInfo, Expr *clause); -extern Cost xfunc_join_expense(JoinPath *path, int whichchild); -extern Cost xfunc_local_expense(Expr *clause); -extern Cost xfunc_func_expense(Expr *node, List *args); -extern int xfunc_width(Expr *clause); - -/* static, moved to xfunc.c */ -/* extern int xfunc_card_unreferenced(Expr *clause, Relids referenced); */ -extern int xfunc_card_product(Relids relids); -extern List *xfunc_find_references(List *clause); -extern List *xfunc_primary_join(JoinPath *pathnode); -extern Cost xfunc_get_path_cost(Path *pathnode); -extern Cost xfunc_total_path_cost(JoinPath *pathnode); -extern Cost xfunc_expense_per_tuple(JoinPath *joinnode, int whichchild); -extern void xfunc_fixvars(Expr *clause, RelOptInfo *rel, int varno); -extern int xfunc_cinfo_compare(void *arg1, void *arg2); -extern int xfunc_clause_compare(void *arg1, void *arg2); -extern void xfunc_disjunct_sort(List *clause_list); -extern int xfunc_disjunct_compare(void *arg1, void *arg2); -extern int xfunc_func_width(RegProcedure funcid, List *args); -extern int xfunc_tuple_width(Relation rd); -extern int xfunc_num_join_clauses(JoinPath *path); -extern List *xfunc_LispRemove(List *foo, List *bar); -extern bool xfunc_copyrel(RelOptInfo *from, RelOptInfo **to); - -/* - * function prototypes for path/predmig.c - */ -extern bool xfunc_do_predmig(Path root); - -#endif /* XFUNC_H */ -- 2.40.0