diff options
author | Bruce Momjian | 1999-02-24 17:29:06 +0000 |
---|---|---|
committer | Bruce Momjian | 1999-02-24 17:29:06 +0000 |
commit | 6df955fd3b6e77ac58a491598c7e97fdb047afa4 (patch) | |
tree | 9a70fec19b03e2e785788f500221ee9f1fa73f8d /src/backend/commands/_deadcode | |
parent | f3f7c2acdeaa19b2d85d0f1522ebdfab1e466210 (diff) |
Fix problem with selectivity error in added columns with ALTER TABLE.
Move files to deadcode.
Diffstat (limited to 'src/backend/commands/_deadcode')
-rw-r--r-- | src/backend/commands/_deadcode/recipe.c | 1333 | ||||
-rw-r--r-- | src/backend/commands/_deadcode/recipe.h | 19 |
2 files changed, 1352 insertions, 0 deletions
diff --git a/src/backend/commands/_deadcode/recipe.c b/src/backend/commands/_deadcode/recipe.c new file mode 100644 index 00000000000..0c8f3985ad6 --- /dev/null +++ b/src/backend/commands/_deadcode/recipe.c @@ -0,0 +1,1333 @@ +/*------------------------------------------------------------------------- + * + * recipe.c + * routines for handling execution of Tioga recipes + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/_deadcode/Attic/recipe.c,v 1.1 1999/02/24 17:29:00 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include <postgres.h> + +#include <nodes/parsenodes.h> +#include <nodes/plannodes.h> +#include <nodes/execnodes.h> +#include <nodes/makefuncs.h> +#include <catalog/pg_type.h> +#include <commands/recipe.h> +#include <libpq/libpq-be.h> +#include <parser/parse_node.h> +#include <utils/builtins.h> +#include <utils/relcache.h> /* for RelationNameGetRelation */ +#include <rewrite/rewriteHandler.h> +#include <rewrite/rewriteManip.h> +#include <tcop/pquery.h> +#include <tcop/dest.h> +#include <optimizer/planner.h> +#include <executor/executor.h> + +/* from tcop/postgres.c */ +extern CommandDest whereToSendOutput; + +#ifndef TIOGA + +void +beginRecipe(RecipeStmt *stmt) +{ + elog(NOTICE, "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(NOTICE, + "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(NOTICE, "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, + * setheapoverride() allows us to see the changes + */ + setheapoverride(true); + 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(NOTICE, "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) */ + + setheapoverride(false); + + /* 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(NOTICE, "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(NOTICE, "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(NOTICE, "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 < 8; i++) + typev[i] = 0; + for (j = 0; j < elem->inTypes->num; j++) + { + if (parameterCount == 8) + { + elog(ERROR, + "getParamTypes: Ingredients cannot take > 8 arguments"); + } + 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(NOTICE, "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[8], /* 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(NOTICE, "calling parser with %s", elem->src); +#endif /* DEBUG_RECIPE */ + + parameterCount = getParamTypes(elem, typev); + + qList = parser(elem->src, typev, parameterCount); + + if (qList->len > 1) + { + elog(NOTICE, + "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", newquery, i); + snprintf(newquery, 1000, "%s)", newquery); + } + else + snprintf(newquery, 1000, "select %s()", funcName); + +#ifdef DEBUG_RECIPE + elog(NOTICE, "calling parser with %s", newquery); +#endif /* DEBUG_RECIPE */ + + qList = parser(newquery, typev, parameterCount); + if (qList->len > 1) + { + elog(NOTICE, + "tg_parseSubQuery: parser produced > 1 query tree"); + } + } + break; + case TG_RECIPE_GRAPH: + elog(NOTICE, "tg_parseSubQuery: can't parse recipe graph ingredients yet!"); + break; + case TG_COMPILED: + elog(NOTICE, "tg_parseSubQuery: can't parse compiled ingredients yet!"); + break; + default: + elog(NOTICE, "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(NOTICE, "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(NOTICE, "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(NOTICE, "tg_parseSubQuery: can't handle embedded recipes yet!"); + else + elog(NOTICE, "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(NOTICE, "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(NOTICE, "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/recipe.h b/src/backend/commands/_deadcode/recipe.h new file mode 100644 index 00000000000..b0c6f9faee3 --- /dev/null +++ b/src/backend/commands/_deadcode/recipe.h @@ -0,0 +1,19 @@ +/*------------------------------------------------------------------------- + * + * recipe.h + * recipe handling routines + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: recipe.h,v 1.1 1999/02/24 17:29:00 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef RECIPE_H +#define RECIPE_H + +#include "nodes/parsenodes.h" + +extern void beginRecipe(RecipeStmt *stmt); + +#endif /* RECIPE_H */ |