rewriteHandler.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:70k
- /*-------------------------------------------------------------------------
- *
- * rewriteHandler.c
- *
- * Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /usr/local/cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.48 1999/07/11 17:54:30 tgl Exp $
- *
- *-------------------------------------------------------------------------
- */
- #include <string.h>
- #include "postgres.h"
- #include "miscadmin.h"
- #include "utils/palloc.h"
- #include "utils/elog.h"
- #include "utils/rel.h"
- #include "nodes/pg_list.h"
- #include "nodes/primnodes.h"
- #include "nodes/relation.h"
- #include "parser/parsetree.h" /* for parsetree manipulation */
- #include "parser/parse_relation.h"
- #include "nodes/parsenodes.h"
- /***S*I***/
- #include "parser/parse_node.h"
- #include "parser/parse_target.h"
- #include "parser/analyze.h"
- #include "optimizer/clauses.h"
- #include "optimizer/prep.h"
- #include "rewrite/rewriteSupport.h"
- #include "rewrite/rewriteHandler.h"
- #include "rewrite/rewriteManip.h"
- #include "rewrite/locks.h"
- #include "commands/creatinh.h"
- #include "access/heapam.h"
- #include "utils/lsyscache.h"
- #include "utils/syscache.h"
- #include "utils/acl.h"
- #include "catalog/pg_shadow.h"
- #include "catalog/pg_type.h"
- static RewriteInfo *gatherRewriteMeta(Query *parsetree,
- Query *rule_action,
- Node *rule_qual,
- int rt_index,
- CmdType event,
- bool *instead_flag);
- static bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up);
- static bool attribute_used(Node *node, int rt_index, int attno, int sublevels_up);
- static void modifyAggrefUplevel(Node *node);
- static void modifyAggrefChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int sublevels_up);
- static void modifyAggrefDropQual(Node **nodePtr, Node *orignode, Expr *expr);
- static SubLink *modifyAggrefMakeSublink(Expr *origexp, Query *parsetree);
- static void modifyAggrefQual(Node **nodePtr, Query *parsetree);
- static bool checkQueryHasAggs(Node *node);
- static bool checkQueryHasAggs_walker(Node *node, void *context);
- static bool checkQueryHasSubLink(Node *node);
- static bool checkQueryHasSubLink_walker(Node *node, void *context);
- static Query *fireRIRrules(Query *parsetree);
- static Query *Except_Intersect_Rewrite(Query *parsetree);
- static void check_targetlists_are_compatible(List *prev_target,
- List *current_target);
- static void create_intersect_list(Node *ptr, List **intersect_list);
- static Node *intersect_tree_analyze(Node *tree, Node *first_select,
- Node *parsetree);
- /*
- * gatherRewriteMeta -
- * Gather meta information about parsetree, and rule. Fix rule body
- * and qualifier so that they can be mixed with the parsetree and
- * maintain semantic validity
- */
- static RewriteInfo *
- gatherRewriteMeta(Query *parsetree,
- Query *rule_action,
- Node *rule_qual,
- int rt_index,
- CmdType event,
- bool *instead_flag)
- {
- RewriteInfo *info;
- int rt_length;
- int result_reln;
- info = (RewriteInfo *) palloc(sizeof(RewriteInfo));
- info->rt_index = rt_index;
- info->event = event;
- info->instead_flag = *instead_flag;
- info->rule_action = (Query *) copyObject(rule_action);
- info->rule_qual = (Node *) copyObject(rule_qual);
- if (info->rule_action == NULL)
- info->nothing = TRUE;
- else
- {
- info->nothing = FALSE;
- info->action = info->rule_action->commandType;
- info->current_varno = rt_index;
- info->rt = parsetree->rtable;
- rt_length = length(info->rt);
- info->rt = nconc(info->rt, copyObject(info->rule_action->rtable));
- info->new_varno = PRS2_NEW_VARNO + rt_length;
- OffsetVarNodes(info->rule_action->qual, rt_length, 0);
- OffsetVarNodes((Node *) info->rule_action->targetList, rt_length, 0);
- OffsetVarNodes(info->rule_qual, rt_length, 0);
- ChangeVarNodes((Node *) info->rule_action->qual,
- PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
- ChangeVarNodes((Node *) info->rule_action->targetList,
- PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
- ChangeVarNodes(info->rule_qual,
- PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
- /*
- * bug here about replace CURRENT -- sort of replace current is
- * deprecated now so this code shouldn't really need to be so
- * clutzy but.....
- */
- if (info->action != CMD_SELECT)
- { /* i.e update XXXXX */
- int new_result_reln = 0;
- result_reln = info->rule_action->resultRelation;
- switch (result_reln)
- {
- case PRS2_CURRENT_VARNO:
- new_result_reln = rt_index;
- break;
- case PRS2_NEW_VARNO: /* XXX */
- default:
- new_result_reln = result_reln + rt_length;
- break;
- }
- info->rule_action->resultRelation = new_result_reln;
- }
- }
- return info;
- }
- /*
- * rangeTableEntry_used -
- * we need to process a RTE for RIR rules only if it is
- * referenced somewhere in var nodes of the query.
- */
- static bool
- rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
- {
- if (node == NULL)
- return FALSE;
- switch (nodeTag(node))
- {
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
- return rangeTableEntry_used(
- (Node *) (tle->expr),
- rt_index,
- sublevels_up);
- }
- break;
- case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
- return rangeTableEntry_used(
- (Node *) (aggref->target),
- rt_index,
- sublevels_up);
- }
- break;
- case T_GroupClause:
- return FALSE;
- case T_Expr:
- {
- Expr *exp = (Expr *) node;
- return rangeTableEntry_used(
- (Node *) (exp->args),
- rt_index,
- sublevels_up);
- }
- break;
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
- return rangeTableEntry_used(
- (Node *) (iter->iterexpr),
- rt_index,
- sublevels_up);
- }
- break;
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
- if (rangeTableEntry_used(
- (Node *) (ref->refupperindexpr),
- rt_index,
- sublevels_up))
- return TRUE;
- if (rangeTableEntry_used(
- (Node *) (ref->reflowerindexpr),
- rt_index,
- sublevels_up))
- return TRUE;
- if (rangeTableEntry_used(
- (Node *) (ref->refexpr),
- rt_index,
- sublevels_up))
- return TRUE;
- if (rangeTableEntry_used(
- (Node *) (ref->refassgnexpr),
- rt_index,
- sublevels_up))
- return TRUE;
- return FALSE;
- }
- break;
- case T_Var:
- {
- Var *var = (Var *) node;
- if (var->varlevelsup == sublevels_up)
- return var->varno == rt_index;
- else
- return FALSE;
- }
- break;
- case T_Param:
- return FALSE;
- case T_Const:
- return FALSE;
- case T_List:
- {
- List *l;
- foreach(l, (List *) node)
- {
- if (rangeTableEntry_used(
- (Node *) lfirst(l),
- rt_index,
- sublevels_up))
- return TRUE;
- }
- return FALSE;
- }
- break;
- case T_SubLink:
- {
- SubLink *sub = (SubLink *) node;
- if (rangeTableEntry_used(
- (Node *) (sub->lefthand),
- rt_index,
- sublevels_up))
- return TRUE;
- if (rangeTableEntry_used(
- (Node *) (sub->subselect),
- rt_index,
- sublevels_up + 1))
- return TRUE;
- return FALSE;
- }
- break;
- case T_CaseExpr:
- {
- CaseExpr *exp = (CaseExpr *) node;
- if (rangeTableEntry_used(
- (Node *) (exp->args),
- rt_index,
- sublevels_up))
- return TRUE;
- if (rangeTableEntry_used(
- (Node *) (exp->defresult),
- rt_index,
- sublevels_up))
- return TRUE;
- return FALSE;
- }
- break;
- case T_CaseWhen:
- {
- CaseWhen *when = (CaseWhen *) node;
- if (rangeTableEntry_used(
- (Node *) (when->expr),
- rt_index,
- sublevels_up))
- return TRUE;
- if (rangeTableEntry_used(
- (Node *) (when->result),
- rt_index,
- sublevels_up))
- return TRUE;
- return FALSE;
- }
- break;
- case T_Query:
- {
- Query *qry = (Query *) node;
- if (rangeTableEntry_used(
- (Node *) (qry->targetList),
- rt_index,
- sublevels_up))
- return TRUE;
- if (rangeTableEntry_used(
- (Node *) (qry->qual),
- rt_index,
- sublevels_up))
- return TRUE;
- if (rangeTableEntry_used(
- (Node *) (qry->havingQual),
- rt_index,
- sublevels_up))
- return TRUE;
- return FALSE;
- }
- break;
- default:
- elog(NOTICE, "unknown node tag %d in rangeTableEntry_used()", nodeTag(node));
- elog(NOTICE, "Node is: %s", nodeToString(node));
- break;
- }
- return FALSE;
- }
- /*
- * attribute_used -
- * Check if a specific attribute number of a RTE is used
- * somewhere in the query
- */
- static bool
- attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
- {
- if (node == NULL)
- return FALSE;
- switch (nodeTag(node))
- {
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
- return attribute_used(
- (Node *) (tle->expr),
- rt_index,
- attno,
- sublevels_up);
- }
- break;
- case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
- return attribute_used(
- (Node *) (aggref->target),
- rt_index,
- attno,
- sublevels_up);
- }
- break;
- case T_GroupClause:
- return FALSE;
- case T_Expr:
- {
- Expr *exp = (Expr *) node;
- return attribute_used(
- (Node *) (exp->args),
- rt_index,
- attno,
- sublevels_up);
- }
- break;
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
- return attribute_used(
- (Node *) (iter->iterexpr),
- rt_index,
- attno,
- sublevels_up);
- }
- break;
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
- if (attribute_used(
- (Node *) (ref->refupperindexpr),
- rt_index,
- attno,
- sublevels_up))
- return TRUE;
- if (attribute_used(
- (Node *) (ref->reflowerindexpr),
- rt_index,
- attno,
- sublevels_up))
- return TRUE;
- if (attribute_used(
- (Node *) (ref->refexpr),
- rt_index,
- attno,
- sublevels_up))
- return TRUE;
- if (attribute_used(
- (Node *) (ref->refassgnexpr),
- rt_index,
- attno,
- sublevels_up))
- return TRUE;
- return FALSE;
- }
- break;
- case T_Var:
- {
- Var *var = (Var *) node;
- if (var->varlevelsup == sublevels_up)
- return var->varno == rt_index;
- else
- return FALSE;
- }
- break;
- case T_Param:
- return FALSE;
- case T_Const:
- return FALSE;
- case T_List:
- {
- List *l;
- foreach(l, (List *) node)
- {
- if (attribute_used(
- (Node *) lfirst(l),
- rt_index,
- attno,
- sublevels_up))
- return TRUE;
- }
- return FALSE;
- }
- break;
- case T_SubLink:
- {
- SubLink *sub = (SubLink *) node;
- if (attribute_used(
- (Node *) (sub->lefthand),
- rt_index,
- attno,
- sublevels_up))
- return TRUE;
- if (attribute_used(
- (Node *) (sub->subselect),
- rt_index,
- attno,
- sublevels_up + 1))
- return TRUE;
- return FALSE;
- }
- break;
- case T_Query:
- {
- Query *qry = (Query *) node;
- if (attribute_used(
- (Node *) (qry->targetList),
- rt_index,
- attno,
- sublevels_up))
- return TRUE;
- if (attribute_used(
- (Node *) (qry->qual),
- rt_index,
- attno,
- sublevels_up))
- return TRUE;
- if (attribute_used(
- (Node *) (qry->havingQual),
- rt_index,
- attno,
- sublevels_up))
- return TRUE;
- return FALSE;
- }
- break;
- default:
- elog(NOTICE, "unknown node tag %d in attribute_used()", nodeTag(node));
- elog(NOTICE, "Node is: %s", nodeToString(node));
- break;
- }
- return FALSE;
- }
- /*
- * modifyAggrefUplevel -
- * In the newly created sublink for an aggregate column used in
- * the qualification, we must adjust the varlevelsup in all the
- * var nodes.
- */
- static void
- modifyAggrefUplevel(Node *node)
- {
- if (node == NULL)
- return;
- switch (nodeTag(node))
- {
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
- modifyAggrefUplevel(
- (Node *) (tle->expr));
- }
- break;
- case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
- modifyAggrefUplevel(
- (Node *) (aggref->target));
- }
- break;
- case T_Expr:
- {
- Expr *exp = (Expr *) node;
- modifyAggrefUplevel(
- (Node *) (exp->args));
- }
- break;
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
- modifyAggrefUplevel(
- (Node *) (iter->iterexpr));
- }
- break;
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
- modifyAggrefUplevel(
- (Node *) (ref->refupperindexpr));
- modifyAggrefUplevel(
- (Node *) (ref->reflowerindexpr));
- modifyAggrefUplevel(
- (Node *) (ref->refexpr));
- modifyAggrefUplevel(
- (Node *) (ref->refassgnexpr));
- }
- break;
- case T_Var:
- {
- Var *var = (Var *) node;
- var->varlevelsup++;
- }
- break;
- case T_Param:
- break;
- case T_Const:
- break;
- case T_List:
- {
- List *l;
- foreach(l, (List *) node)
- modifyAggrefUplevel(
- (Node *) lfirst(l));
- }
- break;
- case T_SubLink:
- {
- SubLink *sub = (SubLink *) node;
- modifyAggrefUplevel(
- (Node *) (sub->lefthand));
- modifyAggrefUplevel(
- (Node *) (sub->oper));
- modifyAggrefUplevel(
- (Node *) (sub->subselect));
- }
- break;
- case T_Query:
- {
- Query *qry = (Query *) node;
- modifyAggrefUplevel(
- (Node *) (qry->targetList));
- modifyAggrefUplevel(
- (Node *) (qry->qual));
- modifyAggrefUplevel(
- (Node *) (qry->havingQual));
- }
- break;
- default:
- elog(NOTICE, "unknown node tag %d in modifyAggrefUplevel()", nodeTag(node));
- elog(NOTICE, "Node is: %s", nodeToString(node));
- break;
- }
- }
- /*
- * modifyAggrefChangeVarnodes -
- * Change the var nodes in a sublink created for an aggregate column
- * used in the qualification that is subject of the aggregate
- * function to point to the correct local RTE.
- */
- static void
- modifyAggrefChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int sublevels_up)
- {
- Node *node = *nodePtr;
- if (node == NULL)
- return;
- switch (nodeTag(node))
- {
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
- modifyAggrefChangeVarnodes(
- (Node **) (&(tle->expr)),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
- case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
- modifyAggrefChangeVarnodes(
- (Node **) (&(aggref->target)),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
- case T_GroupClause:
- break;
- case T_Expr:
- {
- Expr *exp = (Expr *) node;
- modifyAggrefChangeVarnodes(
- (Node **) (&(exp->args)),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
- modifyAggrefChangeVarnodes(
- (Node **) (&(iter->iterexpr)),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
- modifyAggrefChangeVarnodes(
- (Node **) (&(ref->refupperindexpr)),
- rt_index,
- new_index,
- sublevels_up);
- modifyAggrefChangeVarnodes(
- (Node **) (&(ref->reflowerindexpr)),
- rt_index,
- new_index,
- sublevels_up);
- modifyAggrefChangeVarnodes(
- (Node **) (&(ref->refexpr)),
- rt_index,
- new_index,
- sublevels_up);
- modifyAggrefChangeVarnodes(
- (Node **) (&(ref->refassgnexpr)),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
- case T_Var:
- {
- Var *var = (Var *) node;
- if (var->varlevelsup == sublevels_up &&
- var->varno == rt_index)
- {
- var = copyObject(var);
- var->varno = new_index;
- var->varnoold = new_index;
- var->varlevelsup = 0;
- *nodePtr = (Node *) var;
- }
- }
- break;
- case T_Param:
- break;
- case T_Const:
- break;
- case T_List:
- {
- List *l;
- foreach(l, (List *) node)
- modifyAggrefChangeVarnodes(
- (Node **) (&lfirst(l)),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
- case T_SubLink:
- {
- SubLink *sub = (SubLink *) node;
- modifyAggrefChangeVarnodes(
- (Node **) (&(sub->lefthand)),
- rt_index,
- new_index,
- sublevels_up);
- modifyAggrefChangeVarnodes(
- (Node **) (&(sub->oper)),
- rt_index,
- new_index,
- sublevels_up);
- modifyAggrefChangeVarnodes(
- (Node **) (&(sub->subselect)),
- rt_index,
- new_index,
- sublevels_up + 1);
- }
- break;
- case T_Query:
- {
- Query *qry = (Query *) node;
- modifyAggrefChangeVarnodes(
- (Node **) (&(qry->targetList)),
- rt_index,
- new_index,
- sublevels_up);
- modifyAggrefChangeVarnodes(
- (Node **) (&(qry->qual)),
- rt_index,
- new_index,
- sublevels_up);
- modifyAggrefChangeVarnodes(
- (Node **) (&(qry->havingQual)),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
- default:
- elog(NOTICE, "unknown node tag %d in modifyAggrefChangeVarnodes()", nodeTag(node));
- elog(NOTICE, "Node is: %s", nodeToString(node));
- break;
- }
- }
- /*
- * modifyAggrefDropQual -
- * remove the pure aggref clase from a qualification
- */
- static void
- modifyAggrefDropQual(Node **nodePtr, Node *orignode, Expr *expr)
- {
- Node *node = *nodePtr;
- if (node == NULL)
- return;
- switch (nodeTag(node))
- {
- case T_Var:
- break;
- case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
- Aggref *oaggref = (Aggref *) orignode;
- modifyAggrefDropQual(
- (Node **) (&(aggref->target)),
- (Node *) (oaggref->target),
- expr);
- }
- break;
- case T_Param:
- break;
- case T_Const:
- break;
- case T_GroupClause:
- break;
- case T_Expr:
- {
- Expr *this_expr = (Expr *) node;
- Expr *orig_expr = (Expr *) orignode;
- if (orig_expr == expr)
- {
- Const *ctrue;
- if (expr->typeOid != BOOLOID)
- elog(ERROR,
- "aggregate expression in qualification isn't of type bool");
- ctrue = makeNode(Const);
- ctrue->consttype = BOOLOID;
- ctrue->constlen = 1;
- ctrue->constisnull = FALSE;
- ctrue->constvalue = (Datum) TRUE;
- ctrue->constbyval = TRUE;
- *nodePtr = (Node *) ctrue;
- }
- else
- modifyAggrefDropQual(
- (Node **) (&(this_expr->args)),
- (Node *) (orig_expr->args),
- expr);
- }
- break;
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
- Iter *oiter = (Iter *) orignode;
- modifyAggrefDropQual(
- (Node **) (&(iter->iterexpr)),
- (Node *) (oiter->iterexpr),
- expr);
- }
- break;
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
- ArrayRef *oref = (ArrayRef *) orignode;
- modifyAggrefDropQual(
- (Node **) (&(ref->refupperindexpr)),
- (Node *) (oref->refupperindexpr),
- expr);
- modifyAggrefDropQual(
- (Node **) (&(ref->reflowerindexpr)),
- (Node *) (oref->reflowerindexpr),
- expr);
- modifyAggrefDropQual(
- (Node **) (&(ref->refexpr)),
- (Node *) (oref->refexpr),
- expr);
- modifyAggrefDropQual(
- (Node **) (&(ref->refassgnexpr)),
- (Node *) (oref->refassgnexpr),
- expr);
- }
- break;
- case T_List:
- {
- List *l;
- List *ol = (List *) orignode;
- int li = 0;
- foreach(l, (List *) node)
- {
- modifyAggrefDropQual(
- (Node **) (&(lfirst(l))),
- (Node *) nth(li, ol),
- expr);
- li++;
- }
- }
- break;
- case T_SubLink:
- {
- SubLink *sub = (SubLink *) node;
- SubLink *osub = (SubLink *) orignode;
- modifyAggrefDropQual(
- (Node **) (&(sub->subselect)),
- (Node *) (osub->subselect),
- expr);
- }
- break;
- case T_Query:
- {
- Query *qry = (Query *) node;
- Query *oqry = (Query *) orignode;
- modifyAggrefDropQual(
- (Node **) (&(qry->qual)),
- (Node *) (oqry->qual),
- expr);
- modifyAggrefDropQual(
- (Node **) (&(qry->havingQual)),
- (Node *) (oqry->havingQual),
- expr);
- }
- break;
- default:
- elog(NOTICE, "unknown node tag %d in modifyAggrefDropQual()", nodeTag(node));
- elog(NOTICE, "Node is: %s", nodeToString(node));
- break;
- }
- }
- /*
- * modifyAggrefMakeSublink -
- * Create a sublink node for a qualification expression that
- * uses an aggregate column of a view
- */
- static SubLink *
- modifyAggrefMakeSublink(Expr *origexp, Query *parsetree)
- {
- SubLink *sublink;
- Query *subquery;
- Node *subqual;
- RangeTblEntry *rte;
- Aggref *aggref;
- Var *target;
- TargetEntry *tle;
- Resdom *resdom;
- Expr *exp = copyObject(origexp);
- if (nodeTag(nth(0, exp->args)) == T_Aggref)
- {
- if (nodeTag(nth(1, exp->args)) == T_Aggref)
- elog(ERROR, "rewrite: comparision of 2 aggregate columns not supported");
- else
- elog(ERROR, "rewrite: aggregate column of view must be at rigth side in qual");
- }
- aggref = (Aggref *) nth(1, exp->args);
- target = (Var *) (aggref->target);
- rte = (RangeTblEntry *) nth(target->varno - 1, parsetree->rtable);
- tle = makeNode(TargetEntry);
- resdom = makeNode(Resdom);
- aggref->usenulls = TRUE;
- resdom->resno = 1;
- resdom->restype = ((Oper *) (exp->oper))->opresulttype;
- resdom->restypmod = -1;
- resdom->resname = pstrdup("<noname>");
- resdom->reskey = 0;
- resdom->reskeyop = 0;
- resdom->resjunk = false;
- tle->resdom = resdom;
- tle->expr = (Node *) aggref;
- subqual = copyObject(parsetree->qual);
- modifyAggrefDropQual((Node **) &subqual, (Node *) parsetree->qual, origexp);
- sublink = makeNode(SubLink);
- sublink->subLinkType = EXPR_SUBLINK;
- sublink->useor = FALSE;
- sublink->lefthand = lappend(NIL, copyObject(lfirst(exp->args)));
- sublink->oper = lappend(NIL, copyObject(exp));
- sublink->subselect = NULL;
- subquery = makeNode(Query);
- sublink->subselect = (Node *) subquery;
- subquery->commandType = CMD_SELECT;
- subquery->utilityStmt = NULL;
- subquery->resultRelation = 0;
- subquery->into = NULL;
- subquery->isPortal = FALSE;
- subquery->isBinary = FALSE;
- subquery->isTemp = FALSE;
- subquery->unionall = FALSE;
- subquery->uniqueFlag = NULL;
- subquery->sortClause = NULL;
- subquery->rtable = lappend(NIL, rte);
- subquery->targetList = lappend(NIL, tle);
- subquery->qual = subqual;
- subquery->groupClause = NIL;
- subquery->havingQual = NULL;
- subquery->hasAggs = TRUE;
- subquery->hasSubLinks = FALSE;
- subquery->unionClause = NULL;
- modifyAggrefUplevel((Node *) sublink);
- modifyAggrefChangeVarnodes((Node **) &(sublink->lefthand), target->varno,
- 1, target->varlevelsup);
- modifyAggrefChangeVarnodes((Node **) &(sublink->oper), target->varno,
- 1, target->varlevelsup);
- modifyAggrefChangeVarnodes((Node **) &(sublink->subselect), target->varno,
- 1, target->varlevelsup);
- return sublink;
- }
- /*
- * modifyAggrefQual -
- * Search for qualification expressions that contain aggregate
- * functions and substiture them by sublinks. These expressions
- * originally come from qualifications that use aggregate columns
- * of a view.
- */
- static void
- modifyAggrefQual(Node **nodePtr, Query *parsetree)
- {
- Node *node = *nodePtr;
- if (node == NULL)
- return;
- switch (nodeTag(node))
- {
- case T_Var:
- break;
- case T_Param:
- break;
- case T_Const:
- break;
- case T_GroupClause:
- break;
- case T_Expr:
- {
- Expr *exp = (Expr *) node;
- SubLink *sub;
- if (length(exp->args) != 2)
- {
- modifyAggrefQual(
- (Node **) (&(exp->args)),
- parsetree);
- break;
- }
- if (nodeTag(nth(0, exp->args)) != T_Aggref &&
- nodeTag(nth(1, exp->args)) != T_Aggref)
- {
- modifyAggrefQual(
- (Node **) (&(exp->args)),
- parsetree);
- break;
- }
- sub = modifyAggrefMakeSublink(exp,
- parsetree);
- *nodePtr = (Node *) sub;
- parsetree->hasSubLinks = TRUE;
- }
- break;
- case T_CaseExpr:
- {
- /*
- * We're calling recursively, and this routine knows how
- * to handle lists so let it do the work to handle the
- * WHEN clauses...
- */
- modifyAggrefQual(
- (Node **) (&(((CaseExpr *) node)->args)),
- parsetree);
- modifyAggrefQual(
- (Node **) (&(((CaseExpr *) node)->defresult)),
- parsetree);
- }
- break;
- case T_CaseWhen:
- {
- modifyAggrefQual(
- (Node **) (&(((CaseWhen *) node)->expr)),
- parsetree);
- modifyAggrefQual(
- (Node **) (&(((CaseWhen *) node)->result)),
- parsetree);
- }
- break;
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
- modifyAggrefQual(
- (Node **) (&(iter->iterexpr)),
- parsetree);
- }
- break;
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
- modifyAggrefQual(
- (Node **) (&(ref->refupperindexpr)),
- parsetree);
- modifyAggrefQual(
- (Node **) (&(ref->reflowerindexpr)),
- parsetree);
- modifyAggrefQual(
- (Node **) (&(ref->refexpr)),
- parsetree);
- modifyAggrefQual(
- (Node **) (&(ref->refassgnexpr)),
- parsetree);
- }
- break;
- case T_List:
- {
- List *l;
- foreach(l, (List *) node)
- modifyAggrefQual(
- (Node **) (&(lfirst(l))),
- parsetree);
- }
- break;
- case T_SubLink:
- {
- SubLink *sub = (SubLink *) node;
- modifyAggrefQual(
- (Node **) (&(sub->subselect)),
- (Query *) (sub->subselect));
- }
- break;
- case T_Query:
- {
- Query *qry = (Query *) node;
- modifyAggrefQual(
- (Node **) (&(qry->qual)),
- parsetree);
- modifyAggrefQual(
- (Node **) (&(qry->havingQual)),
- parsetree);
- }
- break;
- default:
- elog(NOTICE, "unknown node tag %d in modifyAggrefQual()", nodeTag(node));
- elog(NOTICE, "Node is: %s", nodeToString(node));
- break;
- }
- }
- /*
- * checkQueryHasAggs -
- * Queries marked hasAggs might not have them any longer after
- * rewriting. Check it.
- */
- static bool
- checkQueryHasAggs(Node *node)
- {
- return checkQueryHasAggs_walker(node, NULL);
- }
- static bool
- checkQueryHasAggs_walker(Node *node, void *context)
- {
- if (node == NULL)
- return false;
- if (IsA(node, Aggref))
- return true; /* abort the tree traversal and return true */
- return expression_tree_walker(node, checkQueryHasAggs_walker, context);
- }
- /*
- * checkQueryHasSubLink -
- * Queries marked hasSubLinks might not have them any longer after
- * rewriting. Check it.
- */
- static bool
- checkQueryHasSubLink(Node *node)
- {
- return checkQueryHasSubLink_walker(node, NULL);
- }
- static bool
- checkQueryHasSubLink_walker(Node *node, void *context)
- {
- if (node == NULL)
- return false;
- if (IsA(node, SubLink))
- return true; /* abort the tree traversal and return true */
- /* Note: we assume the tree has not yet been rewritten by subselect.c,
- * therefore we will find bare SubLink nodes and not SUBPLAN nodes.
- */
- return expression_tree_walker(node, checkQueryHasSubLink_walker, context);
- }
- static Node *
- FindMatchingTLEntry(List *tlist, char *e_attname)
- {
- List *i;
- foreach(i, tlist)
- {
- TargetEntry *tle = lfirst(i);
- char *resname;
- resname = tle->resdom->resname;
- if (!strcmp(e_attname, resname))
- return (tle->expr);
- }
- return NULL;
- }
- static Node *
- make_null(Oid type)
- {
- Const *c = makeNode(Const);
- c->consttype = type;
- c->constlen = get_typlen(type);
- c->constvalue = PointerGetDatum(NULL);
- c->constisnull = true;
- c->constbyval = get_typbyval(type);
- return (Node *) c;
- }
- static void
- apply_RIR_adjust_sublevel(Node *node, int sublevels_up)
- {
- if (node == NULL)
- return;
- switch (nodeTag(node))
- {
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
- apply_RIR_adjust_sublevel(
- (Node *) (tle->expr),
- sublevels_up);
- }
- break;
- case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
- apply_RIR_adjust_sublevel(
- (Node *) (aggref->target),
- sublevels_up);
- }
- break;
- case T_GroupClause:
- break;
- case T_Expr:
- {
- Expr *exp = (Expr *) node;
- apply_RIR_adjust_sublevel(
- (Node *) (exp->args),
- sublevels_up);
- }
- break;
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
- apply_RIR_adjust_sublevel(
- (Node *) (iter->iterexpr),
- sublevels_up);
- }
- break;
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
- apply_RIR_adjust_sublevel(
- (Node *) (ref->refupperindexpr),
- sublevels_up);
- apply_RIR_adjust_sublevel(
- (Node *) (ref->reflowerindexpr),
- sublevels_up);
- apply_RIR_adjust_sublevel(
- (Node *) (ref->refexpr),
- sublevels_up);
- apply_RIR_adjust_sublevel(
- (Node *) (ref->refassgnexpr),
- sublevels_up);
- }
- break;
- case T_Var:
- {
- Var *var = (Var *) node;
- var->varlevelsup = sublevels_up;
- }
- break;
- case T_Param:
- break;
- case T_Const:
- break;
- case T_List:
- {
- List *l;
- foreach(l, (List *) node)
- {
- apply_RIR_adjust_sublevel(
- (Node *) lfirst(l),
- sublevels_up);
- }
- }
- break;
- case T_CaseExpr:
- {
- CaseExpr *exp = (CaseExpr *) node;
- apply_RIR_adjust_sublevel(
- (Node *) (exp->args),
- sublevels_up);
- apply_RIR_adjust_sublevel(
- (Node *) (exp->defresult),
- sublevels_up);
- }
- break;
- case T_CaseWhen:
- {
- CaseWhen *exp = (CaseWhen *) node;
- apply_RIR_adjust_sublevel(
- (Node *) (exp->expr),
- sublevels_up);
- apply_RIR_adjust_sublevel(
- (Node *) (exp->result),
- sublevels_up);
- }
- break;
- default:
- elog(NOTICE, "unknown node tag %d in attribute_used()", nodeTag(node));
- elog(NOTICE, "Node is: %s", nodeToString(node));
- break;
- }
- }
- static void
- apply_RIR_view(Node **nodePtr, int rt_index, RangeTblEntry *rte, List *tlist, int *modified, int sublevels_up)
- {
- Node *node = *nodePtr;
- if (node == NULL)
- return;
- switch (nodeTag(node))
- {
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
- apply_RIR_view(
- (Node **) (&(tle->expr)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- }
- break;
- case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
- apply_RIR_view(
- (Node **) (&(aggref->target)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- }
- break;
- case T_GroupClause:
- break;
- case T_Expr:
- {
- Expr *exp = (Expr *) node;
- apply_RIR_view(
- (Node **) (&(exp->args)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- }
- break;
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
- apply_RIR_view(
- (Node **) (&(iter->iterexpr)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- }
- break;
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
- apply_RIR_view(
- (Node **) (&(ref->refupperindexpr)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- apply_RIR_view(
- (Node **) (&(ref->reflowerindexpr)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- apply_RIR_view(
- (Node **) (&(ref->refexpr)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- apply_RIR_view(
- (Node **) (&(ref->refassgnexpr)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- }
- break;
- case T_Var:
- {
- Var *var = (Var *) node;
- if (var->varlevelsup == sublevels_up &&
- var->varno == rt_index)
- {
- Node *exp;
- if (var->varattno < 0)
- elog(ERROR, "system column %s not available - %s is a view", get_attname(rte->relid, var->varattno), rte->relname);
- exp = FindMatchingTLEntry(
- tlist,
- get_attname(rte->relid,
- var->varattno));
- if (exp == NULL)
- {
- *nodePtr = make_null(var->vartype);
- return;
- }
- exp = copyObject(exp);
- if (var->varlevelsup > 0)
- apply_RIR_adjust_sublevel(exp, var->varlevelsup);
- *nodePtr = exp;
- *modified = TRUE;
- }
- }
- break;
- case T_Param:
- break;
- case T_Const:
- break;
- case T_List:
- {
- List *l;
- foreach(l, (List *) node)
- apply_RIR_view(
- (Node **) (&(lfirst(l))),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- }
- break;
- case T_SubLink:
- {
- SubLink *sub = (SubLink *) node;
- List *tmp_lefthand,
- *tmp_oper;
- apply_RIR_view(
- (Node **) (&(sub->lefthand)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- apply_RIR_view(
- (Node **) (&(sub->subselect)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up + 1);
- /***S*I***/
- tmp_lefthand = sub->lefthand;
- foreach(tmp_oper, sub->oper)
- {
- lfirst(((Expr *) lfirst(tmp_oper))->args) =
- lfirst(tmp_lefthand);
- tmp_lefthand = lnext(tmp_lefthand);
- }
- }
- break;
- case T_Query:
- {
- Query *qry = (Query *) node;
- apply_RIR_view(
- (Node **) (&(qry->targetList)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- apply_RIR_view(
- (Node **) (&(qry->qual)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- apply_RIR_view(
- (Node **) (&(qry->havingQual)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- }
- break;
- case T_CaseExpr:
- {
- CaseExpr *exp = (CaseExpr *) node;
- apply_RIR_view(
- (Node **) (&(exp->args)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- apply_RIR_view(
- (Node **) (&(exp->defresult)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- }
- break;
- case T_CaseWhen:
- {
- CaseWhen *exp = (CaseWhen *) node;
- apply_RIR_view(
- (Node **) (&(exp->expr)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- apply_RIR_view(
- (Node **) (&(exp->result)),
- rt_index,
- rte,
- tlist,
- modified,
- sublevels_up);
- }
- break;
- default:
- elog(NOTICE, "unknown node tag %d in apply_RIR_view()", nodeTag(node));
- elog(NOTICE, "Node is: %s", nodeToString(node));
- break;
- }
- }
- extern void CheckSelectForUpdate(Query *rule_action); /* in analyze.c */
- static void
- ApplyRetrieveRule(Query *parsetree,
- RewriteRule *rule,
- int rt_index,
- int relation_level,
- Relation relation,
- int *modified)
- {
- Query *rule_action = NULL;
- Node *rule_qual;
- List *rtable,
- *rt,
- *l;
- int nothing,
- rt_length;
- int badsql = FALSE;
- rule_qual = rule->qual;
- if (rule->actions)
- {
- if (length(rule->actions) > 1) /* ??? because we don't handle
- * rules with more than one
- * action? -ay */
- return;
- rule_action = copyObject(lfirst(rule->actions));
- nothing = FALSE;
- }
- else
- nothing = TRUE;
- rtable = copyObject(parsetree->rtable);
- foreach(rt, rtable)
- {
- RangeTblEntry *rte = lfirst(rt);
- /*
- * this is to prevent add_missing_vars_to_base_rels() from adding
- * a bogus entry to the new target list.
- */
- rte->inFromCl = false;
- }
- rt_length = length(rtable);
- rtable = nconc(rtable, copyObject(rule_action->rtable));
- parsetree->rtable = rtable;
- /* FOR UPDATE of view... */
- foreach(l, parsetree->rowMark)
- {
- if (((RowMark *) lfirst(l))->rti == rt_index)
- break;
- }
- if (l != NULL) /* oh, hell -:) */
- {
- RowMark *newrm;
- Index rti = 1;
- List *l2;
- CheckSelectForUpdate(rule_action);
- /*
- * We believe that rt_index is VIEW - nothing should be marked for
- * VIEW, but ACL check must be done. As for real tables of VIEW -
- * their rows must be marked, but we have to skip ACL check for
- * them.
- */
- ((RowMark *) lfirst(l))->info &= ~ROW_MARK_FOR_UPDATE;
- foreach(l2, rule_action->rtable)
- {
- /*
- * RTable of VIEW has two entries of VIEW itself - we use
- * relid to skip them.
- */
- if (relation->rd_id != ((RangeTblEntry *) lfirst(l2))->relid)
- {
- newrm = makeNode(RowMark);
- newrm->rti = rti + rt_length;
- newrm->info = ROW_MARK_FOR_UPDATE;
- lnext(l) = lcons(newrm, lnext(l));
- l = lnext(l);
- }
- rti++;
- }
- }
- rule_action->rtable = rtable;
- OffsetVarNodes((Node *) rule_qual, rt_length, 0);
- OffsetVarNodes((Node *) rule_action, rt_length, 0);
- ChangeVarNodes((Node *) rule_qual,
- PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
- ChangeVarNodes((Node *) rule_action,
- PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
- if (relation_level)
- {
- apply_RIR_view((Node **) &parsetree, rt_index,
- (RangeTblEntry *) nth(rt_index - 1, rtable),
- rule_action->targetList, modified, 0);
- apply_RIR_view((Node **) &rule_action, rt_index,
- (RangeTblEntry *) nth(rt_index - 1, rtable),
- rule_action->targetList, modified, 0);
- }
- else
- {
- HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
- rt_index, rule->attrno, modified, &badsql);
- }
- if (*modified && !badsql)
- {
- AddQual(parsetree, rule_action->qual);
- AddGroupClause(parsetree, rule_action->groupClause,
- rule_action->targetList);
- AddHavingQual(parsetree, rule_action->havingQual);
- parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
- parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks);
- }
- }
- static void
- fireRIRonSubselect(Node *node)
- {
- if (node == NULL)
- return;
- switch (nodeTag(node))
- {
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
- fireRIRonSubselect(
- (Node *) (tle->expr));
- }
- break;
- case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
- fireRIRonSubselect(
- (Node *) (aggref->target));
- }
- break;
- case T_GroupClause:
- break;
- case T_Expr:
- {
- Expr *exp = (Expr *) node;
- fireRIRonSubselect(
- (Node *) (exp->args));
- }
- break;
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
- fireRIRonSubselect(
- (Node *) (iter->iterexpr));
- }
- break;
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
- fireRIRonSubselect(
- (Node *) (ref->refupperindexpr));
- fireRIRonSubselect(
- (Node *) (ref->reflowerindexpr));
- fireRIRonSubselect(
- (Node *) (ref->refexpr));
- fireRIRonSubselect(
- (Node *) (ref->refassgnexpr));
- }
- break;
- case T_Var:
- break;
- case T_Param:
- break;
- case T_Const:
- break;
- case T_List:
- {
- List *l;
- foreach(l, (List *) node)
- fireRIRonSubselect(
- (Node *) (lfirst(l)));
- }
- break;
- case T_SubLink:
- {
- SubLink *sub = (SubLink *) node;
- Query *qry;
- fireRIRonSubselect(
- (Node *) (sub->lefthand));
- qry = fireRIRrules((Query *) (sub->subselect));
- fireRIRonSubselect(
- (Node *) qry);
- sub->subselect = (Node *) qry;
- }
- break;
- case T_CaseExpr:
- {
- CaseExpr *exp = (CaseExpr *) node;
- fireRIRonSubselect(
- (Node *) (exp->args));
- fireRIRonSubselect(
- (Node *) (exp->defresult));
- }
- break;
- case T_CaseWhen:
- {
- CaseWhen *exp = (CaseWhen *) node;
- fireRIRonSubselect(
- (Node *) (exp->expr));
- fireRIRonSubselect(
- (Node *) (exp->result));
- }
- break;
- case T_Query:
- {
- Query *qry = (Query *) node;
- fireRIRonSubselect(
- (Node *) (qry->targetList));
- fireRIRonSubselect(
- (Node *) (qry->qual));
- fireRIRonSubselect(
- (Node *) (qry->havingQual));
- }
- break;
- default:
- elog(NOTICE, "unknown node tag %d in fireRIRonSubselect()", nodeTag(node));
- elog(NOTICE, "Node is: %s", nodeToString(node));
- break;
- }
- }
- /*
- * fireRIRrules -
- * Apply all RIR rules on each rangetable entry in a query
- */
- static Query *
- fireRIRrules(Query *parsetree)
- {
- int rt_index;
- RangeTblEntry *rte;
- Relation rel;
- List *locks;
- RuleLock *rules;
- RewriteRule *rule;
- RewriteRule RIRonly;
- int modified;
- int i;
- List *l;
- rt_index = 0;
- while (rt_index < length(parsetree->rtable))
- {
- ++rt_index;
- rte = nth(rt_index - 1, parsetree->rtable);
- if (!rangeTableEntry_used((Node *) parsetree, rt_index, 0))
- {
- /*
- * Unused range table entries must not be marked as coming
- * from a clause. Otherwise the planner will generate joins
- * over relations that in fact shouldn't be scanned at all and
- * the result will contain duplicates
- *
- * Jan
- *
- */
- rte->inFromCl = FALSE;
- continue;
- }
- rel = heap_openr(rte->relname);
- if (rel->rd_rules == NULL)
- {
- heap_close(rel);
- continue;
- }
- rules = rel->rd_rules;
- locks = NIL;
- /*
- * Collect the RIR rules that we must apply
- */
- for (i = 0; i < rules->numLocks; i++)
- {
- rule = rules->rules[i];
- if (rule->event != CMD_SELECT)
- continue;
- if (rule->attrno > 0 &&
- !attribute_used((Node *) parsetree,
- rt_index,
- rule->attrno, 0))
- continue;
- locks = lappend(locks, rule);
- }
- /*
- * Check permissions
- */
- checkLockPerms(locks, parsetree, rt_index);
- /*
- * Now apply them
- */
- foreach(l, locks)
- {
- rule = lfirst(l);
- RIRonly.event = rule->event;
- RIRonly.attrno = rule->attrno;
- RIRonly.qual = rule->qual;
- RIRonly.actions = rule->actions;
- ApplyRetrieveRule(parsetree,
- &RIRonly,
- rt_index,
- RIRonly.attrno == -1,
- rel,
- &modified);
- }
- heap_close(rel);
- }
- fireRIRonSubselect((Node *) parsetree);
- modifyAggrefQual((Node **) &(parsetree->qual), parsetree);
- return parsetree;
- }
- /*
- * idea is to fire regular rules first, then qualified instead
- * rules and unqualified instead rules last. Any lemming is counted for.
- */
- static List *
- orderRules(List *locks)
- {
- List *regular = NIL;
- List *instead_rules = NIL;
- List *instead_qualified = NIL;
- List *i;
- foreach(i, locks)
- {
- RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
- if (rule_lock->isInstead)
- {
- if (rule_lock->qual == NULL)
- instead_rules = lappend(instead_rules, rule_lock);
- else
- instead_qualified = lappend(instead_qualified, rule_lock);
- }
- else
- regular = lappend(regular, rule_lock);
- }
- regular = nconc(regular, instead_qualified);
- return nconc(regular, instead_rules);
- }
- static Query *
- CopyAndAddQual(Query *parsetree,
- List *actions,
- Node *rule_qual,
- int rt_index,
- CmdType event)
- {
- Query *new_tree = (Query *) copyObject(parsetree);
- Node *new_qual = NULL;
- Query *rule_action = NULL;
- if (actions)
- rule_action = lfirst(actions);
- if (rule_qual != NULL)
- new_qual = (Node *) copyObject(rule_qual);
- if (rule_action != NULL)
- {
- List *rtable;
- int rt_length;
- rtable = new_tree->rtable;
- rt_length = length(rtable);
- rtable = nconc(rtable, copyObject(rule_action->rtable));
- new_tree->rtable = rtable;
- OffsetVarNodes(new_qual, rt_length, 0);
- ChangeVarNodes(new_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
- }
- /* XXX -- where current doesn't work for instead nothing.... yet */
- AddNotQual(new_tree, new_qual);
- return new_tree;
- }
- /*
- * fireRules -
- * Iterate through rule locks applying rules.
- * All rules create their own parsetrees. Instead rules
- * with rule qualification save the original parsetree
- * and add their negated qualification to it. Real instead
- * rules finally throw away the original parsetree.
- *
- * remember: reality is for dead birds -- glass
- *
- */
- static List *
- fireRules(Query *parsetree,
- int rt_index,
- CmdType event,
- bool *instead_flag,
- List *locks,
- List **qual_products)
- {
- RewriteInfo *info;
- List *results = NIL;
- List *i;
- /* choose rule to fire from list of rules */
- if (locks == NIL)
- return NIL;
- locks = orderRules(locks); /* real instead rules last */
- foreach(i, locks)
- {
- RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
- Node *qual,
- *event_qual;
- List *actions;
- List *r;
- /*
- * Instead rules change the resultRelation of the query. So the
- * permission checks on the initial resultRelation would never be
- * done (this is normally done in the executor deep down). So we
- * must do it here. The result relations resulting from earlier
- * rewrites are already checked against the rules eventrelation
- * owner (during matchLocks) and have the skipAcl flag set.
- */
- if (rule_lock->isInstead &&
- parsetree->commandType != CMD_SELECT)
- {
- RangeTblEntry *rte;
- int32 acl_rc;
- int32 reqperm;
- switch (parsetree->commandType)
- {
- case CMD_INSERT:
- reqperm = ACL_AP;
- break;
- default:
- reqperm = ACL_WR;
- break;
- }
- rte = (RangeTblEntry *) nth(parsetree->resultRelation - 1,
- parsetree->rtable);
- if (!rte->skipAcl)
- {
- acl_rc = pg_aclcheck(rte->relname,
- GetPgUserName(), reqperm);
- if (acl_rc != ACLCHECK_OK)
- {
- elog(ERROR, "%s: %s",
- rte->relname,
- aclcheck_error_strings[acl_rc]);
- }
- }
- }
- /* multiple rule action time */
- *instead_flag = rule_lock->isInstead;
- event_qual = rule_lock->qual;
- actions = rule_lock->actions;
- if (event_qual != NULL && *instead_flag)
- {
- Query *qual_product;
- RewriteInfo qual_info;
- /* ----------
- * If there are instead rules with qualifications,
- * the original query is still performed. But all
- * the negated rule qualifications of the instead
- * rules are added so it does it's actions only
- * in cases where the rule quals of all instead
- * rules are false. Think of it as the default
- * action in a case. We save this in *qual_products
- * so deepRewriteQuery() can add it to the query
- * list after we mangled it up enough.
- * ----------
- */
- if (*qual_products == NIL)
- qual_product = parsetree;
- else
- qual_product = (Query *) nth(0, *qual_products);
- qual_info.event = qual_product->commandType;
- qual_info.new_varno = length(qual_product->rtable) + 2;
- qual_product = CopyAndAddQual(qual_product,
- actions,
- event_qual,
- rt_index,
- event);
- qual_info.rule_action = qual_product;
- if (event == CMD_INSERT || event == CMD_UPDATE)
- FixNew(&qual_info, qual_product);
- *qual_products = lappend(NIL, qual_product);
- }
- foreach(r, actions)
- {
- Query *rule_action = lfirst(r);
- Node *rule_qual = copyObject(event_qual);
- if (rule_action->commandType == CMD_NOTHING)
- continue;
- /*--------------------------------------------------
- * We copy the qualifications of the parsetree
- * to the action and vice versa. So force
- * hasSubLinks if one of them has it.
- *
- * As of 6.4 only parsetree qualifications can
- * have sublinks. If this changes, we must make
- * this a node lookup at the end of rewriting.
- *
- * Jan
- *--------------------------------------------------
- */
- if (parsetree->hasSubLinks && !rule_action->hasSubLinks)
- {
- rule_action = copyObject(rule_action);
- rule_action->hasSubLinks = TRUE;
- }
- if (!parsetree->hasSubLinks && rule_action->hasSubLinks)
- parsetree->hasSubLinks = TRUE;
- /*--------------------------------------------------
- * Step 1:
- * Rewrite current.attribute or current to tuple variable
- * this appears to be done in parser?
- *--------------------------------------------------
- */
- info = gatherRewriteMeta(parsetree, rule_action, rule_qual,
- rt_index, event, instead_flag);
- /* handle escapable cases, or those handled by other code */
- if (info->nothing)
- {
- if (*instead_flag)
- return NIL;
- else
- continue;
- }
- if (info->action == info->event &&
- info->event == CMD_SELECT)
- continue;
- /*
- * Event Qualification forces copying of parsetree and
- * splitting into two queries one w/rule_qual, one w/NOT
- * rule_qual. Also add user query qual onto rule action
- */
- qual = parsetree->qual;
- AddQual(info->rule_action, qual);
- if (info->rule_qual != NULL)
- AddQual(info->rule_action, info->rule_qual);
- /*--------------------------------------------------
- * Step 2:
- * Rewrite new.attribute w/ right hand side of target-list
- * entry for appropriate field name in insert/update
- *--------------------------------------------------
- */
- if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE))
- FixNew(info, parsetree);
- /*--------------------------------------------------
- * Step 3:
- * rewriting due to retrieve rules
- *--------------------------------------------------
- */
- info->rule_action->rtable = info->rt;
- /*
- * ProcessRetrieveQuery(info->rule_action, info->rt,
- * &orig_instead_flag, TRUE);
- */
- /*--------------------------------------------------
- * Step 4
- * Simplify? hey, no algorithm for simplification... let
- * the planner do it.
- *--------------------------------------------------
- */
- results = lappend(results, info->rule_action);
- pfree(info);
- }
- /* ----------
- * If this was an unqualified instead rule,
- * throw away an eventually saved 'default' parsetree
- * ----------
- */
- if (event_qual == NULL && *instead_flag)
- *qual_products = NIL;
- }
- return results;
- }
- static List *
- RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
- {
- CmdType event;
- List *product_queries = NIL;
- int result_relation = 0;
- RangeTblEntry *rt_entry;
- Relation rt_entry_relation = NULL;
- RuleLock *rt_entry_locks = NULL;
- Assert(parsetree != NULL);
- event = parsetree->commandType;
- /*
- * SELECT rules are handled later when we have all the queries that
- * should get executed
- */
- if (event == CMD_SELECT)
- return NIL;
- /*
- * Utilities aren't rewritten at all - why is this here?
- */
- if (event == CMD_UTILITY)
- return NIL;
- /*
- * only for a delete may the targetlist be NULL
- */
- if (event != CMD_DELETE)
- Assert(parsetree->targetList != NULL);
- result_relation = parsetree->resultRelation;
- /*
- * the statement is an update, insert or delete - fire rules on it.
- */
- rt_entry = rt_fetch(result_relation, parsetree->rtable);
- rt_entry_relation = heap_openr(rt_entry->relname);
- rt_entry_locks = rt_entry_relation->rd_rules;
- heap_close(rt_entry_relation);
- if (rt_entry_locks != NULL)
- {
- List *locks = matchLocks(event, rt_entry_locks, result_relation, parsetree);
- product_queries = fireRules(parsetree,
- result_relation,
- event,
- instead_flag,
- locks,
- qual_products);
- }
- return product_queries;
- }
- /*
- * to avoid infinite recursion, we restrict the number of times a query
- * can be rewritten. Detecting cycles is left for the reader as an excercise.
- */
- #ifndef REWRITE_INVOKE_MAX
- #define REWRITE_INVOKE_MAX 10
- #endif
- static int numQueryRewriteInvoked = 0;
- /*
- * deepRewriteQuery -
- * rewrites the query and apply the rules again on the queries rewritten
- */
- static List *
- deepRewriteQuery(Query *parsetree)
- {
- List *n;
- List *rewritten = NIL;
- List *result = NIL;
- bool instead;
- List *qual_products = NIL;
- if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX)
- {
- elog(ERROR, "query rewritten %d times, may contain cycles",
- numQueryRewriteInvoked - 1);
- }
- instead = FALSE;
- result = RewriteQuery(parsetree, &instead, &qual_products);
- foreach(n, result)
- {
- Query *pt = lfirst(n);
- List *newstuff = NIL;
- newstuff = deepRewriteQuery(pt);
- if (newstuff != NIL)
- rewritten = nconc(rewritten, newstuff);
- }
- /* ----------
- * qual_products are the original query with the negated
- * rule qualification of an instead rule
- * ----------
- */
- if (qual_products != NIL)
- rewritten = nconc(rewritten, qual_products);
- /* ----------
- * The original query is appended last if not instead
- * because update and delete rule actions might not do
- * anything if they are invoked after the update or
- * delete is performed. The command counter increment
- * between the query execution makes the deleted (and
- * maybe the updated) tuples disappear so the scans
- * for them in the rule actions cannot find them.
- * ----------
- */
- if (!instead)
- rewritten = lappend(rewritten, parsetree);
- return rewritten;
- }
- /*
- * QueryOneRewrite -
- * rewrite one query
- */
- static List *
- QueryRewriteOne(Query *parsetree)
- {
- numQueryRewriteInvoked = 0;
- /*
- * take a deep breath and apply all the rewrite rules - ay
- */
- return deepRewriteQuery(parsetree);
- }
- /* ----------
- * RewritePreprocessQuery -
- * adjust details in the parsetree, the rule system
- * depends on
- * ----------
- */
- static void
- RewritePreprocessQuery(Query *parsetree)
- {
- /* ----------
- * if the query has a resultRelation, reassign the
- * result domain numbers to the attribute numbers in the
- * target relation. FixNew() depends on it when replacing
- * *new* references in a rule action by the expressions
- * from the rewritten query.
- * resjunk targets are somewhat arbitrarily given a resno of 0;
- * this is to prevent FixNew() from matching them to var nodes.
- * ----------
- */
- if (parsetree->resultRelation > 0)
- {
- RangeTblEntry *rte;
- Relation rd;
- List *tl;
- rte = (RangeTblEntry *) nth(parsetree->resultRelation - 1,
- parsetree->rtable);
- rd = heap_openr(rte->relname);
- foreach(tl, parsetree->targetList)
- {
- TargetEntry *tle = (TargetEntry *) lfirst(tl);
- if (! tle->resdom->resjunk)
- tle->resdom->resno = attnameAttNum(rd, tle->resdom->resname);
- else
- tle->resdom->resno = 0;
- }
- heap_close(rd);
- }
- }
- /*
- * BasicQueryRewrite -
- * rewrite one query via query rewrite system, possibly returning 0
- * or many queries
- */
- static List *
- BasicQueryRewrite(Query *parsetree)
- {
- List *querylist;
- List *results = NIL;
- List *l;
- Query *query;
- /*
- * Step 1
- *
- * There still seems something broken with the resdom numbers so we
- * reassign them first.
- */
- RewritePreprocessQuery(parsetree);
- /*
- * Step 2
- *
- * Apply all non-SELECT rules possibly getting 0 or many queries
- */
- querylist = QueryRewriteOne(parsetree);
- /*
- * Step 3
- *
- * Apply all the RIR rules on each query
- */
- foreach(l, querylist)
- {
- query = fireRIRrules((Query *) lfirst(l));
- /*
- * If the query was marked having aggregates, check if this is
- * still true after rewriting. This check must get expanded when
- * someday aggregates can appear somewhere else than in the
- * targetlist or the having qual.
- */
- if (query->hasAggs)
- query->hasAggs = checkQueryHasAggs((Node *) (query->targetList))
- | checkQueryHasAggs((Node *) (query->havingQual));
- query->hasSubLinks = checkQueryHasSubLink((Node *) (query->qual))
- | checkQueryHasSubLink((Node *) (query->havingQual));
- results = lappend(results, query);
- }
- return results;
- }
- /*
- * QueryRewrite -
- * Primary entry point to the query rewriter.
- * Rewrite one query via query rewrite system, possibly returning 0
- * or many queries.
- *
- * NOTE: The code in QueryRewrite was formerly in pg_parse_and_plan(), and was
- * moved here so that it would be invoked during EXPLAIN. The division of
- * labor between this routine and BasicQueryRewrite is not obviously correct
- * ... at least not to me ... tgl 5/99.
- */
- List *
- QueryRewrite(Query *parsetree)
- {
- List *rewritten,
- *rewritten_item;
- /***S*I***/
- /*
- * Rewrite Union, Intersect and Except Queries to normal Union Queries
- * using IN and NOT IN subselects
- */
- if (parsetree->intersectClause)
- parsetree = Except_Intersect_Rewrite(parsetree);
- /* Rewrite basic queries (retrieve, append, delete, replace) */
- rewritten = BasicQueryRewrite(parsetree);
- /*
- * Rewrite the UNIONS.
- */
- foreach(rewritten_item, rewritten)
- {
- Query *qry = (Query *) lfirst(rewritten_item);
- List *union_result = NIL;
- List *union_item;
- foreach(union_item, qry->unionClause)
- {
- union_result = nconc(union_result,
- BasicQueryRewrite((Query *) lfirst(union_item)));
- }
- qry->unionClause = union_result;
- }
- return rewritten;
- }
- /***S*I***/
- /* This function takes two targetlists as arguments and checks if the
- * targetlists are compatible (i.e. both select for the same number of
- * attributes and the types are compatible */
- static void
- check_targetlists_are_compatible(List *prev_target, List *current_target)
- {
- List *tl,
- *next_target;
- int prev_len = 0,
- next_len = 0;
- foreach(tl, prev_target)
- if (!((TargetEntry *) lfirst(tl))->resdom->resjunk)
- prev_len++;
- foreach(next_target, current_target)
- if (!((TargetEntry *) lfirst(next_target))->resdom->resjunk)
- next_len++;
- if (prev_len != next_len)
- elog(ERROR, "Each UNION | EXCEPT | INTERSECT query must have the same number of columns.");
- foreach(next_target, current_target)
- {
- Oid itype;
- Oid otype;
- otype = ((TargetEntry *) lfirst(prev_target))->resdom->restype;
- itype = ((TargetEntry *) lfirst(next_target))->resdom->restype;
- /* one or both is a NULL column? then don't convert... */
- if (otype == InvalidOid)
- {
- /* propagate a known type forward, if available */
- if (itype != InvalidOid)
- ((TargetEntry *) lfirst(prev_target))->resdom->restype = itype;
- #ifdef NOT_USED
- else
- {
- ((TargetEntry *) lfirst(prev_target))->resdom->restype = UNKNOWNOID;
- ((TargetEntry *) lfirst(next_target))->resdom->restype = UNKNOWNOID;
- }
- #endif
- }
- else if (itype == InvalidOid)
- {
- }
- /* they don't match in type? then convert... */
- else if (itype != otype)
- {
- Node *expr;
- expr = ((TargetEntry *) lfirst(next_target))->expr;
- expr = CoerceTargetExpr(NULL, expr, itype, otype);
- if (expr == NULL)
- {
- elog(ERROR, "Unable to transform %s to %s"
- "ntEach UNION | EXCEPT | INTERSECT clause must have compatible target types",
- typeidTypeName(itype),
- typeidTypeName(otype));
- }
- ((TargetEntry *) lfirst(next_target))->expr = expr;
- ((TargetEntry *) lfirst(next_target))->resdom->restype = otype;
- }
- /* both are UNKNOWN? then evaluate as text... */
- else if (itype == UNKNOWNOID)
- {
- ((TargetEntry *) lfirst(next_target))->resdom->restype = TEXTOID;
- ((TargetEntry *) lfirst(prev_target))->resdom->restype = TEXTOID;
- }
- prev_target = lnext(prev_target);
- }
- }
- /***S*I***/
- /* Rewrites UNION INTERSECT and EXCEPT queries to semantiacally equivalent
- * queries that use IN and NOT IN subselects.
- *
- * The operator tree is attached to 'intersectClause' (see rule
- * 'SelectStmt' in gram.y) of the 'parsetree' given as an
- * argument. First we remember some clauses (the sortClause, the
- * unique flag etc.) Then we translate the operator tree to DNF
- * (disjunctive normal form) by 'cnfify'. (Note that 'cnfify' produces
- * CNF but as we exchanged ANDs with ORs in function A_Expr_to_Expr()
- * earlier we get DNF after exchanging ANDs and ORs again in the
- * result.) Now we create a new query by evaluating the new operator
- * tree which is in DNF now. For every AND we create an entry in the
- * union list and for every OR we create an IN subselect. (NOT IN
- * subselects are created for OR NOT nodes). The first entry of the
- * union list is handed back but before that the remembered clauses
- * (sortClause etc) are attached to the new top Node (Note that the
- * new top Node can differ from the parsetree given as argument because of
- * the translation to DNF. That's why we have to remember the sortClause or
- * unique flag!) */
- static Query *
- Except_Intersect_Rewrite(Query *parsetree)
- {
- SubLink *n;
- Query *result,
- *intersect_node;
- List *elist,
- *intersect_list = NIL,
- *intersect,
- *intersectClause;
- List *union_list = NIL,
- *sortClause;
- List *left_expr,
- *right_expr,
- *resnames = NIL;
- char *op,
- *uniqueFlag,
- *into;
- bool isBinary,
- isPortal,
- isTemp;
- CmdType commandType = CMD_SELECT;
- List *rtable_insert = NIL;
- List *prev_target = NIL;
- /*
- * Remember the Resnames of the given parsetree's targetlist (these
- * are the resnames of the first Select Statement of the query
- * formulated by the user and he wants the columns named by these
- * strings. The transformation to DNF can cause another Select
- * Statment to be the top one which uses other names for its columns.
- * Therefore we remeber the original names and attach them to the
- * targetlist of the new topmost Node at the end of this function
- */
- foreach(elist, parsetree->targetList)
- {
- TargetEntry *tent = (TargetEntry *) lfirst(elist);
- resnames = lappend(resnames, tent->resdom->resname);
- }
- /*
- * If the Statement is an INSERT INTO ... (SELECT...) statement using
- * UNIONs, INTERSECTs or EXCEPTs and the transformation to DNF makes
- * another Node to the top node we have to transform the new top node
- * to an INSERT node and the original INSERT node to a SELECT node
- */
- if (parsetree->commandType == CMD_INSERT)
- {
- parsetree->commandType = CMD_SELECT;
- commandType = CMD_INSERT;
- parsetree->resultRelation = 0;
- /*
- * The result relation ( = the one to insert into) has to be
- * attached to the rtable list of the new top node
- */
- rtable_insert = nth(length(parsetree->rtable) - 1, parsetree->rtable);
- }
- /*
- * Save some items, to be able to attach them to the resulting top
- * node at the end of the function
- */
- sortClause = parsetree->sortClause;
- uniqueFlag = parsetree->uniqueFlag;
- into = parsetree->into;
- isBinary = parsetree->isBinary;
- isPortal = parsetree->isPortal;
- isTemp = parsetree->isTemp;
- /*
- * The operator tree attached to parsetree->intersectClause is still
- * 'raw' ( = the leaf nodes are still SelectStmt nodes instead of
- * Query nodes) So step through the tree and transform the nodes using
- * parse_analyze().
- *
- * The parsetree (given as an argument to Except_Intersect_Rewrite()) has
- * already been transformed and transforming it again would cause
- * troubles. So we give the 'raw' version (of the cooked parsetree)
- * to the function to prevent an additional transformation. Instead we
- * hand back the 'cooked' version also given as an argument to
- * intersect_tree_analyze()
- */
- intersectClause =
- (List *) intersect_tree_analyze((Node *) parsetree->intersectClause,
- (Node *) lfirst(parsetree->unionClause),
- (Node *) parsetree);
- /* intersectClause is no longer needed so set it to NIL */
- parsetree->intersectClause = NIL;
- /*
- * unionClause will be needed later on but the list it delivered is no
- * longer needed, so set it to NIL
- */
- parsetree->unionClause = NIL;
- /*
- * Transform the operator tree to DNF (remember ANDs and ORs have been
- * exchanged, that's why we get DNF by using cnfify)
- *
- * After the call, explicit ANDs are removed and all AND operands are
- * simply items in the intersectClause list
- */
- intersectClause = cnfify((Expr *) intersectClause, true);
- /*
- * For every entry of the intersectClause list we generate one entry
- * in the union_list
- */
- foreach(intersect, intersectClause)
- {
- /*
- * for every OR we create an IN subselect and for every OR NOT we
- * create a NOT IN subselect, so first extract all the Select
- * Query nodes from the tree (that contains only OR or OR NOTs any
- * more because we did a transformation to DNF
- *
- * There must be at least one node that is not negated (i.e. just OR
- * and not OR NOT) and this node will be the first in the list
- * returned
- */
- intersect_list = NIL;
- create_intersect_list((Node *) lfirst(intersect), &intersect_list);
- /*
- * This one will become the Select Query node, all other nodes are
- * transformed into subselects under this node!
- */
- intersect_node = (Query *) lfirst(intersect_list);
- intersect_list = lnext(intersect_list);
- /*
- * Check if all Select Statements use the same number of
- * attributes and if all corresponding attributes are of the same
- * type
- */
- if (prev_target)
- check_targetlists_are_compatible(prev_target, intersect_node->targetList);
- prev_target = intersect_node->targetList;
- /* End of check for corresponding targetlists */
- /*
- * Transform all nodes remaining into subselects and add them to
- * the qualifications of the Select Query node
- */
- while (intersect_list != NIL)
- {
- n = makeNode(SubLink);
- /* Here we got an OR so transform it to an IN subselect */
- if (IsA(lfirst(intersect_list), Query))
- {
- /*
- * Check if all Select Statements use the same number of
- * attributes and if all corresponding attributes are of
- * the same type
- */
- check_targetlists_are_compatible(prev_target,
- ((Query *) lfirst(intersect_list))->targetList);
- /* End of check for corresponding targetlists */
- n->subselect = lfirst(intersect_list);
- op = "=";
- n->subLinkType = ANY_SUBLINK;
- n->useor = false;
- }
- /*
- * Here we got an OR NOT node so transform it to a NOT IN
- * subselect
- */
- else
- {
- /*
- * Check if all Select Statements use the same number of
- * attributes and if all corresponding attributes are of
- * the same type
- */
- check_targetlists_are_compatible(prev_target,
- ((Query *) lfirst(((Expr *) lfirst(intersect_list))->args))->targetList);
- /* End of check for corresponding targetlists */
- n->subselect = (Node *) lfirst(((Expr *) lfirst(intersect_list))->args);
- op = "<>";
- n->subLinkType = ALL_SUBLINK;
- n->useor = true;
- }
- /*
- * Prepare the lefthand side of the Sublinks: All the entries
- * of the targetlist must be (IN) or must not be (NOT IN) the
- * subselect
- */
- foreach(elist, intersect_node->targetList)
- {
- Node *expr = lfirst(elist);
- TargetEntry *tent = (TargetEntry *) expr;
- n->lefthand = lappend(n->lefthand, tent->expr);
- }
- /*
- * The first arguments of oper also have to be created for the
- * sublink (they are the same as the lefthand side!)
- */
- left_expr = n->lefthand;
- right_expr = ((Query *) (n->subselect))->targetList;
- foreach(elist, left_expr)
- {
- Node *lexpr = lfirst(elist);
- Node *rexpr = lfirst(right_expr);
- TargetEntry *tent = (TargetEntry *) rexpr;
- Expr *op_expr;
- op_expr = make_op(op, lexpr, tent->expr);
- n->oper = lappend(n->oper, op_expr);
- right_expr = lnext(right_expr);
- }
- /*
- * If the Select Query node has aggregates in use add all the
- * subselects to the HAVING qual else to the WHERE qual
- */
- if (intersect_node->hasAggs == false)
- AddQual(intersect_node, (Node *) n);
- else
- AddHavingQual(intersect_node, (Node *) n);
- /* Now we got sublinks */
- intersect_node->hasSubLinks = true;
- intersect_list = lnext(intersect_list);
- }
- intersect_node->intersectClause = NIL;
- union_list = lappend(union_list, intersect_node);
- }
- /* The first entry to union_list is our new top node */
- result = (Query *) lfirst(union_list);
- /* attach the rest to unionClause */
- result->unionClause = lnext(union_list);
- /* Attach all the items remembered in the beginning of the function */
- result->sortClause = sortClause;
- result->uniqueFlag = uniqueFlag;
- result->into = into;
- result->isPortal = isPortal;
- result->isBinary = isBinary;
- result->isTemp = isTemp;
- /*
- * The relation to insert into is attached to the range table of the
- * new top node
- */
- if (commandType == CMD_INSERT)
- {
- result->rtable = lappend(result->rtable, rtable_insert);
- result->resultRelation = length(result->rtable);
- result->commandType = commandType;
- }
- /*
- * The resnames of the originally first SelectStatement are attached
- * to the new first SelectStatement
- */
- foreach(elist, result->targetList)
- {
- TargetEntry *tent = (TargetEntry *) lfirst(elist);
- tent->resdom->resname = lfirst(resnames);
- resnames = lnext(resnames);
- }
- return result;
- }
- /* Create a list of nodes that are either Query nodes of NOT Expr
- * nodes followed by a Query node. The tree given in ptr contains at
- * least one non negated Query node. This node is attached to the
- * beginning of the list */
- static void
- create_intersect_list(Node *ptr, List **intersect_list)
- {
- List *arg;
- if (IsA(ptr, Query))
- {
- /* The non negated node is attached at the beginning (lcons) */
- *intersect_list = lcons(ptr, *intersect_list);
- return;
- }
- if (IsA(ptr, Expr))
- {
- if (((Expr *) ptr)->opType == NOT_EXPR)
- {
- /* negated nodes are appended to the end (lappend) */
- *intersect_list = lappend(*intersect_list, ptr);
- return;
- }
- else
- {
- foreach(arg, ((Expr *) ptr)->args)
- create_intersect_list(lfirst(arg), intersect_list);
- return;
- }
- return;
- }
- }
- /* The nodes given in 'tree' are still 'raw' so 'cook' them using parse_analyze().
- * The node given in first_select has already been cooked, so don't transform
- * it again but return a pointer to the previously cooked version given in 'parsetree'
- * instead. */
- static Node *
- intersect_tree_analyze(Node *tree, Node *first_select, Node *parsetree)
- {
- Node *result = (Node *) NIL;
- List *arg;
- if (IsA(tree, SelectStmt))
- {
- /*
- * If we get to the tree given in first_select return parsetree
- * instead of performing parse_analyze()
- */
- if (tree == first_select)
- result = parsetree;
- else
- {
- /* transform the 'raw' nodes to 'cooked' Query nodes */
- List *qtree = parse_analyze(lcons(tree, NIL), NULL);
- result = (Node *) lfirst(qtree);
- }
- }
- if (IsA(tree, Expr))
- {
- /* Call recursively for every argument of the node */
- foreach(arg, ((Expr *) tree)->args)
- lfirst(arg) = intersect_tree_analyze(lfirst(arg), first_select, parsetree);
- result = tree;
- }
- return result;
- }