Commit 389af07c authored by Tom Lane's avatar Tom Lane

Clean up rewriter routines to use expression_tree_walker and

expression_tree_mutator rather than ad-hoc tree walking code.  This shortens
the code materially and fixes a fair number of sins of omission.  Also,
change modifyAggrefQual to *not* recurse into subselects, since its mission
is satisfied if it removes aggregate functions from the top level of a
WHERE clause.  This cures problems with queries of the form SELECT ...
WHERE x IN (SELECT ... HAVING something-using-an-aggregate), which would
formerly get mucked up by modifyAggrefQual.  The routine is still
fundamentally broken, of course, but I don't think there's any way to get
rid of it before we implement subselects in FROM ...
parent ce1f5ed5
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.22 1999/09/18 19:07:18 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.23 1999/10/01 04:08:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/pg_shadow.h" #include "catalog/pg_shadow.h"
#include "optimizer/clauses.h"
#include "rewrite/locks.h" #include "rewrite/locks.h"
#include "utils/acl.h" #include "utils/acl.h"
#include "utils/builtins.h" #include "utils/builtins.h"
...@@ -22,102 +23,86 @@ ...@@ -22,102 +23,86 @@
/* /*
* ThisLockWasTriggered * thisLockWasTriggered
* *
* walk the tree, if there we find a varnode, * walk the tree, if there we find a varnode,
* we check the varattno against the attnum * we check the varattno against the attnum
* if we find at least one such match, we return true * if we find at least one such match, we return true
* otherwise, we return false * otherwise, we return false
*
* XXX this should be unified with attribute_used()
*/ */
typedef struct {
int varno;
int attnum;
int sublevels_up;
} thisLockWasTriggered_context;
static bool static bool
nodeThisLockWasTriggered(Node *node, int varno, AttrNumber attnum, thisLockWasTriggered_walker (Node *node,
int sublevels_up) thisLockWasTriggered_context *context)
{ {
if (node == NULL) if (node == NULL)
return FALSE; return false;
switch (nodeTag(node)) if (IsA(node, Var))
{
case T_Var:
{ {
Var *var = (Var *) node; Var *var = (Var *) node;
if (varno == var->varno && if (var->varlevelsup == context->sublevels_up &&
(attnum == var->varattno || attnum == -1)) var->varno == context->varno &&
return TRUE; (var->varattno == context->attnum || context->attnum == -1))
} return true;
break; return false;
case T_Expr:
{
Expr *expr = (Expr *) node;
return nodeThisLockWasTriggered((Node *) expr->args, varno,
attnum, sublevels_up);
}
break;
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *) node;
return nodeThisLockWasTriggered(tle->expr, varno, attnum,
sublevels_up);
}
break;
case T_Aggref:
{
Aggref *aggref = (Aggref *) node;
return nodeThisLockWasTriggered(aggref->target, varno, attnum,
sublevels_up);
} }
break; if (IsA(node, SubLink))
case T_List:
{ {
List *l; /*
* Standard expression_tree_walker will not recurse into subselect,
* but here we must do so.
*/
SubLink *sub = (SubLink *) node;
foreach(l, (List *) node) if (thisLockWasTriggered_walker((Node *) (sub->lefthand), context))
return true;
context->sublevels_up++;
if (thisLockWasTriggered_walker((Node *) (sub->subselect), context))
{ {
if (nodeThisLockWasTriggered(lfirst(l), varno, attnum, context->sublevels_up--; /* not really necessary */
sublevels_up)) return true;
return TRUE;
} }
return FALSE; context->sublevels_up--;
return false;
} }
break; if (IsA(node, Query))
case T_SubLink:
{ {
SubLink *sublink = (SubLink *) node; /* Reach here after recursing down into subselect above... */
Query *query = (Query *) sublink->subselect; Query *qry = (Query *) node;
return nodeThisLockWasTriggered(query->qual, varno, attnum, if (thisLockWasTriggered_walker((Node *) (qry->targetList), context))
sublevels_up + 1); return true;
} if (thisLockWasTriggered_walker((Node *) (qry->qual), context))
break; return true;
default: if (thisLockWasTriggered_walker((Node *) (qry->havingQual), context))
break; return true;
return false;
} }
return FALSE; return expression_tree_walker(node, thisLockWasTriggered_walker,
(void *) context);
} }
/*
* thisLockWasTriggered -
* walk the tree, if there we find a varnode, we check the varattno
* against the attnum if we find at least one such match, we return true
* otherwise, we return false
*/
static bool static bool
thisLockWasTriggered(int varno, thisLockWasTriggered(int varno,
AttrNumber attnum, int attnum,
Query *parsetree) Query *parsetree)
{ {
thisLockWasTriggered_context context;
if (nodeThisLockWasTriggered(parsetree->qual, varno, attnum, 0)) context.varno = varno;
return true; context.attnum = attnum;
context.sublevels_up = 0;
if (nodeThisLockWasTriggered((Node *) parsetree->targetList, varno, attnum, 0))
return true;
return false;
return thisLockWasTriggered_walker((Node *) parsetree, &context);
} }
/* /*
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.57 1999/09/19 17:20:58 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.58 1999/10/01 04:08:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -32,6 +32,19 @@ ...@@ -32,6 +32,19 @@
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
extern void CheckSelectForUpdate(Query *rule_action); /* in analyze.c */
/* macros borrowed from expression_tree_mutator */
#define FLATCOPY(newnode, node, nodetype) \
( (newnode) = makeNode(nodetype), \
memcpy((newnode), (node), sizeof(nodetype)) )
#define MUTATE(newfield, oldfield, fieldtype, mutator, context) \
( (newfield) = (fieldtype) mutator((Node *) (oldfield), (context)) )
static RewriteInfo *gatherRewriteMeta(Query *parsetree, static RewriteInfo *gatherRewriteMeta(Query *parsetree,
Query *rule_action, Query *rule_action,
Node *rule_qual, Node *rule_qual,
...@@ -39,12 +52,14 @@ static RewriteInfo *gatherRewriteMeta(Query *parsetree, ...@@ -39,12 +52,14 @@ static RewriteInfo *gatherRewriteMeta(Query *parsetree,
CmdType event, CmdType event,
bool *instead_flag); bool *instead_flag);
static bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up); 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 bool attribute_used(Node *node, int rt_index, int attno,
static void modifyAggrefUplevel(Node *node); int sublevels_up);
static void modifyAggrefChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int sublevels_up); static bool modifyAggrefUplevel(Node *node, void *context);
static void modifyAggrefDropQual(Node **nodePtr, Node *orignode, Expr *expr); static bool modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
int sublevels_up);
static Node *modifyAggrefDropQual(Node *node, Node *targetNode);
static SubLink *modifyAggrefMakeSublink(Expr *origexp, Query *parsetree); static SubLink *modifyAggrefMakeSublink(Expr *origexp, Query *parsetree);
static void modifyAggrefQual(Node **nodePtr, Query *parsetree); static Node *modifyAggrefQual(Node *node, Query *parsetree);
static bool checkQueryHasAggs(Node *node); static bool checkQueryHasAggs(Node *node);
static bool checkQueryHasAggs_walker(Node *node, void *context); static bool checkQueryHasAggs_walker(Node *node, void *context);
static bool checkQueryHasSubLink(Node *node); static bool checkQueryHasSubLink(Node *node);
...@@ -135,926 +150,350 @@ gatherRewriteMeta(Query *parsetree, ...@@ -135,926 +150,350 @@ gatherRewriteMeta(Query *parsetree,
* we need to process a RTE for RIR rules only if it is * we need to process a RTE for RIR rules only if it is
* referenced somewhere in var nodes of the query. * referenced somewhere in var nodes of the query.
*/ */
typedef struct {
int rt_index;
int sublevels_up;
} rangeTableEntry_used_context;
static bool static bool
rangeTableEntry_used(Node *node, int rt_index, int sublevels_up) rangeTableEntry_used_walker (Node *node,
rangeTableEntry_used_context *context)
{ {
if (node == NULL) if (node == NULL)
return FALSE; return false;
if (IsA(node, Var))
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; Var *var = (Var *) node;
return rangeTableEntry_used( if (var->varlevelsup == context->sublevels_up &&
(Node *) (aggref->target), var->varno == context->rt_index)
rt_index, return true;
sublevels_up); return false;
} }
break; if (IsA(node, SubLink))
case T_GroupClause:
return FALSE;
case T_Expr:
{ {
Expr *exp = (Expr *) node; /*
* Standard expression_tree_walker will not recurse into subselect,
* but here we must do so.
*/
SubLink *sub = (SubLink *) node;
return rangeTableEntry_used( if (rangeTableEntry_used_walker((Node *) (sub->lefthand), context))
(Node *) (exp->args), return true;
rt_index, if (rangeTableEntry_used((Node *) (sub->subselect),
sublevels_up); context->rt_index,
context->sublevels_up + 1))
return true;
return false;
} }
break; if (IsA(node, Query))
case T_Iter:
{ {
Iter *iter = (Iter *) node; /* Reach here after recursing down into subselect above... */
Query *qry = (Query *) node;
return rangeTableEntry_used( if (rangeTableEntry_used_walker((Node *) (qry->targetList), context))
(Node *) (iter->iterexpr), return true;
rt_index, if (rangeTableEntry_used_walker((Node *) (qry->qual), context))
sublevels_up); return true;
if (rangeTableEntry_used_walker((Node *) (qry->havingQual), context))
return true;
return false;
} }
break; return expression_tree_walker(node, rangeTableEntry_used_walker,
(void *) context);
case T_ArrayRef: }
{
ArrayRef *ref = (ArrayRef *) node;
if (rangeTableEntry_used( static bool
(Node *) (ref->refupperindexpr), rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
rt_index, {
sublevels_up)) rangeTableEntry_used_context context;
return TRUE;
if (rangeTableEntry_used( context.rt_index = rt_index;
(Node *) (ref->reflowerindexpr), context.sublevels_up = sublevels_up;
rt_index, return rangeTableEntry_used_walker(node, &context);
sublevels_up)) }
return TRUE;
if (rangeTableEntry_used(
(Node *) (ref->refexpr),
rt_index,
sublevels_up))
return TRUE;
if (rangeTableEntry_used( /*
(Node *) (ref->refassgnexpr), * attribute_used -
rt_index, * Check if a specific attribute number of a RTE is used
sublevels_up)) * somewhere in the query
return TRUE; */
return FALSE; typedef struct {
} int rt_index;
break; int attno;
int sublevels_up;
} attribute_used_context;
case T_Var: static bool
attribute_used_walker (Node *node,
attribute_used_context *context)
{
if (node == NULL)
return false;
if (IsA(node, Var))
{ {
Var *var = (Var *) node; Var *var = (Var *) node;
if (var->varlevelsup == sublevels_up) if (var->varlevelsup == context->sublevels_up &&
return var->varno == rt_index; var->varno == context->rt_index &&
else var->varattno == context->attno)
return FALSE; return true;
} 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; if (IsA(node, SubLink))
case T_SubLink:
{ {
/*
* Standard expression_tree_walker will not recurse into subselect,
* but here we must do so.
*/
SubLink *sub = (SubLink *) node; SubLink *sub = (SubLink *) node;
if (rangeTableEntry_used( if (attribute_used_walker((Node *) (sub->lefthand), context))
(Node *) (sub->lefthand), return true;
rt_index, if (attribute_used((Node *) (sub->subselect),
sublevels_up)) context->rt_index,
return TRUE; context->attno,
context->sublevels_up + 1))
if (rangeTableEntry_used( return true;
(Node *) (sub->subselect), return false;
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; if (IsA(node, Query))
case T_Query:
{ {
/* Reach here after recursing down into subselect above... */
Query *qry = (Query *) node; Query *qry = (Query *) node;
if (rangeTableEntry_used( if (attribute_used_walker((Node *) (qry->targetList), context))
(Node *) (qry->targetList), return true;
rt_index, if (attribute_used_walker((Node *) (qry->qual), context))
sublevels_up)) return true;
return TRUE; if (attribute_used_walker((Node *) (qry->havingQual), context))
return true;
if (rangeTableEntry_used( return false;
(Node *) (qry->qual),
rt_index,
sublevels_up))
return TRUE;
if (rangeTableEntry_used(
(Node *) (qry->havingQual),
rt_index,
sublevels_up))
return TRUE;
return FALSE;
} }
break; return expression_tree_walker(node, attribute_used_walker,
(void *) context);
default: }
elog(NOTICE, "unknown node tag %d in rangeTableEntry_used()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
} static bool
attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
{
attribute_used_context context;
return FALSE; context.rt_index = rt_index;
context.attno = attno;
context.sublevels_up = sublevels_up;
return attribute_used_walker(node, &context);
} }
/* /*
* attribute_used - * modifyAggrefUplevel -
* Check if a specific attribute number of a RTE is used * In the newly created sublink for an aggregate column used in
* somewhere in the query * the qualification, we must increment the varlevelsup in all the
* var nodes.
*
* NOTE: although this has the form of a walker, we cheat and modify the
* Var nodes in-place. The given expression tree should have been copied
* earlier to ensure that no unwanted side-effects occur!
*/ */
static bool static bool
attribute_used(Node *node, int rt_index, int attno, int sublevels_up) modifyAggrefUplevel(Node *node, void *context)
{ {
if (node == NULL) if (node == NULL)
return FALSE; return false;
if (IsA(node, Var))
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; Var *var = (Var *) node;
return attribute_used( var->varlevelsup++;
(Node *) (aggref->target), return false;
rt_index,
attno,
sublevels_up);
} }
break; if (IsA(node, SubLink))
case T_GroupClause:
return FALSE;
case T_Expr:
{ {
Expr *exp = (Expr *) node; /*
* Standard expression_tree_walker will not recurse into subselect,
* but here we must do so.
*/
SubLink *sub = (SubLink *) node;
return attribute_used( if (modifyAggrefUplevel((Node *) (sub->lefthand), context))
(Node *) (exp->args), return true;
rt_index, if (modifyAggrefUplevel((Node *) (sub->subselect), context))
attno, return true;
sublevels_up); return false;
} }
break; if (IsA(node, Query))
case T_Iter:
{ {
Iter *iter = (Iter *) node; /* Reach here after recursing down into subselect above... */
Query *qry = (Query *) node;
return attribute_used( if (modifyAggrefUplevel((Node *) (qry->targetList), context))
(Node *) (iter->iterexpr), return true;
rt_index, if (modifyAggrefUplevel((Node *) (qry->qual), context))
attno, return true;
sublevels_up); if (modifyAggrefUplevel((Node *) (qry->havingQual), context))
return true;
return false;
} }
break; return expression_tree_walker(node, modifyAggrefUplevel,
(void *) context);
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), * modifyAggrefChangeVarnodes -
rt_index, * Change the var nodes in a sublink created for an aggregate column
attno, * used in the qualification to point to the correct local RTE.
sublevels_up)) *
return TRUE; * NOTE: although this has the form of a walker, we cheat and modify the
* Var nodes in-place. The given expression tree should have been copied
* earlier to ensure that no unwanted side-effects occur!
*/
return FALSE; typedef struct {
} int rt_index;
break; int new_index;
int sublevels_up;
} modifyAggrefChangeVarnodes_context;
case T_Var: static bool
modifyAggrefChangeVarnodes_walker(Node *node,
modifyAggrefChangeVarnodes_context *context)
{
if (node == NULL)
return false;
if (IsA(node, Var))
{ {
Var *var = (Var *) node; Var *var = (Var *) node;
if (var->varlevelsup == sublevels_up) if (var->varlevelsup == context->sublevels_up &&
return var->varno == rt_index; var->varno == context->rt_index)
else
return FALSE;
}
break;
case T_Param:
return FALSE;
case T_Const:
return FALSE;
case T_List:
{ {
List *l; var->varno = context->new_index;
var->varnoold = context->new_index;
foreach(l, (List *) node) var->varlevelsup = 0;
{
if (attribute_used(
(Node *) lfirst(l),
rt_index,
attno,
sublevels_up))
return TRUE;
} }
return FALSE; return false;
} }
break; if (IsA(node, SubLink))
case T_SubLink:
{ {
/*
* Standard expression_tree_walker will not recurse into subselect,
* but here we must do so.
*/
SubLink *sub = (SubLink *) node; SubLink *sub = (SubLink *) node;
if (attribute_used( if (modifyAggrefChangeVarnodes_walker((Node *) (sub->lefthand),
(Node *) (sub->lefthand), context))
rt_index, return true;
attno, if (modifyAggrefChangeVarnodes((Node *) (sub->subselect),
sublevels_up)) context->rt_index,
return TRUE; context->new_index,
context->sublevels_up + 1))
if (attribute_used( return true;
(Node *) (sub->subselect), return false;
rt_index,
attno,
sublevels_up + 1))
return TRUE;
return FALSE;
} }
break; if (IsA(node, Query))
case T_Query:
{ {
/* Reach here after recursing down into subselect above... */
Query *qry = (Query *) node; Query *qry = (Query *) node;
if (attribute_used( if (modifyAggrefChangeVarnodes_walker((Node *) (qry->targetList),
(Node *) (qry->targetList), context))
rt_index, return true;
attno, if (modifyAggrefChangeVarnodes_walker((Node *) (qry->qual),
sublevels_up)) context))
return TRUE; return true;
if (modifyAggrefChangeVarnodes_walker((Node *) (qry->havingQual),
if (attribute_used( context))
(Node *) (qry->qual), return true;
rt_index, return false;
attno,
sublevels_up))
return TRUE;
if (attribute_used(
(Node *) (qry->havingQual),
rt_index,
attno,
sublevels_up))
return TRUE;
return FALSE;
} }
break; return expression_tree_walker(node, modifyAggrefChangeVarnodes_walker,
(void *) context);
default: }
elog(NOTICE, "unknown node tag %d in attribute_used()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
} static bool
modifyAggrefChangeVarnodes(Node *node, int rt_index, int new_index,
int sublevels_up)
{
modifyAggrefChangeVarnodes_context context;
return FALSE; context.rt_index = rt_index;
context.new_index = new_index;
context.sublevels_up = sublevels_up;
return modifyAggrefChangeVarnodes_walker(node, &context);
} }
/* /*
* modifyAggrefUplevel - * modifyAggrefDropQual -
* In the newly created sublink for an aggregate column used in * remove the pure aggref clause from a qualification
* the qualification, we must adjust the varlevelsup in all the *
* var nodes. * targetNode is a boolean expression node somewhere within the given
* expression tree. When we find it, replace it with a constant TRUE.
* The return tree is a modified copy of the given tree; the given tree
* is not altered.
*
* Note: we don't recurse into subselects looking for targetNode; that's
* not necessary in the current usage, since in fact targetNode will be
* within the same select level as the given toplevel node.
*/ */
static void static Node *
modifyAggrefUplevel(Node *node) modifyAggrefDropQual(Node *node, Node *targetNode)
{ {
if (node == NULL) if (node == NULL)
return; return NULL;
if (node == targetNode)
switch (nodeTag(node))
{
case T_TargetEntry:
{ {
TargetEntry *tle = (TargetEntry *) node; Expr *expr = (Expr *) node;
modifyAggrefUplevel( if (! IsA(expr, Expr) || expr->typeOid != BOOLOID)
(Node *) (tle->expr)); elog(ERROR,
"aggregate expression in qualification isn't of type bool");
return (Node *) makeConst(BOOLOID, 1, (Datum) true,
false, true, false, false);
} }
break; return expression_tree_mutator(node, modifyAggrefDropQual,
(void *) targetNode);
}
case T_Aggref: /*
{ * modifyAggrefMakeSublink -
Aggref *aggref = (Aggref *) node; * 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;
RangeTblEntry *rte;
Aggref *aggref;
Var *target;
TargetEntry *tle;
Resdom *resdom;
Expr *exp = copyObject(origexp);
modifyAggrefUplevel( if (IsA(nth(0, exp->args), Aggref))
(Node *) (aggref->target)); {
} if (IsA(nth(1, exp->args), Aggref))
break; elog(ERROR, "rewrite: comparison of 2 aggregate columns not supported");
else
case T_Expr: elog(ERROR, "rewrite: aggregate column of view must be at right side in qual");
{ /* XXX could try to commute operator, instead of failing */
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->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->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;
/* what about the lefthand? */
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 right side in qual");
} }
aggref = (Aggref *) nth(1, exp->args); aggref = (Aggref *) nth(1, exp->args);
target = (Var *) (aggref->target); target = (Var *) (aggref->target);
/* XXX bogus --- agg's target might not be a Var! */ if (! IsA(target, Var))
elog(ERROR, "rewrite: aggregates of views only allowed on simple variables for now");
rte = (RangeTblEntry *) nth(target->varno - 1, parsetree->rtable); rte = (RangeTblEntry *) nth(target->varno - 1, parsetree->rtable);
tle = makeNode(TargetEntry);
resdom = makeNode(Resdom); resdom = makeNode(Resdom);
aggref->usenulls = TRUE; /* XXX safe for all aggs?? */
resdom->resno = 1; resdom->resno = 1;
resdom->restype = aggref->aggtype; resdom->restype = aggref->aggtype;
resdom->restypmod = -1; resdom->restypmod = -1;
...@@ -1063,15 +502,14 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree) ...@@ -1063,15 +502,14 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree)
resdom->reskeyop = 0; resdom->reskeyop = 0;
resdom->resjunk = false; resdom->resjunk = false;
tle = makeNode(TargetEntry);
tle->resdom = resdom; tle->resdom = resdom;
tle->expr = (Node *) aggref; tle->expr = (Node *) aggref; /* note this is from the copied expr */
subqual = copyObject(parsetree->qual);
modifyAggrefDropQual((Node **) &subqual, (Node *) parsetree->qual, origexp);
sublink = makeNode(SubLink); sublink = makeNode(SubLink);
sublink->subLinkType = EXPR_SUBLINK; sublink->subLinkType = EXPR_SUBLINK;
sublink->useor = FALSE; sublink->useor = false;
/* note lefthand and oper are made from the copied expr */
sublink->lefthand = lcons(lfirst(exp->args), NIL); sublink->lefthand = lcons(lfirst(exp->args), NIL);
sublink->oper = lcons(exp->oper, NIL); sublink->oper = lcons(exp->oper, NIL);
...@@ -1088,21 +526,25 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree) ...@@ -1088,21 +526,25 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree)
subquery->unionall = FALSE; subquery->unionall = FALSE;
subquery->uniqueFlag = NULL; subquery->uniqueFlag = NULL;
subquery->sortClause = NULL; subquery->sortClause = NULL;
subquery->rtable = lappend(NIL, rte); subquery->rtable = lcons(rte, NIL);
subquery->targetList = lappend(NIL, tle); subquery->targetList = lcons(tle, NIL);
subquery->qual = subqual; subquery->qual = modifyAggrefDropQual((Node *) parsetree->qual,
(Node *) origexp);
subquery->groupClause = NIL; subquery->groupClause = NIL;
subquery->havingQual = NULL; subquery->havingQual = NULL;
subquery->hasAggs = TRUE; subquery->hasAggs = TRUE;
subquery->hasSubLinks = FALSE; subquery->hasSubLinks = FALSE;
subquery->unionClause = NULL; subquery->unionClause = NULL;
modifyAggrefUplevel((Node *) subquery, NULL);
modifyAggrefUplevel((Node *) sublink); /*
* Note: it might appear that we should be passing target->varlevelsup+1
modifyAggrefChangeVarnodes((Node **) &(sublink->lefthand), target->varno, * here, since modifyAggrefUplevel has increased all the varlevelsup
1, target->varlevelsup); * values in the subquery. However, target itself is a pointer to a
modifyAggrefChangeVarnodes((Node **) &(sublink->subselect), target->varno, * Var node in the subquery, so it's been incremented too! What a kluge
* this all is ... we need to make subquery RTEs so it can go away...
*/
modifyAggrefChangeVarnodes((Node *) subquery, target->varno,
1, target->varlevelsup); 1, target->varlevelsup);
return sublink; return sublink;
...@@ -1112,644 +554,250 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree) ...@@ -1112,644 +554,250 @@ modifyAggrefMakeSublink(Expr *origexp, Query *parsetree)
/* /*
* modifyAggrefQual - * modifyAggrefQual -
* Search for qualification expressions that contain aggregate * Search for qualification expressions that contain aggregate
* functions and substiture them by sublinks. These expressions * functions and substitute them by sublinks. These expressions
* originally come from qualifications that use aggregate columns * originally come from qualifications that use aggregate columns
* of a view. * of a view.
*
* The return value is a modified copy of the given expression tree.
*/ */
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;
/* lefthand ??? */
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 */
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 * static Node *
make_null(Oid type) modifyAggrefQual(Node *node, Query *parsetree)
{
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) if (node == NULL)
return; return NULL;
if (IsA(node, Expr))
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; Expr *expr = (Expr *) node;
apply_RIR_adjust_sublevel(
(Node *) (exp->args),
sublevels_up);
apply_RIR_adjust_sublevel(
(Node *) (exp->defresult),
sublevels_up);
}
break;
case T_CaseWhen: if (length(expr->args) == 2 &&
(IsA(lfirst(expr->args), Aggref) ||
IsA(lsecond(expr->args), Aggref)))
{ {
CaseWhen *exp = (CaseWhen *) node; SubLink *sub = modifyAggrefMakeSublink(expr,
parsetree);
apply_RIR_adjust_sublevel( parsetree->hasSubLinks = true;
(Node *) (exp->expr), return (Node *) sub;
sublevels_up);
apply_RIR_adjust_sublevel(
(Node *) (exp->result),
sublevels_up);
} }
break; /* otherwise, fall through and copy the expr normally */
default:
elog(NOTICE, "unknown node tag %d in attribute_used()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
} }
/* We do NOT recurse into subselects in this routine. It's sufficient
* to get rid of aggregates that are in the qual expression proper.
*/
return expression_tree_mutator(node, modifyAggrefQual,
(void *) parsetree);
} }
static void /*
apply_RIR_view(Node **nodePtr, int rt_index, RangeTblEntry *rte, List *tlist, int *modified, int sublevels_up) * checkQueryHasAggs -
* Queries marked hasAggs might not have them any longer after
* rewriting. Check it.
*/
static bool
checkQueryHasAggs(Node *node)
{ {
Node *node = *nodePtr; return checkQueryHasAggs_walker(node, NULL);
}
static bool
checkQueryHasAggs_walker(Node *node, void *context)
{
if (node == NULL) if (node == NULL)
return; return false;
if (IsA(node, Aggref))
switch (nodeTag(node)) return true; /* abort the tree traversal and return true */
{ return expression_tree_walker(node, checkQueryHasAggs_walker, context);
case T_TargetEntry: }
{
TargetEntry *tle = (TargetEntry *) node;
apply_RIR_view( /*
(Node **) (&(tle->expr)), * checkQueryHasSubLink -
rt_index, * Queries marked hasSubLinks might not have them any longer after
rte, * rewriting. Check it.
tlist, */
modified, static bool
sublevels_up); checkQueryHasSubLink(Node *node)
} {
break; return checkQueryHasSubLink_walker(node, NULL);
}
case T_Aggref: static bool
{ checkQueryHasSubLink_walker(Node *node, void *context)
Aggref *aggref = (Aggref *) node; {
if (node == NULL)
return false;
if (IsA(node, SubLink))
return true; /* abort the tree traversal and return true */
return expression_tree_walker(node, checkQueryHasSubLink_walker, context);
}
apply_RIR_view(
(Node **) (&(aggref->target)),
rt_index,
rte,
tlist,
modified,
sublevels_up);
}
break;
case T_GroupClause: static Node *
break; FindMatchingTLEntry(List *tlist, char *e_attname)
{
List *i;
case T_Expr: foreach(i, tlist)
{ {
Expr *exp = (Expr *) node; TargetEntry *tle = lfirst(i);
char *resname;
apply_RIR_view( resname = tle->resdom->resname;
(Node **) (&(exp->args)), if (!strcmp(e_attname, resname))
rt_index, return (tle->expr);
rte,
tlist,
modified,
sublevels_up);
} }
break; return NULL;
}
case T_Iter:
{
Iter *iter = (Iter *) node;
apply_RIR_view( static Node *
(Node **) (&(iter->iterexpr)), make_null(Oid type)
rt_index, {
rte, Const *c = makeNode(Const);
tlist,
modified,
sublevels_up);
}
break;
case T_ArrayRef: c->consttype = type;
{ c->constlen = get_typlen(type);
ArrayRef *ref = (ArrayRef *) node; c->constvalue = PointerGetDatum(NULL);
c->constisnull = true;
c->constbyval = get_typbyval(type);
return (Node *) c;
}
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: /*
* apply_RIR_adjust_sublevel -
* Set the varlevelsup field of all Var nodes in the given expression tree
* to sublevels_up. We do NOT recurse into subselects.
*
* NOTE: although this has the form of a walker, we cheat and modify the
* Var nodes in-place. The given expression tree should have been copied
* earlier to ensure that no unwanted side-effects occur!
*/
static bool
apply_RIR_adjust_sublevel_walker(Node *node, int *sublevels_up)
{
if (node == NULL)
return false;
if (IsA(node, Var))
{ {
Var *var = (Var *) node; Var *var = (Var *) node;
if (var->varlevelsup == sublevels_up && var->varlevelsup = *sublevels_up;
var->varno == rt_index) return false;
{
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; return expression_tree_walker(node, apply_RIR_adjust_sublevel_walker,
(void *) sublevels_up);
}
case T_Param: static void
break; apply_RIR_adjust_sublevel(Node *node, int sublevels_up)
{
apply_RIR_adjust_sublevel_walker(node, &sublevels_up);
}
case T_Const:
break;
case T_List: /*
{ * apply_RIR_view
List *l; * Replace Vars matching a given RT index with copies of TL expressions.
*/
foreach(l, (List *) node) typedef struct {
apply_RIR_view( int rt_index;
(Node **) (&(lfirst(l))), int sublevels_up;
rt_index, RangeTblEntry *rte;
rte, List *tlist;
tlist, int *modified;
modified, } apply_RIR_view_context;
sublevels_up);
}
break;
case T_SubLink: static Node *
apply_RIR_view_mutator(Node *node,
apply_RIR_view_context *context)
{
if (node == NULL)
return NULL;
if (IsA(node, Var))
{ {
SubLink *sub = (SubLink *) node; Var *var = (Var *) node;
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);
}
break;
case T_Query: if (var->varlevelsup == context->sublevels_up &&
var->varno == context->rt_index)
{ {
Query *qry = (Query *) node; Node *expr;
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( if (var->varattno < 0)
(Node **) (&(qry->havingQual)), elog(ERROR, "system column %s not available - %s is a view",
rt_index, get_attname(context->rte->relid, var->varattno),
rte, context->rte->relname);
tlist,
modified,
sublevels_up);
}
break;
case T_CaseExpr: expr = FindMatchingTLEntry(context->tlist,
get_attname(context->rte->relid,
var->varattno));
if (expr == NULL)
{ {
CaseExpr *exp = (CaseExpr *) node; /* XXX shouldn't this be an error condition? */
return make_null(var->vartype);
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: expr = copyObject(expr);
if (var->varlevelsup > 0)
apply_RIR_adjust_sublevel(expr, var->varlevelsup);
*(context->modified) = true;
return (Node *) expr;
}
/* otherwise fall through to copy the var normally */
}
/*
* Since expression_tree_mutator won't touch subselects, we have to
* handle them specially.
*/
if (IsA(node, SubLink))
{ {
CaseWhen *exp = (CaseWhen *) node; SubLink *sublink = (SubLink *) node;
SubLink *newnode;
FLATCOPY(newnode, sublink, SubLink);
MUTATE(newnode->lefthand, sublink->lefthand, List *,
apply_RIR_view_mutator, context);
context->sublevels_up++;
MUTATE(newnode->subselect, sublink->subselect, Node *,
apply_RIR_view_mutator, context);
context->sublevels_up--;
return (Node *) newnode;
}
if (IsA(node, Query))
{
Query *query = (Query *) node;
Query *newnode;
FLATCOPY(newnode, query, Query);
MUTATE(newnode->targetList, query->targetList, List *,
apply_RIR_view_mutator, context);
MUTATE(newnode->qual, query->qual, Node *,
apply_RIR_view_mutator, context);
MUTATE(newnode->havingQual, query->havingQual, Node *,
apply_RIR_view_mutator, context);
return (Node *) newnode;
}
return expression_tree_mutator(node, apply_RIR_view_mutator,
(void *) context);
}
apply_RIR_view( static Node *
(Node **) (&(exp->expr)), apply_RIR_view(Node *node, int rt_index, RangeTblEntry *rte, List *tlist,
rt_index, int *modified, int sublevels_up)
rte, {
tlist, apply_RIR_view_context context;
modified,
sublevels_up);
apply_RIR_view( context.rt_index = rt_index;
(Node **) (&(exp->result)), context.sublevels_up = sublevels_up;
rt_index, context.rte = rte;
rte, context.tlist = tlist;
tlist, context.modified = modified;
modified,
sublevels_up);
}
break;
default: return apply_RIR_view_mutator(node, &context);
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 static Query *
ApplyRetrieveRule(Query *parsetree, ApplyRetrieveRule(Query *parsetree,
RewriteRule *rule, RewriteRule *rule,
int rt_index, int rt_index,
...@@ -1764,7 +812,7 @@ ApplyRetrieveRule(Query *parsetree, ...@@ -1764,7 +812,7 @@ ApplyRetrieveRule(Query *parsetree,
*l; *l;
int nothing, int nothing,
rt_length; rt_length;
int badsql = FALSE; int badsql = false;
rule_qual = rule->qual; rule_qual = rule->qual;
if (rule->actions) if (rule->actions)
...@@ -1773,7 +821,7 @@ ApplyRetrieveRule(Query *parsetree, ...@@ -1773,7 +821,7 @@ ApplyRetrieveRule(Query *parsetree,
* rules with more than one * rules with more than one
* action? -ay */ * action? -ay */
return; return parsetree;
rule_action = copyObject(lfirst(rule->actions)); rule_action = copyObject(lfirst(rule->actions));
nothing = FALSE; nothing = FALSE;
} }
...@@ -1817,6 +865,7 @@ ApplyRetrieveRule(Query *parsetree, ...@@ -1817,6 +865,7 @@ ApplyRetrieveRule(Query *parsetree,
* them. * them.
*/ */
((RowMark *) lfirst(l))->info &= ~ROW_MARK_FOR_UPDATE; ((RowMark *) lfirst(l))->info &= ~ROW_MARK_FOR_UPDATE;
foreach(l2, rule_action->rtable) foreach(l2, rule_action->rtable)
{ {
...@@ -1847,12 +896,16 @@ ApplyRetrieveRule(Query *parsetree, ...@@ -1847,12 +896,16 @@ ApplyRetrieveRule(Query *parsetree,
if (relation_level) if (relation_level)
{ {
apply_RIR_view((Node **) &parsetree, rt_index, RangeTblEntry *rte = (RangeTblEntry *) nth(rt_index - 1, rtable);
(RangeTblEntry *) nth(rt_index - 1, rtable),
rule_action->targetList, modified, 0); parsetree = (Query *) apply_RIR_view((Node *) parsetree,
apply_RIR_view((Node **) &rule_action, rt_index, rt_index, rte,
(RangeTblEntry *) nth(rt_index - 1, rtable), rule_action->targetList,
rule_action->targetList, modified, 0); modified, 0);
rule_action = (Query *) apply_RIR_view((Node *) rule_action,
rt_index, rte,
rule_action->targetList,
modified, 0);
} }
else else
{ {
...@@ -1868,153 +921,59 @@ ApplyRetrieveRule(Query *parsetree, ...@@ -1868,153 +921,59 @@ ApplyRetrieveRule(Query *parsetree,
parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs); parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks); parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks);
} }
return parsetree;
} }
static void /*
fireRIRonSubselect(Node *node) * fireRIRonSubselect -
* Apply fireRIRrules() to each subselect found in the given tree.
*
* NOTE: although this has the form of a walker, we cheat and modify the
* SubLink nodes in-place. It is caller's responsibility to ensure that
* no unwanted side-effects occur!
*/
static bool
fireRIRonSubselect(Node *node, void *context)
{ {
if (node == NULL) if (node == NULL)
return; return false;
if (IsA(node, SubLink))
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:
{ {
/*
* Standard expression_tree_walker will not recurse into subselect,
* but here we must do so.
*/
SubLink *sub = (SubLink *) node; SubLink *sub = (SubLink *) node;
Query *qry; Query *qry;
fireRIRonSubselect( /* Process lefthand args */
(Node *) (sub->lefthand)); if (fireRIRonSubselect((Node *) (sub->lefthand), context))
return true;
/* Do what we came for */
qry = fireRIRrules((Query *) (sub->subselect)); qry = fireRIRrules((Query *) (sub->subselect));
fireRIRonSubselect(
(Node *) qry);
sub->subselect = (Node *) qry; sub->subselect = (Node *) qry;
/* Must recurse to handle any sub-subselects! */
if (fireRIRonSubselect((Node *) qry, context))
return true;
return false;
} }
break; if (IsA(node, Query))
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:
{ {
/* Reach here after recursing down into subselect above... */
Query *qry = (Query *) node; Query *qry = (Query *) node;
fireRIRonSubselect( if (fireRIRonSubselect((Node *) (qry->targetList), context))
(Node *) (qry->targetList)); return true;
if (fireRIRonSubselect((Node *) (qry->qual), context))
fireRIRonSubselect( return true;
(Node *) (qry->qual)); if (fireRIRonSubselect((Node *) (qry->havingQual), context))
return true;
fireRIRonSubselect( return false;
(Node *) (qry->havingQual));
}
break;
default:
elog(NOTICE, "unknown node tag %d in fireRIRonSubselect()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
} }
return expression_tree_walker(node, fireRIRonSubselect,
(void *) context);
} }
...@@ -2032,7 +991,7 @@ fireRIRrules(Query *parsetree) ...@@ -2032,7 +991,7 @@ fireRIRrules(Query *parsetree)
RuleLock *rules; RuleLock *rules;
RewriteRule *rule; RewriteRule *rule;
RewriteRule RIRonly; RewriteRule RIRonly;
int modified; int modified = false;
int i; int i;
List *l; List *l;
...@@ -2104,7 +1063,7 @@ fireRIRrules(Query *parsetree) ...@@ -2104,7 +1063,7 @@ fireRIRrules(Query *parsetree)
RIRonly.qual = rule->qual; RIRonly.qual = rule->qual;
RIRonly.actions = rule->actions; RIRonly.actions = rule->actions;
ApplyRetrieveRule(parsetree, parsetree = ApplyRetrieveRule(parsetree,
&RIRonly, &RIRonly,
rt_index, rt_index,
RIRonly.attrno == -1, RIRonly.attrno == -1,
...@@ -2115,8 +1074,9 @@ fireRIRrules(Query *parsetree) ...@@ -2115,8 +1074,9 @@ fireRIRrules(Query *parsetree)
heap_close(rel, AccessShareLock); heap_close(rel, AccessShareLock);
} }
fireRIRonSubselect((Node *) parsetree); fireRIRonSubselect((Node *) parsetree, NULL);
modifyAggrefQual((Node **) &(parsetree->qual), parsetree);
parsetree->qual = modifyAggrefQual(parsetree->qual, parsetree);
return parsetree; return parsetree;
} }
...@@ -2639,9 +1599,9 @@ BasicQueryRewrite(Query *parsetree) ...@@ -2639,9 +1599,9 @@ BasicQueryRewrite(Query *parsetree)
*/ */
if (query->hasAggs) if (query->hasAggs)
query->hasAggs = checkQueryHasAggs((Node *) (query->targetList)) query->hasAggs = checkQueryHasAggs((Node *) (query->targetList))
| checkQueryHasAggs((Node *) (query->havingQual)); || checkQueryHasAggs((Node *) (query->havingQual));
query->hasSubLinks = checkQueryHasSubLink((Node *) (query->qual)) query->hasSubLinks = checkQueryHasSubLink((Node *) (query->qual))
| checkQueryHasSubLink((Node *) (query->havingQual)); || checkQueryHasSubLink((Node *) (query->havingQual));
results = lappend(results, query); results = lappend(results, query);
} }
return results; return results;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.40 1999/08/25 23:21:43 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.41 1999/10/01 04:08:24 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,410 +19,182 @@ ...@@ -19,410 +19,182 @@
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
static void ResolveNew(RewriteInfo *info, List *targetlist,
Node **node, int sublevels_up);
/* macros borrowed from expression_tree_mutator */
/* #define FLATCOPY(newnode, node, nodetype) \
* OffsetVarnodes - ( (newnode) = makeNode(nodetype), \
*/ memcpy((newnode), (node), sizeof(nodetype)) )
void
OffsetVarNodes(Node *node, int offset, int sublevels_up)
{
if (node == NULL)
return;
switch (nodeTag(node))
{
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *) node;
OffsetVarNodes(
(Node *) (tle->expr),
offset,
sublevels_up);
}
break;
case T_Aggref:
{
Aggref *aggref = (Aggref *) node;
OffsetVarNodes(
(Node *) (aggref->target),
offset,
sublevels_up);
}
break;
case T_GroupClause:
break;
case T_Expr:
{
Expr *exp = (Expr *) node;
OffsetVarNodes( #define MUTATE(newfield, oldfield, fieldtype, mutator, context) \
(Node *) (exp->args), ( (newfield) = (fieldtype) mutator((Node *) (oldfield), (context)) )
offset,
sublevels_up);
}
break;
case T_Iter:
{
Iter *iter = (Iter *) node;
OffsetVarNodes( /*
(Node *) (iter->iterexpr), * OffsetVarNodes - adjust Vars when appending one query's RT to another
offset, *
sublevels_up); * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
} * and increment their varno fields (rangetable indexes) by 'offset'.
break; * The varnoold fields are adjusted similarly.
*
* NOTE: although this has the form of a walker, we cheat and modify the
* Var nodes in-place. The given expression tree should have been copied
* earlier to ensure that no unwanted side-effects occur!
*/
case T_ArrayRef: typedef struct {
{ int offset;
ArrayRef *ref = (ArrayRef *) node; int sublevels_up;
} OffsetVarNodes_context;
OffsetVarNodes(
(Node *) (ref->refupperindexpr),
offset,
sublevels_up);
OffsetVarNodes(
(Node *) (ref->reflowerindexpr),
offset,
sublevels_up);
OffsetVarNodes(
(Node *) (ref->refexpr),
offset,
sublevels_up);
OffsetVarNodes(
(Node *) (ref->refassgnexpr),
offset,
sublevels_up);
}
break;
case T_Var: static bool
OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
{
if (node == NULL)
return false;
if (IsA(node, Var))
{ {
Var *var = (Var *) node; Var *var = (Var *) node;
if (var->varlevelsup == sublevels_up) if (var->varlevelsup == context->sublevels_up)
{ {
var->varno += offset; var->varno += context->offset;
var->varnoold += offset; var->varnoold += context->offset;
} }
return false;
} }
break; if (IsA(node, SubLink))
case T_Param:
break;
case T_Const:
break;
case T_List:
{
List *l;
foreach(l, (List *) node)
OffsetVarNodes(
(Node *) lfirst(l),
offset,
sublevels_up);
}
break;
case T_SubLink:
{ {
/*
* Standard expression_tree_walker will not recurse into subselect,
* but here we must do so.
*/
SubLink *sub = (SubLink *) node; SubLink *sub = (SubLink *) node;
OffsetVarNodes( if (OffsetVarNodes_walker((Node *) (sub->lefthand),
(Node *) (sub->lefthand), context))
offset, return true;
sublevels_up); OffsetVarNodes((Node *) (sub->subselect),
context->offset,
OffsetVarNodes( context->sublevels_up + 1);
(Node *) (sub->subselect), return false;
offset,
sublevels_up + 1);
} }
break; if (IsA(node, Query))
case T_Query:
{ {
/* Reach here after recursing down into subselect above... */
Query *qry = (Query *) node; Query *qry = (Query *) node;
OffsetVarNodes( if (OffsetVarNodes_walker((Node *) (qry->targetList),
(Node *) (qry->targetList), context))
offset, return true;
sublevels_up); if (OffsetVarNodes_walker((Node *) (qry->qual),
context))
OffsetVarNodes( return true;
(Node *) (qry->qual), if (OffsetVarNodes_walker((Node *) (qry->havingQual),
offset, context))
sublevels_up); return true;
return false;
OffsetVarNodes( }
(Node *) (qry->havingQual), return expression_tree_walker(node, OffsetVarNodes_walker,
offset, (void *) context);
sublevels_up);
}
break;
case T_CaseExpr:
{
CaseExpr *exp = (CaseExpr *) node;
OffsetVarNodes(
(Node *) (exp->args),
offset,
sublevels_up);
OffsetVarNodes(
(Node *) (exp->defresult),
offset,
sublevels_up);
}
break;
case T_CaseWhen:
{
CaseWhen *exp = (CaseWhen *) node;
OffsetVarNodes(
(Node *) (exp->expr),
offset,
sublevels_up);
OffsetVarNodes(
(Node *) (exp->result),
offset,
sublevels_up);
}
break;
default:
elog(NOTICE, "unknown node tag %d in OffsetVarNodes()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
}
} }
/*
* ChangeVarNodes -
*/
void void
ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up) OffsetVarNodes(Node *node, int offset, int sublevels_up)
{ {
if (node == NULL) OffsetVarNodes_context context;
return;
switch (nodeTag(node))
{
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *) node;
ChangeVarNodes(
(Node *) (tle->expr),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Aggref:
{
Aggref *aggref = (Aggref *) node;
ChangeVarNodes(
(Node *) (aggref->target),
rt_index,
new_index,
sublevels_up);
}
break;
case T_GroupClause:
break;
case T_Expr:
{
Expr *exp = (Expr *) node;
ChangeVarNodes(
(Node *) (exp->args),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Iter: context.offset = offset;
{ context.sublevels_up = sublevels_up;
Iter *iter = (Iter *) node; OffsetVarNodes_walker(node, &context);
}
ChangeVarNodes( /*
(Node *) (iter->iterexpr), * ChangeVarNodes - adjust Var nodes for a specific change of RT index
rt_index, *
new_index, * Find all Var nodes in the given tree belonging to a specific relation
sublevels_up); * (identified by sublevels_up and rt_index), and change their varno fields
} * to 'new_index'. The varnoold fields are changed too.
break; *
* NOTE: although this has the form of a walker, we cheat and modify the
* Var nodes in-place. The given expression tree should have been copied
* earlier to ensure that no unwanted side-effects occur!
*/
case T_ArrayRef: typedef struct {
{ int rt_index;
ArrayRef *ref = (ArrayRef *) node; int new_index;
int sublevels_up;
ChangeVarNodes( } ChangeVarNodes_context;
(Node *) (ref->refupperindexpr),
rt_index,
new_index,
sublevels_up);
ChangeVarNodes(
(Node *) (ref->reflowerindexpr),
rt_index,
new_index,
sublevels_up);
ChangeVarNodes(
(Node *) (ref->refexpr),
rt_index,
new_index,
sublevels_up);
ChangeVarNodes(
(Node *) (ref->refassgnexpr),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Var: static bool
ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
{
if (node == NULL)
return false;
if (IsA(node, Var))
{ {
Var *var = (Var *) node; Var *var = (Var *) node;
if (var->varlevelsup == sublevels_up && if (var->varlevelsup == context->sublevels_up &&
var->varno == rt_index) var->varno == context->rt_index)
{ {
var->varno = new_index; var->varno = context->new_index;
var->varnoold = new_index; var->varnoold = context->new_index;
}
} }
break; return false;
case T_Param:
break;
case T_Const:
break;
case T_List:
{
List *l;
foreach(l, (List *) node)
ChangeVarNodes(
(Node *) lfirst(l),
rt_index,
new_index,
sublevels_up);
} }
break; if (IsA(node, SubLink))
case T_SubLink:
{ {
/*
* Standard expression_tree_walker will not recurse into subselect,
* but here we must do so.
*/
SubLink *sub = (SubLink *) node; SubLink *sub = (SubLink *) node;
ChangeVarNodes( if (ChangeVarNodes_walker((Node *) (sub->lefthand),
(Node *) (sub->lefthand), context))
rt_index, return true;
new_index, ChangeVarNodes((Node *) (sub->subselect),
sublevels_up); context->rt_index,
context->new_index,
ChangeVarNodes( context->sublevels_up + 1);
(Node *) (sub->subselect), return false;
rt_index,
new_index,
sublevels_up + 1);
} }
break; if (IsA(node, Query))
case T_Query:
{ {
/* Reach here after recursing down into subselect above... */
Query *qry = (Query *) node; Query *qry = (Query *) node;
ChangeVarNodes( if (ChangeVarNodes_walker((Node *) (qry->targetList),
(Node *) (qry->targetList), context))
rt_index, return true;
new_index, if (ChangeVarNodes_walker((Node *) (qry->qual),
sublevels_up); context))
return true;
ChangeVarNodes( if (ChangeVarNodes_walker((Node *) (qry->havingQual),
(Node *) (qry->qual), context))
rt_index, return true;
new_index, return false;
sublevels_up); }
return expression_tree_walker(node, ChangeVarNodes_walker,
ChangeVarNodes( (void *) context);
(Node *) (qry->havingQual),
rt_index,
new_index,
sublevels_up);
}
break;
case T_CaseExpr:
{
CaseExpr *exp = (CaseExpr *) node;
ChangeVarNodes(
(Node *) (exp->args),
rt_index,
new_index,
sublevels_up);
ChangeVarNodes(
(Node *) (exp->defresult),
rt_index,
new_index,
sublevels_up);
}
break;
case T_CaseWhen:
{
CaseWhen *exp = (CaseWhen *) node;
ChangeVarNodes(
(Node *) (exp->expr),
rt_index,
new_index,
sublevels_up);
ChangeVarNodes(
(Node *) (exp->result),
rt_index,
new_index,
sublevels_up);
}
break;
default:
elog(NOTICE, "unknown node tag %d in ChangeVarNodes()", nodeTag(node));
elog(NOTICE, "Node is: %s", nodeToString(node));
break;
}
} }
void
ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
{
ChangeVarNodes_context context;
context.rt_index = rt_index;
context.new_index = new_index;
context.sublevels_up = sublevels_up;
ChangeVarNodes_walker(node, &context);
}
/*
* Add the given qualifier condition to the query's WHERE clause
*/
void void
AddQual(Query *parsetree, Node *qual) AddQual(Query *parsetree, Node *qual)
{ {
...@@ -433,18 +205,19 @@ AddQual(Query *parsetree, Node *qual) ...@@ -433,18 +205,19 @@ AddQual(Query *parsetree, Node *qual)
return; return;
/* INTERSECT want's the original, but we need to copy - Jan */ /* INTERSECT want's the original, but we need to copy - Jan */
/* copy = qual; */
copy = copyObject(qual); copy = copyObject(qual);
old = parsetree->qual; old = parsetree->qual;
if (old == NULL) if (old == NULL)
parsetree->qual = copy; parsetree->qual = copy;
else else
parsetree->qual = (Node *) make_andclause(makeList(parsetree->qual, copy, -1)); parsetree->qual = (Node *) make_andclause(makeList(old, copy, -1));
} }
/* Adds the given havingQual to the one already contained in the parsetree just as /*
* AddQual does for the normal 'where' qual */ * Add the given havingQual to the one already contained in the parsetree
* just as AddQual does for the normal 'where' qual
*/
void void
AddHavingQual(Query *parsetree, Node *havingQual) AddHavingQual(Query *parsetree, Node *havingQual)
{ {
...@@ -455,46 +228,43 @@ AddHavingQual(Query *parsetree, Node *havingQual) ...@@ -455,46 +228,43 @@ AddHavingQual(Query *parsetree, Node *havingQual)
return; return;
/* INTERSECT want's the original, but we need to copy - Jan */ /* INTERSECT want's the original, but we need to copy - Jan */
/* copy = havingQual; */
copy = copyObject(havingQual); copy = copyObject(havingQual);
old = parsetree->havingQual; old = parsetree->havingQual;
if (old == NULL) if (old == NULL)
parsetree->havingQual = copy; parsetree->havingQual = copy;
else else
parsetree->havingQual = (Node *) make_andclause(makeList(parsetree->havingQual, copy, -1)); parsetree->havingQual = (Node *) make_andclause(makeList(old, copy, -1));
} }
#ifdef NOT_USED #ifdef NOT_USED
void void
AddNotHavingQual(Query *parsetree, Node *havingQual) AddNotHavingQual(Query *parsetree, Node *havingQual)
{ {
Node *copy; Node *notqual;
if (havingQual == NULL) if (havingQual == NULL)
return; return;
/* INTERSECT want's the original, but we need to copy - Jan */ /* Need not copy input qual, because AddHavingQual will... */
/* copy = (Node *) make_notclause((Expr *)havingQual); */ notqual = (Node *) make_notclause((Expr *) havingQual);
copy = (Node *) make_notclause((Expr *) copyObject(havingQual));
AddHavingQual(parsetree, copy); AddHavingQual(parsetree, notqual);
} }
#endif #endif
void void
AddNotQual(Query *parsetree, Node *qual) AddNotQual(Query *parsetree, Node *qual)
{ {
Node *copy; Node *notqual;
if (qual == NULL) if (qual == NULL)
return; return;
/* INTERSECT want's the original, but we need to copy - Jan */ /* Need not copy input qual, because AddQual will... */
/* copy = (Node *) make_notclause((Expr *)qual); */ notqual = (Node *) make_notclause((Expr *) qual);
copy = (Node *) make_notclause((Expr *) copyObject(qual));
AddQual(parsetree, copy); AddQual(parsetree, notqual);
} }
...@@ -583,6 +353,7 @@ FixResdomTypes(List *tlist) ...@@ -583,6 +353,7 @@ FixResdomTypes(List *tlist)
#endif #endif
/* Find a targetlist entry by resno */
static Node * static Node *
FindMatchingNew(List *tlist, int attno) FindMatchingNew(List *tlist, int attno)
{ {
...@@ -598,6 +369,7 @@ FindMatchingNew(List *tlist, int attno) ...@@ -598,6 +369,7 @@ FindMatchingNew(List *tlist, int attno)
return NULL; return NULL;
} }
/* Find a targetlist entry by resname */
static Node * static Node *
FindMatchingTLEntry(List *tlist, char *e_attname) FindMatchingTLEntry(List *tlist, char *e_attname)
{ {
...@@ -615,202 +387,178 @@ FindMatchingTLEntry(List *tlist, char *e_attname) ...@@ -615,202 +387,178 @@ FindMatchingTLEntry(List *tlist, char *e_attname)
return NULL; return NULL;
} }
static void
ResolveNew(RewriteInfo *info, List *targetlist, Node **nodePtr,
int sublevels_up)
{
Node *node = *nodePtr;
if (node == NULL) /*
return; * ResolveNew - replace Vars with corresponding items from a targetlist
*
* Vars matching info->new_varno and sublevels_up are replaced by the
* entry with matching resno from targetlist, if there is one.
*/
switch (nodeTag(node)) typedef struct {
{ RewriteInfo *info;
case T_TargetEntry: List *targetlist;
ResolveNew(info, targetlist, &((TargetEntry *) node)->expr, int sublevels_up;
sublevels_up); } ResolveNew_context;
break;
case T_Aggref: static Node *
ResolveNew(info, targetlist, &((Aggref *) node)->target, ResolveNew_mutator(Node *node, ResolveNew_context *context)
sublevels_up); {
break; if (node == NULL)
case T_Expr: return NULL;
ResolveNew(info, targetlist, (Node **) (&(((Expr *) node)->args)), if (IsA(node, Var))
sublevels_up);
break;
case T_Iter:
ResolveNew(info, targetlist, (Node **) (&(((Iter *) node)->iterexpr)),
sublevels_up);
break;
case T_ArrayRef:
ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refupperindexpr)),
sublevels_up);
ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->reflowerindexpr)),
sublevels_up);
ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refexpr)),
sublevels_up);
ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refassgnexpr)),
sublevels_up);
break;
case T_Var:
{ {
int this_varno = (int) ((Var *) node)->varno; Var *var = (Var *) node;
int this_varlevelsup = (int) ((Var *) node)->varlevelsup; int this_varno = (int) var->varno;
Node *n; int this_varlevelsup = (int) var->varlevelsup;
if (this_varno == info->new_varno && if (this_varno == context->info->new_varno &&
this_varlevelsup == sublevels_up) this_varlevelsup == context->sublevels_up)
{ {
n = FindMatchingNew(targetlist, Node *n = FindMatchingNew(context->targetlist,
((Var *) node)->varattno); var->varattno);
if (n == NULL) if (n == NULL)
{ {
if (info->event == CMD_UPDATE) if (context->info->event == CMD_UPDATE)
{ {
*nodePtr = n = copyObject(node); /* For update, just change unmatched var's varno */
((Var *) n)->varno = info->current_varno; n = copyObject(node);
((Var *) n)->varnoold = info->current_varno; ((Var *) n)->varno = context->info->current_varno;
((Var *) n)->varnoold = context->info->current_varno;
return n;
} }
else else
*nodePtr = make_null(((Var *) node)->vartype); {
/* Otherwise replace unmatched var with a null */
return make_null(var->vartype);
}
} }
else else
{ {
*nodePtr = copyObject(n); /* Make a copy of the tlist item to return */
((Var *) *nodePtr)->varlevelsup = this_varlevelsup; n = copyObject(n);
if (IsA(n, Var))
{
((Var *) n)->varlevelsup = this_varlevelsup;
} }
/* XXX what to do if tlist item is NOT a var?
* Should we be using something like apply_RIR_adjust_sublevel?
*/
return n;
} }
break;
} }
case T_List: /* otherwise fall through to copy the var normally */
{
List *l;
foreach(l, (List *) node)
ResolveNew(info, targetlist, (Node **) &(lfirst(l)),
sublevels_up);
break;
} }
case T_SubLink: /*
* Since expression_tree_mutator won't touch subselects, we have to
* handle them specially.
*/
if (IsA(node, SubLink))
{ {
SubLink *sublink = (SubLink *) node; SubLink *sublink = (SubLink *) node;
Query *query = (Query *) sublink->subselect; SubLink *newnode;
/* XXX what about lefthand? What about rest of subquery? */ FLATCOPY(newnode, sublink, SubLink);
ResolveNew(info, targetlist, (Node **) &(query->qual), sublevels_up + 1); MUTATE(newnode->lefthand, sublink->lefthand, List *,
} ResolveNew_mutator, context);
break; context->sublevels_up++;
case T_GroupClause: MUTATE(newnode->subselect, sublink->subselect, Node *,
break; ResolveNew_mutator, context);
default: context->sublevels_up--;
/* ignore the others */ return (Node *) newnode;
break;
} }
} if (IsA(node, Query))
{
Query *query = (Query *) node;
Query *newnode;
void /*
FixNew(RewriteInfo *info, Query *parsetree) * XXX original code for ResolveNew only recursed into qual field
{ * of subquery. I'm assuming that was an oversight ... tgl 9/99
ResolveNew(info, parsetree->targetList, */
(Node **) &(info->rule_action->targetList), 0);
ResolveNew(info, parsetree->targetList, FLATCOPY(newnode, query, Query);
(Node **) &info->rule_action->qual, 0); MUTATE(newnode->targetList, query->targetList, List *,
ResolveNew(info, parsetree->targetList, ResolveNew_mutator, context);
(Node **) &(info->rule_action->groupClause), 0); MUTATE(newnode->qual, query->qual, Node *,
ResolveNew_mutator, context);
MUTATE(newnode->havingQual, query->havingQual, Node *,
ResolveNew_mutator, context);
return (Node *) newnode;
}
return expression_tree_mutator(node, ResolveNew_mutator,
(void *) context);
} }
static void static Node *
nodeHandleRIRAttributeRule(Node **nodePtr, ResolveNew(Node *node, RewriteInfo *info, List *targetlist,
List *rtable,
List *targetlist,
int rt_index,
int attr_num,
int *modified,
int *badsql,
int sublevels_up) int sublevels_up)
{ {
Node *node = *nodePtr; ResolveNew_context context;
if (node == NULL) context.info = info;
return; context.targetlist = targetlist;
switch (nodeTag(node)) context.sublevels_up = sublevels_up;
{
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *) node;
nodeHandleRIRAttributeRule(&tle->expr, rtable, targetlist, return ResolveNew_mutator(node, &context);
rt_index, attr_num, modified, badsql, }
sublevels_up);
}
break;
case T_Aggref:
{
Aggref *aggref = (Aggref *) node;
nodeHandleRIRAttributeRule(&aggref->target, rtable, targetlist, void
rt_index, attr_num, modified, badsql, FixNew(RewriteInfo *info, Query *parsetree)
sublevels_up); {
} info->rule_action->targetList = (List *)
break; ResolveNew((Node *) info->rule_action->targetList,
case T_Expr: info, parsetree->targetList, 0);
{ info->rule_action->qual = ResolveNew(info->rule_action->qual,
Expr *expr = (Expr *) node; info, parsetree->targetList, 0);
/* XXX original code didn't fix havingQual; presumably an oversight? */
info->rule_action->havingQual = ResolveNew(info->rule_action->havingQual,
info, parsetree->targetList, 0);
}
nodeHandleRIRAttributeRule((Node **) (&(expr->args)), rtable, /*
targetlist, rt_index, attr_num, * HandleRIRAttributeRule
modified, badsql, * Replace Vars matching a given RT index with copies of TL expressions.
sublevels_up); *
} * Handles 'on retrieve to relation.attribute
break; * do instead retrieve (attribute = expression) w/qual'
case T_Iter: *
{ * XXX Why is this not unified with apply_RIR_view()?
Iter *iter = (Iter *) node; */
nodeHandleRIRAttributeRule((Node **) (&(iter->iterexpr)), rtable, typedef struct {
targetlist, rt_index, attr_num, List *rtable;
modified, badsql, List *targetlist;
sublevels_up); int rt_index;
} int attr_num;
break; int *modified;
case T_ArrayRef: int *badsql;
{ int sublevels_up;
ArrayRef *ref = (ArrayRef *) node; } HandleRIRAttributeRule_context;
nodeHandleRIRAttributeRule((Node **) (&(ref->refupperindexpr)), rtable, static Node *
targetlist, rt_index, attr_num, HandleRIRAttributeRule_mutator(Node *node,
modified, badsql, HandleRIRAttributeRule_context *context)
sublevels_up); {
nodeHandleRIRAttributeRule((Node **) (&(ref->reflowerindexpr)), rtable, if (node == NULL)
targetlist, rt_index, attr_num, return NULL;
modified, badsql, if (IsA(node, Var))
sublevels_up);
nodeHandleRIRAttributeRule((Node **) (&(ref->refexpr)), rtable,
targetlist, rt_index, attr_num,
modified, badsql,
sublevels_up);
nodeHandleRIRAttributeRule((Node **) (&(ref->refassgnexpr)), rtable,
targetlist, rt_index, attr_num,
modified, badsql,
sublevels_up);
}
break;
case T_Var:
{ {
int this_varno = ((Var *) node)->varno; Var *var = (Var *) node;
int this_varattno = ((Var *) node)->varattno; int this_varno = var->varno;
int this_varlevelsup = ((Var *) node)->varlevelsup; int this_varattno = var->varattno;
int this_varlevelsup = var->varlevelsup;
if (this_varno == rt_index && if (this_varno == context->rt_index &&
this_varattno == attr_num && this_varattno == context->attr_num &&
this_varlevelsup == sublevels_up) this_varlevelsup == context->sublevels_up)
{ {
if (((Var *) node)->vartype == 32) if (var->vartype == 32)
{ /* HACK */ { /* HACK: disallow SET variables */
*nodePtr = make_null(((Var *) node)->vartype); *context->modified = TRUE;
*modified = TRUE; *context->badsql = TRUE;
*badsql = TRUE; return make_null(var->vartype);
break;
} }
else else
{ {
...@@ -819,260 +567,90 @@ nodeHandleRIRAttributeRule(Node **nodePtr, ...@@ -819,260 +567,90 @@ nodeHandleRIRAttributeRule(Node **nodePtr,
name_to_look_for.data[0] = '\0'; name_to_look_for.data[0] = '\0';
namestrcpy(&name_to_look_for, namestrcpy(&name_to_look_for,
(char *) get_attname(getrelid(this_varno, (char *) get_attname(getrelid(this_varno,
rtable), context->rtable),
attr_num)); this_varattno));
if (name_to_look_for.data[0]) if (name_to_look_for.data[0])
{ {
Node *n; Node *n;
n = FindMatchingTLEntry(targetlist, (char *) &name_to_look_for); *context->modified = TRUE;
n = FindMatchingTLEntry(context->targetlist,
(char *) &name_to_look_for);
if (n == NULL) if (n == NULL)
*nodePtr = make_null(((Var *) node)->vartype); return make_null(var->vartype);
else else
*nodePtr = n; return copyObject(n);
*modified = TRUE;
}
} }
} }
} }
break; /* otherwise fall through to copy the var normally */
case T_List:
{
List *i;
foreach(i, (List *) node)
{
nodeHandleRIRAttributeRule((Node **) (&(lfirst(i))), rtable,
targetlist, rt_index, attr_num,
modified, badsql, sublevels_up);
}
}
break;
case T_SubLink:
{
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) sublink->subselect;
/* XXX what about lefthand? What about rest of subquery? */
nodeHandleRIRAttributeRule((Node **) &(query->qual), rtable, targetlist,
rt_index, attr_num, modified, badsql,
sublevels_up + 1);
} }
break;
default:
/* ignore the others */
break;
}
}
/*
* Handles 'on retrieve to relation.attribute
* do instead retrieve (attribute = expression) w/qual'
*/
void
HandleRIRAttributeRule(Query *parsetree,
List *rtable,
List *targetlist,
int rt_index,
int attr_num,
int *modified,
int *badsql)
{
nodeHandleRIRAttributeRule((Node **) (&(parsetree->targetList)), rtable,
targetlist, rt_index, attr_num,
modified, badsql, 0);
nodeHandleRIRAttributeRule(&parsetree->qual, rtable, targetlist,
rt_index, attr_num, modified, badsql, 0);
}
#ifdef NOT_USED
static void
nodeHandleViewRule(Node **nodePtr,
List *rtable,
List *targetlist,
int rt_index,
int *modified,
int sublevels_up)
{
Node *node = *nodePtr;
if (node == NULL)
return;
switch (nodeTag(node))
{
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *) node;
nodeHandleViewRule(&(tle->expr), rtable, targetlist,
rt_index, modified, sublevels_up);
}
break;
case T_Aggref:
{
Aggref *aggref = (Aggref *) node;
nodeHandleViewRule(&(aggref->target), rtable, targetlist,
rt_index, modified, sublevels_up);
}
break;
/* /*
* This has to be done to make queries using groupclauses work * Since expression_tree_mutator won't touch subselects, we have to
* on views * handle them specially.
*/ */
case T_GroupClause: if (IsA(node, SubLink))
{
GroupClause *group = (GroupClause *) node;
nodeHandleViewRule((Node **) (&(group->entry)), rtable, targetlist,
rt_index, modified, sublevels_up);
}
break;
case T_Expr:
{ {
Expr *expr = (Expr *) node; SubLink *sublink = (SubLink *) node;
SubLink *newnode;
nodeHandleViewRule((Node **) (&(expr->args)), FLATCOPY(newnode, sublink, SubLink);
rtable, targetlist, MUTATE(newnode->lefthand, sublink->lefthand, List *,
rt_index, modified, sublevels_up); HandleRIRAttributeRule_mutator, context);
context->sublevels_up++;
MUTATE(newnode->subselect, sublink->subselect, Node *,
HandleRIRAttributeRule_mutator, context);
context->sublevels_up--;
return (Node *) newnode;
} }
break; if (IsA(node, Query))
case T_Iter:
{
Iter *iter = (Iter *) node;
nodeHandleViewRule((Node **) (&(iter->iterexpr)),
rtable, targetlist,
rt_index, modified, sublevels_up);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *) node;
nodeHandleViewRule((Node **) (&(ref->refupperindexpr)),
rtable, targetlist,
rt_index, modified, sublevels_up);
nodeHandleViewRule((Node **) (&(ref->reflowerindexpr)),
rtable, targetlist,
rt_index, modified, sublevels_up);
nodeHandleViewRule((Node **) (&(ref->refexpr)),
rtable, targetlist,
rt_index, modified, sublevels_up);
nodeHandleViewRule((Node **) (&(ref->refassgnexpr)),
rtable, targetlist,
rt_index, modified, sublevels_up);
}
break;
case T_Var:
{
Var *var = (Var *) node;
int this_varno = var->varno;
int this_varlevelsup = var->varlevelsup;
Node *n;
if (this_varno == rt_index &&
this_varlevelsup == sublevels_up)
{
n = FindMatchingTLEntry(targetlist,
get_attname(getrelid(this_varno,
rtable),
var->varattno));
if (n == NULL)
*nodePtr = make_null(((Var *) node)->vartype);
else
{ {
Query *query = (Query *) node;
Query *newnode;
/* /*
* This is a hack: The varlevelsup of the orignal * XXX original code for HandleRIRAttributeRule only recursed into
* variable and the new one should be the same. * qual field of subquery. I'm assuming that was an oversight ...
* Normally we adapt the node by changing a
* pointer to point to a var contained in
* 'targetlist'. In the targetlist all
* varlevelsups are 0 so if we want to change it
* to the original value we have to copy the node
* before! (Maybe this will cause troubles with
* some sophisticated queries on views?)
*/ */
if (this_varlevelsup > 0)
*nodePtr = copyObject(n);
else
*nodePtr = n;
if (nodeTag(nodePtr) == T_Var)
((Var *) *nodePtr)->varlevelsup = this_varlevelsup;
else
nodeHandleViewRule(&n, rtable, targetlist,
rt_index, modified, sublevels_up);
}
*modified = TRUE;
}
break;
}
case T_List:
{
List *l;
foreach(l, (List *) node) FLATCOPY(newnode, query, Query);
{ MUTATE(newnode->targetList, query->targetList, List *,
nodeHandleViewRule((Node **) (&(lfirst(l))), HandleRIRAttributeRule_mutator, context);
rtable, targetlist, MUTATE(newnode->qual, query->qual, Node *,
rt_index, modified, sublevels_up); HandleRIRAttributeRule_mutator, context);
} MUTATE(newnode->havingQual, query->havingQual, Node *,
} HandleRIRAttributeRule_mutator, context);
break; return (Node *) newnode;
case T_SubLink: }
{ return expression_tree_mutator(node, HandleRIRAttributeRule_mutator,
SubLink *sublink = (SubLink *) node; (void *) context);
Query *query = (Query *) sublink->subselect;
nodeHandleViewRule((Node **) &(query->qual), rtable, targetlist,
rt_index, modified, sublevels_up + 1);
/***S*H*D***/
nodeHandleViewRule((Node **) &(query->havingQual), rtable, targetlist,
rt_index, modified, sublevels_up + 1);
nodeHandleViewRule((Node **) &(query->targetList), rtable, targetlist,
rt_index, modified, sublevels_up + 1);
/*
* We also have to adapt the variables used in
* sublink->lefthand
*/
nodeHandleViewRule((Node **) &(sublink->lefthand), rtable,
targetlist, rt_index, modified, sublevels_up);
}
break;
default:
/* ignore the others */
break;
}
} }
void void
HandleViewRule(Query *parsetree, HandleRIRAttributeRule(Query *parsetree,
List *rtable, List *rtable,
List *targetlist, List *targetlist,
int rt_index, int rt_index,
int *modified) int attr_num,
int *modified,
int *badsql)
{ {
nodeHandleViewRule(&parsetree->qual, rtable, targetlist, rt_index, HandleRIRAttributeRule_context context;
modified, 0);
nodeHandleViewRule((Node **) (&(parsetree->targetList)), rtable, targetlist, context.rtable = rtable;
rt_index, modified, 0); context.targetlist = targetlist;
context.rt_index = rt_index;
/* context.attr_num = attr_num;
* The variables in the havingQual and groupClause also have to be context.modified = modified;
* adapted context.badsql = badsql;
*/ context.sublevels_up = 0;
nodeHandleViewRule(&parsetree->havingQual, rtable, targetlist, rt_index,
modified, 0); parsetree->targetList = (List *)
nodeHandleViewRule((Node **) (&(parsetree->groupClause)), rtable, targetlist, rt_index, HandleRIRAttributeRule_mutator((Node *) parsetree->targetList,
modified, 0); &context);
parsetree->qual = HandleRIRAttributeRule_mutator(parsetree->qual,
&context);
/* XXX original code did not fix havingQual ... oversight? */
parsetree->havingQual = HandleRIRAttributeRule_mutator(parsetree->havingQual,
&context);
} }
#endif
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: rewriteManip.h,v 1.17 1999/07/15 15:21:30 momjian Exp $ * $Id: rewriteManip.h,v 1.18 1999/10/01 04:08:15 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,19 +16,18 @@ ...@@ -16,19 +16,18 @@
#include "rewrite/rewriteHandler.h" #include "rewrite/rewriteHandler.h"
/* RewriteManip.c */ /* RewriteManip.c */
void OffsetVarNodes(Node *node, int offset, int sublevels_up); extern void OffsetVarNodes(Node *node, int offset, int sublevels_up);
void ChangeVarNodes(Node *node, int old_varno, int new_varno, extern void ChangeVarNodes(Node *node, int old_varno, int new_varno,
int sublevels_up); int sublevels_up);
void AddQual(Query *parsetree, Node *qual); extern void AddQual(Query *parsetree, Node *qual);
void AddHavingQual(Query *parsetree, Node *havingQual); extern void AddHavingQual(Query *parsetree, Node *havingQual);
extern void AddNotQual(Query *parsetree, Node *qual);
extern void AddGroupClause(Query *parsetree, List *group_by, List *tlist);
void AddNotQual(Query *parsetree, Node *qual); extern void FixNew(RewriteInfo *info, Query *parsetree);
void AddGroupClause(Query *parsetree, List *group_by, List *tlist);
void FixNew(RewriteInfo *info, Query *parsetree); extern void HandleRIRAttributeRule(Query *parsetree, List *rtable,
List *targetlist, int rt_index,
void HandleRIRAttributeRule(Query *parsetree, List *rtable, List *targetlist, int attr_num, int *modified, int *badsql);
int rt_index, int attr_num, int *modified,
int *badpostquel);
#endif /* REWRITEMANIP_H */ #endif /* REWRITEMANIP_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment