diff options
Diffstat (limited to 'src/backend/rewrite')
-rw-r--r-- | src/backend/rewrite/Makefile.inc | 22 | ||||
-rw-r--r-- | src/backend/rewrite/locks.c | 131 | ||||
-rw-r--r-- | src/backend/rewrite/locks.h | 21 | ||||
-rw-r--r-- | src/backend/rewrite/prs2lock.h | 43 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteDefine.c | 255 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteDefine.h | 18 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteHandler.c | 622 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteHandler.h | 35 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteManip.c | 435 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteManip.h | 31 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteRemove.c | 181 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteRemove.h | 20 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteSupport.c | 270 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteSupport.h | 27 |
14 files changed, 2111 insertions, 0 deletions
diff --git a/src/backend/rewrite/Makefile.inc b/src/backend/rewrite/Makefile.inc new file mode 100644 index 00000000000..9212f50a968 --- /dev/null +++ b/src/backend/rewrite/Makefile.inc @@ -0,0 +1,22 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the rewrite rules module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ +# +#------------------------------------------------------------------------- + +VPATH:= $(VPATH):$(CURDIR)/rewrite + + +SRCS_REWRITE= rewriteRemove.c rewriteDefine.c \ + rewriteHandler.c rewriteManip.c rewriteSupport.c locks.c + +HEADERS+= rewriteRemove.h rewriteDefine.h rewriteHandler.h \ + rewriteManip.h rewriteSupport.h locks.h prs2lock.h + diff --git a/src/backend/rewrite/locks.c b/src/backend/rewrite/locks.c new file mode 100644 index 00000000000..a45457f61de --- /dev/null +++ b/src/backend/rewrite/locks.c @@ -0,0 +1,131 @@ +/*------------------------------------------------------------------------- + * + * locks.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" /* for oid defs */ +#include "utils/elog.h" /* for elog */ +#include "nodes/pg_list.h" /* lisp support package */ +#include "nodes/parsenodes.h" +#include "nodes/primnodes.h" /* Var node def */ +#include "utils/syscache.h" /* for SearchSysCache */ +#include "rewrite/locks.h" /* for rewrite specific lock defns */ + +/* + * 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 + */ +bool +nodeThisLockWasTriggered(Node *node, int varno, AttrNumber attnum) +{ + if (node==NULL) + return FALSE; + switch(nodeTag(node)) { + case T_Var: + { + Var *var = (Var *)node; + if (varno == var->varno && + (attnum == var->varattno || attnum == -1)) + return TRUE; + } + break; + case T_Expr: + { + Expr *expr = (Expr*)node; + return + nodeThisLockWasTriggered((Node*)expr->args, varno, attnum); + } + break; + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + return + nodeThisLockWasTriggered(tle->expr, varno, attnum); + } + break; + case T_List: + { + List *l; + + foreach(l, (List*)node) { + if (nodeThisLockWasTriggered(lfirst(l), varno, attnum)) + return TRUE; + } + return FALSE; + } + break; + default: + break; + } + return (FALSE); +} + +/* + * 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 +thisLockWasTriggered(int varno, + AttrNumber attnum, + Query *parsetree) +{ + return + (nodeThisLockWasTriggered(parsetree->qual, varno, attnum) || + nodeThisLockWasTriggered((Node*)parsetree->targetList, + varno, attnum)); +} + +/* + * 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); + } + } + } + + return (real_locks); +} + diff --git a/src/backend/rewrite/locks.h b/src/backend/rewrite/locks.h new file mode 100644 index 00000000000..a1e56c4b5a5 --- /dev/null +++ b/src/backend/rewrite/locks.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * locks.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: locks.h,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef LOCKS_H +#define LOCKS_H + +#include "rewrite/prs2lock.h" + +extern List *matchLocks(CmdType event, RuleLock *rulelocks, int varno, + Query *parsetree); + +#endif /* LOCKS_H */ diff --git a/src/backend/rewrite/prs2lock.h b/src/backend/rewrite/prs2lock.h new file mode 100644 index 00000000000..6385158f5e0 --- /dev/null +++ b/src/backend/rewrite/prs2lock.h @@ -0,0 +1,43 @@ +/*------------------------------------------------------------------------- + * + * prs2lock.h-- + * data structures for POSTGRES Rule System II (rewrite rules only) + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: prs2lock.h,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PRS2LOCK_H +#define PRS2LOCK_H + +#include "access/attnum.h" +#include "nodes/pg_list.h" + +/* + * RewriteRule - + * holds a info for a rewrite rule + * + */ +typedef struct RewriteRule { + Oid ruleId; + CmdType event; + AttrNumber attrno; + Node *qual; + List *actions; + bool isInstead; +} RewriteRule; + +/* + * RuleLock - + * all rules that apply to a particular relation. Even though we only + * have the rewrite rule system left and these are not really "locks", + * the name is kept for historical reasons. + */ +typedef struct RuleLock { + int numLocks; + RewriteRule **rules; +} RuleLock; + +#endif /* REWRITE_H */ diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c new file mode 100644 index 00000000000..36026a53585 --- /dev/null +++ b/src/backend/rewrite/rewriteDefine.c @@ -0,0 +1,255 @@ +/*------------------------------------------------------------------------- + * + * rewriteDefine.c-- + * routines for defining a rewrite rule + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <string.h> +#include "postgres.h" + +#include "utils/rel.h" /* for Relation stuff */ +#include "access/heapam.h" /* access methods like amopenr */ +#include "utils/builtins.h" +#include "utils/elog.h" /* for elog */ +#include "utils/palloc.h" +#include "utils/lsyscache.h" /* for get_typlen */ +#include "nodes/pg_list.h" /* for Lisp support */ +#include "nodes/parsenodes.h" +#include "parser/catalog_utils.h" +#include "rewrite/locks.h" +#include "rewrite/rewriteRemove.h" +#include "rewrite/rewriteSupport.h" +#include "tcop/tcopprot.h" + +Oid LastOidProcessed = InvalidOid; + +/* + * This is too small for many rule plans, but it'll have to do for now. + * Rule plans, etc will eventually have to be large objects. + * + * should this be smaller? + */ +#define RULE_PLAN_SIZE 8192 + +static void +strcpyq(char *dest, char *source) +{ + char *current=source,*destp= dest; + + for(current=source; *current; current++) { + if (*current == '\"') { + *destp = '\\'; + destp++; + } + *destp = *current; + destp++; + } + *destp = '\0'; +} + +/* + * InsertRule - + * takes the arguments and inserts them as attributes into the system + * relation "pg_rewrite" + * + * MODS : changes the value of LastOidProcessed as a side + * effect of inserting the rule tuple + * + * ARGS : rulname - name of the rule + * evtype - one of RETRIEVE,REPLACE,DELETE,APPEND + * evobj - name of relation + * evslot - comma delimited list of slots + * if null => multi-attr rule + * evinstead - is an instead rule + * actiontree - parsetree(s) of rule action + */ +static Oid +InsertRule(char *rulname, + int evtype, + char *evobj, + char *evslot, + char *evqual, + bool evinstead, + char *actiontree) +{ + static char rulebuf[RULE_PLAN_SIZE]; + static char actionbuf[RULE_PLAN_SIZE]; + static char qualbuf[RULE_PLAN_SIZE]; + Oid eventrel_oid = InvalidOid; + AttrNumber evslot_index = InvalidAttrNumber; + Relation eventrel = NULL; + char *is_instead = "f"; + extern void eval_as_new_xact(); + char *template; + + eventrel = heap_openr(evobj); + if (eventrel == NULL) { + elog(WARN, "rules cannot be defined on relations not in schema"); + } + eventrel_oid = RelationGetRelationId(eventrel); + + /* + * if the slotname is null, we know that this is a multi-attr + * rule + */ + if (evslot == NULL) + evslot_index = -1; + else + evslot_index = varattno(eventrel, (char*)evslot); + heap_close(eventrel); + + if (evinstead) + is_instead = "t"; + + if (evqual == NULL) + evqual = "nil"; + + if (IsDefinedRewriteRule(rulname)) + elog(WARN, "Attempt to insert rule '%s' failed: already exists", + rulname); + strcpyq(actionbuf,actiontree); + strcpyq(qualbuf, evqual); + + template = "INSERT INTO pg_rewrite \ +(rulename, ev_type, ev_class, ev_attr, action, ev_qual, is_instead) VALUES \ +('%s', %d::char, %d::oid, %d::int2, '%s'::text, '%s'::text, \ + '%s'::bool);"; + if (strlen(template) + strlen(rulname) + strlen(actionbuf) + + strlen(qualbuf) + 20 /* fudge fac */ > RULE_PLAN_SIZE) { + elog(WARN, "DefineQueryRewrite: rule plan string too big."); + } + sprintf(rulebuf, template, + rulname, evtype, eventrel_oid, evslot_index, actionbuf, + qualbuf, is_instead); + + pg_eval(rulebuf, (char **) NULL, (Oid *) NULL, 0); + + return (LastOidProcessed); +} + +/* + * for now, event_object must be a single attribute + */ +static void +ValidateRule(int event_type, + char *eobj_string, + char *eslot_string, + Node *event_qual, + List **action, + int is_instead, + Oid event_attype) +{ + if (((event_type == CMD_INSERT) || (event_type == CMD_DELETE)) && + eslot_string) { + elog(WARN, + "rules not allowed for insert or delete events to an attribute"); + } + + if (event_qual && !*action && is_instead) + elog(WARN, + "event_quals on 'instead nothing' rules not currently supported"); + +#if 0 + /* on retrieve to class.attribute do instead nothing is converted + * to 'on retrieve to class.attribute do instead + * retrieve (attribute = NULL)' + * --- this is also a terrible hack that works well -- glass*/ + if (is_instead && !*action && eslot_string && event_type == CMD_SELECT) { + char *temp_buffer = (char *) palloc(strlen(template)+80); + sprintf(temp_buffer, template, event_attype, + get_typlen(event_attype), eslot_string, + event_attype); + + *action = (List*) stringToNode(temp_buffer); + + pfree(temp_buffer); + } +#endif +} + +void +DefineQueryRewrite(RuleStmt *stmt) +{ + CmdType event_type = stmt->event; + Attr *event_obj = stmt->object; + Node *event_qual = stmt->whereClause; + bool is_instead = stmt->instead; + List *action = stmt->actions; + Relation event_relation = NULL ; + Oid ruleId; + Oid ev_relid = 0; + char *eslot_string = NULL; + int event_attno = 0; + Oid event_attype = 0; + char *actionP, *event_qualP; + + extern Oid att_typeid(); + + if (event_obj->attrs) + eslot_string = strVal(lfirst(event_obj->attrs)); + else + eslot_string = NULL; + + event_relation = heap_openr(event_obj->relname); + if ( event_relation == NULL ) { + elog(WARN, "virtual relations not supported yet"); + } + ev_relid = RelationGetRelationId(event_relation); + + if (eslot_string == NULL) { + event_attno = -1; + event_attype = -1; /* XXX - don't care */ + } else { + event_attno = varattno(event_relation, eslot_string); + event_attype = att_typeid(event_relation,event_attno); + } + heap_close(event_relation); + + /* fix bug about instead nothing */ + ValidateRule(event_type, event_obj->relname, + eslot_string, event_qual, &action, + is_instead,event_attype); + + if (action == NULL) { + if (!is_instead) return; /* doesn't do anything */ + + event_qualP = nodeToString(event_qual); + + ruleId = InsertRule(stmt->rulename, + event_type, + event_obj->relname, + eslot_string, + event_qualP, + true, + "nil"); + prs2_addToRelation(ev_relid, ruleId, event_type, event_attno, TRUE, + event_qual, NIL); + + } else { + event_qualP = nodeToString(event_qual); + actionP = nodeToString(action); + + ruleId = InsertRule(stmt->rulename, + event_type, + event_obj->relname, + eslot_string, + event_qualP, + is_instead, + actionP); + + /* what is the max size of type text? XXX -- glass */ + if (length(action) > 15 ) + elog(WARN,"max # of actions exceeded"); + prs2_addToRelation(ev_relid, ruleId, event_type, event_attno, + is_instead, event_qual, action); + } +} + diff --git a/src/backend/rewrite/rewriteDefine.h b/src/backend/rewrite/rewriteDefine.h new file mode 100644 index 00000000000..e4fa8048a99 --- /dev/null +++ b/src/backend/rewrite/rewriteDefine.h @@ -0,0 +1,18 @@ +/*------------------------------------------------------------------------- + * + * rewriteDefine.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rewriteDefine.h,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef REWRITEDEFINE_H +#define REWRITEDEFINE_H + +extern void DefineQueryRewrite(RuleStmt *args); + +#endif /* REWRITEDEFINE_H */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c new file mode 100644 index 00000000000..ee61d293deb --- /dev/null +++ b/src/backend/rewrite/rewriteHandler.c @@ -0,0 +1,622 @@ +/*------------------------------------------------------------------------- + * + * rewriteHandler.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "miscadmin.h" +#include "utils/palloc.h" +#include "utils/elog.h" +#include "utils/rel.h" +#include "nodes/pg_list.h" +#include "nodes/primnodes.h" + +#include "parser/parsetree.h" /* for parsetree manipulation */ +#include "nodes/parsenodes.h" + +#include "rewrite/rewriteSupport.h" +#include "rewrite/rewriteHandler.h" +#include "rewrite/rewriteManip.h" +#include "rewrite/locks.h" + +#include "commands/creatinh.h" +#include "access/heapam.h" + +static void ApplyRetrieveRule(Query *parsetree, RewriteRule *rule, + int rt_index, int relation_level, int *modified); +static List *fireRules(Query *parsetree, int rt_index, CmdType event, + bool *instead_flag, List *locks, List **qual_products); +static List *deepRewriteQuery(Query *parsetree); + +/* + * gatherRewriteMeta - + * Gather meta information about parsetree, and rule. Fix rule body + * and qualifier so that they can be mixed with the parsetree and + * maintain semantic validity + */ +static RewriteInfo * +gatherRewriteMeta(Query *parsetree, + Query *rule_action, + Node *rule_qual, + int rt_index, + CmdType event, + bool *instead_flag) +{ + RewriteInfo *info; + int rt_length; + int result_reln; + + info = (RewriteInfo *) palloc(sizeof(RewriteInfo)); + info->rt_index = rt_index; + info->event = event; + info->instead_flag = *instead_flag; +/* info->rule_action = rule_action; this needs to be a copy here, I think! - jolly*/ + info->rule_action = (Query*)copyObject(rule_action); + info->rule_qual = (Node*)copyObject(rule_qual); + info->nothing = FALSE; + info->action = info->rule_action->commandType; + if (info->rule_action == NULL) info->nothing = TRUE; + if (info->nothing) + return info; + + info->current_varno = rt_index; + info->rt = parsetree->rtable; + rt_length = length(info->rt); + info->rt = append(info->rt, info->rule_action->rtable); + + + info->new_varno = PRS2_NEW_VARNO + rt_length; + OffsetVarNodes(info->rule_action->qual, rt_length); + OffsetVarNodes((Node*)info->rule_action->targetList, rt_length); + OffsetVarNodes(info->rule_qual, rt_length); + ChangeVarNodes((Node*)info->rule_action->qual, + PRS2_CURRENT_VARNO+rt_length, rt_index); + ChangeVarNodes((Node*)info->rule_action->targetList, + PRS2_CURRENT_VARNO+rt_length, rt_index); + ChangeVarNodes(info->rule_qual, PRS2_CURRENT_VARNO+rt_length, rt_index); + + /* + * bug here about replace CURRENT -- sort of + * replace current is deprecated now so this code shouldn't really + * need to be so clutzy but..... + */ + if (info->action != CMD_SELECT) { /* i.e update XXXXX */ + int new_result_reln = 0; + result_reln = info->rule_action->resultRelation; + switch (result_reln) { + case PRS2_CURRENT_VARNO: new_result_reln = rt_index; + break; + case PRS2_NEW_VARNO: /* XXX */ + default: + new_result_reln = result_reln + rt_length; + break; + } + info->rule_action->resultRelation = new_result_reln; + } + + return info; +} + +static List * +OptimizeRIRRules(List *locks) +{ + List *attr_level = NIL, *i; + List *relation_level = NIL; + + foreach (i, locks) { + RewriteRule *rule_lock = lfirst(i); + + if (rule_lock->attrno == -1) + relation_level = lappend(relation_level, rule_lock); + else + attr_level = lappend(attr_level, rule_lock); + } + return nconc(relation_level, attr_level); +} + +/* + * idea is to put instead rules before regular rules so that + * excess semantically queasy queries aren't processed + */ +static List * +orderRules(List *locks) +{ + List *regular = NIL, *i; + List *instead_rules = NIL; + + foreach (i, locks) { + RewriteRule *rule_lock = (RewriteRule *)lfirst(i); + + if (rule_lock->isInstead) + instead_rules = lappend(instead_rules, rule_lock); + else + regular = lappend(regular, rule_lock); + } + return nconc(regular, instead_rules); +} + +static int +AllRetrieve(List *actions) +{ + List *n; + + foreach(n, actions) { + Query *pt = lfirst(n); + + /* + * in the old postgres code, we check whether command_type is + * a consp of '('*'.commandType). but we've never supported transitive + * closures. Hence removed - ay 10/94. + */ + if (pt->commandType != CMD_SELECT) + return false; + } + return true; +} + +static List * +FireRetrieveRulesAtQuery(Query *parsetree, + int rt_index, + Relation relation, + bool *instead_flag, + int rule_flag) +{ + List *i, *locks; + RuleLock *rt_entry_locks = NULL; + List *work = NIL; + + if ((rt_entry_locks = relation->rd_rules) == NULL) + return NIL; + + locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree); + + /* find all retrieve instead */ + foreach (i, locks) { + RewriteRule *rule_lock = (RewriteRule *)lfirst(i); + + if (!rule_lock->isInstead) + continue; + work = lappend(work, rule_lock); + } + if (work != NIL) { + work = OptimizeRIRRules(locks); + foreach (i, work) { + RewriteRule *rule_lock = lfirst(i); + int relation_level; + int modified = FALSE; + + relation_level = (rule_lock->attrno == -1); + if (rule_lock->actions == NIL) { + *instead_flag = TRUE; + return NIL; + } + if (!rule_flag && + length(rule_lock->actions) >= 2 && + AllRetrieve(rule_lock->actions)) { + *instead_flag = TRUE; + return rule_lock->actions; + } + ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level, + &modified); + if (modified) { + *instead_flag = TRUE; + FixResdomTypes(parsetree->targetList); + return lcons(parsetree,NIL); + } + } + } + return NIL; +} + + +/* Idea is like this: + * + * retrieve-instead-retrieve rules have different semantics than update nodes + * Separate RIR rules from others. Pass others to FireRules. + * Order RIR rules and process. + * + * side effect: parsetree's rtable field might be changed + */ +static void +ApplyRetrieveRule(Query *parsetree, + RewriteRule *rule, + int rt_index, + int relation_level, + int *modified) +{ + Query *rule_action = NULL; + Node *rule_qual; + List *rtable, *rt; + int nothing, rt_length; + int badsql= FALSE; + + rule_qual = rule->qual; + if (rule->actions) { + if (length(rule->actions) > 1) /* ??? because we don't handle rules + with more than one action? -ay */ + return; + rule_action = copyObject(lfirst(rule->actions)); + nothing = FALSE; + } else { + nothing = TRUE; + } + + rtable = copyObject(parsetree->rtable); + foreach (rt, rtable) { + RangeTblEntry *rte = lfirst(rt); + /* + * this is to prevent add_missing_vars_to_base_rels() from + * adding a bogus entry to the new target list. + */ + rte->inFromCl = false; + } + rt_length = length(rtable); + rtable = nconc(rtable, copyObject(rule_action->rtable)); + parsetree->rtable = rtable; + + rule_action->rtable = rtable; + OffsetVarNodes(rule_action->qual, rt_length); + OffsetVarNodes((Node*)rule_action->targetList, rt_length); + OffsetVarNodes(rule_qual, rt_length); + ChangeVarNodes(rule_action->qual, + PRS2_CURRENT_VARNO+rt_length, rt_index); + ChangeVarNodes((Node*)rule_action->targetList, + PRS2_CURRENT_VARNO+rt_length, rt_index); + ChangeVarNodes(rule_qual, PRS2_CURRENT_VARNO+rt_length, rt_index); + if (relation_level) { + HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index, + modified); + } else { + HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList, + rt_index, rule->attrno, modified, &badsql); + } + if (*modified && !badsql) + AddQual(parsetree, rule_action->qual); +} + +static List * +ProcessRetrieveQuery(Query *parsetree, + List *rtable, + bool *instead_flag, + bool rule) +{ + List *rt; + List *product_queries = NIL; + int rt_index = 0; + + foreach (rt, rtable) { + RangeTblEntry *rt_entry = lfirst(rt); + Relation rt_entry_relation = NULL; + List *result = NIL; + + rt_index++; + rt_entry_relation = heap_openr(rt_entry->relname); + + if (rt_entry_relation->rd_rules != NULL) { + result = + FireRetrieveRulesAtQuery(parsetree, + rt_index, + rt_entry_relation, + instead_flag, + rule); + } + heap_close(rt_entry_relation); + if (*instead_flag) + return result; + } + if (rule) + return NIL; + + foreach (rt, rtable) { + RangeTblEntry *rt_entry = lfirst(rt); + Relation rt_entry_relation = NULL; + RuleLock *rt_entry_locks = NULL; + List *result = NIL; + List *locks = NIL; + List *dummy_products; + + rt_index++; + rt_entry_relation = heap_openr(rt_entry->relname); + rt_entry_locks = rt_entry_relation->rd_rules; + heap_close(rt_entry_relation); + + if (rt_entry_locks) { + locks = + matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree); + } + if (locks != NIL) { + result = fireRules(parsetree, rt_index, CMD_SELECT, + instead_flag, locks, &dummy_products); + if (*instead_flag) + return lappend(NIL, result); + if (result != NIL) + product_queries = nconc(product_queries, result); + } + } + return product_queries; +} + +static Query * +CopyAndAddQual(Query *parsetree, + List *actions, + Node *rule_qual, + int rt_index, + CmdType event) +{ + Query *new_tree = (Query *) copyObject(parsetree); + Node *new_qual = NULL; + Query *rule_action = NULL; + + if (actions) + rule_action = lfirst(actions); + if (rule_qual != NULL) + new_qual = (Node *)copyObject(rule_qual); + if (rule_action != NULL) { + List *rtable; + int rt_length; + + rtable = new_tree->rtable; + rt_length = length(rtable); + rtable = append(rtable,listCopy(rule_action->rtable)); + new_tree->rtable = rtable; + OffsetVarNodes(new_qual, rt_length); + ChangeVarNodes(new_qual, PRS2_CURRENT_VARNO+rt_length, rt_index); + } + /* XXX -- where current doesn't work for instead nothing.... yet*/ + AddNotQual(new_tree, new_qual); + + return new_tree; +} + + +/* + * fireRules - + * Iterate through rule locks applying rules. After an instead rule + * rule has been applied, return just new parsetree and let RewriteQuery + * start the process all over again. The locks are reordered to maintain + * sensible semantics. remember: reality is for dead birds -- glass + * + */ +static List * +fireRules(Query *parsetree, + int rt_index, + CmdType event, + bool *instead_flag, + List *locks, + List **qual_products) +{ + RewriteInfo *info; + List *results = NIL; + List *i; + + /* choose rule to fire from list of rules */ + if (locks == NIL) { + (void) ProcessRetrieveQuery(parsetree, + parsetree->rtable, + instead_flag, TRUE); + if (*instead_flag) + return lappend(NIL, parsetree); + else + return NIL; + } + + locks = orderRules(locks); /* instead rules first */ + foreach (i, locks) { + RewriteRule *rule_lock = (RewriteRule *)lfirst(i); + Node *qual, *event_qual; + List *actions; + List *r; + bool orig_instead_flag = *instead_flag; + + /* multiple rule action time */ + *instead_flag = rule_lock->isInstead; + event_qual = rule_lock->qual; + actions = rule_lock->actions; + if (event_qual != NULL && *instead_flag) + *qual_products = + lappend(*qual_products, + CopyAndAddQual(parsetree, actions, event_qual, + rt_index, event)); + foreach (r, actions) { + Query *rule_action = lfirst(r); + Node *rule_qual = copyObject(event_qual); + + /*-------------------------------------------------- + * Step 1: + * Rewrite current.attribute or current to tuple variable + * this appears to be done in parser? + *-------------------------------------------------- + */ + info = gatherRewriteMeta(parsetree, rule_action, rule_qual, + rt_index,event,instead_flag); + + /* handle escapable cases, or those handled by other code */ + if (info->nothing) { + if (*instead_flag) + return NIL; + else + continue; + } + + if (info->action == info->event && + info->event == CMD_SELECT) + continue; + + /* + * Event Qualification forces copying of parsetree --- XXX + * and splitting into two queries one w/rule_qual, one + * w/NOT rule_qual. Also add user query qual onto rule action + */ + qual = parsetree->qual; + AddQual(info->rule_action, qual); + + if (info->rule_qual != NULL) + AddQual(info->rule_action, info->rule_qual); + + /*-------------------------------------------------- + * Step 2: + * Rewrite new.attribute w/ right hand side of target-list + * entry for appropriate field name in insert/update + *-------------------------------------------------- + */ + if ((info->event == CMD_INSERT) || (info->event == CMD_UPDATE)) { + FixNew(info, parsetree); + } + + /*-------------------------------------------------- + * Step 3: + * rewriting due to retrieve rules + *-------------------------------------------------- + */ + info->rule_action->rtable = info->rt; + (void) ProcessRetrieveQuery(info->rule_action, info->rt, + &orig_instead_flag, TRUE); + + /*-------------------------------------------------- + * Step 4 + * Simplify? hey, no algorithm for simplification... let + * the planner do it. + *-------------------------------------------------- + */ + results = lappend(results, info->rule_action); + + pfree(info); + } + if (*instead_flag) break; + } + return results; +} + +static List * +RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products) +{ + CmdType event; + List *product_queries = NIL; + int result_relation = 0; + + Assert(parsetree != NULL); + + event = parsetree->commandType; + + if (event == CMD_UTILITY) + return NIL; + + /* + * only for a delete may the targetlist be NULL + */ + if (event != CMD_DELETE) { + Assert(parsetree->targetList != NULL); + } + + result_relation = parsetree->resultRelation; + + if (event != CMD_SELECT) { + /* + * the statement is an update, insert or delete + */ + RangeTblEntry *rt_entry; + Relation rt_entry_relation = NULL; + RuleLock *rt_entry_locks = NULL; + + rt_entry = rt_fetch(result_relation, parsetree->rtable); + rt_entry_relation = heap_openr(rt_entry->relname); + rt_entry_locks = rt_entry_relation->rd_rules; + heap_close(rt_entry_relation); + + if (rt_entry_locks != NULL) { + List *locks = + matchLocks(event, rt_entry_locks, result_relation, parsetree); + + product_queries = + fireRules(parsetree, + result_relation, + event, + instead_flag, + locks, + qual_products); + } + return product_queries; + }else { + /* + * the statement is a select + */ + Query *other; + + other = copyObject(parsetree); /* ApplyRetrieveRule changes the + range table */ + return + ProcessRetrieveQuery(other, parsetree->rtable, + instead_flag, FALSE); + } +} + +/* + * to avoid infinite recursion, we restrict the number of times a query + * can be rewritten. Detecting cycles is left for the reader as an excercise. + */ +#ifndef REWRITE_INVOKE_MAX +#define REWRITE_INVOKE_MAX 10 +#endif + +static int numQueryRewriteInvoked = 0; + +/* + * QueryRewrite - + * rewrite one query via QueryRewrite system, possibly returning 0, or many + * queries + */ +List * +QueryRewrite(Query *parsetree) +{ + numQueryRewriteInvoked = 0; + + /* + * take a deep breath and apply all the rewrite rules - ay + */ + return deepRewriteQuery(parsetree); +} + +/* + * deepRewriteQuery - + * rewrites the query and apply the rules again on the queries rewritten + */ +static List * +deepRewriteQuery(Query *parsetree) +{ + List *n; + List *rewritten = NIL; + List *result = NIL; + bool instead; + List *qual_products = NIL; + + if (++numQueryRewriteInvoked > REWRITE_INVOKE_MAX) { + elog(WARN, "query rewritten %d times, may contain cycles", + numQueryRewriteInvoked-1); + } + + instead = FALSE; + result = RewriteQuery(parsetree, &instead, &qual_products); + if (!instead) + rewritten = lcons(parsetree, NIL); + + foreach(n, result) { + Query *pt = lfirst(n); + List *newstuff = NIL; + + newstuff = deepRewriteQuery(pt); + if (newstuff != NIL) + rewritten = nconc(rewritten, newstuff); + } + if (qual_products != NIL) + rewritten = nconc(rewritten, qual_products); + + return rewritten; +} + diff --git a/src/backend/rewrite/rewriteHandler.h b/src/backend/rewrite/rewriteHandler.h new file mode 100644 index 00000000000..a76360479e4 --- /dev/null +++ b/src/backend/rewrite/rewriteHandler.h @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------- + * + * rewriteHandler.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rewriteHandler.h,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef REWRITEHANDLER_H +#define REWRITEHANDLER_H + + +struct _rewrite_meta_knowledge { + List *rt; + int rt_index; + bool instead_flag; + int event; + CmdType action; + int current_varno; + int new_varno; + Query *rule_action; + Node *rule_qual; + bool nothing; +}; + +typedef struct _rewrite_meta_knowledge RewriteInfo; + + +extern List *QueryRewrite(Query *parsetree); + +#endif /*REWRITEHANDLER_H */ diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c new file mode 100644 index 00000000000..d5b6934649d --- /dev/null +++ b/src/backend/rewrite/rewriteManip.c @@ -0,0 +1,435 @@ +/*------------------------------------------------------------------------- + * + * rewriteManip.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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/rewriteSupport.h" +#include "rewrite/locks.h" + +#include "nodes/plannodes.h" +#include "optimizer/clauses.h" + +static void ResolveNew(RewriteInfo *info, List *targetlist, Node **node); + + + +void +OffsetVarNodes(Node *node, int offset) +{ + if (node==NULL) + return; + switch (nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + OffsetVarNodes(tle->expr, offset); + } + break; + case T_Expr: + { + Expr *expr = (Expr*)node; + OffsetVarNodes((Node*)expr->args, offset); + } + break; + case T_Var: + { + Var *var = (Var*)node; + var->varno += offset; + var->varnoold += offset; + } + break; + case T_List: + { + List *l; + + foreach(l, (List*)node) { + OffsetVarNodes(lfirst(l), offset); + } + } + break; + default: + /* ignore the others */ + break; + } +} + +void +ChangeVarNodes(Node *node, int old_varno, int new_varno) +{ + if (node==NULL) + return; + switch (nodeTag(node)) { + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + ChangeVarNodes(tle->expr, old_varno, new_varno); + } + break; + case T_Expr: + { + Expr *expr = (Expr*)node; + ChangeVarNodes((Node*)expr->args, old_varno, new_varno); + } + break; + case T_Var: + { + Var *var = (Var*)node; + if (var->varno == old_varno) { + var->varno = new_varno; + var->varnoold = new_varno; + } + } + break; + case T_List: + { + List *l; + foreach (l, (List*)node) { + ChangeVarNodes(lfirst(l), old_varno, new_varno); + } + } + break; + default: + /* ignore the others */ + 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)); +} + +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; +} + +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->reslen = get_typlen(var->vartype); + } + } +} + +static Node * +FindMatchingNew(List *tlist, int attno) +{ + List *i; + + foreach (i, tlist ) { + TargetEntry *tle = lfirst(i); + + if (tle->resdom->resno == attno ) { + 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)) + return (tle->expr); + } + return NULL; +} + +static void +ResolveNew(RewriteInfo *info, List *targetlist, Node **nodePtr) +{ + Node *node = *nodePtr; + + if (node == NULL) + return; + + switch(nodeTag(node)) { + case T_TargetEntry: + ResolveNew(info, targetlist, &((TargetEntry*)node)->expr); + break; + case T_Expr: + ResolveNew(info, targetlist, (Node**)(&(((Expr*)node)->args))); + break; + case T_Var: { + int this_varno = (int)((Var*)node)->varno; + Node *n; + + if (this_varno == info->new_varno) { + n = FindMatchingNew(targetlist, + ((Var*)node)->varattno); + if (n == NULL) { + if (info->event == CMD_UPDATE) { + ((Var*)node)->varno = info->current_varno; + ((Var*)node)->varnoold = info->current_varno; + } else { + *nodePtr = make_null(((Var*)node)->vartype); + } + } else { + *nodePtr = n; + } + } + break; + } + case T_List: { + List *l; + foreach(l, (List*)node) { + ResolveNew(info, targetlist, (Node**)&(lfirst(l))); + } + break; + } + default: + /* ignore the others */ + break; + } +} + +void +FixNew(RewriteInfo* info, Query *parsetree) +{ + ResolveNew(info, parsetree->targetList, + (Node**)&(info->rule_action->targetList)); + ResolveNew(info, parsetree->targetList, &info->rule_action->qual); +} + +static void +nodeHandleRIRAttributeRule(Node **nodePtr, + List *rtable, + List *targetlist, + int rt_index, + int attr_num, + int *modified, + int *badsql) +{ + Node *node = *nodePtr; + + if (node == NULL) + return; + switch (nodeTag(node)) { + case T_List: + { + List *i; + foreach(i, (List*)node) { + nodeHandleRIRAttributeRule((Node**)(&(lfirst(i))), rtable, + targetlist, rt_index, attr_num, + modified, badsql); + } + } + break; + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + nodeHandleRIRAttributeRule(&tle->expr, rtable, targetlist, + rt_index, attr_num, modified, badsql); + } + break; + case T_Expr: + { + Expr *expr = (Expr *)node; + nodeHandleRIRAttributeRule((Node**)(&(expr->args)), rtable, + targetlist, rt_index, attr_num, + modified, badsql); + } + break; + case T_Var: + { + int this_varno = (int) ((Var*)node)->varno; + NameData name_to_look_for; + memset(name_to_look_for.data, 0, NAMEDATALEN); + + if (this_varno == rt_index && + ((Var*) node)->varattno == attr_num) { + if (((Var*)node)->vartype == 32) { /* HACK */ + *nodePtr = make_null(((Var*)node)->vartype); + *modified = TRUE; + *badsql = TRUE; + break; + } else { + namestrcpy(&name_to_look_for, + (char *)get_attname(getrelid(this_varno, + rtable), + attr_num)); + } + } + if (name_to_look_for.data[0]) { + Node *n; + + n = FindMatchingTLEntry(targetlist, &name_to_look_for); + if (n == NULL) { + *nodePtr = make_null(((Var*) node)->vartype); + } else { + *nodePtr = n; + } + *modified = TRUE; + } + } + 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); + nodeHandleRIRAttributeRule(&parsetree->qual, rtable, targetlist, + rt_index, attr_num, modified, badsql); +} + + +static void +nodeHandleViewRule(Node **nodePtr, + List *rtable, + List *targetlist, + int rt_index, + int *modified) +{ + Node *node = *nodePtr; + + if (node == NULL) + return; + + switch (nodeTag(node)) { + case T_List: + { + List *l; + foreach (l, (List*)node) { + nodeHandleViewRule((Node**) (&(lfirst(l))), + rtable, targetlist, + rt_index, modified); + } + } + break; + case T_TargetEntry: + { + TargetEntry *tle = (TargetEntry *)node; + nodeHandleViewRule(&(tle->expr), rtable, targetlist, + rt_index, modified); + } + break; + case T_Expr: + { + Expr *expr = (Expr*)node; + nodeHandleViewRule((Node**)(&(expr->args)), + rtable, targetlist, + rt_index, modified); + } + break; + case T_Var: + { + Var *var = (Var*)node; + int this_varno = var->varno; + Node *n; + + if (this_varno == rt_index) { + n = FindMatchingTLEntry(targetlist, + get_attname(getrelid(this_varno, + rtable), + var->varattno)); + if (n == NULL) { + *nodePtr = make_null(((Var*) node)->vartype); + } else { + *nodePtr = n; + } + *modified = TRUE; + } + break; + } + default: + /* ignore the others */ + break; + } +} + +void +HandleViewRule(Query *parsetree, + List *rtable, + List *targetlist, + int rt_index, + int *modified) +{ + nodeHandleViewRule(&parsetree->qual, rtable, targetlist, rt_index, + modified); + nodeHandleViewRule((Node**)(&(parsetree->targetList)), rtable, targetlist, + rt_index, modified); +} + diff --git a/src/backend/rewrite/rewriteManip.h b/src/backend/rewrite/rewriteManip.h new file mode 100644 index 00000000000..9f5804fc3fd --- /dev/null +++ b/src/backend/rewrite/rewriteManip.h @@ -0,0 +1,31 @@ +/*------------------------------------------------------------------------- + * + * rewriteManip.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rewriteManip.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef REWRITEMANIP_H +#define REWRITEMANIP_H + +/* RewriteManip.c */ +void OffsetVarNodes(Node *node, int offset); +void ChangeVarNodes(Node *node, int old_varno, int new_varno); +void AddQual(Query *parsetree, Node *qual); +void AddNotQual(Query *parsetree, Node *qual); +void FixResdomTypes(List *user_tlist); +void FixNew(RewriteInfo *info, Query *parsetree); + +void HandleRIRAttributeRule(Query *parsetree, List *rtable, List *targetlist, + int rt_index, int attr_num, int *modified, + int *badpostquel); +void HandleViewRule(Query *parsetree, List *rtable, List *targetlist, + int rt_index, int *modified); + +#endif /* REWRITEMANIP_H */ + diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c new file mode 100644 index 00000000000..9146d137c13 --- /dev/null +++ b/src/backend/rewrite/rewriteRemove.c @@ -0,0 +1,181 @@ +/*------------------------------------------------------------------------- + * + * rewriteRemove.c-- + * routines for removing rewrite rules + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "access/skey.h" +#include "catalog/pg_rewrite.h" +#include "catalog/catname.h" /* for RewriteRelationName */ +#include "utils/syscache.h" +#include "utils/elog.h" /* for elog stuff */ +#include "utils/palloc.h" +#include "utils/tqual.h" /* 'NowTimeQual' defined here.. */ +#include "access/heapam.h" /* heap AM calls defined here */ +#include "fmgr.h" /* for CHAR_16_EQ */ + +#include "rewrite/rewriteRemove.h" /* where the decls go */ +#include "rewrite/rewriteSupport.h" + +/*----------------------------------------------------------------------- + * RewriteGetRuleEventRel + *----------------------------------------------------------------------- + */ +char* +RewriteGetRuleEventRel(char *rulename) +{ + HeapTuple htp; + Oid eventrel; + + htp = SearchSysCacheTuple(REWRITENAME, PointerGetDatum(rulename), + 0,0,0); + if (!HeapTupleIsValid(htp)) + elog(WARN, "RewriteGetRuleEventRel: rule \"%s\" not found", + rulename); + eventrel = ((Form_pg_rewrite) GETSTRUCT(htp))->ev_class; + htp = SearchSysCacheTuple(RELOID, PointerGetDatum(eventrel), + 0,0,0); + if (!HeapTupleIsValid(htp)) + elog(WARN, "RewriteGetRuleEventRel: class %d not found", + eventrel); + return ((Form_pg_class) GETSTRUCT(htp))->relname.data; +} + +/* ---------------------------------------------------------------- + * + * RemoveRewriteRule + * + * Delete a rule given its rulename. + * + * There are three steps. + * 1) Find the corresponding tuple in 'pg_rewrite' relation. + * Find the rule Id (i.e. the Oid of the tuple) and finally delete + * the tuple. + * 3) Delete the locks from the 'pg_class' relation. + * + * + * ---------------------------------------------------------------- + */ +void +RemoveRewriteRule(char *ruleName) +{ + Relation RewriteRelation = NULL; + HeapScanDesc scanDesc = NULL; + ScanKeyData scanKeyData; + HeapTuple tuple = NULL; + Oid ruleId = (Oid)0; + Oid eventRelationOid = (Oid)NULL; + Datum eventRelationOidDatum = (Datum)NULL; + Buffer buffer = (Buffer)NULL; + bool isNull = false; + + /* + * Open the pg_rewrite relation. + */ + RewriteRelation = heap_openr(RewriteRelationName); + + /* + * Scan the RuleRelation ('pg_rewrite') until we find a tuple + */ + ScanKeyEntryInitialize(&scanKeyData, 0, Anum_pg_rewrite_rulename, + F_CHAR16EQ, NameGetDatum(ruleName)); + scanDesc = heap_beginscan(RewriteRelation, + 0, NowTimeQual, 1, &scanKeyData); + + tuple = heap_getnext(scanDesc, 0, (Buffer *)NULL); + + /* + * complain if no rule with such name existed + */ + if (!HeapTupleIsValid(tuple)) { + heap_close(RewriteRelation); + elog(WARN, "No rule with name = '%s' was found.\n", ruleName); + } + + /* + * Store the OID of the rule (i.e. the tuple's OID) + * and the event relation's OID + */ + ruleId = tuple->t_oid; + eventRelationOidDatum = + PointerGetDatum(heap_getattr(tuple, + buffer, + Anum_pg_rewrite_ev_class, + RelationGetTupleDescriptor(RewriteRelation), + &isNull)); + if (isNull) { + /* XXX strange!!! */ + elog(WARN, "RemoveRewriteRule: null event target relation!"); + } + eventRelationOid = DatumGetObjectId(eventRelationOidDatum); + + /* + * Now delete the relation level locks from the updated relation. + * (Make sure we do this before we remove the rule from pg_rewrite. + * Otherwise, heap_openr on eventRelationOid which reads pg_rwrite + * for the rules will fail.) + */ + prs2_deleteFromRelation(eventRelationOid, ruleId); + + /* + * Now delete the tuple... + */ + heap_delete(RewriteRelation, &(tuple->t_ctid)); + heap_close(RewriteRelation); + heap_endscan(scanDesc); +} + +/* + * RelationRemoveRules - + * removes all rules associated with the relation when the relation is + * being removed. + */ +void +RelationRemoveRules(Oid relid) +{ + Relation RewriteRelation = NULL; + HeapScanDesc scanDesc = NULL; + ScanKeyData scanKeyData; + HeapTuple tuple = NULL; + + /* + * Open the pg_rewrite relation. + */ + RewriteRelation = heap_openr(RewriteRelationName); + + /* + * Scan the RuleRelation ('pg_rewrite') for all the tuples that + * has the same ev_class as relid (the relation to be removed). + */ + ScanKeyEntryInitialize(&scanKeyData, + 0, + Anum_pg_rewrite_ev_class, + F_OIDEQ, + ObjectIdGetDatum(relid)); + scanDesc = heap_beginscan(RewriteRelation, + 0, NowTimeQual, 1, &scanKeyData); + + for(;;) { + tuple = heap_getnext(scanDesc, 0, (Buffer *)NULL); + + if (!HeapTupleIsValid(tuple)) { + break; /* we're done */ + } + + /* + * delete the tuple... + */ + heap_delete(RewriteRelation, &(tuple->t_ctid)); + } + + heap_endscan(scanDesc); + heap_close(RewriteRelation); +} + diff --git a/src/backend/rewrite/rewriteRemove.h b/src/backend/rewrite/rewriteRemove.h new file mode 100644 index 00000000000..245b612ef41 --- /dev/null +++ b/src/backend/rewrite/rewriteRemove.h @@ -0,0 +1,20 @@ +/*------------------------------------------------------------------------- + * + * rewriteRemove.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rewriteRemove.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef REWRITEREMOVE_H +#define REWRITEREMOVE_H + +extern char *RewriteGetRuleEventRel(char *rulename); +extern void RemoveRewriteRule(char *ruleName); +extern void RelationRemoveRules(Oid relid); + +#endif /* REWRITEREMOVE_H */ diff --git a/src/backend/rewrite/rewriteSupport.c b/src/backend/rewrite/rewriteSupport.c new file mode 100644 index 00000000000..1a05fc87b03 --- /dev/null +++ b/src/backend/rewrite/rewriteSupport.c @@ -0,0 +1,270 @@ +/*------------------------------------------------------------------------- + * + * rewriteSupport.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "catalog/catname.h" +#include "catalog/pg_rewrite.h" +#include "utils/syscache.h" /* for SearchSysCache */ +#include "nodes/pg_list.h" +#include "nodes/parsenodes.h" +#include "utils/builtins.h" /* for textout */ +#include "utils/rel.h" /* for Relation, RelationData ... */ +#include "utils/elog.h" /* for elog */ +#include "storage/buf.h" /* for InvalidBuffer */ +#include "rewrite/rewriteSupport.h" +#include "access/heapam.h" +#include "catalog/pg_class.h" +#include "catalog/pg_proc.h" +#include "catalog/indexing.h" +#include "utils/catcache.h" /* for CacheContext */ +#include "utils/mcxt.h" /* MemoryContext stuff */ +#include "utils/palloc.h" +#include "fmgr.h" + +/* + * RuleIdGetActionInfo - + * given a rule oid, look it up and return the rule-event-qual and + * list of parsetrees for the rule (in parseTrees) + */ +static Node * +RuleIdGetActionInfo(Oid ruleoid, bool *instead_flag, Query **parseTrees) +{ + HeapTuple ruletuple; + char *ruleaction = NULL; + bool action_is_null = false; + bool instead_is_null = false; + Relation ruleRelation = NULL; + TupleDesc ruleTupdesc = NULL; + Query *ruleparse = NULL; + char *rule_evqual_string = NULL; + Node *rule_evqual = NULL; + + ruleRelation = heap_openr (RewriteRelationName); + ruleTupdesc = RelationGetTupleDescriptor(ruleRelation); + ruletuple = SearchSysCacheTuple (RULOID, + ObjectIdGetDatum(ruleoid), + 0,0,0); + if (ruletuple == NULL) + elog(WARN, "rule %d isn't in rewrite system relation"); + + ruleaction = heap_getattr(ruletuple, + InvalidBuffer, + Anum_pg_rewrite_action, + ruleTupdesc, + &action_is_null ) ; + rule_evqual_string = heap_getattr(ruletuple, InvalidBuffer, + Anum_pg_rewrite_ev_qual, + ruleTupdesc, &action_is_null) ; + *instead_flag = (bool) heap_getattr(ruletuple, InvalidBuffer, + Anum_pg_rewrite_is_instead, + ruleTupdesc, &instead_is_null) ; + + if (action_is_null || instead_is_null) { + elog(WARN, "internal error: rewrite rule not properly set up"); + } + + ruleaction = textout((struct varlena *)ruleaction); + rule_evqual_string = textout((struct varlena *)rule_evqual_string); + + ruleparse = (Query*)stringToNode(ruleaction); + rule_evqual = (Node*)stringToNode(rule_evqual_string); + + heap_close(ruleRelation); + + *parseTrees = ruleparse; + return rule_evqual; +} + +int +IsDefinedRewriteRule(char *ruleName) +{ + Relation RewriteRelation = NULL; + HeapScanDesc scanDesc = NULL; + ScanKeyData scanKey; + HeapTuple tuple = NULL; + + + /* + * Open the pg_rewrite relation. + */ + RewriteRelation = heap_openr(RewriteRelationName); + + /* + * Scan the RuleRelation ('pg_rewrite') until we find a tuple + */ + ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_rewrite_rulename, + NameEqualRegProcedure, PointerGetDatum(ruleName)); + scanDesc = heap_beginscan(RewriteRelation, + 0, NowTimeQual, 1, &scanKey); + + tuple = heap_getnext(scanDesc, 0, (Buffer *)NULL); + + /* + * return whether or not the rewrite rule existed + */ + heap_close(RewriteRelation); + heap_endscan(scanDesc); + return (HeapTupleIsValid(tuple)); +} + +static void +setRelhasrulesInRelation(Oid relationId, bool relhasrules) +{ + Relation relationRelation; + HeapTuple tuple; + HeapTuple newTuple; + Relation idescs[Num_pg_class_indices]; + Form_pg_class relp; + + /* + * Lock a relation given its Oid. + * Go to the RelationRelation (i.e. pg_relation), find the + * appropriate tuple, and add the specified lock to it. + */ + relationRelation = heap_openr(RelationRelationName); + tuple = ClassOidIndexScan(relationRelation, relationId); + + /* + * Create a new tuple (i.e. a copy of the old tuple + * with its rule lock field changed and replace the old + * tuple in the RelationRelation + * NOTE: XXX ??? do we really need to make that copy ???? + */ + newTuple = heap_copytuple(tuple); + + relp = (Form_pg_class) GETSTRUCT(newTuple); + relp->relhasrules = relhasrules; + + (void) heap_replace(relationRelation, &(tuple->t_ctid), newTuple); + + /* keep the catalog indices up to date */ + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, + newTuple); + CatalogCloseIndices(Num_pg_class_indices, idescs); + + /* be tidy */ + pfree(tuple); + pfree(newTuple); + + heap_close(relationRelation); +} + +void +prs2_addToRelation(Oid relid, + Oid ruleId, + CmdType event_type, + AttrNumber attno, + bool isInstead, + Node *qual, + List *actions) +{ + Relation relation; + RewriteRule *thisRule; + RuleLock *rulelock; + MemoryContext oldcxt; + + /* + * create an in memory RewriteRule data structure which is cached by + * every Relation descriptor. (see utils/cache/relcache.c) + */ + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + thisRule = (RewriteRule *)palloc(sizeof(RewriteRule)); + MemoryContextSwitchTo(oldcxt); + + thisRule->ruleId = ruleId; + thisRule->event = event_type; + thisRule->attrno = attno; + thisRule->qual = qual; + thisRule->actions = actions; + thisRule->isInstead = isInstead; + + relation = heap_open(relid); + + /* + * modify or create a RuleLock cached by Relation + */ + if (relation->rd_rules == NULL) { + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + rulelock = (RuleLock *)palloc(sizeof(RuleLock)); + rulelock->numLocks = 1; + rulelock->rules = (RewriteRule **)palloc(sizeof(RewriteRule*)); + rulelock->rules[0] = thisRule; + relation->rd_rules = rulelock; + MemoryContextSwitchTo(oldcxt); + + /* + * the fact that relation->rd_rules is NULL means the relhasrules + * attribute of the tuple of this relation in pg_class is false. We + * need to set it to true. + */ + setRelhasrulesInRelation(relid, TRUE); + } else { + int numlock; + + rulelock = relation->rd_rules; + numlock = rulelock->numLocks; + /* expand, for safety reasons */ + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + rulelock->rules = + (RewriteRule **)repalloc(rulelock->rules, + sizeof(RewriteRule*)*(numlock+1)); + MemoryContextSwitchTo(oldcxt); + rulelock->rules[numlock] = thisRule; + rulelock->numLocks++; + } + + heap_close(relation); + + return; +} + +void +prs2_deleteFromRelation(Oid relid, Oid ruleId) +{ + RuleLock *rulelock; + Relation relation; + int numlock; + int i; + MemoryContext oldcxt; + + relation = heap_open(relid); + rulelock = relation->rd_rules; + Assert(rulelock != NULL); + + numlock = rulelock->numLocks; + for(i=0; i < numlock; i++) { + if (rulelock->rules[i]->ruleId == ruleId) + break; + } + Assert(i<numlock); + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + pfree(rulelock->rules[i]); + MemoryContextSwitchTo(oldcxt); + if (numlock==1) { + relation->rd_rules = NULL; + /* + * we don't have rules any more, flag the relhasrules attribute of + * the tuple of this relation in pg_class false. + */ + setRelhasrulesInRelation(relid, FALSE); + } else { + rulelock->rules[i] = rulelock->rules[numlock-1]; + rulelock->rules[numlock-1] = NULL; + rulelock->numLocks--; + } + + heap_close(relation); +} + diff --git a/src/backend/rewrite/rewriteSupport.h b/src/backend/rewrite/rewriteSupport.h new file mode 100644 index 00000000000..5cc4b674f87 --- /dev/null +++ b/src/backend/rewrite/rewriteSupport.h @@ -0,0 +1,27 @@ +/*------------------------------------------------------------------------- + * + * rewriteSupport.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rewriteSupport.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef REWRITESUPPORT_H +#define REWRITESUPPORT_H + +#include "nodes/pg_list.h" + +extern int IsDefinedRewriteRule(char *ruleName); + +extern void prs2_addToRelation(Oid relid, Oid ruleId, CmdType event_type, + AttrNumber attno, bool isInstead, Node *qual, + List *actions); +extern void prs2_deleteFromRelation(Oid relid, Oid ruleId); + + +#endif /* REWRITESUPPORT_H */ + |