From 6df955fd3b6e77ac58a491598c7e97fdb047afa4 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Wed, 24 Feb 1999 17:29:06 +0000 Subject: Fix problem with selectivity error in added columns with ALTER TABLE. Move files to deadcode. --- src/backend/commands/Makefile | 4 +- src/backend/commands/_deadcode/recipe.c | 1333 +++++++++++++++++++++++++++++++ src/backend/commands/_deadcode/recipe.h | 19 + src/backend/commands/command.c | 4 +- src/backend/commands/defind.c | 576 ------------- src/backend/commands/indexcmds.c | 576 +++++++++++++ src/backend/commands/recipe.c | 1333 ------------------------------- src/backend/tcop/utility.c | 4 +- src/include/commands/recipe.h | 19 - src/include/nodes/nodes.h | 3 +- 10 files changed, 1936 insertions(+), 1935 deletions(-) create mode 100644 src/backend/commands/_deadcode/recipe.c create mode 100644 src/backend/commands/_deadcode/recipe.h delete mode 100644 src/backend/commands/defind.c create mode 100644 src/backend/commands/indexcmds.c delete mode 100644 src/backend/commands/recipe.c delete mode 100644 src/include/commands/recipe.h (limited to 'src') diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile index b4a935181d..36a7f105d7 100644 --- a/src/backend/commands/Makefile +++ b/src/backend/commands/Makefile @@ -4,7 +4,7 @@ # Makefile for commands # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.16 1998/10/08 18:37:14 momjian Exp $ +# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.17 1999/02/24 17:28:57 momjian Exp $ # #------------------------------------------------------------------------- @@ -17,7 +17,7 @@ ifdef MULTIBYTE CFLAGS+= $(MBFLAGS) endif -OBJS = async.o creatinh.o command.o copy.o defind.o define.o \ +OBJS = async.o creatinh.o command.o copy.o indexcmds.o define.o \ remove.o rename.o vacuum.o view.o cluster.o \ recipe.o explain.o sequence.o trigger.o user.o proclang.o \ dbcommands.o variable.o diff --git a/src/backend/commands/_deadcode/recipe.c b/src/backend/commands/_deadcode/recipe.c new file mode 100644 index 0000000000..0c8f3985ad --- /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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for RelationNameGetRelation */ +#include +#include +#include +#include +#include +#include + +/* 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 + +#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 0000000000..b0c6f9faee --- /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 */ diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 9d5a496209..ca5cbeff9e 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.38 1999/02/13 23:15:03 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.39 1999/02/24 17:28:57 momjian Exp $ * * NOTES * The PortalExecutorHeapMemory crap needs to be eliminated @@ -476,7 +476,7 @@ PerformAddAttribute(char *relationName, namestrcpy(&(attribute->attname), colDef->colname); attribute->atttypid = typeTuple->t_data->t_oid; attribute->attlen = form->typlen; - attributeD.attdisbursion = 0; + attribute->attdisbursion = 0; attribute->attcacheoff = -1; attribute->atttypmod = colDef->typename->typmod; attribute->attnum = i; diff --git a/src/backend/commands/defind.c b/src/backend/commands/defind.c deleted file mode 100644 index b5c991c9d7..0000000000 --- a/src/backend/commands/defind.c +++ /dev/null @@ -1,576 +0,0 @@ -/*------------------------------------------------------------------------- - * - * defind.c - * POSTGRES define, extend and remove index code. - * - * Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/defind.c,v 1.34 1999/02/13 23:15:05 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for getrelid() */ -#include -#include -#include -#include - -#define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args!=NULL) - -/* non-export function prototypes */ -static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid); -static void CheckPredExpr(Node *predicate, List *rangeTable, - Oid baseRelOid); -static void - CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid); -static void FuncIndexArgs(IndexElem *funcIndex, AttrNumber *attNumP, - Oid *argTypes, Oid *opOidP, Oid relId); -static void NormIndexAttrs(List *attList, AttrNumber *attNumP, - Oid *opOidP, Oid relId); -static char *GetDefaultOpClass(Oid atttypid); - -/* - * DefineIndex - * Creates a new index. - * - * 'attributeList' is a list of IndexElem specifying either a functional - * index or a list of attributes to index on. - * 'parameterList' is a list of ParamString specified in the with clause. - * 'predicate' is the qual specified in the where clause. - * 'rangetable' is for the predicate - * - * Exceptions: - * XXX - */ -void -DefineIndex(char *heapRelationName, - char *indexRelationName, - char *accessMethodName, - List *attributeList, - List *parameterList, - bool unique, - bool primary, - Expr *predicate, - List *rangetable) -{ - Oid *classObjectId; - Oid accessMethodId; - Oid relationId; - int numberOfAttributes; - AttrNumber *attributeNumberA; - HeapTuple tuple; - uint16 parameterCount = 0; - Datum *parameterA = NULL; - FuncIndexInfo fInfo; - List *cnfPred = NULL; - bool lossy = FALSE; - List *pl; - - /* - * Handle attributes - */ - numberOfAttributes = length(attributeList); - if (numberOfAttributes <= 0) - elog(ERROR, "DefineIndex: must specify at least one attribute"); - - /* - * compute heap relation id - */ - if ((relationId = RelnameFindRelid(heapRelationName)) == InvalidOid) - { - elog(ERROR, "DefineIndex: %s relation not found", - heapRelationName); - } - - if (unique && strcmp(accessMethodName, "btree") != 0) - elog(ERROR, "DefineIndex: unique indices are only available with the btree access method"); - - if (numberOfAttributes > 1 && strcmp(accessMethodName, "btree") != 0) - elog(ERROR, "DefineIndex: multi-column indices are only available with the btree access method"); - - /* - * compute access method id - */ - tuple = SearchSysCacheTuple(AMNAME, - PointerGetDatum(accessMethodName), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - { - elog(ERROR, "DefineIndex: %s access method not found", - accessMethodName); - } - accessMethodId = tuple->t_data->t_oid; - - - /* - * Handle parameters [param list is now different (NOT USED, really) - - * ay 10/94] - * - * WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96 - */ - foreach(pl, parameterList) - { - ParamString *param = (ParamString *) lfirst(pl); - - if (!strcasecmp(param->name, "islossy")) - lossy = TRUE; - } - - - /* - * Convert the partial-index predicate from parsetree form to plan - * form, so it can be readily evaluated during index creation. Note: - * "predicate" comes in as a list containing (1) the predicate itself - * (a where_clause), and (2) a corresponding range table. - * - * [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94] - */ - if (predicate != NULL && rangetable != NIL) - { - cnfPred = cnfify((Expr *) copyObject(predicate), true); - fix_opids(cnfPred); - CheckPredicate(cnfPred, rangetable, relationId); - } - - if (IsFuncIndex(attributeList)) - { - IndexElem *funcIndex = lfirst(attributeList); - int nargs; - - nargs = length(funcIndex->args); - if (nargs > INDEX_MAX_KEYS) - { - elog(ERROR, - "Too many args to function, limit of %d", - INDEX_MAX_KEYS); - } - - FIsetnArgs(&fInfo, nargs); - - strcpy(FIgetname(&fInfo), funcIndex->name); - - attributeNumberA = (AttrNumber *) palloc(nargs * sizeof attributeNumberA[0]); - - classObjectId = (Oid *) palloc(sizeof classObjectId[0]); - - - FuncIndexArgs(funcIndex, attributeNumberA, - &(FIgetArg(&fInfo, 0)), - classObjectId, relationId); - - index_create(heapRelationName, - indexRelationName, - &fInfo, NULL, accessMethodId, - numberOfAttributes, attributeNumberA, - classObjectId, parameterCount, parameterA, (Node *) cnfPred, - lossy, unique, primary); - } - else - { - attributeNumberA = (AttrNumber *) palloc(numberOfAttributes * - sizeof attributeNumberA[0]); - - classObjectId = (Oid *) palloc(numberOfAttributes * sizeof classObjectId[0]); - - NormIndexAttrs(attributeList, attributeNumberA, - classObjectId, relationId); - - index_create(heapRelationName, indexRelationName, NULL, - attributeList, - accessMethodId, numberOfAttributes, attributeNumberA, - classObjectId, parameterCount, parameterA, (Node *) cnfPred, - lossy, unique, primary); - } -} - - -/* - * ExtendIndex - * Extends a partial index. - * - * Exceptions: - * XXX - */ -void -ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable) -{ - Oid *classObjectId; - Oid accessMethodId; - Oid indexId, - relationId; - Oid indproc; - int numberOfAttributes; - AttrNumber *attributeNumberA; - HeapTuple tuple; - FuncIndexInfo fInfo; - FuncIndexInfo *funcInfo = NULL; - Form_pg_index index; - Node *oldPred = NULL; - List *cnfPred = NULL; - PredInfo *predInfo; - Relation heapRelation; - Relation indexRelation; - int i; - - /* - * compute index relation id and access method id - */ - tuple = SearchSysCacheTuple(RELNAME, - PointerGetDatum(indexRelationName), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - { - elog(ERROR, "ExtendIndex: %s index not found", - indexRelationName); - } - indexId = tuple->t_data->t_oid; - accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam; - - /* - * find pg_index tuple - */ - tuple = SearchSysCacheTuple(INDEXRELID, - ObjectIdGetDatum(indexId), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - { - elog(ERROR, "ExtendIndex: %s is not an index", - indexRelationName); - } - - /* - * Extract info from the pg_index tuple - */ - index = (Form_pg_index) GETSTRUCT(tuple); - Assert(index->indexrelid == indexId); - relationId = index->indrelid; - indproc = index->indproc; - - for (i = 0; i < INDEX_MAX_KEYS; i++) - if (index->indkey[i] == InvalidAttrNumber) - break; - numberOfAttributes = i; - - if (VARSIZE(&index->indpred) != 0) - { - char *predString; - - predString = fmgr(F_TEXTOUT, &index->indpred); - oldPred = stringToNode(predString); - pfree(predString); - } - if (oldPred == NULL) - elog(ERROR, "ExtendIndex: %s is not a partial index", - indexRelationName); - - /* - * Convert the extension predicate from parsetree form to plan form, - * so it can be readily evaluated during index creation. Note: - * "predicate" comes in as a list containing (1) the predicate itself - * (a where_clause), and (2) a corresponding range table. - */ - if (rangetable != NIL) - { - cnfPred = cnfify((Expr *) copyObject(predicate), true); - fix_opids(cnfPred); - CheckPredicate(cnfPred, rangetable, relationId); - } - - /* make predInfo list to pass to index_build */ - predInfo = (PredInfo *) palloc(sizeof(PredInfo)); - predInfo->pred = (Node *) cnfPred; - predInfo->oldPred = oldPred; - - attributeNumberA = (AttrNumber *) palloc(numberOfAttributes * - sizeof attributeNumberA[0]); - classObjectId = (Oid *) palloc(numberOfAttributes * sizeof classObjectId[0]); - - - for (i = 0; i < numberOfAttributes; i++) - { - attributeNumberA[i] = index->indkey[i]; - classObjectId[i] = index->indclass[i]; - } - - if (indproc != InvalidOid) - { - funcInfo = &fInfo; -/* FIgetnArgs(funcInfo) = numberOfAttributes; */ - FIsetnArgs(funcInfo, numberOfAttributes); - - tuple = SearchSysCacheTuple(PROOID, - ObjectIdGetDatum(indproc), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "ExtendIndex: index procedure not found"); - - namecpy(&(funcInfo->funcName), - &(((Form_pg_proc) GETSTRUCT(tuple))->proname)); - - FIsetProcOid(funcInfo, tuple->t_data->t_oid); - } - - heapRelation = heap_open(relationId); - indexRelation = index_open(indexId); - - LockRelation(heapRelation, ShareLock); - - InitIndexStrategy(numberOfAttributes, indexRelation, accessMethodId); - - index_build(heapRelation, indexRelation, numberOfAttributes, - attributeNumberA, 0, NULL, funcInfo, predInfo); -} - - -/* - * CheckPredicate - * Checks that the given list of partial-index predicates refer - * (via the given range table) only to the given base relation oid, - * and that they're in a form the planner can handle, i.e., - * boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR - * has to be on the left). - */ - -static void -CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid) -{ - List *item; - - foreach(item, predList) - CheckPredExpr(lfirst(item), rangeTable, baseRelOid); -} - -static void -CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid) -{ - List *clauses = NIL, - *clause; - - if (is_opclause(predicate)) - { - CheckPredClause((Expr *) predicate, rangeTable, baseRelOid); - return; - } - else if (or_clause(predicate) || and_clause(predicate)) - clauses = ((Expr *) predicate)->args; - else - elog(ERROR, "Unsupported partial-index predicate expression type"); - - foreach(clause, clauses) - CheckPredExpr(lfirst(clause), rangeTable, baseRelOid); -} - -static void -CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid) -{ - Var *pred_var; - Const *pred_const; - - pred_var = (Var *) get_leftop(predicate); - pred_const = (Const *) get_rightop(predicate); - - if (!IsA(predicate->oper, Oper) || - !IsA(pred_var, Var) || - !IsA(pred_const, Const)) - elog(ERROR, "Unsupported partial-index predicate clause type"); - - if (getrelid(pred_var->varno, rangeTable) != baseRelOid) - elog(ERROR, - "Partial-index predicates may refer only to the base relation"); -} - - -static void -FuncIndexArgs(IndexElem *funcIndex, - AttrNumber *attNumP, - Oid *argTypes, - Oid *opOidP, - Oid relId) -{ - List *rest; - HeapTuple tuple; - Form_pg_attribute att; - - tuple = SearchSysCacheTuple(CLANAME, - PointerGetDatum(funcIndex->class), - 0, 0, 0); - - if (!HeapTupleIsValid(tuple)) - { - elog(ERROR, "DefineIndex: %s class not found", - funcIndex->class); - } - *opOidP = tuple->t_data->t_oid; - - MemSet(argTypes, 0, 8 * sizeof(Oid)); - - /* - * process the function arguments - */ - for (rest = funcIndex->args; rest != NIL; rest = lnext(rest)) - { - char *arg; - - arg = strVal(lfirst(rest)); - - tuple = SearchSysCacheTuple(ATTNAME, - ObjectIdGetDatum(relId), - PointerGetDatum(arg), 0, 0); - - if (!HeapTupleIsValid(tuple)) - { - elog(ERROR, - "DefineIndex: attribute \"%s\" not found", - arg); - } - att = (Form_pg_attribute) GETSTRUCT(tuple); - *attNumP++ = att->attnum; - *argTypes++ = att->atttypid; - } -} - -static void -NormIndexAttrs(List *attList, /* list of IndexElem's */ - AttrNumber *attNumP, - Oid *classOidP, - Oid relId) -{ - List *rest; - HeapTuple atttuple, - tuple; - - /* - * process attributeList - */ - - for (rest = attList; rest != NIL; rest = lnext(rest)) - { - IndexElem *attribute; - Form_pg_attribute attform; - - attribute = lfirst(rest); - - if (attribute->name == NULL) - elog(ERROR, "missing attribute for define index"); - - atttuple = SearchSysCacheTupleCopy(ATTNAME, - ObjectIdGetDatum(relId), - PointerGetDatum(attribute->name), - 0, 0); - if (!HeapTupleIsValid(atttuple)) - { - elog(ERROR, - "DefineIndex: attribute \"%s\" not found", - attribute->name); - } - - attform = (Form_pg_attribute) GETSTRUCT(atttuple); - *attNumP++ = attform->attnum; - - /* we want the type so we can set the proper alignment, etc. */ - if (attribute->typename == NULL) - { - tuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(attform->atttypid), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "create index: type for attribute '%s' undefined", - attribute->name); - /* we just set the type name because that is all we need */ - attribute->typename = makeNode(TypeName); - attribute->typename->name = nameout(&((Form_pg_type) GETSTRUCT(tuple))->typname); - } - - if (attribute->class == NULL) - { - /* no operator class specified, so find the default */ - attribute->class = GetDefaultOpClass(attform->atttypid); - if (attribute->class == NULL) - { - elog(ERROR, - "Can't find a default operator class for type %d.", - attform->atttypid); - } - } - - tuple = SearchSysCacheTuple(CLANAME, - PointerGetDatum(attribute->class), - 0, 0, 0); - - if (!HeapTupleIsValid(tuple)) - { - elog(ERROR, "DefineIndex: %s class not found", - attribute->class); - } - *classOidP++ = tuple->t_data->t_oid; - pfree(atttuple); - } -} - -static char * -GetDefaultOpClass(Oid atttypid) -{ - HeapTuple tuple; - - tuple = SearchSysCacheTuple(CLADEFTYPE, - ObjectIdGetDatum(atttypid), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - return 0; - - return nameout(&(((Form_pg_opclass) GETSTRUCT(tuple))->opcname)); -} - -/* - * RemoveIndex - * Deletes an index. - * - * Exceptions: - * BadArg if name is invalid. - * "WARN" if index nonexistent. - * ... - */ -void -RemoveIndex(char *name) -{ - HeapTuple tuple; - - tuple = SearchSysCacheTuple(RELNAME, - PointerGetDatum(name), - 0, 0, 0); - - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "index \"%s\" nonexistent", name); - - if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX) - { - elog(ERROR, "relation \"%s\" is of type \"%c\"", - name, - ((Form_pg_class) GETSTRUCT(tuple))->relkind); - } - - index_destroy(tuple->t_data->t_oid); -} diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c new file mode 100644 index 0000000000..e492b9163c --- /dev/null +++ b/src/backend/commands/indexcmds.c @@ -0,0 +1,576 @@ +/*------------------------------------------------------------------------- + * + * defind.c + * POSTGRES define, extend and remove index code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.1 1999/02/24 17:28:58 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for getrelid() */ +#include +#include +#include +#include + +#define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args!=NULL) + +/* non-export function prototypes */ +static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid); +static void CheckPredExpr(Node *predicate, List *rangeTable, + Oid baseRelOid); +static void + CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid); +static void FuncIndexArgs(IndexElem *funcIndex, AttrNumber *attNumP, + Oid *argTypes, Oid *opOidP, Oid relId); +static void NormIndexAttrs(List *attList, AttrNumber *attNumP, + Oid *opOidP, Oid relId); +static char *GetDefaultOpClass(Oid atttypid); + +/* + * DefineIndex + * Creates a new index. + * + * 'attributeList' is a list of IndexElem specifying either a functional + * index or a list of attributes to index on. + * 'parameterList' is a list of ParamString specified in the with clause. + * 'predicate' is the qual specified in the where clause. + * 'rangetable' is for the predicate + * + * Exceptions: + * XXX + */ +void +DefineIndex(char *heapRelationName, + char *indexRelationName, + char *accessMethodName, + List *attributeList, + List *parameterList, + bool unique, + bool primary, + Expr *predicate, + List *rangetable) +{ + Oid *classObjectId; + Oid accessMethodId; + Oid relationId; + int numberOfAttributes; + AttrNumber *attributeNumberA; + HeapTuple tuple; + uint16 parameterCount = 0; + Datum *parameterA = NULL; + FuncIndexInfo fInfo; + List *cnfPred = NULL; + bool lossy = FALSE; + List *pl; + + /* + * Handle attributes + */ + numberOfAttributes = length(attributeList); + if (numberOfAttributes <= 0) + elog(ERROR, "DefineIndex: must specify at least one attribute"); + + /* + * compute heap relation id + */ + if ((relationId = RelnameFindRelid(heapRelationName)) == InvalidOid) + { + elog(ERROR, "DefineIndex: %s relation not found", + heapRelationName); + } + + if (unique && strcmp(accessMethodName, "btree") != 0) + elog(ERROR, "DefineIndex: unique indices are only available with the btree access method"); + + if (numberOfAttributes > 1 && strcmp(accessMethodName, "btree") != 0) + elog(ERROR, "DefineIndex: multi-column indices are only available with the btree access method"); + + /* + * compute access method id + */ + tuple = SearchSysCacheTuple(AMNAME, + PointerGetDatum(accessMethodName), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + { + elog(ERROR, "DefineIndex: %s access method not found", + accessMethodName); + } + accessMethodId = tuple->t_data->t_oid; + + + /* + * Handle parameters [param list is now different (NOT USED, really) - + * ay 10/94] + * + * WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96 + */ + foreach(pl, parameterList) + { + ParamString *param = (ParamString *) lfirst(pl); + + if (!strcasecmp(param->name, "islossy")) + lossy = TRUE; + } + + + /* + * Convert the partial-index predicate from parsetree form to plan + * form, so it can be readily evaluated during index creation. Note: + * "predicate" comes in as a list containing (1) the predicate itself + * (a where_clause), and (2) a corresponding range table. + * + * [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94] + */ + if (predicate != NULL && rangetable != NIL) + { + cnfPred = cnfify((Expr *) copyObject(predicate), true); + fix_opids(cnfPred); + CheckPredicate(cnfPred, rangetable, relationId); + } + + if (IsFuncIndex(attributeList)) + { + IndexElem *funcIndex = lfirst(attributeList); + int nargs; + + nargs = length(funcIndex->args); + if (nargs > INDEX_MAX_KEYS) + { + elog(ERROR, + "Too many args to function, limit of %d", + INDEX_MAX_KEYS); + } + + FIsetnArgs(&fInfo, nargs); + + strcpy(FIgetname(&fInfo), funcIndex->name); + + attributeNumberA = (AttrNumber *) palloc(nargs * sizeof attributeNumberA[0]); + + classObjectId = (Oid *) palloc(sizeof classObjectId[0]); + + + FuncIndexArgs(funcIndex, attributeNumberA, + &(FIgetArg(&fInfo, 0)), + classObjectId, relationId); + + index_create(heapRelationName, + indexRelationName, + &fInfo, NULL, accessMethodId, + numberOfAttributes, attributeNumberA, + classObjectId, parameterCount, parameterA, (Node *) cnfPred, + lossy, unique, primary); + } + else + { + attributeNumberA = (AttrNumber *) palloc(numberOfAttributes * + sizeof attributeNumberA[0]); + + classObjectId = (Oid *) palloc(numberOfAttributes * sizeof classObjectId[0]); + + NormIndexAttrs(attributeList, attributeNumberA, + classObjectId, relationId); + + index_create(heapRelationName, indexRelationName, NULL, + attributeList, + accessMethodId, numberOfAttributes, attributeNumberA, + classObjectId, parameterCount, parameterA, (Node *) cnfPred, + lossy, unique, primary); + } +} + + +/* + * ExtendIndex + * Extends a partial index. + * + * Exceptions: + * XXX + */ +void +ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable) +{ + Oid *classObjectId; + Oid accessMethodId; + Oid indexId, + relationId; + Oid indproc; + int numberOfAttributes; + AttrNumber *attributeNumberA; + HeapTuple tuple; + FuncIndexInfo fInfo; + FuncIndexInfo *funcInfo = NULL; + Form_pg_index index; + Node *oldPred = NULL; + List *cnfPred = NULL; + PredInfo *predInfo; + Relation heapRelation; + Relation indexRelation; + int i; + + /* + * compute index relation id and access method id + */ + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(indexRelationName), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + { + elog(ERROR, "ExtendIndex: %s index not found", + indexRelationName); + } + indexId = tuple->t_data->t_oid; + accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam; + + /* + * find pg_index tuple + */ + tuple = SearchSysCacheTuple(INDEXRELID, + ObjectIdGetDatum(indexId), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + { + elog(ERROR, "ExtendIndex: %s is not an index", + indexRelationName); + } + + /* + * Extract info from the pg_index tuple + */ + index = (Form_pg_index) GETSTRUCT(tuple); + Assert(index->indexrelid == indexId); + relationId = index->indrelid; + indproc = index->indproc; + + for (i = 0; i < INDEX_MAX_KEYS; i++) + if (index->indkey[i] == InvalidAttrNumber) + break; + numberOfAttributes = i; + + if (VARSIZE(&index->indpred) != 0) + { + char *predString; + + predString = fmgr(F_TEXTOUT, &index->indpred); + oldPred = stringToNode(predString); + pfree(predString); + } + if (oldPred == NULL) + elog(ERROR, "ExtendIndex: %s is not a partial index", + indexRelationName); + + /* + * Convert the extension predicate from parsetree form to plan form, + * so it can be readily evaluated during index creation. Note: + * "predicate" comes in as a list containing (1) the predicate itself + * (a where_clause), and (2) a corresponding range table. + */ + if (rangetable != NIL) + { + cnfPred = cnfify((Expr *) copyObject(predicate), true); + fix_opids(cnfPred); + CheckPredicate(cnfPred, rangetable, relationId); + } + + /* make predInfo list to pass to index_build */ + predInfo = (PredInfo *) palloc(sizeof(PredInfo)); + predInfo->pred = (Node *) cnfPred; + predInfo->oldPred = oldPred; + + attributeNumberA = (AttrNumber *) palloc(numberOfAttributes * + sizeof attributeNumberA[0]); + classObjectId = (Oid *) palloc(numberOfAttributes * sizeof classObjectId[0]); + + + for (i = 0; i < numberOfAttributes; i++) + { + attributeNumberA[i] = index->indkey[i]; + classObjectId[i] = index->indclass[i]; + } + + if (indproc != InvalidOid) + { + funcInfo = &fInfo; +/* FIgetnArgs(funcInfo) = numberOfAttributes; */ + FIsetnArgs(funcInfo, numberOfAttributes); + + tuple = SearchSysCacheTuple(PROOID, + ObjectIdGetDatum(indproc), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "ExtendIndex: index procedure not found"); + + namecpy(&(funcInfo->funcName), + &(((Form_pg_proc) GETSTRUCT(tuple))->proname)); + + FIsetProcOid(funcInfo, tuple->t_data->t_oid); + } + + heapRelation = heap_open(relationId); + indexRelation = index_open(indexId); + + LockRelation(heapRelation, ShareLock); + + InitIndexStrategy(numberOfAttributes, indexRelation, accessMethodId); + + index_build(heapRelation, indexRelation, numberOfAttributes, + attributeNumberA, 0, NULL, funcInfo, predInfo); +} + + +/* + * CheckPredicate + * Checks that the given list of partial-index predicates refer + * (via the given range table) only to the given base relation oid, + * and that they're in a form the planner can handle, i.e., + * boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR + * has to be on the left). + */ + +static void +CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid) +{ + List *item; + + foreach(item, predList) + CheckPredExpr(lfirst(item), rangeTable, baseRelOid); +} + +static void +CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid) +{ + List *clauses = NIL, + *clause; + + if (is_opclause(predicate)) + { + CheckPredClause((Expr *) predicate, rangeTable, baseRelOid); + return; + } + else if (or_clause(predicate) || and_clause(predicate)) + clauses = ((Expr *) predicate)->args; + else + elog(ERROR, "Unsupported partial-index predicate expression type"); + + foreach(clause, clauses) + CheckPredExpr(lfirst(clause), rangeTable, baseRelOid); +} + +static void +CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid) +{ + Var *pred_var; + Const *pred_const; + + pred_var = (Var *) get_leftop(predicate); + pred_const = (Const *) get_rightop(predicate); + + if (!IsA(predicate->oper, Oper) || + !IsA(pred_var, Var) || + !IsA(pred_const, Const)) + elog(ERROR, "Unsupported partial-index predicate clause type"); + + if (getrelid(pred_var->varno, rangeTable) != baseRelOid) + elog(ERROR, + "Partial-index predicates may refer only to the base relation"); +} + + +static void +FuncIndexArgs(IndexElem *funcIndex, + AttrNumber *attNumP, + Oid *argTypes, + Oid *opOidP, + Oid relId) +{ + List *rest; + HeapTuple tuple; + Form_pg_attribute att; + + tuple = SearchSysCacheTuple(CLANAME, + PointerGetDatum(funcIndex->class), + 0, 0, 0); + + if (!HeapTupleIsValid(tuple)) + { + elog(ERROR, "DefineIndex: %s class not found", + funcIndex->class); + } + *opOidP = tuple->t_data->t_oid; + + MemSet(argTypes, 0, 8 * sizeof(Oid)); + + /* + * process the function arguments + */ + for (rest = funcIndex->args; rest != NIL; rest = lnext(rest)) + { + char *arg; + + arg = strVal(lfirst(rest)); + + tuple = SearchSysCacheTuple(ATTNAME, + ObjectIdGetDatum(relId), + PointerGetDatum(arg), 0, 0); + + if (!HeapTupleIsValid(tuple)) + { + elog(ERROR, + "DefineIndex: attribute \"%s\" not found", + arg); + } + att = (Form_pg_attribute) GETSTRUCT(tuple); + *attNumP++ = att->attnum; + *argTypes++ = att->atttypid; + } +} + +static void +NormIndexAttrs(List *attList, /* list of IndexElem's */ + AttrNumber *attNumP, + Oid *classOidP, + Oid relId) +{ + List *rest; + HeapTuple atttuple, + tuple; + + /* + * process attributeList + */ + + for (rest = attList; rest != NIL; rest = lnext(rest)) + { + IndexElem *attribute; + Form_pg_attribute attform; + + attribute = lfirst(rest); + + if (attribute->name == NULL) + elog(ERROR, "missing attribute for define index"); + + atttuple = SearchSysCacheTupleCopy(ATTNAME, + ObjectIdGetDatum(relId), + PointerGetDatum(attribute->name), + 0, 0); + if (!HeapTupleIsValid(atttuple)) + { + elog(ERROR, + "DefineIndex: attribute \"%s\" not found", + attribute->name); + } + + attform = (Form_pg_attribute) GETSTRUCT(atttuple); + *attNumP++ = attform->attnum; + + /* we want the type so we can set the proper alignment, etc. */ + if (attribute->typename == NULL) + { + tuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(attform->atttypid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "create index: type for attribute '%s' undefined", + attribute->name); + /* we just set the type name because that is all we need */ + attribute->typename = makeNode(TypeName); + attribute->typename->name = nameout(&((Form_pg_type) GETSTRUCT(tuple))->typname); + } + + if (attribute->class == NULL) + { + /* no operator class specified, so find the default */ + attribute->class = GetDefaultOpClass(attform->atttypid); + if (attribute->class == NULL) + { + elog(ERROR, + "Can't find a default operator class for type %d.", + attform->atttypid); + } + } + + tuple = SearchSysCacheTuple(CLANAME, + PointerGetDatum(attribute->class), + 0, 0, 0); + + if (!HeapTupleIsValid(tuple)) + { + elog(ERROR, "DefineIndex: %s class not found", + attribute->class); + } + *classOidP++ = tuple->t_data->t_oid; + pfree(atttuple); + } +} + +static char * +GetDefaultOpClass(Oid atttypid) +{ + HeapTuple tuple; + + tuple = SearchSysCacheTuple(CLADEFTYPE, + ObjectIdGetDatum(atttypid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + return 0; + + return nameout(&(((Form_pg_opclass) GETSTRUCT(tuple))->opcname)); +} + +/* + * RemoveIndex + * Deletes an index. + * + * Exceptions: + * BadArg if name is invalid. + * "WARN" if index nonexistent. + * ... + */ +void +RemoveIndex(char *name) +{ + HeapTuple tuple; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(name), + 0, 0, 0); + + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "index \"%s\" nonexistent", name); + + if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX) + { + elog(ERROR, "relation \"%s\" is of type \"%c\"", + name, + ((Form_pg_class) GETSTRUCT(tuple))->relkind); + } + + index_destroy(tuple->t_data->t_oid); +} diff --git a/src/backend/commands/recipe.c b/src/backend/commands/recipe.c deleted file mode 100644 index c96b1b5995..0000000000 --- a/src/backend/commands/recipe.c +++ /dev/null @@ -1,1333 +0,0 @@ -/*------------------------------------------------------------------------- - * - * 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/Attic/recipe.c,v 1.28 1999/02/13 23:15:07 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for RelationNameGetRelation */ -#include -#include -#include -#include -#include -#include - -/* 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 - -#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/tcop/utility.c b/src/backend/tcop/utility.c index 5cf10f034b..da9391ba14 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.55 1999/02/13 23:18:48 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.56 1999/02/24 17:29:01 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -663,6 +663,7 @@ ProcessUtility(Node *parsetree, } break; +#ifdef NOT_USED /* * ******************************** Tioga-related statements ******************************* */ @@ -675,6 +676,7 @@ ProcessUtility(Node *parsetree, beginRecipe(stmt); } break; +#endif /* * ******************************** set variable statements ******************************* diff --git a/src/include/commands/recipe.h b/src/include/commands/recipe.h deleted file mode 100644 index 842165e872..0000000000 --- a/src/include/commands/recipe.h +++ /dev/null @@ -1,19 +0,0 @@ -/*------------------------------------------------------------------------- - * - * recipe.h - * recipe handling routines - * - * Copyright (c) 1994, Regents of the University of California - * - * $Id: recipe.h,v 1.7 1999/02/13 23:21:19 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#ifndef RECIPE_H -#define RECIPE_H - -#include "nodes/parsenodes.h" - -extern void beginRecipe(RecipeStmt *stmt); - -#endif /* RECIPE_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 4dd868b681..7375745976 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: nodes.h,v 1.44 1999/02/23 07:55:24 thomas Exp $ + * $Id: nodes.h,v 1.45 1999/02/24 17:29:06 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -163,7 +163,6 @@ typedef enum NodeTag T_FetchStmt, T_IndexStmt, T_ProcedureStmt, - T_RecipeStmt, T_RemoveAggrStmt, T_RemoveFuncStmt, T_RemoveOperStmt, -- cgit v1.2.1