1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-25 01:02:05 +03:00

Subselects in FROM clause, per ISO syntax: FROM (SELECT ...) [AS] alias.

(Don't forget that an alias is required.)  Views reimplemented as expanding
to subselect-in-FROM.  Grouping, aggregates, DISTINCT in views actually
work now (he says optimistically).  No UNION support in subselects/views
yet, but I have some ideas about that.  Rule-related permissions checking
moved out of rewriter and into executor.
INITDB REQUIRED!
This commit is contained in:
Tom Lane
2000-09-29 18:21:41 +00:00
parent 6f64c2e54a
commit 3a94e789f5
77 changed files with 3176 additions and 2661 deletions

View File

@ -4,7 +4,7 @@
# Makefile for rewrite
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/rewrite/Makefile,v 1.13 2000/08/31 16:10:27 petere Exp $
# $Header: /cvsroot/pgsql/src/backend/rewrite/Makefile,v 1.14 2000/09/29 18:21:24 tgl Exp $
#
#-------------------------------------------------------------------------
@ -13,7 +13,7 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = rewriteRemove.o rewriteDefine.o \
rewriteHandler.o rewriteManip.o rewriteSupport.o locks.o
rewriteHandler.o rewriteManip.o rewriteSupport.o
all: SUBSYS.o

View File

@ -1,268 +0,0 @@
/*-------------------------------------------------------------------------
*
* locks.c
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.32 2000/09/12 21:07:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/pg_shadow.h"
#include "optimizer/clauses.h"
#include "rewrite/locks.h"
#include "parser/parsetree.h"
#include "utils/acl.h"
#include "utils/syscache.h"
/*
* 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
*
* XXX this should be unified with attribute_used()
*/
typedef struct
{
int varno;
int attnum;
int sublevels_up;
} thisLockWasTriggered_context;
static bool
thisLockWasTriggered_walker(Node *node,
thisLockWasTriggered_context *context)
{
if (node == NULL)
return false;
if (IsA(node, Var))
{
Var *var = (Var *) node;
if (var->varlevelsup == context->sublevels_up &&
var->varno == context->varno &&
(var->varattno == context->attnum || context->attnum == -1))
return true;
return false;
}
if (IsA(node, Query))
{
/* Recurse into subselects */
bool result;
context->sublevels_up++;
result = query_tree_walker((Query *) node, thisLockWasTriggered_walker,
(void *) context);
context->sublevels_up--;
return result;
}
return expression_tree_walker(node, thisLockWasTriggered_walker,
(void *) context);
}
static bool
thisLockWasTriggered(int varno,
int attnum,
Query *parsetree)
{
thisLockWasTriggered_context context;
context.varno = varno;
context.attnum = attnum;
context.sublevels_up = 0;
return thisLockWasTriggered_walker((Node *) parsetree, &context);
}
/*
* matchLocks -
* match the list of locks and returns the matching rules
*/
List *
matchLocks(CmdType event,
RuleLock *rulelocks,
int varno,
Query *parsetree)
{
List *real_locks = NIL;
int nlocks;
int i;
Assert(rulelocks != NULL); /* we get called iff there is some lock */
Assert(parsetree != NULL);
if (parsetree->commandType != CMD_SELECT)
{
if (parsetree->resultRelation != varno)
return NULL;
}
nlocks = rulelocks->numLocks;
for (i = 0; i < nlocks; i++)
{
RewriteRule *oneLock = rulelocks->rules[i];
if (oneLock->event == event)
{
if (parsetree->commandType != CMD_SELECT ||
thisLockWasTriggered(varno,
oneLock->attrno,
parsetree))
real_locks = lappend(real_locks, oneLock);
}
}
checkLockPerms(real_locks, parsetree, varno);
return real_locks;
}
/*
* Check the access permissions of tables that are referred to by a rule.
* We want to check the access permissions using the userid of the rule's
* owner, *not* of the current user (the one accessing the rule). So, we
* do the permission check here and set skipAcl = TRUE in each of the rule's
* RTEs, to prevent the executor from running another check with the current
* user's ID.
*
* XXX This routine is called before the rule's query tree has been copied
* out of the relcache entry where it is kept. Therefore, when we set
* skipAcl = TRUE, we are destructively modifying the relcache entry for
* the event relation! This seems fairly harmless because the relcache
* querytree is only used as a source for the rewriter, but it's a tad
* unclean anyway.
*
* Note that we must check permissions every time, even if skipAcl was
* already set TRUE by a prior call. This ensures that we enforce the
* current permission settings for each referenced table, even if they
* have changed since the relcache entry was loaded.
*/
typedef struct
{
Oid evowner;
} checkLockPerms_context;
static bool
checkLockPerms_walker(Node *node,
checkLockPerms_context *context)
{
if (node == NULL)
return false;
if (IsA(node, Query))
{
Query *qry = (Query *) node;
int rtablength = length(qry->rtable);
int i;
/* Check all the RTEs in this query node, except OLD and NEW */
for (i = 1; i <= rtablength; i++)
{
RangeTblEntry *rte = rt_fetch(i, qry->rtable);
int32 reqperm;
int32 aclcheck_res;
if (strcmp(rte->eref->relname, "*NEW*") == 0)
continue;
if (strcmp(rte->eref->relname, "*OLD*") == 0)
continue;
if (i == qry->resultRelation)
switch (qry->commandType)
{
case CMD_INSERT:
reqperm = ACL_AP;
break;
default:
reqperm = ACL_WR;
break;
}
else
reqperm = ACL_RD;
aclcheck_res = pg_aclcheck(rte->relname,
context->evowner,
reqperm);
if (aclcheck_res != ACLCHECK_OK)
elog(ERROR, "%s: %s",
rte->relname,
aclcheck_error_strings[aclcheck_res]);
/*
* Mark RTE to prevent executor from checking again with the
* current user's ID...
*/
rte->skipAcl = true;
}
/* If there are sublinks, search for them and check their RTEs */
if (qry->hasSubLinks)
return query_tree_walker(qry, checkLockPerms_walker,
(void *) context);
return false;
}
return expression_tree_walker(node, checkLockPerms_walker,
(void *) context);
}
void
checkLockPerms(List *locks, Query *parsetree, int rt_index)
{
RangeTblEntry *rte;
Relation ev_rel;
HeapTuple usertup;
Form_pg_shadow userform;
checkLockPerms_context context;
List *l;
if (locks == NIL)
return; /* nothing to check */
/*
* Get the userid of the rule's event relation owner
*/
rte = rt_fetch(rt_index, parsetree->rtable);
ev_rel = heap_openr(rte->relname, AccessShareLock);
usertup = SearchSysCacheTuple(SHADOWSYSID,
ObjectIdGetDatum(ev_rel->rd_rel->relowner),
0, 0, 0);
if (!HeapTupleIsValid(usertup))
elog(ERROR, "cache lookup for userid %d failed",
ev_rel->rd_rel->relowner);
userform = (Form_pg_shadow) GETSTRUCT(usertup);
context.evowner = userform->usesysid;
heap_close(ev_rel, AccessShareLock);
/*
* Check all the locks that should get fired on this query
*/
foreach(l, locks)
{
RewriteRule *onelock = (RewriteRule *) lfirst(l);
List *action;
/*
* In each lock check every action. We must scan the action
* recursively in case there are any sub-queries within it.
*/
foreach(action, onelock->actions)
{
Query *query = (Query *) lfirst(action);
checkLockPerms_walker((Node *) query, &context);
}
}
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.52 2000/09/12 20:38:09 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.53 2000/09/29 18:21:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -16,16 +16,21 @@
#include "postgres.h"
#include "access/heapam.h"
#include "utils/builtins.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/pg_rewrite.h"
#include "commands/view.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
#include "parser/parse_relation.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteSupport.h"
#include "utils/syscache.h"
#include "storage/smgr.h"
#include "commands/view.h"
#include "utils/builtins.h"
static void setRuleCheckAsUser(Query *qry, Oid userid);
static bool setRuleCheckAsUser_walker(Node *node, Oid *context);
/*
@ -52,7 +57,7 @@ InsertRule(char *rulname,
Oid rewriteObjectId;
if (IsDefinedRewriteRule(rulname))
elog(ERROR, "Attempt to insert rule '%s' failed: already exists",
elog(ERROR, "Attempt to insert rule \"%s\" failed: already exists",
rulname);
/* ----------------
@ -217,10 +222,7 @@ DefineQueryRewrite(RuleStmt *stmt)
*/
if (event_type == CMD_SELECT)
{
TargetEntry *tle;
Resdom *resdom;
Form_pg_attribute attr;
char *attname;
List *tllist;
int i;
char *expected_name;
@ -245,6 +247,10 @@ DefineQueryRewrite(RuleStmt *stmt)
query = (Query *) lfirst(action);
if (!is_instead || query->commandType != CMD_SELECT)
elog(ERROR, "only instead-select rules currently supported on select");
/*
* ... there can be no rule qual, ...
*/
if (event_qual != NULL)
elog(ERROR, "event qualifications not supported for rules on select");
@ -252,26 +258,36 @@ DefineQueryRewrite(RuleStmt *stmt)
* ... the targetlist of the SELECT action must exactly match the
* event relation, ...
*/
if (event_relation->rd_att->natts != length(query->targetList))
elog(ERROR, "select rules target list must match event relations structure");
for (i = 1; i <= event_relation->rd_att->natts; i++)
i = 0;
foreach(tllist, query->targetList)
{
tle = (TargetEntry *) nth(i - 1, query->targetList);
resdom = tle->resdom;
TargetEntry *tle = (TargetEntry *) lfirst(tllist);
Resdom *resdom = tle->resdom;
Form_pg_attribute attr;
char *attname;
if (resdom->resjunk)
continue;
i++;
if (i > event_relation->rd_att->natts)
elog(ERROR, "select rule's target list has too many entries");
attr = event_relation->rd_att->attrs[i - 1];
attname = NameStr(attr->attname);
if (strcmp(resdom->resname, attname) != 0)
elog(ERROR, "select rules target entry %d has different column name from %s", i, attname);
elog(ERROR, "select rule's target entry %d has different column name from %s", i, attname);
if (attr->atttypid != resdom->restype)
elog(ERROR, "select rules target entry %d has different type from attribute %s", i, attname);
elog(ERROR, "select rule's target entry %d has different type from attribute %s", i, attname);
if (attr->atttypmod != resdom->restypmod)
elog(ERROR, "select rules target entry %d has different size from attribute %s", i, attname);
elog(ERROR, "select rule's target entry %d has different size from attribute %s", i, attname);
}
if (i != event_relation->rd_att->natts)
elog(ERROR, "select rule's target list has too few entries");
/*
* ... there must not be another ON SELECT rule already ...
*/
@ -283,7 +299,7 @@ DefineQueryRewrite(RuleStmt *stmt)
rule = event_relation->rd_rules->rules[i];
if (rule->event == CMD_SELECT)
elog(ERROR, "%s is already a view",
elog(ERROR, "\"%s\" is already a view",
RelationGetRelationName(event_relation));
}
}
@ -294,25 +310,13 @@ DefineQueryRewrite(RuleStmt *stmt)
if (query->limitOffset != NULL || query->limitCount != NULL)
elog(ERROR, "LIMIT clause not supported in views");
/*
* DISTINCT on view is not supported
*/
if (query->distinctClause != NIL)
elog(ERROR, "DISTINCT not supported in views");
/*
* ORDER BY in view is not supported
*/
if (query->sortClause != NIL)
elog(ERROR, "ORDER BY not supported in views");
/*
* ... and finally the rule must be named _RETviewname.
*/
expected_name = MakeRetrieveViewRuleName(event_obj->relname);
if (strcmp(expected_name, stmt->rulename) != 0)
{
elog(ERROR, "view rule for %s must be named %s",
elog(ERROR, "view rule for \"%s\" must be named \"%s\"",
event_obj->relname, expected_name);
}
pfree(expected_name);
@ -342,7 +346,7 @@ DefineQueryRewrite(RuleStmt *stmt)
}
/*
* This rule is allowed - install it.
* This rule is allowed - prepare to install it.
*/
if (eslot_string == NULL)
{
@ -360,13 +364,21 @@ DefineQueryRewrite(RuleStmt *stmt)
eslot_string, event_qual, &action,
is_instead, event_attype);
/*
* We want the rule's table references to be checked as though by
* the rule owner, not the user referencing the rule. Therefore,
* scan through the rule's rtables and set the checkAsUser field
* on all rtable entries (except *OLD* and *NEW*).
*/
foreach(l, action)
{
query = (Query *) lfirst(l);
setRuleCheckAsUser(query, GetUserId());
}
/* discard rule if it's null action and not INSTEAD; it's a no-op */
if (action != NIL || is_instead)
{
Relation relationRelation;
HeapTuple tuple;
Relation idescs[Num_pg_class_indices];
event_qualP = nodeToString(event_qual);
actionP = nodeToString(action);
@ -386,34 +398,8 @@ DefineQueryRewrite(RuleStmt *stmt)
* Important side effect: an SI notice is broadcast to force all
* backends (including me!) to update relcache entries with the new
* rule.
*
* NOTE : Used to call setRelhasrulesInRelation. The code
* was inlined so that two updates were not needed. mhh 31-aug-2000
*/
/*
* Find the tuple to update in pg_class, using syscache for the lookup.
*/
relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
tuple = SearchSysCacheTupleCopy(RELOID,
ObjectIdGetDatum(ev_relid),
0, 0, 0);
Assert(HeapTupleIsValid(tuple));
/* Do the update */
((Form_pg_class) GETSTRUCT(tuple))->relhasrules = true;
if (RelisBecomingView)
((Form_pg_class) GETSTRUCT(tuple))->relkind = RELKIND_VIEW;
heap_update(relationRelation, &tuple->t_self, tuple, NULL);
/* Keep the catalog indices up to date */
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
CatalogCloseIndices(Num_pg_class_indices, idescs);
heap_freetuple(tuple);
heap_close(relationRelation, RowExclusiveLock);
SetRelationRuleStatus(ev_relid, true, RelisBecomingView);
}
/*
@ -427,3 +413,60 @@ DefineQueryRewrite(RuleStmt *stmt)
/* Close rel, but keep lock till commit... */
heap_close(event_relation, NoLock);
}
/*
* setRuleCheckAsUser
* Recursively scan a query and set the checkAsUser field to the
* given userid in all rtable entries except *OLD* and *NEW*.
*/
static void
setRuleCheckAsUser(Query *qry, Oid userid)
{
List *l;
/* Set all the RTEs in this query node, except OLD and NEW */
foreach(l, qry->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
if (strcmp(rte->eref->relname, "*NEW*") == 0)
continue;
if (strcmp(rte->eref->relname, "*OLD*") == 0)
continue;
if (rte->subquery)
{
/*
* Recurse into subquery in FROM
*/
setRuleCheckAsUser(rte->subquery, userid);
}
else
{
rte->checkAsUser = userid;
}
}
/* If there are sublinks, search for them and process their RTEs */
if (qry->hasSubLinks)
query_tree_walker(qry, setRuleCheckAsUser_walker, (void *) &userid);
}
/*
* Expression-tree walker to find sublink queries
*/
static bool
setRuleCheckAsUser_walker(Node *node, Oid *context)
{
if (node == NULL)
return false;
if (IsA(node, Query))
{
Query *qry = (Query *) node;
setRuleCheckAsUser(qry, *context);
return false;
}
return expression_tree_walker(node, setRuleCheckAsUser_walker,
(void *) context);
}

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.48 2000/09/12 21:07:04 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.49 2000/09/29 18:21:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -473,8 +473,7 @@ attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
void
AddQual(Query *parsetree, Node *qual)
{
Node *copy,
*old;
Node *copy;
if (qual == NULL)
return;
@ -482,11 +481,8 @@ AddQual(Query *parsetree, Node *qual)
/* INTERSECT want's the original, but we need to copy - Jan */
copy = copyObject(qual);
old = parsetree->qual;
if (old == NULL)
parsetree->qual = copy;
else
parsetree->qual = (Node *) make_andclause(makeList(old, copy, -1));
parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals,
copy);
/*
* Make sure query is marked correctly if added qual has sublinks or
@ -504,8 +500,7 @@ AddQual(Query *parsetree, Node *qual)
void
AddHavingQual(Query *parsetree, Node *havingQual)
{
Node *copy,
*old;
Node *copy;
if (havingQual == NULL)
return;
@ -513,11 +508,8 @@ AddHavingQual(Query *parsetree, Node *havingQual)
/* INTERSECT want's the original, but we need to copy - Jan */
copy = copyObject(havingQual);
old = parsetree->havingQual;
if (old == NULL)
parsetree->havingQual = copy;
else
parsetree->havingQual = (Node *) make_andclause(makeList(old, copy, -1));
parsetree->havingQual = make_and_qual(parsetree->havingQual,
copy);
/*
* Make sure query is marked correctly if added qual has sublinks or
@ -560,45 +552,7 @@ AddNotQual(Query *parsetree, Node *qual)
}
/*
* Add all expressions used by the given GroupClause list to the
* parsetree's targetlist and groupclause list.
*
* tlist is the old targetlist associated with the input groupclauses.
*
* XXX shouldn't we be checking to see if there are already matching
* entries in parsetree->targetlist?
*/
void
AddGroupClause(Query *parsetree, List *group_by, List *tlist)
{
List *l;
foreach(l, group_by)
{
GroupClause *groupclause = (GroupClause *) copyObject(lfirst(l));
TargetEntry *tle = get_sortgroupclause_tle(groupclause, tlist);
/* copy the groupclause's TLE from the old tlist */
tle = (TargetEntry *) copyObject(tle);
/*
* The ressortgroupref number in the old tlist might be already
* taken in the new tlist, so force assignment of a new number.
*/
tle->resdom->ressortgroupref = 0;
groupclause->tleSortGroupRef =
assignSortGroupRef(tle, parsetree->targetList);
/* Also need to set the resno and mark it resjunk. */
tle->resdom->resno = length(parsetree->targetList) + 1;
tle->resdom->resjunk = true;
parsetree->targetList = lappend(parsetree->targetList, tle);
parsetree->groupClause = lappend(parsetree->groupClause, groupclause);
}
}
/* Build a NULL constant expression of the given type */
static Node *
make_null(Oid type)
{
@ -612,28 +566,6 @@ make_null(Oid 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
/* Find a targetlist entry by resno */
static Node *
FindMatchingNew(List *tlist, int attno)
@ -650,6 +582,8 @@ FindMatchingNew(List *tlist, int attno)
return NULL;
}
#ifdef NOT_USED
/* Find a targetlist entry by resname */
static Node *
FindMatchingTLEntry(List *tlist, char *e_attname)
@ -662,25 +596,31 @@ FindMatchingTLEntry(List *tlist, char *e_attname)
char *resname;
resname = tle->resdom->resname;
if (!strcmp(e_attname, resname))
if (strcmp(e_attname, resname) == 0)
return tle->expr;
}
return NULL;
}
#endif
/*
* ResolveNew - replace Vars with corresponding items from a targetlist
*
* Vars matching info->new_varno and sublevels_up are replaced by the
* Vars matching target_varno and sublevels_up are replaced by the
* entry with matching resno from targetlist, if there is one.
* If not, we either change the unmatched Var's varno to update_varno
* (when event == CMD_UPDATE) or replace it with a constant NULL.
*/
typedef struct
{
RewriteInfo *info;
List *targetlist;
int target_varno;
int sublevels_up;
List *targetlist;
int event;
int update_varno;
} ResolveNew_context;
static Node *
@ -694,7 +634,7 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
int this_varno = (int) var->varno;
int this_varlevelsup = (int) var->varlevelsup;
if (this_varno == context->info->new_varno &&
if (this_varno == context->target_varno &&
this_varlevelsup == context->sublevels_up)
{
Node *n = FindMatchingNew(context->targetlist,
@ -702,13 +642,13 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
if (n == NULL)
{
if (context->info->event == CMD_UPDATE)
if (context->event == CMD_UPDATE)
{
/* For update, just change unmatched var's varno */
n = copyObject(node);
((Var *) n)->varno = context->info->current_varno;
((Var *) n)->varnoold = context->info->current_varno;
return n;
var = (Var *) copyObject(node);
var->varno = context->update_varno;
var->varnoold = context->update_varno;
return (Node *) var;
}
else
{
@ -755,54 +695,68 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
FLATCOPY(newnode, query, Query);
MUTATE(newnode->targetList, query->targetList, List *,
ResolveNew_mutator, context);
MUTATE(newnode->qual, query->qual, Node *,
MUTATE(newnode->jointree, query->jointree, FromExpr *,
ResolveNew_mutator, context);
MUTATE(newnode->havingQual, query->havingQual, Node *,
ResolveNew_mutator, context);
MUTATE(newnode->jointree, query->jointree, List *,
ResolveNew_mutator, context);
return (Node *) newnode;
}
return expression_tree_mutator(node, ResolveNew_mutator,
(void *) context);
}
static Node *
ResolveNew(Node *node, RewriteInfo *info, List *targetlist,
int sublevels_up)
Node *
ResolveNew(Node *node, int target_varno, int sublevels_up,
List *targetlist, int event, int update_varno)
{
ResolveNew_context context;
context.info = info;
context.targetlist = targetlist;
context.target_varno = target_varno;
context.sublevels_up = sublevels_up;
context.targetlist = targetlist;
context.event = event;
context.update_varno = update_varno;
/*
* Note: if an entire Query is passed, the right things will happen,
* because ResolveNew_mutator increments sublevels_up when it sees
* a SubLink, not a Query.
*/
return ResolveNew_mutator(node, &context);
}
/*
* Alternate interface to ResolveNew: substitute Vars in info->rule_action
* with targetlist items from the parsetree's targetlist.
*/
void
FixNew(RewriteInfo *info, Query *parsetree)
{
ResolveNew_context context;
context.target_varno = info->new_varno;
context.sublevels_up = 0;
context.targetlist = parsetree->targetList;
context.event = info->event;
context.update_varno = info->current_varno;
info->rule_action->targetList = (List *)
ResolveNew((Node *) info->rule_action->targetList,
info, parsetree->targetList, 0);
info->rule_action->qual = ResolveNew(info->rule_action->qual,
info, parsetree->targetList, 0);
info->rule_action->havingQual = ResolveNew(info->rule_action->havingQual,
info, parsetree->targetList, 0);
info->rule_action->jointree = (List *)
ResolveNew((Node *) info->rule_action->jointree,
info, parsetree->targetList, 0);
ResolveNew_mutator((Node *) info->rule_action->targetList, &context);
info->rule_action->jointree = (FromExpr *)
ResolveNew_mutator((Node *) info->rule_action->jointree, &context);
info->rule_action->havingQual =
ResolveNew_mutator(info->rule_action->havingQual, &context);
}
#ifdef NOT_USED
/*
* HandleRIRAttributeRule
* Replace Vars matching a given RT index with copies of TL expressions.
*
* Handles 'on retrieve to relation.attribute
* do instead retrieve (attribute = expression) w/qual'
*
* XXX Why is this not unified with apply_RIR_view()?
*/
typedef struct
@ -897,12 +851,12 @@ HandleRIRAttributeRule_mutator(Node *node,
FLATCOPY(newnode, query, Query);
MUTATE(newnode->targetList, query->targetList, List *,
HandleRIRAttributeRule_mutator, context);
MUTATE(newnode->qual, query->qual, Node *,
MUTATE(newnode->jointree, query->jointree, FromExpr *,
HandleRIRAttributeRule_mutator, context);
MUTATE(newnode->havingQual, query->havingQual, Node *,
HandleRIRAttributeRule_mutator, context);
MUTATE(newnode->jointree, query->jointree, List *,
HandleRIRAttributeRule_mutator, context);
return (Node *) newnode;
}
return expression_tree_mutator(node, HandleRIRAttributeRule_mutator,
@ -931,13 +885,12 @@ HandleRIRAttributeRule(Query *parsetree,
parsetree->targetList = (List *)
HandleRIRAttributeRule_mutator((Node *) parsetree->targetList,
&context);
parsetree->qual =
HandleRIRAttributeRule_mutator(parsetree->qual,
parsetree->jointree = (FromExpr *)
HandleRIRAttributeRule_mutator((Node *) parsetree->jointree,
&context);
parsetree->havingQual =
HandleRIRAttributeRule_mutator(parsetree->havingQual,
&context);
parsetree->jointree = (List *)
HandleRIRAttributeRule_mutator((Node *) parsetree->jointree,
&context);
}
#endif /* NOT_USED */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.39 2000/09/12 04:49:09 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.40 2000/09/29 18:21:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -40,14 +40,14 @@ RewriteGetRuleEventRel(char *rulename)
PointerGetDatum(rulename),
0, 0, 0);
if (!HeapTupleIsValid(htup))
elog(ERROR, "Rule or view '%s' not found",
((!strncmp(rulename, "_RET", 4)) ? (rulename + 4) : rulename));
elog(ERROR, "Rule or view \"%s\" not found",
((strncmp(rulename, "_RET", 4) == 0) ? (rulename + 4) : rulename));
eventrel = ((Form_pg_rewrite) GETSTRUCT(htup))->ev_class;
htup = SearchSysCacheTuple(RELOID,
PointerGetDatum(eventrel),
0, 0, 0);
if (!HeapTupleIsValid(htup))
elog(ERROR, "Class '%u' not found", eventrel);
elog(ERROR, "Relation %u not found", eventrel);
return NameStr(((Form_pg_class) GETSTRUCT(htup))->relname);
}
@ -85,7 +85,7 @@ RemoveRewriteRule(char *ruleName)
if (!HeapTupleIsValid(tuple))
{
heap_close(RewriteRelation, RowExclusiveLock);
elog(ERROR, "Rule '%s' not found\n", ruleName);
elog(ERROR, "Rule \"%s\" not found", ruleName);
}
/*
@ -105,7 +105,7 @@ RemoveRewriteRule(char *ruleName)
/* do not allow the removal of a view's SELECT rule */
if (event_relation->rd_rel->relkind == RELKIND_VIEW &&
((Form_pg_rewrite) GETSTRUCT(tuple))->ev_type == '1' )
((Form_pg_rewrite) GETSTRUCT(tuple))->ev_type == '1' )
elog(ERROR, "Cannot remove a view's SELECT rule");
hasMoreRules = event_relation->rd_rules != NULL &&
@ -133,7 +133,7 @@ RemoveRewriteRule(char *ruleName)
* new rule set. Therefore, must do this even if relhasrules is
* still true!
*/
setRelhasrulesInRelation(eventRelationOid, hasMoreRules);
SetRelationRuleStatus(eventRelationOid, hasMoreRules, false);
/* Close rel, but keep lock till commit... */
heap_close(event_relation, NoLock);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.43 2000/06/30 07:04:23 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.44 2000/09/29 18:21:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -34,10 +34,11 @@ IsDefinedRewriteRule(char *ruleName)
}
/*
* setRelhasrulesInRelation
* Set the value of the relation's relhasrules field in pg_class.
* SetRelationRuleStatus
* Set the value of the relation's relhasrules field in pg_class;
* if the relation is becoming a view, also adjust its relkind.
*
* NOTE: caller should be holding an appropriate lock on the relation.
* NOTE: caller must be holding an appropriate lock on the relation.
*
* NOTE: an important side-effect of this operation is that an SI invalidation
* message is sent out to all backends --- including me --- causing relcache
@ -47,7 +48,8 @@ IsDefinedRewriteRule(char *ruleName)
* an SI message in that case.
*/
void
setRelhasrulesInRelation(Oid relationId, bool relhasrules)
SetRelationRuleStatus(Oid relationId, bool relHasRules,
bool relIsBecomingView)
{
Relation relationRelation;
HeapTuple tuple;
@ -63,7 +65,10 @@ setRelhasrulesInRelation(Oid relationId, bool relhasrules)
Assert(HeapTupleIsValid(tuple));
/* Do the update */
((Form_pg_class) GETSTRUCT(tuple))->relhasrules = relhasrules;
((Form_pg_class) GETSTRUCT(tuple))->relhasrules = relHasRules;
if (relIsBecomingView)
((Form_pg_class) GETSTRUCT(tuple))->relkind = RELKIND_VIEW;
heap_update(relationRelation, &tuple->t_self, tuple, NULL);
/* Keep the catalog indices up to date */