aboutsummaryrefslogtreecommitdiff
path: root/src/backend/rewrite
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/rewrite')
-rw-r--r--src/backend/rewrite/Makefile.inc22
-rw-r--r--src/backend/rewrite/locks.c131
-rw-r--r--src/backend/rewrite/locks.h21
-rw-r--r--src/backend/rewrite/prs2lock.h43
-rw-r--r--src/backend/rewrite/rewriteDefine.c255
-rw-r--r--src/backend/rewrite/rewriteDefine.h18
-rw-r--r--src/backend/rewrite/rewriteHandler.c622
-rw-r--r--src/backend/rewrite/rewriteHandler.h35
-rw-r--r--src/backend/rewrite/rewriteManip.c435
-rw-r--r--src/backend/rewrite/rewriteManip.h31
-rw-r--r--src/backend/rewrite/rewriteRemove.c181
-rw-r--r--src/backend/rewrite/rewriteRemove.h20
-rw-r--r--src/backend/rewrite/rewriteSupport.c270
-rw-r--r--src/backend/rewrite/rewriteSupport.h27
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 */
+