postgresql/src/backend/rewrite/rewriteManip.c

1031 lines
21 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* rewriteManip.c--
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.23 1998/12/14 00:02:17 thomas Exp $
*
*-------------------------------------------------------------------------
*/
#include <string.h>
#include "postgres.h"
#include "nodes/pg_list.h"
#include "utils/elog.h"
#include "nodes/nodes.h"
#include "nodes/relation.h"
#include "nodes/primnodes.h"
#include "parser/parsetree.h" /* for getrelid() */
#include "utils/lsyscache.h"
#include "utils/builtins.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSupport.h"
#include "rewrite/locks.h"
#include "nodes/plannodes.h"
#include "optimizer/clauses.h"
static void ResolveNew(RewriteInfo *info, List *targetlist,
Node **node, int sublevels_up);
/*
* OffsetVarnodes -
*/
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_Aggreg:
{
Aggreg *agg = (Aggreg *)node;
OffsetVarNodes(
(Node *)(agg->target),
offset,
sublevels_up);
}
break;
case T_GroupClause:
{
GroupClause *grp = (GroupClause *)node;
OffsetVarNodes(
(Node *)(grp->entry),
offset,
sublevels_up);
}
break;
case T_Expr:
{
Expr *exp = (Expr *)node;
OffsetVarNodes(
(Node *)(exp->args),
offset,
sublevels_up);
}
break;
case T_Iter:
{
Iter *iter = (Iter *)node;
OffsetVarNodes(
(Node *)(iter->iterexpr),
offset,
sublevels_up);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *)node;
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:
{
Var *var = (Var *)node;
if (var->varlevelsup == sublevels_up) {
var->varno += offset;
var->varnoold += offset;
}
}
break;
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:
{
SubLink *sub = (SubLink *)node;
OffsetVarNodes(
(Node *)(sub->lefthand),
offset,
sublevels_up);
OffsetVarNodes(
(Node *)(sub->subselect),
offset,
sublevels_up + 1);
}
break;
case T_Query:
{
Query *qry = (Query *)node;
OffsetVarNodes(
(Node *)(qry->targetList),
offset,
sublevels_up);
OffsetVarNodes(
(Node *)(qry->qual),
offset,
sublevels_up);
OffsetVarNodes(
(Node *)(qry->havingQual),
offset,
sublevels_up);
OffsetVarNodes(
(Node *)(qry->groupClause),
offset,
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
ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
{
if (node == NULL)
return;
switch(nodeTag(node)) {
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *)node;
ChangeVarNodes(
(Node *)(tle->expr),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Aggreg:
{
Aggreg *agg = (Aggreg *)node;
ChangeVarNodes(
(Node *)(agg->target),
rt_index,
new_index,
sublevels_up);
}
break;
case T_GroupClause:
{
GroupClause *grp = (GroupClause *)node;
ChangeVarNodes(
(Node *)(grp->entry),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Expr:
{
Expr *exp = (Expr *)node;
ChangeVarNodes(
(Node *)(exp->args),
rt_index,
new_index,
sublevels_up);
}
break;
case T_Iter:
{
Iter *iter = (Iter *)node;
ChangeVarNodes(
(Node *)(iter->iterexpr),
rt_index,
new_index,
sublevels_up);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *)node;
ChangeVarNodes(
(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:
{
Var *var = (Var *)node;
if (var->varlevelsup == sublevels_up &&
var->varno == rt_index) {
var->varno = new_index;
var->varnoold = new_index;
}
}
break;
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);
1998-01-21 05:24:46 +01:00
}
break;
1998-01-21 05:24:46 +01:00
case T_SubLink:
{
SubLink *sub = (SubLink *)node;
ChangeVarNodes(
(Node *)(sub->lefthand),
rt_index,
new_index,
sublevels_up);
ChangeVarNodes(
(Node *)(sub->subselect),
rt_index,
new_index,
sublevels_up + 1);
}
break;
case T_Query:
{
Query *qry = (Query *)node;
ChangeVarNodes(
(Node *)(qry->targetList),
rt_index,
new_index,
sublevels_up);
ChangeVarNodes(
(Node *)(qry->qual),
rt_index,
new_index,
sublevels_up);
ChangeVarNodes(
(Node *)(qry->havingQual),
rt_index,
new_index,
sublevels_up);
ChangeVarNodes(
(Node *)(qry->groupClause),
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
AddQual(Query *parsetree, Node *qual)
{
Node *copy,
*old;
if (qual == NULL)
return;
copy = copyObject(qual);
old = parsetree->qual;
if (old == NULL)
parsetree->qual = copy;
else
parsetree->qual =
(Node *) make_andclause(makeList(parsetree->qual, copy, -1));
}
/* Adds the given havingQual to the one already contained in the parsetree just as
* AddQual does for the normal 'where' qual */
void
AddHavingQual(Query *parsetree, Node *havingQual)
{
Node *copy,
*old;
if (havingQual == NULL)
return;
copy = copyObject(havingQual);
old = parsetree->havingQual;
if (old == NULL)
parsetree->havingQual = copy;
else
parsetree->havingQual =
(Node *) make_andclause(makeList(parsetree->havingQual, copy, -1));
}
void
AddNotQual(Query *parsetree, Node *qual)
{
Node *copy;
if (qual == NULL)
return;
copy = (Node *) make_notclause(copyObject(qual));
AddQual(parsetree, copy);
}
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;
}
#ifdef NOT_USED
void
FixResdomTypes(List *tlist)
{
List *i;
foreach(i, tlist)
{
TargetEntry *tle = lfirst(i);
if (nodeTag(tle->expr) == T_Var)
{
Var *var = (Var *) tle->expr;
tle->resdom->restype = var->vartype;
tle->resdom->restypmod = var->vartypmod;
}
}
}
#endif
static Node *
FindMatchingNew(List *tlist, int attno)
{
List *i;
foreach(i, tlist)
{
TargetEntry *tle = lfirst(i);
if (tle->resdom->resno == attno)
1998-09-01 05:29:17 +02:00
return tle->expr;
}
return NULL;
}
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))
1998-09-01 05:29:17 +02:00
return tle->expr;
}
return NULL;
}
static void
1998-01-21 05:24:46 +01:00
ResolveNew(RewriteInfo *info, List *targetlist, Node **nodePtr,
int sublevels_up)
{
Node *node = *nodePtr;
if (node == NULL)
return;
switch (nodeTag(node))
{
case T_TargetEntry:
1998-01-21 05:24:46 +01:00
ResolveNew(info, targetlist, &((TargetEntry *) node)->expr,
sublevels_up);
break;
case T_Aggreg:
1998-01-21 05:24:46 +01:00
ResolveNew(info, targetlist, &((Aggreg *) node)->target,
sublevels_up);
break;
case T_Expr:
1998-01-21 05:24:46 +01:00
ResolveNew(info, targetlist, (Node **) (&(((Expr *) node)->args)),
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;
int this_varlevelsup = (int) ((Var *) node)->varlevelsup;
Node *n;
1998-01-21 05:24:46 +01:00
if (this_varno == info->new_varno &&
this_varlevelsup == sublevels_up)
{
n = FindMatchingNew(targetlist,
((Var *) node)->varattno);
if (n == NULL)
{
if (info->event == CMD_UPDATE)
{
1998-10-20 19:21:44 +02:00
*nodePtr = n = copyObject(node);
((Var *) n)->varno = info->current_varno;
((Var *) n)->varnoold = info->current_varno;
}
else
*nodePtr = make_null(((Var *) node)->vartype);
}
else
1998-10-20 19:21:44 +02:00
*nodePtr = copyObject(n);
}
break;
}
case T_List:
{
List *l;
foreach(l, (List *) node)
1998-01-21 05:24:46 +01:00
ResolveNew(info, targetlist, (Node **) &(lfirst(l)),
sublevels_up);
break;
}
1998-01-21 05:24:46 +01:00
case T_SubLink:
{
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) sublink->subselect;
1998-01-21 05:24:46 +01:00
ResolveNew(info, targetlist, (Node **) &(query->qual), sublevels_up + 1);
1998-01-21 05:24:46 +01:00
}
break;
default:
/* ignore the others */
break;
}
}
void
FixNew(RewriteInfo *info, Query *parsetree)
{
ResolveNew(info, parsetree->targetList,
1998-01-21 05:24:46 +01:00
(Node **) &(info->rule_action->targetList), 0);
ResolveNew(info, parsetree->targetList, &info->rule_action->qual, 0);
}
static void
nodeHandleRIRAttributeRule(Node **nodePtr,
List *rtable,
List *targetlist,
int rt_index,
int attr_num,
int *modified,
1998-01-21 05:24:46 +01:00
int *badsql,
int sublevels_up)
{
Node *node = *nodePtr;
if (node == NULL)
return;
switch (nodeTag(node))
{
case T_TargetEntry:
{
TargetEntry *tle = (TargetEntry *) node;
nodeHandleRIRAttributeRule(&tle->expr, rtable, targetlist,
rt_index, attr_num, modified, badsql,
sublevels_up);
}
break;
case T_Aggreg:
{
Aggreg *agg = (Aggreg *) node;
nodeHandleRIRAttributeRule(&agg->target, rtable, targetlist,
rt_index, attr_num, modified, badsql,
sublevels_up);
}
break;
case T_Expr:
{
Expr *expr = (Expr *) node;
nodeHandleRIRAttributeRule((Node **) (&(expr->args)), rtable,
targetlist, rt_index, attr_num,
1998-01-21 05:24:46 +01:00
modified, badsql,
sublevels_up);
}
break;
case T_Iter:
{
Iter *iter = (Iter *) node;
nodeHandleRIRAttributeRule((Node **) (&(iter->iterexpr)), rtable,
targetlist, rt_index, attr_num,
modified, badsql,
sublevels_up);
}
break;
case T_ArrayRef:
{
ArrayRef *ref = (ArrayRef *) node;
nodeHandleRIRAttributeRule((Node **) (&(ref->refupperindexpr)), rtable,
targetlist, rt_index, attr_num,
modified, badsql,
sublevels_up);
nodeHandleRIRAttributeRule((Node **) (&(ref->reflowerindexpr)), rtable,
targetlist, rt_index, attr_num,
modified, badsql,
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:
{
1998-01-21 05:24:46 +01:00
int this_varno = ((Var *) node)->varno;
int this_varattno = ((Var *) node)->varattno;
int this_varlevelsup = ((Var *) node)->varlevelsup;
if (this_varno == rt_index &&
1998-01-21 05:24:46 +01:00
this_varattno == attr_num &&
this_varlevelsup == sublevels_up)
{
if (((Var *) node)->vartype == 32)
{ /* HACK */
*nodePtr = make_null(((Var *) node)->vartype);
*modified = TRUE;
*badsql = TRUE;
break;
}
else
{
1998-01-21 05:24:46 +01:00
NameData name_to_look_for;
name_to_look_for.data[0] = '\0';
namestrcpy(&name_to_look_for,
(char *) get_attname(getrelid(this_varno,
rtable),
attr_num));
1998-01-21 05:24:46 +01:00
if (name_to_look_for.data[0])
{
Node *n;
1998-01-21 05:24:46 +01:00
n = FindMatchingTLEntry(targetlist, (char *) &name_to_look_for);
if (n == NULL)
*nodePtr = make_null(((Var *) node)->vartype);
else
*nodePtr = n;
*modified = TRUE;
}
}
}
1998-01-21 05:24:46 +01:00
}
break;
case T_List:
{
List *i;
1998-01-21 05:24:46 +01:00
foreach(i, (List *) node)
{
nodeHandleRIRAttributeRule((Node **) (&(lfirst(i))), rtable,
targetlist, rt_index, attr_num,
modified, badsql, sublevels_up);
}
}
break;
1998-01-21 05:24:46 +01:00
case T_SubLink:
{
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) sublink->subselect;
1998-01-21 05:24:46 +01:00
nodeHandleRIRAttributeRule((Node **) &(query->qual), rtable, targetlist,
rt_index, attr_num, modified, badsql,
sublevels_up + 1);
1998-01-21 05:24:46 +01:00
}
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,
1998-01-21 05:24:46 +01:00
modified, badsql, 0);
nodeHandleRIRAttributeRule(&parsetree->qual, rtable, targetlist,
1998-01-21 05:24:46 +01:00
rt_index, attr_num, modified, badsql, 0);
}
static void
nodeHandleViewRule(Node **nodePtr,
List *rtable,
List *targetlist,
int rt_index,
1998-01-21 05:24:46 +01:00
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,
1998-01-21 05:24:46 +01:00
rt_index, modified, sublevels_up);
}
break;
case T_Aggreg:
{
Aggreg *agg = (Aggreg *) node;
nodeHandleViewRule(&(agg->target), rtable, targetlist,
rt_index, modified, sublevels_up);
}
break;
/*
* This has to be done to make queries using groupclauses work
* on views
*/
case T_GroupClause:
{
GroupClause *group = (GroupClause *) node;
nodeHandleViewRule((Node **) (&(group->entry)), rtable, targetlist,
rt_index, modified, sublevels_up);
}
break;
case T_Expr:
{
Expr *expr = (Expr *) node;
nodeHandleViewRule((Node **) (&(expr->args)),
rtable, targetlist,
1998-01-21 05:24:46 +01:00
rt_index, modified, sublevels_up);
}
break;
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;
1998-01-21 05:24:46 +01:00
int this_varlevelsup = var->varlevelsup;
Node *n;
1998-01-21 05:24:46 +01:00
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
{
/*
* This is a hack: The varlevelsup of the orignal
* variable and the new one should be the same.
* 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;
}
1998-01-21 05:24:46 +01:00
case T_List:
{
List *l;
foreach(l, (List *) node)
{
nodeHandleViewRule((Node **) (&(lfirst(l))),
rtable, targetlist,
rt_index, modified, sublevels_up);
}
}
break;
case T_SubLink:
{
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) sublink->subselect;
1998-01-21 05:24:46 +01:00
nodeHandleViewRule((Node **) &(query->qual), rtable, targetlist,
rt_index, modified, sublevels_up + 1);
/*
* We also have to adapt the variables used in
* sublink->lefthand and sublink->oper
*/
nodeHandleViewRule((Node **) &(sublink->lefthand), rtable,
targetlist, rt_index, modified, sublevels_up);
/*
* Make sure the first argument of sublink->oper points to
* the same var as sublink->lefthand does otherwise we
* will run into troubles using aggregates (aggno will not
* be set correctly
*/
pfree(lfirst(((Expr *) lfirst(sublink->oper))->args));
lfirst(((Expr *) lfirst(sublink->oper))->args) =
lfirst(sublink->lefthand);
1998-01-21 05:24:46 +01:00
}
break;
default:
/* ignore the others */
break;
}
}
#ifdef NOT_USED
void
HandleViewRule(Query *parsetree,
List *rtable,
List *targetlist,
int rt_index,
int *modified)
{
nodeHandleViewRule(&parsetree->qual, rtable, targetlist, rt_index,
1998-01-21 05:24:46 +01:00
modified, 0);
nodeHandleViewRule((Node **) (&(parsetree->targetList)), rtable, targetlist,
1998-01-21 05:24:46 +01:00
rt_index, modified, 0);
/*
* The variables in the havingQual and groupClause also have to be
* adapted
*/
nodeHandleViewRule(&parsetree->havingQual, rtable, targetlist, rt_index,
modified, 0);
nodeHandleViewRule((Node **) (&(parsetree->groupClause)), rtable, targetlist, rt_index,
modified, 0);
}
#endif