aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/access/index/indexam.c8
-rw-r--r--src/backend/rewrite/locks.c5
-rw-r--r--src/backend/rewrite/rewriteDefine.c79
-rw-r--r--src/backend/rewrite/rewriteHandler.c2623
-rw-r--r--src/backend/rewrite/rewriteManip.c109
-rw-r--r--src/backend/utils/adt/ruleutils.c532
-rw-r--r--src/bin/initdb/initdb.sh59
-rw-r--r--src/include/catalog/pg_proc.h6
-rw-r--r--src/include/rewrite/locks.h3
-rw-r--r--src/test/regress/expected/rules.out178
-rw-r--r--src/test/regress/sql/rules.sql97
11 files changed, 3101 insertions, 598 deletions
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index f695d71107e..aa2d9446504 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.27 1998/09/07 05:35:30 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.28 1998/10/02 16:27:43 momjian Exp $
*
* INTERFACE ROUTINES
* index_open - open an index relation by relationId
@@ -362,7 +362,7 @@ GetIndexValue(HeapTuple tuple,
bool *attNull)
{
Datum returnVal;
- bool isNull;
+ bool isNull = FALSE;
if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid)
{
@@ -375,13 +375,15 @@ GetIndexValue(HeapTuple tuple,
attrNums[i],
hTupDesc,
attNull);
+ if (*attNull)
+ isNull = TRUE;
}
returnVal = (Datum) fmgr_array_args(FIgetProcOid(fInfo),
FIgetnArgs(fInfo),
(char **) attData,
&isNull);
pfree(attData);
- *attNull = FALSE;
+ *attNull = isNull;
}
else
returnVal = heap_getattr(tuple, attrNums[attOff], hTupDesc, attNull);
diff --git a/src/backend/rewrite/locks.c b/src/backend/rewrite/locks.c
index f57a4364207..5c9b0887a1a 100644
--- a/src/backend/rewrite/locks.c
+++ b/src/backend/rewrite/locks.c
@@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.13 1998/09/01 04:31:30 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.14 1998/10/02 16:27:45 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -24,7 +24,6 @@
#include "utils/builtins.h"
#include "catalog/pg_shadow.h"
-static void checkLockPerms(List *locks, Query *parsetree, int rt_index);
/*
* ThisLockWasTriggered
@@ -170,7 +169,7 @@ matchLocks(CmdType event,
}
-static void
+void
checkLockPerms(List *locks, Query *parsetree, int rt_index)
{
Relation ev_rel;
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 20eff2fbb03..64d46efa408 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.21 1998/09/01 04:31:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.22 1998/10/02 16:27:46 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -199,11 +199,8 @@ DefineQueryRewrite(RuleStmt *stmt)
/* ----------
* The current rewrite handler is known to work on relation level
* rules only. And for SELECT events, it expects one non-nothing
- * action that is instead. Since we now hand out views and rules
- * to regular users, we must deny anything else.
- *
- * I know that I must write a new rewrite handler from scratch
- * for 6.5 so we can remove these checks and allow all the rules.
+ * action that is instead and returns exactly a tuple of the
+ * rewritten relation. This restricts SELECT rules to views.
*
* Jan
* ----------
@@ -217,6 +214,9 @@ DefineQueryRewrite(RuleStmt *stmt)
else
eslot_string = NULL;
+ /*
+ * No rule actions that modify OLD or NEW
+ */
if (action != NIL)
foreach(l, action)
{
@@ -233,23 +233,86 @@ DefineQueryRewrite(RuleStmt *stmt)
}
}
+ /*
+ * Rules ON SELECT are restricted to view definitions
+ */
if (event_type == CMD_SELECT)
{
+ TargetEntry *tle;
+ Resdom *resdom;
+ Form_pg_attribute attr;
+ char *attname;
+ int i;
+
+ /*
+ * So there cannot be INSTEAD NOTHING, ...
+ */
if (length(action) == 0)
{
elog(NOTICE, "instead nothing rules on select currently not supported");
elog(ERROR, " use views instead");
}
+
+ /*
+ * ... there cannot be multiple actions, ...
+ */
if (length(action) > 1)
elog(ERROR, "multiple action rules on select currently not supported");
+ /*
+ * ... the one action must be a SELECT, ...
+ */
query = (Query *) lfirst(action);
if (!is_instead || query->commandType != CMD_SELECT)
elog(ERROR, "only instead-select rules currently supported on select");
+ if (event_qual != NULL)
+ elog(ERROR, "event qualifications not supported for rules on select");
+
+ /*
+ * ... the targetlist of the SELECT action must
+ * exactly match the event relation ...
+ */
+ event_relation = heap_openr(event_obj->relname);
+ if (event_relation == NULL)
+ elog(ERROR, "virtual relations not supported yet");
+
+ if (event_relation->rd_att->natts != length(query->targetList))
+ elog(ERROR, "select rules target list must match event relations structure");
+
+ for (i = 1; i <= event_relation->rd_att->natts; i++) {
+ tle = (TargetEntry *)nth(i - 1, query->targetList);
+ resdom = tle->resdom;
+ attr = event_relation->rd_att->attrs[i - 1];
+ attname = nameout(&(attr->attname));
+
+ if (strcmp(resdom->resname, attname) != 0)
+ elog(ERROR, "select rules target entry %d has different column name from %s", i, attname);
+
+ if (attr->atttypid != resdom->restype)
+ elog(ERROR, "select rules target entry %d has different type from attribute %s", i, attname);
+
+ if (attr->atttypmod != resdom->restypmod)
+ elog(ERROR, "select rules target entry %d has different size from attribute %s", i, attname);
+ }
+
+ /*
+ * ... and final there must not be another ON SELECT
+ * rule already.
+ */
+ if (event_relation->rd_rules != NULL) {
+ for (i = 0; i < event_relation->rd_rules->numLocks; i++) {
+ RewriteRule *rule;
+
+ rule = event_relation->rd_rules->rules[i];
+ if (rule->event == CMD_SELECT)
+ elog(ERROR, "%s is already a view", nameout(&(event_relation->rd_rel->relname)));
+ }
+ }
+
+ heap_close(event_relation);
}
/*
- * This rule is currently allowed - too restricted I know - but women
- * and children first Jan
+ * This rule is allowed - install it.
*/
event_relation = heap_openr(event_obj->relname);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 7a4637e91e5..0bbeeb0c51b 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.21 1998/09/01 04:31:33 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.22 1998/10/02 16:27:47 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,6 +18,7 @@
#include "utils/rel.h"
#include "nodes/pg_list.h"
#include "nodes/primnodes.h"
+#include "nodes/relation.h"
#include "parser/parsetree.h" /* for parsetree manipulation */
#include "parser/parse_relation.h"
@@ -31,20 +32,44 @@
#include "commands/creatinh.h"
#include "access/heapam.h"
+#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/acl.h"
#include "catalog/pg_shadow.h"
+#include "catalog/pg_type.h"
+
+
+
+static RewriteInfo *gatherRewriteMeta(Query *parsetree,
+ Query *rule_action,
+ Node *rule_qual,
+ int rt_index,
+ CmdType event,
+ bool *instead_flag);
+static bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up);
+static bool attribute_used(Node *node, int rt_index, int attno, int sublevels_up);
+static void offset_varnodes(Node *node, int offset, int sublevels_up);
+static void change_varnodes(Node *node, int rt_index, int new_index, int sublevels_up);
+static void modifyAggregUplevel(Node *node);
+static void modifyAggregChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int sublevels_up);
+static void modifyAggregDropQual(Node **nodePtr, Node *orignode, Expr *expr);
+static SubLink *modifyAggregMakeSublink(Expr *origexp, Query *parsetree);
+static void modifyAggregQual(Node **nodePtr, Query *parsetree);
+
+
+
+
+
+
+
+
+
+
+
+
+
+static Query *fireRIRrules(Query *parsetree);
-static void ApplyRetrieveRule(Query *parsetree, RewriteRule *rule,
- int rt_index, int relation_level,
- Relation relation, int *modified);
-static List *fireRules(Query *parsetree, int rt_index, CmdType event,
- bool *instead_flag, List *locks, List **qual_products);
-static void QueryRewriteSubLink(Node *node);
-static List *QueryRewriteOne(Query *parsetree);
-static List *deepRewriteQuery(Query *parsetree);
-static void RewritePreprocessQuery(Query *parsetree);
-static Query *RewritePostprocessNonSelect(Query *parsetree);
/*
* gatherRewriteMeta -
@@ -118,146 +143,1753 @@ gatherRewriteMeta(Query *parsetree,
return info;
}
-static List *
-OptimizeRIRRules(List *locks)
+
+/*
+ * rangeTableEntry_used -
+ * we need to process a RTE for RIR rules only if it is
+ * referenced somewhere in var nodes of the query.
+ */
+static bool
+rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
{
- List *attr_level = NIL,
- *i;
- List *relation_level = NIL;
+ if (node == NULL)
+ return FALSE;
+
+ switch(nodeTag(node)) {
+ case T_TargetEntry:
+ {
+ TargetEntry *tle = (TargetEntry *)node;
+
+ return rangeTableEntry_used(
+ (Node *)(tle->expr),
+ rt_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_Aggreg:
+ {
+ Aggreg *agg = (Aggreg *)node;
+
+ return rangeTableEntry_used(
+ (Node *)(agg->target),
+ rt_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_GroupClause:
+ {
+ GroupClause *grp = (GroupClause *)node;
+
+ return rangeTableEntry_used(
+ (Node *)(grp->entry),
+ rt_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_Expr:
+ {
+ Expr *exp = (Expr *)node;
+
+ return rangeTableEntry_used(
+ (Node *)(exp->args),
+ rt_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_Iter:
+ {
+ Iter *iter = (Iter *)node;
+
+ return rangeTableEntry_used(
+ (Node *)(iter->iterexpr),
+ rt_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *)node;
+
+ if (rangeTableEntry_used(
+ (Node *)(ref->refupperindexpr),
+ rt_index,
+ sublevels_up))
+ return TRUE;
+
+ if (rangeTableEntry_used(
+ (Node *)(ref->reflowerindexpr),
+ rt_index,
+ sublevels_up))
+ return TRUE;
+
+ if (rangeTableEntry_used(
+ (Node *)(ref->refexpr),
+ rt_index,
+ sublevels_up))
+ return TRUE;
+
+ if (rangeTableEntry_used(
+ (Node *)(ref->refassgnexpr),
+ rt_index,
+ sublevels_up))
+ return TRUE;
+
+ return FALSE;
+ }
+ break;
+
+ case T_Var:
+ {
+ Var *var = (Var *)node;
+
+ if (var->varlevelsup == sublevels_up)
+ return var->varno == rt_index;
+ else
+ return FALSE;
+ }
+ break;
+
+ case T_Param:
+ return FALSE;
+
+ case T_Const:
+ return FALSE;
+
+ case T_List:
+ {
+ List *l;
+
+ foreach (l, (List *)node) {
+ if (rangeTableEntry_used(
+ (Node *)lfirst(l),
+ rt_index,
+ sublevels_up))
+ return TRUE;
+ }
+ return FALSE;
+ }
+ break;
+
+ case T_SubLink:
+ {
+ SubLink *sub = (SubLink *)node;
+
+ if (rangeTableEntry_used(
+ (Node *)(sub->lefthand),
+ rt_index,
+ sublevels_up))
+ return TRUE;
+
+ if (rangeTableEntry_used(
+ (Node *)(sub->subselect),
+ rt_index,
+ sublevels_up + 1))
+ return TRUE;
+
+ return FALSE;
+ }
+ break;
+
+ case T_Query:
+ {
+ Query *qry = (Query *)node;
+
+ if (rangeTableEntry_used(
+ (Node *)(qry->targetList),
+ rt_index,
+ sublevels_up))
+ return TRUE;
+
+ if (rangeTableEntry_used(
+ (Node *)(qry->qual),
+ rt_index,
+ sublevels_up))
+ return TRUE;
+
+ if (rangeTableEntry_used(
+ (Node *)(qry->havingQual),
+ rt_index,
+ sublevels_up))
+ return TRUE;
+
+ if (rangeTableEntry_used(
+ (Node *)(qry->groupClause),
+ rt_index,
+ sublevels_up))
+ return TRUE;
+
+ return FALSE;
+ }
+ break;
+
+ default:
+ elog(NOTICE, "unknown node tag %d in rangeTableEntry_used()", nodeTag(node));
+ elog(NOTICE, "Node is: %s", nodeToString(node));
+ break;
- 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);
+
+ return FALSE;
}
+
/*
- * idea is to fire regular rules first, then qualified instead
- * rules and unqualified instead rules last. Any lemming is counted for.
+ * attribute_used -
+ * Check if a specific attribute number of a RTE is used
+ * somewhere in the query
*/
-static List *
-orderRules(List *locks)
+static bool
+attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
{
- List *regular = NIL;
- List *instead_rules = NIL;
- List *instead_qualified = NIL;
- List *i;
+ if (node == NULL)
+ return FALSE;
+
+ switch(nodeTag(node)) {
+ case T_TargetEntry:
+ {
+ TargetEntry *tle = (TargetEntry *)node;
+
+ return attribute_used(
+ (Node *)(tle->expr),
+ rt_index,
+ attno,
+ sublevels_up);
+ }
+ break;
+
+ case T_Aggreg:
+ {
+ Aggreg *agg = (Aggreg *)node;
+
+ return attribute_used(
+ (Node *)(agg->target),
+ rt_index,
+ attno,
+ sublevels_up);
+ }
+ break;
+
+ case T_GroupClause:
+ {
+ GroupClause *grp = (GroupClause *)node;
+
+ return attribute_used(
+ (Node *)(grp->entry),
+ rt_index,
+ attno,
+ sublevels_up);
+ }
+ break;
+
+ case T_Expr:
+ {
+ Expr *exp = (Expr *)node;
+
+ return attribute_used(
+ (Node *)(exp->args),
+ rt_index,
+ attno,
+ sublevels_up);
+ }
+ break;
+
+ case T_Iter:
+ {
+ Iter *iter = (Iter *)node;
+
+ return attribute_used(
+ (Node *)(iter->iterexpr),
+ rt_index,
+ attno,
+ sublevels_up);
+ }
+ break;
+
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *)node;
+
+ if (attribute_used(
+ (Node *)(ref->refupperindexpr),
+ rt_index,
+ attno,
+ sublevels_up))
+ return TRUE;
+
+ if (attribute_used(
+ (Node *)(ref->reflowerindexpr),
+ rt_index,
+ attno,
+ sublevels_up))
+ return TRUE;
+
+ if (attribute_used(
+ (Node *)(ref->refexpr),
+ rt_index,
+ attno,
+ sublevels_up))
+ return TRUE;
+
+ if (attribute_used(
+ (Node *)(ref->refassgnexpr),
+ rt_index,
+ attno,
+ sublevels_up))
+ return TRUE;
+
+ return FALSE;
+ }
+ break;
+
+ case T_Var:
+ {
+ Var *var = (Var *)node;
+
+ if (var->varlevelsup == sublevels_up)
+ return var->varno == rt_index;
+ else
+ return FALSE;
+ }
+ break;
+
+ case T_Param:
+ return FALSE;
+
+ case T_Const:
+ return FALSE;
+
+ case T_List:
+ {
+ List *l;
+
+ foreach (l, (List *)node) {
+ if (attribute_used(
+ (Node *)lfirst(l),
+ rt_index,
+ attno,
+ sublevels_up))
+ return TRUE;
+ }
+ return FALSE;
+ }
+ break;
+
+ case T_SubLink:
+ {
+ SubLink *sub = (SubLink *)node;
+
+ if (attribute_used(
+ (Node *)(sub->lefthand),
+ rt_index,
+ attno,
+ sublevels_up))
+ return TRUE;
+
+ if (attribute_used(
+ (Node *)(sub->subselect),
+ rt_index,
+ attno,
+ sublevels_up + 1))
+ return TRUE;
+
+ return FALSE;
+ }
+ break;
+
+ case T_Query:
+ {
+ Query *qry = (Query *)node;
+
+ if (attribute_used(
+ (Node *)(qry->targetList),
+ rt_index,
+ attno,
+ sublevels_up))
+ return TRUE;
+
+ if (attribute_used(
+ (Node *)(qry->qual),
+ rt_index,
+ attno,
+ sublevels_up))
+ return TRUE;
+
+ if (attribute_used(
+ (Node *)(qry->havingQual),
+ rt_index,
+ attno,
+ sublevels_up))
+ return TRUE;
+
+ if (attribute_used(
+ (Node *)(qry->groupClause),
+ rt_index,
+ attno,
+ sublevels_up))
+ return TRUE;
+
+ return FALSE;
+ }
+ break;
+
+ default:
+ elog(NOTICE, "unknown node tag %d in attribute_used()", nodeTag(node));
+ elog(NOTICE, "Node is: %s", nodeToString(node));
+ break;
- foreach(i, locks)
- {
- RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
- if (rule_lock->isInstead)
- {
- if (rule_lock->qual == NULL)
- instead_rules = lappend(instead_rules, rule_lock);
- else
- instead_qualified = lappend(instead_qualified, rule_lock);
- }
- else
- regular = lappend(regular, rule_lock);
}
- regular = nconc(regular, instead_qualified);
- return nconc(regular, instead_rules);
+
+ return FALSE;
}
-static int
-AllRetrieve(List *actions)
+
+/*
+ * offset_varnodes -
+ * We need another version of OffsetVarNodes() when processing
+ * RIR rules
+ */
+static void
+offset_varnodes(Node *node, int offset, int sublevels_up)
{
- List *n;
+ if (node == NULL)
+ return;
+
+ switch(nodeTag(node)) {
+ case T_TargetEntry:
+ {
+ TargetEntry *tle = (TargetEntry *)node;
+
+ offset_varnodes(
+ (Node *)(tle->expr),
+ offset,
+ sublevels_up);
+ }
+ break;
+
+ case T_Aggreg:
+ {
+ Aggreg *agg = (Aggreg *)node;
+
+ offset_varnodes(
+ (Node *)(agg->target),
+ offset,
+ sublevels_up);
+ }
+ break;
+
+ case T_GroupClause:
+ {
+ GroupClause *grp = (GroupClause *)node;
+
+ offset_varnodes(
+ (Node *)(grp->entry),
+ offset,
+ sublevels_up);
+ }
+ break;
+
+ case T_Expr:
+ {
+ Expr *exp = (Expr *)node;
+
+ offset_varnodes(
+ (Node *)(exp->args),
+ offset,
+ sublevels_up);
+ }
+ break;
+
+ case T_Iter:
+ {
+ Iter *iter = (Iter *)node;
+
+ offset_varnodes(
+ (Node *)(iter->iterexpr),
+ offset,
+ sublevels_up);
+ }
+ break;
+
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *)node;
+
+ offset_varnodes(
+ (Node *)(ref->refupperindexpr),
+ offset,
+ sublevels_up);
+ offset_varnodes(
+ (Node *)(ref->reflowerindexpr),
+ offset,
+ sublevels_up);
+ offset_varnodes(
+ (Node *)(ref->refexpr),
+ offset,
+ sublevels_up);
+ offset_varnodes(
+ (Node *)(ref->refassgnexpr),
+ offset,
+ sublevels_up);
+ }
+ break;
+
+ case T_Var:
+ {
+ Var *var = (Var *)node;
+
+ if (var->varlevelsup == sublevels_up) {
+ var->varno += offset;
+ var->varnoold += offset;
+ }
+ }
+ break;
+
+ case T_Param:
+ break;
+
+ case T_Const:
+ break;
+
+ case T_List:
+ {
+ List *l;
+
+ foreach (l, (List *)node)
+ offset_varnodes(
+ (Node *)lfirst(l),
+ offset,
+ sublevels_up);
+ }
+ break;
+
+ case T_SubLink:
+ {
+ SubLink *sub = (SubLink *)node;
+
+ offset_varnodes(
+ (Node *)(sub->lefthand),
+ offset,
+ sublevels_up);
+
+ offset_varnodes(
+ (Node *)(sub->subselect),
+ offset,
+ sublevels_up + 1);
+ }
+ break;
+
+ case T_Query:
+ {
+ Query *qry = (Query *)node;
+
+ offset_varnodes(
+ (Node *)(qry->targetList),
+ offset,
+ sublevels_up);
+
+ offset_varnodes(
+ (Node *)(qry->qual),
+ offset,
+ sublevels_up);
+
+ offset_varnodes(
+ (Node *)(qry->havingQual),
+ offset,
+ sublevels_up);
+
+ offset_varnodes(
+ (Node *)(qry->groupClause),
+ offset,
+ sublevels_up);
+ }
+ break;
+
+ default:
+ elog(NOTICE, "unknown node tag %d in offset_varnodes()", nodeTag(node));
+ elog(NOTICE, "Node is: %s", nodeToString(node));
+ break;
- 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)
+
+/*
+ * change_varnodes -
+ * and another ChangeVarNodes() too
+ */
+static void
+change_varnodes(Node *node, int rt_index, int new_index, int sublevels_up)
{
- List *i,
- *locks;
- RuleLock *rt_entry_locks = NULL;
- List *work = NIL;
+ if (node == NULL)
+ return;
- if ((rt_entry_locks = relation->rd_rules) == NULL)
- return NIL;
+ switch(nodeTag(node)) {
+ case T_TargetEntry:
+ {
+ TargetEntry *tle = (TargetEntry *)node;
- locks = matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
+ change_varnodes(
+ (Node *)(tle->expr),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_Aggreg:
+ {
+ Aggreg *agg = (Aggreg *)node;
+
+ change_varnodes(
+ (Node *)(agg->target),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_GroupClause:
+ {
+ GroupClause *grp = (GroupClause *)node;
+
+ change_varnodes(
+ (Node *)(grp->entry),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_Expr:
+ {
+ Expr *exp = (Expr *)node;
+
+ change_varnodes(
+ (Node *)(exp->args),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_Iter:
+ {
+ Iter *iter = (Iter *)node;
+
+ change_varnodes(
+ (Node *)(iter->iterexpr),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *)node;
+
+ change_varnodes(
+ (Node *)(ref->refupperindexpr),
+ rt_index,
+ new_index,
+ sublevels_up);
+ change_varnodes(
+ (Node *)(ref->reflowerindexpr),
+ rt_index,
+ new_index,
+ sublevels_up);
+ change_varnodes(
+ (Node *)(ref->refexpr),
+ rt_index,
+ new_index,
+ sublevels_up);
+ change_varnodes(
+ (Node *)(ref->refassgnexpr),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_Var:
+ {
+ Var *var = (Var *)node;
+
+ if (var->varlevelsup == sublevels_up &&
+ var->varno == rt_index) {
+ var->varno = new_index;
+ var->varnoold = new_index;
+ }
+ }
+ break;
+
+ case T_Param:
+ break;
+
+ case T_Const:
+ break;
+
+ case T_List:
+ {
+ List *l;
+
+ foreach (l, (List *)node)
+ change_varnodes(
+ (Node *)lfirst(l),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_SubLink:
+ {
+ SubLink *sub = (SubLink *)node;
+
+ change_varnodes(
+ (Node *)(sub->lefthand),
+ rt_index,
+ new_index,
+ sublevels_up);
+
+ change_varnodes(
+ (Node *)(sub->subselect),
+ rt_index,
+ new_index,
+ sublevels_up + 1);
+ }
+ break;
+
+ case T_Query:
+ {
+ Query *qry = (Query *)node;
+
+ change_varnodes(
+ (Node *)(qry->targetList),
+ rt_index,
+ new_index,
+ sublevels_up);
+
+ change_varnodes(
+ (Node *)(qry->qual),
+ rt_index,
+ new_index,
+ sublevels_up);
+
+ change_varnodes(
+ (Node *)(qry->havingQual),
+ rt_index,
+ new_index,
+ sublevels_up);
+
+ change_varnodes(
+ (Node *)(qry->groupClause),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ default:
+ elog(NOTICE, "unknown node tag %d in change_varnodes()", nodeTag(node));
+ elog(NOTICE, "Node is: %s", nodeToString(node));
+ break;
- /* 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;
+}
+
+
+/*
+ * modifyAggregUplevel -
+ * In the newly created sublink for an aggregate column used in
+ * the qualification, we must adjust the varlevelsup in all the
+ * var nodes.
+ */
+static void
+modifyAggregUplevel(Node *node)
+{
+ if (node == NULL)
+ return;
+
+ switch(nodeTag(node)) {
+ case T_TargetEntry:
+ {
+ TargetEntry *tle = (TargetEntry *)node;
+
+ modifyAggregUplevel(
+ (Node *)(tle->expr));
+ }
+ break;
- relation_level = (rule_lock->attrno == -1);
- if (rule_lock->actions == NIL)
+ case T_Aggreg:
{
- *instead_flag = TRUE;
- return NIL;
+ Aggreg *agg = (Aggreg *)node;
+
+ modifyAggregUplevel(
+ (Node *)(agg->target));
}
- if (!rule_flag &&
- length(rule_lock->actions) >= 2 &&
- AllRetrieve(rule_lock->actions))
+ break;
+
+ case T_Expr:
{
- *instead_flag = TRUE;
- return rule_lock->actions;
+ Expr *exp = (Expr *)node;
+
+ modifyAggregUplevel(
+ (Node *)(exp->args));
}
- ApplyRetrieveRule(parsetree, rule_lock, rt_index, relation_level, relation,
- &modified);
- if (modified)
+ break;
+
+ case T_Iter:
{
- *instead_flag = TRUE;
- FixResdomTypes(parsetree->targetList);
- return lcons(parsetree, NIL);
+ Iter *iter = (Iter *)node;
+
+ modifyAggregUplevel(
+ (Node *)(iter->iterexpr));
}
- }
+ break;
+
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *)node;
+
+ modifyAggregUplevel(
+ (Node *)(ref->refupperindexpr));
+ modifyAggregUplevel(
+ (Node *)(ref->reflowerindexpr));
+ modifyAggregUplevel(
+ (Node *)(ref->refexpr));
+ modifyAggregUplevel(
+ (Node *)(ref->refassgnexpr));
+ }
+ break;
+
+ case T_Var:
+ {
+ Var *var = (Var *)node;
+
+ var->varlevelsup++;
+ }
+ break;
+
+ case T_Param:
+ break;
+
+ case T_Const:
+ break;
+
+ case T_List:
+ {
+ List *l;
+
+ foreach (l, (List *)node)
+ modifyAggregUplevel(
+ (Node *)lfirst(l));
+ }
+ break;
+
+ case T_SubLink:
+ {
+ SubLink *sub = (SubLink *)node;
+
+ modifyAggregUplevel(
+ (Node *)(sub->lefthand));
+
+ modifyAggregUplevel(
+ (Node *)(sub->oper));
+
+ modifyAggregUplevel(
+ (Node *)(sub->subselect));
+ }
+ break;
+
+ case T_Query:
+ {
+ Query *qry = (Query *)node;
+
+ modifyAggregUplevel(
+ (Node *)(qry->targetList));
+
+ modifyAggregUplevel(
+ (Node *)(qry->qual));
+
+ modifyAggregUplevel(
+ (Node *)(qry->havingQual));
+
+ modifyAggregUplevel(
+ (Node *)(qry->groupClause));
+ }
+ break;
+
+ default:
+ elog(NOTICE, "unknown node tag %d in modifyAggregUplevel()", nodeTag(node));
+ elog(NOTICE, "Node is: %s", nodeToString(node));
+ break;
+
+
}
- 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
+/*
+ * modifyAggregChangeVarnodes -
+ * Change the var nodes in a sublink created for an aggregate column
+ * used in the qualification that is subject of the aggregate
+ * function to point to the correct local RTE.
+ */
+static void
+modifyAggregChangeVarnodes(Node **nodePtr, int rt_index, int new_index, int sublevels_up)
+{
+ Node *node = *nodePtr;
+
+ if (node == NULL)
+ return;
+
+ switch(nodeTag(node)) {
+ case T_TargetEntry:
+ {
+ TargetEntry *tle = (TargetEntry *)node;
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(tle->expr)),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_Aggreg:
+ {
+ Aggreg *agg = (Aggreg *)node;
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(agg->target)),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_GroupClause:
+ {
+ GroupClause *grp = (GroupClause *)node;
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(grp->entry)),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_Expr:
+ {
+ Expr *exp = (Expr *)node;
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(exp->args)),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_Iter:
+ {
+ Iter *iter = (Iter *)node;
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(iter->iterexpr)),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *)node;
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(ref->refupperindexpr)),
+ rt_index,
+ new_index,
+ sublevels_up);
+ modifyAggregChangeVarnodes(
+ (Node **)(&(ref->reflowerindexpr)),
+ rt_index,
+ new_index,
+ sublevels_up);
+ modifyAggregChangeVarnodes(
+ (Node **)(&(ref->refexpr)),
+ rt_index,
+ new_index,
+ sublevels_up);
+ modifyAggregChangeVarnodes(
+ (Node **)(&(ref->refassgnexpr)),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_Var:
+ {
+ Var *var = (Var *)node;
+
+ if (var->varlevelsup == sublevels_up &&
+ var->varno == rt_index) {
+ var = copyObject(var);
+ var->varno = new_index;
+ var->varnoold = new_index;
+ var->varlevelsup = 0;
+
+ *nodePtr = (Node *)var;
+ }
+ }
+ break;
+
+ case T_Param:
+ break;
+
+ case T_Const:
+ break;
+
+ case T_List:
+ {
+ List *l;
+
+ foreach (l, (List *)node)
+ modifyAggregChangeVarnodes(
+ (Node **)(&lfirst(l)),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ case T_SubLink:
+ {
+ SubLink *sub = (SubLink *)node;
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(sub->lefthand)),
+ rt_index,
+ new_index,
+ sublevels_up);
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(sub->oper)),
+ rt_index,
+ new_index,
+ sublevels_up);
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(sub->subselect)),
+ rt_index,
+ new_index,
+ sublevels_up + 1);
+ }
+ break;
+
+ case T_Query:
+ {
+ Query *qry = (Query *)node;
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(qry->targetList)),
+ rt_index,
+ new_index,
+ sublevels_up);
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(qry->qual)),
+ rt_index,
+ new_index,
+ sublevels_up);
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(qry->havingQual)),
+ rt_index,
+ new_index,
+ sublevels_up);
+
+ modifyAggregChangeVarnodes(
+ (Node **)(&(qry->groupClause)),
+ rt_index,
+ new_index,
+ sublevels_up);
+ }
+ break;
+
+ default:
+ elog(NOTICE, "unknown node tag %d in modifyAggregChangeVarnodes()", nodeTag(node));
+ elog(NOTICE, "Node is: %s", nodeToString(node));
+ break;
+
+
+ }
+}
+
+
+/*
+ * modifyAggregDropQual -
+ * remove the pure aggreg clase from a qualification
+ */
+static void
+modifyAggregDropQual(Node **nodePtr, Node *orignode, Expr *expr)
+{
+ Node *node = *nodePtr;
+
+ if (node == NULL)
+ return;
+
+ switch(nodeTag(node)) {
+ case T_Var:
+ break;
+
+ case T_Aggreg:
+ {
+ Aggreg *agg = (Aggreg *)node;
+ Aggreg *oagg = (Aggreg *)orignode;
+
+ modifyAggregDropQual(
+ (Node **)(&(agg->target)),
+ (Node *)(oagg->target),
+ expr);
+ }
+ break;
+
+ case T_Param:
+ break;
+
+ case T_Const:
+ break;
+
+ case T_GroupClause:
+ break;
+
+ case T_Expr:
+ {
+ Expr *this_expr = (Expr *)node;
+ Expr *orig_expr = (Expr *)orignode;
+
+ if (orig_expr == expr) {
+ Const *ctrue;
+
+ if (expr->typeOid != BOOLOID)
+ elog(ERROR,
+ "aggregate expression in qualification isn't of type bool");
+ ctrue = makeNode(Const);
+ ctrue->consttype = BOOLOID;
+ ctrue->constlen = 1;
+ ctrue->constisnull = FALSE;
+ ctrue->constvalue = (Datum)TRUE;
+ ctrue->constbyval = TRUE;
+
+ *nodePtr = (Node *)ctrue;
+ }
+ else
+ modifyAggregDropQual(
+ (Node **)(&(this_expr->args)),
+ (Node *)(orig_expr->args),
+ expr);
+ }
+ break;
+
+ case T_Iter:
+ {
+ Iter *iter = (Iter *)node;
+ Iter *oiter = (Iter *)orignode;
+
+ modifyAggregDropQual(
+ (Node **)(&(iter->iterexpr)),
+ (Node *)(oiter->iterexpr),
+ expr);
+ }
+ break;
+
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *)node;
+ ArrayRef *oref = (ArrayRef *)orignode;
+
+ modifyAggregDropQual(
+ (Node **)(&(ref->refupperindexpr)),
+ (Node *)(oref->refupperindexpr),
+ expr);
+ modifyAggregDropQual(
+ (Node **)(&(ref->reflowerindexpr)),
+ (Node *)(oref->reflowerindexpr),
+ expr);
+ modifyAggregDropQual(
+ (Node **)(&(ref->refexpr)),
+ (Node *)(oref->refexpr),
+ expr);
+ modifyAggregDropQual(
+ (Node **)(&(ref->refassgnexpr)),
+ (Node *)(oref->refassgnexpr),
+ expr);
+ }
+ break;
+
+ case T_List:
+ {
+ List *l;
+ List *ol = (List *)orignode;
+ int li = 0;
+
+ foreach (l, (List *)node) {
+ modifyAggregDropQual(
+ (Node **)(&(lfirst(l))),
+ (Node *)nth(li, ol),
+ expr);
+ li++;
+ }
+ }
+ break;
+
+ case T_SubLink:
+ {
+ SubLink *sub = (SubLink *)node;
+ SubLink *osub = (SubLink *)orignode;
+
+ modifyAggregDropQual(
+ (Node **)(&(sub->subselect)),
+ (Node *)(osub->subselect),
+ expr);
+ }
+ break;
+
+ case T_Query:
+ {
+ Query *qry = (Query *)node;
+ Query *oqry = (Query *)orignode;
+
+ modifyAggregDropQual(
+ (Node **)(&(qry->qual)),
+ (Node *)(oqry->qual),
+ expr);
+
+ modifyAggregDropQual(
+ (Node **)(&(qry->havingQual)),
+ (Node *)(oqry->havingQual),
+ expr);
+ }
+ break;
+
+ default:
+ elog(NOTICE, "unknown node tag %d in modifyAggregDropQual()", nodeTag(node));
+ elog(NOTICE, "Node is: %s", nodeToString(node));
+ break;
+
+
+ }
+}
+
+
+/*
+ * modifyAggregMakeSublink -
+ * Create a sublink node for a qualification expression that
+ * uses an aggregate column of a view
+ */
+static SubLink *
+modifyAggregMakeSublink(Expr *origexp, Query *parsetree)
+{
+ SubLink *sublink;
+ Query *subquery;
+ Node *subqual;
+ RangeTblEntry *rte;
+ Aggreg *aggreg;
+ Var *target;
+ TargetEntry *tle;
+ Resdom *resdom;
+ Expr *exp = copyObject(origexp);
+
+ if (nodeTag(nth(0, exp->args)) == T_Aggreg)
+ if (nodeTag(nth(1, exp->args)) == T_Aggreg)
+ elog(ERROR, "rewrite: comparision of 2 aggregate columns not supported");
+ else
+ elog(ERROR, "rewrite: aggregate column of view must be at rigth side in qual");
+
+ aggreg = (Aggreg *)nth(1, exp->args);
+ target = (Var *)(aggreg->target);
+ rte = (RangeTblEntry *)nth(target->varno - 1, parsetree->rtable);
+ tle = makeNode(TargetEntry);
+ resdom = makeNode(Resdom);
+
+ aggreg->usenulls = TRUE;
+
+ resdom->resno = 1;
+ resdom->restype = ((Oper *)(exp->oper))->opresulttype;
+ resdom->restypmod = -1;
+ resdom->resname = pstrdup("<noname>");
+ resdom->reskey = 0;
+ resdom->reskeyop = 0;
+ resdom->resjunk = 0;
+
+ tle->resdom = resdom;
+ tle->expr = (Node *)aggreg;
+
+ subqual = copyObject(parsetree->qual);
+ modifyAggregDropQual((Node **)&subqual, (Node *)parsetree->qual, origexp);
+
+ sublink = makeNode(SubLink);
+ sublink->subLinkType = EXPR_SUBLINK;
+ sublink->useor = FALSE;
+ sublink->lefthand = lappend(NIL, copyObject(lfirst(exp->args)));
+ sublink->oper = lappend(NIL, copyObject(exp));
+ sublink->subselect = NULL;
+
+ subquery = makeNode(Query);
+ sublink->subselect = (Node *)subquery;
+
+ subquery->commandType = CMD_SELECT;
+ subquery->utilityStmt = NULL;
+ subquery->resultRelation = 0;
+ subquery->into = NULL;
+ subquery->isPortal = FALSE;
+ subquery->isBinary = FALSE;
+ subquery->unionall = FALSE;
+ subquery->uniqueFlag = NULL;
+ subquery->sortClause = NULL;
+ subquery->rtable = lappend(NIL, rte);
+ subquery->targetList = lappend(NIL, tle);
+ subquery->qual = subqual;
+ subquery->groupClause = NIL;
+ subquery->havingQual = NULL;
+ subquery->hasAggs = TRUE;
+ subquery->hasSubLinks = FALSE;
+ subquery->unionClause = NULL;
+
+
+ modifyAggregUplevel((Node *)sublink);
+
+ modifyAggregChangeVarnodes((Node **)&(sublink->lefthand), target->varno,
+ 1, target->varlevelsup);
+ modifyAggregChangeVarnodes((Node **)&(sublink->oper), target->varno,
+ 1, target->varlevelsup);
+ modifyAggregChangeVarnodes((Node **)&(sublink->subselect), target->varno,
+ 1, target->varlevelsup);
+
+ return sublink;
+}
+
+
+/*
+ * modifyAggregQual -
+ * Search for qualification expressions that contain aggregate
+ * functions and substiture them by sublinks. These expressions
+ * originally come from qualifications that use aggregate columns
+ * of a view.
*/
static void
+modifyAggregQual(Node **nodePtr, Query *parsetree)
+{
+ Node *node = *nodePtr;
+
+ if (node == NULL)
+ return;
+
+ switch(nodeTag(node)) {
+ case T_Var:
+ break;
+
+ case T_Param:
+ break;
+
+ case T_Const:
+ break;
+
+ case T_GroupClause:
+ {
+ GroupClause *grp = (GroupClause *)node;
+
+ modifyAggregQual(
+ (Node **)(&(grp->entry)),
+ parsetree);
+ }
+ break;
+
+ case T_Expr:
+ {
+ Expr *exp = (Expr *)node;
+ SubLink *sub;
+
+
+ if (length(exp->args) != 2) {
+ modifyAggregQual(
+ (Node **)(&(exp->args)),
+ parsetree);
+ break;
+ }
+
+ if (nodeTag(nth(0, exp->args)) != T_Aggreg &&
+ nodeTag(nth(1, exp->args)) != T_Aggreg) {
+
+ modifyAggregQual(
+ (Node **)(&(exp->args)),
+ parsetree);
+ break;
+ }
+
+ sub = modifyAggregMakeSublink(exp,
+ parsetree);
+
+ *nodePtr = (Node *)sub;
+ parsetree->hasSubLinks = TRUE;
+ }
+ break;
+
+ case T_Iter:
+ {
+ Iter *iter = (Iter *)node;
+
+ modifyAggregQual(
+ (Node **)(&(iter->iterexpr)),
+ parsetree);
+ }
+ break;
+
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *)node;
+
+ modifyAggregQual(
+ (Node **)(&(ref->refupperindexpr)),
+ parsetree);
+ modifyAggregQual(
+ (Node **)(&(ref->reflowerindexpr)),
+ parsetree);
+ modifyAggregQual(
+ (Node **)(&(ref->refexpr)),
+ parsetree);
+ modifyAggregQual(
+ (Node **)(&(ref->refassgnexpr)),
+ parsetree);
+ }
+ break;
+
+ case T_List:
+ {
+ List *l;
+
+ foreach (l, (List *)node)
+ modifyAggregQual(
+ (Node **)(&(lfirst(l))),
+ parsetree);
+ }
+ break;
+
+ case T_SubLink:
+ {
+ SubLink *sub = (SubLink *)node;
+
+ modifyAggregQual(
+ (Node **)(&(sub->subselect)),
+ (Query *)(sub->subselect));
+ }
+ break;
+
+ case T_Query:
+ {
+ Query *qry = (Query *)node;
+
+ modifyAggregQual(
+ (Node **)(&(qry->qual)),
+ parsetree);
+
+ modifyAggregQual(
+ (Node **)(&(qry->havingQual)),
+ parsetree);
+ }
+ break;
+
+ default:
+ elog(NOTICE, "unknown node tag %d in modifyAggregQual()", nodeTag(node));
+ elog(NOTICE, "Node is: %s", nodeToString(node));
+ break;
+
+
+ }
+}
+
+
+static Node *
+FindMatchingTLEntry(List *tlist, char *e_attname)
+{
+ List *i;
+
+ foreach(i, tlist)
+ {
+ TargetEntry *tle = lfirst(i);
+ char *resname;
+
+ resname = tle->resdom->resname;
+ if (!strcmp(e_attname, resname))
+ return (tle->expr);
+ }
+ return NULL;
+}
+
+
+static Node *
+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;
+}
+
+
+static void
+apply_RIR_view(Node **nodePtr, int rt_index, RangeTblEntry *rte, List *tlist, int *modified, int sublevels_up)
+{
+ Node *node = *nodePtr;
+
+ if (node == NULL)
+ return;
+
+ switch(nodeTag(node)) {
+ case T_TargetEntry:
+ {
+ TargetEntry *tle = (TargetEntry *)node;
+
+ apply_RIR_view(
+ (Node **)(&(tle->expr)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+ }
+ break;
+
+ case T_Aggreg:
+ {
+ Aggreg *agg = (Aggreg *)node;
+
+ apply_RIR_view(
+ (Node **)(&(agg->target)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+ }
+ break;
+
+ case T_GroupClause:
+ {
+ GroupClause *grp = (GroupClause *)node;
+
+ apply_RIR_view(
+ (Node **)(&(grp->entry)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+ }
+ break;
+
+ case T_Expr:
+ {
+ Expr *exp = (Expr *)node;
+
+ apply_RIR_view(
+ (Node **)(&(exp->args)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+ }
+ break;
+
+ case T_Iter:
+ {
+ Iter *iter = (Iter *)node;
+
+ apply_RIR_view(
+ (Node **)(&(iter->iterexpr)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+ }
+ break;
+
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *)node;
+
+ apply_RIR_view(
+ (Node **)(&(ref->refupperindexpr)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+ apply_RIR_view(
+ (Node **)(&(ref->reflowerindexpr)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+ apply_RIR_view(
+ (Node **)(&(ref->refexpr)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+ apply_RIR_view(
+ (Node **)(&(ref->refassgnexpr)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+ }
+ break;
+
+ case T_Var:
+ {
+ Var *var = (Var *)node;
+
+ if (var->varlevelsup == sublevels_up &&
+ var->varno == rt_index) {
+ Node *exp;
+
+ exp = FindMatchingTLEntry(
+ tlist,
+ get_attname(rte->relid,
+ var->varattno));
+
+ if (exp == NULL) {
+ *nodePtr = make_null(var->vartype);
+ return;
+ }
+
+ if (var->varlevelsup > 0 &&
+ nodeTag(exp) == T_Var) {
+ exp = copyObject(exp);
+ ((Var *)exp)->varlevelsup = var->varlevelsup;
+ }
+ *nodePtr = exp;
+ *modified = TRUE;
+ }
+ }
+ break;
+
+ case T_Param:
+ break;
+
+ case T_Const:
+ break;
+
+ case T_List:
+ {
+ List *l;
+
+ foreach (l, (List *)node)
+ apply_RIR_view(
+ (Node **)(&(lfirst(l))),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+ }
+ break;
+
+ case T_SubLink:
+ {
+ SubLink *sub = (SubLink *)node;
+
+ apply_RIR_view(
+ (Node **)(&(sub->lefthand)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+
+ apply_RIR_view(
+ (Node **)(&(sub->subselect)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up + 1);
+ }
+ break;
+
+ case T_Query:
+ {
+ Query *qry = (Query *)node;
+
+ apply_RIR_view(
+ (Node **)(&(qry->targetList)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+
+ apply_RIR_view(
+ (Node **)(&(qry->qual)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+
+ apply_RIR_view(
+ (Node **)(&(qry->havingQual)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+
+ apply_RIR_view(
+ (Node **)(&(qry->groupClause)),
+ rt_index,
+ rte,
+ tlist,
+ modified,
+ sublevels_up);
+ }
+ break;
+
+ default:
+ elog(NOTICE, "unknown node tag %d in apply_RIR_view()", nodeTag(node));
+ elog(NOTICE, "Node is: %s", nodeToString(node));
+ break;
+
+
+ }
+}
+
+
+static void
ApplyRetrieveRule(Query *parsetree,
RewriteRule *rule,
int rt_index,
@@ -280,12 +1912,6 @@ ApplyRetrieveRule(Query *parsetree,
* rules with more than one
* action? -ay */
- /*
- * WARNING!!! If we sometimes handle rules with more than one
- * action, the view acl checks might get broken.
- * viewAclOverride should only become true (below) if this is
- * a relation_level, instead, select query - Jan
- */
return;
rule_action = copyObject(lfirst(rule->actions));
nothing = FALSE;
@@ -310,124 +1936,290 @@ ApplyRetrieveRule(Query *parsetree,
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);
-
- OffsetVarNodes((Node *) rule_action->groupClause, rt_length);
- OffsetVarNodes((Node *) rule_action->havingQual, rt_length);
-
- ChangeVarNodes(rule_action->qual,
- PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
- ChangeVarNodes((Node *) rule_action->targetList,
- PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
- ChangeVarNodes(rule_qual, PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
+ offset_varnodes((Node *) rule_qual, rt_length, 0);
+ offset_varnodes((Node *) rule_action, rt_length, 0);
- ChangeVarNodes((Node *) rule_action->groupClause,
+ change_varnodes((Node *) rule_qual,
PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
- ChangeVarNodes((Node *) rule_action->havingQual,
+ change_varnodes((Node *) rule_action,
PRS2_CURRENT_VARNO + rt_length, rt_index, 0);
if (relation_level)
{
- HandleViewRule(parsetree, rtable, rule_action->targetList, rt_index,
- modified);
+ apply_RIR_view((Node **) &parsetree, rt_index,
+ (RangeTblEntry *)nth(rt_index - 1, rtable),
+ rule_action->targetList, modified, 0);
+ apply_RIR_view((Node **) &rule_action, rt_index,
+ (RangeTblEntry *)nth(rt_index - 1, rtable),
+ rule_action->targetList, modified, 0);
}
else
{
- HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
- rt_index, rule->attrno, modified, &badsql);
+ HandleRIRAttributeRule(parsetree, rtable, rule_action->targetList,
+ rt_index, rule->attrno, modified, &badsql);
}
- if (*modified && !badsql)
- {
- AddQual(parsetree, rule_action->qual);
+ if (*modified && !badsql) {
+ AddQual(parsetree, rule_action->qual);
+ /* This will only work if the query made to the view defined by the following
+ * groupClause groups by the same attributes or does not use group at all! */
+ if (parsetree->groupClause == NULL)
+ parsetree->groupClause=rule_action->groupClause;
+ AddHavingQual(parsetree, rule_action->havingQual);
+ parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
+ parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks);
+ }
+}
+
+
+static void
+fireRIRonSubselect(Node *node)
+{
+ if (node == NULL)
+ return;
+
+ switch(nodeTag(node)) {
+ case T_TargetEntry:
+ {
+ TargetEntry *tle = (TargetEntry *)node;
+
+ fireRIRonSubselect(
+ (Node *)(tle->expr));
+ }
+ break;
+
+ case T_Aggreg:
+ {
+ Aggreg *agg = (Aggreg *)node;
+
+ fireRIRonSubselect(
+ (Node *)(agg->target));
+ }
+ break;
+
+ case T_GroupClause:
+ {
+ GroupClause *grp = (GroupClause *)node;
+
+ fireRIRonSubselect(
+ (Node *)(grp->entry));
+ }
+ break;
+
+ case T_Expr:
+ {
+ Expr *exp = (Expr *)node;
+
+ fireRIRonSubselect(
+ (Node *)(exp->args));
+ }
+ break;
+
+ case T_Iter:
+ {
+ Iter *iter = (Iter *)node;
+
+ fireRIRonSubselect(
+ (Node *)(iter->iterexpr));
+ }
+ break;
+
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *)node;
+
+ fireRIRonSubselect(
+ (Node *)(ref->refupperindexpr));
+ fireRIRonSubselect(
+ (Node *)(ref->reflowerindexpr));
+ fireRIRonSubselect(
+ (Node *)(ref->refexpr));
+ fireRIRonSubselect(
+ (Node *)(ref->refassgnexpr));
+ }
+ break;
+
+ case T_Var:
+ break;
+
+ case T_Param:
+ break;
+
+ case T_Const:
+ break;
+
+ case T_List:
+ {
+ List *l;
+
+ foreach (l, (List *)node)
+ fireRIRonSubselect(
+ (Node *)(lfirst(l)));
+ }
+ break;
+
+ case T_SubLink:
+ {
+ SubLink *sub = (SubLink *)node;
+ Query *qry;
+
+ fireRIRonSubselect(
+ (Node *)(sub->lefthand));
+
+ qry = fireRIRrules((Query *)(sub->subselect));
+
+ fireRIRonSubselect(
+ (Node *)qry);
+
+ sub->subselect = (Node *) qry;
+ }
+ break;
+
+ case T_Query:
+ {
+ Query *qry = (Query *)node;
+
+ fireRIRonSubselect(
+ (Node *)(qry->targetList));
+
+ fireRIRonSubselect(
+ (Node *)(qry->qual));
+
+ fireRIRonSubselect(
+ (Node *)(qry->havingQual));
+
+ fireRIRonSubselect(
+ (Node *)(qry->groupClause));
+ }
+ break;
+
+ default:
+ elog(NOTICE, "unknown node tag %d in fireRIRonSubselect()", nodeTag(node));
+ elog(NOTICE, "Node is: %s", nodeToString(node));
+ break;
+
- /*
- * This will only work if the query made to the view defined by
- * the following groupClause groups by the same attributes or does
- * not use group at all!
- */
- if (parsetree->groupClause == NULL)
- parsetree->groupClause = rule_action->groupClause;
- AddHavingQual(parsetree, rule_action->havingQual);
- parsetree->hasAggs = (rule_action->hasAggs || parsetree->hasAggs);
- parsetree->hasSubLinks = (rule_action->hasSubLinks || parsetree->hasSubLinks);
}
}
-static List *
-ProcessRetrieveQuery(Query *parsetree,
- List *rtable,
- bool *instead_flag,
- bool rule)
+
+/*
+ * fireRIRrules -
+ * Apply all RIR rules on each rangetable entry in a query
+ */
+static Query *
+fireRIRrules(Query *parsetree)
{
- List *rt;
- List *product_queries = NIL;
- int rt_index = 0;
+ int rt_index;
+ RangeTblEntry *rte;
+ Relation rel;
+ List *locks;
+ RuleLock *rules;
+ RewriteRule *rule;
+ RewriteRule RIRonly;
+ int modified;
+ int i;
+ List *l;
+
+ rt_index = 0;
+ while(rt_index < length(parsetree->rtable)) {
+ ++rt_index;
+ if (!rangeTableEntry_used((Node *)parsetree, rt_index, 0))
+ continue;
+
+ rte = nth(rt_index - 1, parsetree->rtable);
+ rel = heap_openr(rte->relname);
+ if (rel->rd_rules == NULL) {
+ heap_close(rel);
+ continue;
+ }
- foreach(rt, rtable)
- {
- RangeTblEntry *rt_entry = lfirst(rt);
- Relation rt_entry_relation = NULL;
- List *result = NIL;
+ rules = rel->rd_rules;
+ locks = NIL;
- rt_index++;
- rt_entry_relation = heap_openr(rt_entry->relname);
+ /*
+ * Collect the RIR rules that we must apply
+ */
+ for (i = 0; i < rules->numLocks; i++) {
+ rule = rules->rules[i];
+ if (rule->event != CMD_SELECT)
+ continue;
+
+ if (rule->attrno > 0 &&
+ !attribute_used((Node *)parsetree,
+ rt_index,
+ rule->attrno, 0))
+ continue;
+ locks = lappend(locks, rule);
+ }
+ /*
+ * Check permissions
+ */
+ checkLockPerms(locks, parsetree, rt_index);
- if (rt_entry_relation->rd_rules != NULL)
- {
- result =
- FireRetrieveRulesAtQuery(parsetree,
- rt_index,
- rt_entry_relation,
- instead_flag,
- rule);
+ /*
+ * Now apply them
+ */
+ foreach (l, locks) {
+ rule = lfirst(l);
+
+ RIRonly.event = rule->event;
+ RIRonly.attrno = rule->attrno;
+ RIRonly.qual = rule->qual;
+ RIRonly.actions = rule->actions;
+
+ ApplyRetrieveRule(parsetree,
+ &RIRonly,
+ rt_index,
+ RIRonly.attrno == -1,
+ rel,
+ &modified);
}
- heap_close(rt_entry_relation);
- if (*instead_flag)
- return result;
+
+ heap_close(rel);
}
- if (rule)
- return NIL;
- rt_index = 0;
+ fireRIRonSubselect((Node *) parsetree);
+ modifyAggregQual((Node **) &(parsetree->qual), parsetree);
+
+ return parsetree;
+}
- 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);
+/*
+ * idea is to fire regular rules first, then qualified instead
+ * rules and unqualified instead rules last. Any lemming is counted for.
+ */
+static List *
+orderRules(List *locks)
+{
+ List *regular = NIL;
+ List *instead_rules = NIL;
+ List *instead_qualified = NIL;
+ List *i;
+ foreach(i, locks)
+ {
+ RewriteRule *rule_lock = (RewriteRule *) lfirst(i);
- if (rt_entry_locks)
- {
- locks =
- matchLocks(CMD_SELECT, rt_entry_locks, rt_index, parsetree);
- }
- if (locks != NIL)
+ if (rule_lock->isInstead)
{
- 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);
+ if (rule_lock->qual == NULL)
+ instead_rules = lappend(instead_rules, rule_lock);
+ else
+ instead_qualified = lappend(instead_qualified, rule_lock);
}
+ else
+ regular = lappend(regular, rule_lock);
}
- return product_queries;
+ regular = nconc(regular, instead_qualified);
+ return nconc(regular, instead_rules);
}
+
+
static Query *
CopyAndAddQual(Query *parsetree,
List *actions,
@@ -462,6 +2254,7 @@ CopyAndAddQual(Query *parsetree,
}
+
/*
* fireRules -
* Iterate through rule locks applying rules.
@@ -488,13 +2281,7 @@ fireRules(Query *parsetree,
/* choose rule to fire from list of rules */
if (locks == NIL)
{
- ProcessRetrieveQuery(parsetree,
- parsetree->rtable,
- instead_flag, TRUE);
- if (*instead_flag)
- return lappend(NIL, parsetree);
- else
- return NIL;
+ return NIL;
}
locks = orderRules(locks); /* real instead rules last */
@@ -505,7 +2292,6 @@ fireRules(Query *parsetree,
*event_qual;
List *actions;
List *r;
- bool orig_instead_flag = *instead_flag;
/*
* Instead rules change the resultRelation of the query. So the
@@ -645,8 +2431,10 @@ fireRules(Query *parsetree,
*--------------------------------------------------
*/
info->rule_action->rtable = info->rt;
+ /*
ProcessRetrieveQuery(info->rule_action, info->rt,
&orig_instead_flag, TRUE);
+ */
/*--------------------------------------------------
* Step 4
@@ -670,128 +2458,32 @@ fireRules(Query *parsetree,
return results;
}
-/* ----------
- * RewritePreprocessQuery -
- * adjust details in the parsetree, the rule system
- * depends on
- * ----------
- */
-static void
-RewritePreprocessQuery(Query *parsetree)
-{
- /* ----------
- * if the query has a resultRelation, reassign the
- * result domain numbers to the attribute numbers in the
- * target relation. FixNew() depends on it when replacing
- * *new* references in a rule action by the expressions
- * from the rewritten query.
- * ----------
- */
- if (parsetree->resultRelation > 0)
- {
- RangeTblEntry *rte;
- Relation rd;
- List *tl;
- TargetEntry *tle;
- int resdomno;
-
- rte = (RangeTblEntry *) nth(parsetree->resultRelation - 1,
- parsetree->rtable);
- rd = heap_openr(rte->relname);
-
- foreach(tl, parsetree->targetList)
- {
- tle = (TargetEntry *) lfirst(tl);
- resdomno = attnameAttNum(rd, tle->resdom->resname);
- tle->resdom->resno = resdomno;
- }
-
- heap_close(rd);
- }
-}
-
-
-/* ----------
- * RewritePostprocessNonSelect -
- * apply instead select rules on a query fired in by
- * the rewrite system
- * ----------
- */
-static Query *
-RewritePostprocessNonSelect(Query *parsetree)
-{
- List *rt;
- int rt_index = 0;
- Query *newtree = copyObject(parsetree);
-
- foreach(rt, parsetree->rtable)
- {
- RangeTblEntry *rt_entry = lfirst(rt);
- Relation rt_entry_relation = NULL;
- RuleLock *rt_entry_locks = NULL;
- List *locks = NIL;
- List *instead_locks = NIL;
- List *lock;
- RewriteRule *rule;
-
- rt_index++;
- rt_entry_relation = heap_openr(rt_entry->relname);
- rt_entry_locks = rt_entry_relation->rd_rules;
-
- if (rt_entry_locks)
- {
- int origcmdtype = newtree->commandType;
-
- newtree->commandType = CMD_SELECT;
- locks =
- matchLocks(CMD_SELECT, rt_entry_locks, rt_index, newtree);
- newtree->commandType = origcmdtype;
- }
- if (locks != NIL)
- {
- foreach(lock, locks)
- {
- rule = (RewriteRule *) lfirst(lock);
- if (rule->isInstead)
- instead_locks = nconc(instead_locks, lock);
- }
- }
- if (instead_locks != NIL)
- {
- foreach(lock, instead_locks)
- {
- int relation_level;
- int modified = 0;
-
- rule = (RewriteRule *) lfirst(lock);
- relation_level = (rule->attrno == -1);
-
- ApplyRetrieveRule(newtree,
- rule,
- rt_index,
- relation_level,
- rt_entry_relation,
- &modified);
- }
- }
-
- heap_close(rt_entry_relation);
- }
- return newtree;
-}
static List *
RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
{
CmdType event;
- List *product_queries = NIL;
- int result_relation = 0;
+ List *product_queries = NIL;
+ int result_relation = 0;
+ RangeTblEntry *rt_entry;
+ Relation rt_entry_relation = NULL;
+ RuleLock *rt_entry_locks = NULL;
Assert(parsetree != NULL);
event = parsetree->commandType;
+ /*
+ * SELECT rules are handled later when we have all the
+ * queries that should get executed
+ */
+ if (event == CMD_SELECT)
+ return NIL;
+
+ /*
+ * Utilities aren't rewritten at all - why is this here?
+ */
if (event == CMD_UTILITY)
return NIL;
@@ -803,79 +2495,34 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
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);
- }
-
- /* ----------
- * deepRewriteQuery does not handle the situation
- * where a query fired by a rule uses relations that
- * have instead select rules defined (views and the like).
- * So we care for them here.
- * ----------
- */
- if (product_queries != NIL)
- {
- List *pq;
- Query *tmp;
- List *new_products = NIL;
-
- foreach(pq, product_queries)
- {
- tmp = (Query *) lfirst(pq);
- tmp = RewritePostprocessNonSelect(tmp);
- new_products = lappend(new_products, tmp);
- }
- product_queries = new_products;
- }
+ /*
+ * the statement is an update, insert or delete - fire rules
+ * on it.
+ */
+ 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);
- return product_queries;
- }
- else
+ 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);
+ }
- /*
- * the statement is a select
- */
- Query *other;
-
- /*
- * ApplyRetrieveRule changes the range table XXX Unions are copied
- * again.
- */
- other = copyObject(parsetree);
+ return product_queries;
- 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.
@@ -887,103 +2534,6 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
static int numQueryRewriteInvoked = 0;
/*
- * QueryRewrite -
- * rewrite one query via QueryRewrite system, possibly returning 0, or many
- * queries
- */
-List *
-QueryRewrite(Query *parsetree)
-{
- RewritePreprocessQuery(parsetree);
-
- QueryRewriteSubLink(parsetree->qual);
- QueryRewriteSubLink(parsetree->havingQual);
-
- return QueryRewriteOne(parsetree);
-}
-
-/*
- * QueryRewriteSubLink
- *
- * This rewrites the SubLink subqueries first, doing the lowest ones first.
- * We already have code in the main rewrite loops to process correlated
- * variables from upper queries that exist in subqueries.
- */
-static void
-QueryRewriteSubLink(Node *node)
-{
- if (node == NULL)
- return;
-
- switch (nodeTag(node))
- {
- case T_TargetEntry:
- break;
- case T_Aggreg:
- break;
- case T_Expr:
- {
- Expr *expr = (Expr *) node;
-
- QueryRewriteSubLink((Node *) expr->args);
- }
- break;
- case T_Var:
- break;
- case T_List:
- {
- List *l;
-
- foreach(l, (List *) node)
- QueryRewriteSubLink(lfirst(l));
- }
- break;
- case T_SubLink:
- {
- SubLink *sublink = (SubLink *) node;
- Query *query = (Query *) sublink->subselect;
- List *ret;
-
- /*
- * Nest down first. We do this so if a rewrite adds a
- * SubLink we don't process it as part of this loop.
- */
- QueryRewriteSubLink((Node *) query->qual);
-
- QueryRewriteSubLink((Node *) query->havingQual);
-
- ret = QueryRewriteOne(query);
- if (!ret)
- sublink->subselect = NULL;
- else if (lnext(ret) == NIL)
- sublink->subselect = lfirst(ret);
- else
- elog(ERROR, "Don't know how to process subquery that rewrites to multiple queries.");
- }
- break;
- default:
- /* ignore the others */
- break;
- }
- return;
-}
-
-/*
- * QueryOneRewrite -
- * rewrite one query
- */
-static List *
-QueryRewriteOne(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
*/
@@ -1040,3 +2590,104 @@ deepRewriteQuery(Query *parsetree)
return rewritten;
}
+
+
+/*
+ * QueryOneRewrite -
+ * rewrite one query
+ */
+static List *
+QueryRewriteOne(Query *parsetree)
+{
+ numQueryRewriteInvoked = 0;
+
+ /*
+ * take a deep breath and apply all the rewrite rules - ay
+ */
+ return deepRewriteQuery(parsetree);
+}
+
+
+/* ----------
+ * RewritePreprocessQuery -
+ * adjust details in the parsetree, the rule system
+ * depends on
+ * ----------
+ */
+static void
+RewritePreprocessQuery(Query *parsetree)
+{
+ /* ----------
+ * if the query has a resultRelation, reassign the
+ * result domain numbers to the attribute numbers in the
+ * target relation. FixNew() depends on it when replacing
+ * *new* references in a rule action by the expressions
+ * from the rewritten query.
+ * ----------
+ */
+ if (parsetree->resultRelation > 0)
+ {
+ RangeTblEntry *rte;
+ Relation rd;
+ List *tl;
+ TargetEntry *tle;
+ int resdomno;
+
+ rte = (RangeTblEntry *) nth(parsetree->resultRelation - 1,
+ parsetree->rtable);
+ rd = heap_openr(rte->relname);
+
+ foreach(tl, parsetree->targetList)
+ {
+ tle = (TargetEntry *) lfirst(tl);
+ resdomno = attnameAttNum(rd, tle->resdom->resname);
+ tle->resdom->resno = resdomno;
+ }
+
+ heap_close(rd);
+ }
+}
+
+
+/*
+ * QueryRewrite -
+ * rewrite one query via query rewrite system, possibly returning 0
+ * or many queries
+ */
+List *
+QueryRewrite(Query *parsetree)
+{
+ List *querylist;
+ List *results = NIL;
+ List *l;
+ Query *query;
+
+ /*
+ * Step 1
+ *
+ * There still seems something broken with the resdom numbers
+ * so we reassign them first.
+ */
+ RewritePreprocessQuery(parsetree);
+
+ /*
+ * Step 2
+ *
+ * Apply all non-SELECT rules possibly getting 0 or many queries
+ */
+ querylist = QueryRewriteOne(parsetree);
+
+ /*
+ * Step 3
+ *
+ * Apply all the RIR rules on each query
+ */
+ foreach (l, querylist) {
+ query = (Query *)lfirst(l);
+ results = lappend(results, fireRIRrules(query));
+ }
+
+ return results;
+}
+
+
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 15c68064a6a..87786d9cdd0 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -6,7 +6,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.18 1998/09/11 16:39:59 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.19 1998/10/02 16:27:49 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -73,6 +73,23 @@ OffsetVarNodes(Node *node, int offset)
OffsetVarNodes((Node *) expr->args, offset);
}
break;
+ case T_Iter:
+ {
+ Iter *iter = (Iter *) node;
+
+ OffsetVarNodes((Node *) iter->iterexpr, offset);
+ }
+ break;
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *) node;
+
+ OffsetVarNodes((Node *) ref->refupperindexpr, offset);
+ OffsetVarNodes((Node *) ref->reflowerindexpr, offset);
+ OffsetVarNodes((Node *) ref->refexpr, offset);
+ OffsetVarNodes((Node *) ref->refassgnexpr, offset);
+ }
+ break;
case T_Var:
{
Var *var = (Var *) node;
@@ -157,6 +174,23 @@ ChangeVarNodes(Node *node, int old_varno, int new_varno, int sublevels_up)
ChangeVarNodes((Node *) expr->args, old_varno, new_varno, sublevels_up);
}
break;
+ case T_Iter:
+ {
+ Iter *iter = (Iter *) node;
+
+ ChangeVarNodes((Node *) iter->iterexpr, old_varno, new_varno, sublevels_up);
+ }
+ break;
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *) node;
+
+ ChangeVarNodes((Node *) ref->refupperindexpr, old_varno, new_varno, sublevels_up);
+ ChangeVarNodes((Node *) ref->reflowerindexpr, old_varno, new_varno, sublevels_up);
+ ChangeVarNodes((Node *) ref->refexpr, old_varno, new_varno, sublevels_up);
+ ChangeVarNodes((Node *) ref->refassgnexpr, old_varno, new_varno, sublevels_up);
+ }
+ break;
case T_Var:
{
Var *var = (Var *) node;
@@ -353,6 +387,20 @@ ResolveNew(RewriteInfo *info, List *targetlist, Node **nodePtr,
ResolveNew(info, targetlist, (Node **) (&(((Expr *) node)->args)),
sublevels_up);
break;
+ case T_Iter:
+ ResolveNew(info, targetlist, (Node **) (&(((Iter *) node)->iterexpr)),
+ sublevels_up);
+ break;
+ case T_ArrayRef:
+ ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refupperindexpr)),
+ sublevels_up);
+ ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->reflowerindexpr)),
+ sublevels_up);
+ ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refexpr)),
+ sublevels_up);
+ ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refassgnexpr)),
+ sublevels_up);
+ break;
case T_Var:
{
int this_varno = (int) ((Var *) node)->varno;
@@ -454,6 +502,38 @@ nodeHandleRIRAttributeRule(Node **nodePtr,
sublevels_up);
}
break;
+ case T_Iter:
+ {
+ Iter *iter = (Iter *) node;
+
+ nodeHandleRIRAttributeRule((Node **) (&(iter->iterexpr)), rtable,
+ targetlist, rt_index, attr_num,
+ modified, badsql,
+ sublevels_up);
+ }
+ break;
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *) node;
+
+ nodeHandleRIRAttributeRule((Node **) (&(ref->refupperindexpr)), rtable,
+ targetlist, rt_index, attr_num,
+ modified, badsql,
+ sublevels_up);
+ nodeHandleRIRAttributeRule((Node **) (&(ref->reflowerindexpr)), rtable,
+ targetlist, rt_index, attr_num,
+ modified, badsql,
+ sublevels_up);
+ nodeHandleRIRAttributeRule((Node **) (&(ref->refexpr)), rtable,
+ targetlist, rt_index, attr_num,
+ modified, badsql,
+ sublevels_up);
+ nodeHandleRIRAttributeRule((Node **) (&(ref->refassgnexpr)), rtable,
+ targetlist, rt_index, attr_num,
+ modified, badsql,
+ sublevels_up);
+ }
+ break;
case T_Var:
{
int this_varno = ((Var *) node)->varno;
@@ -598,6 +678,33 @@ nodeHandleViewRule(Node **nodePtr,
rt_index, modified, sublevels_up);
}
break;
+ case T_Iter:
+ {
+ Iter *iter = (Iter *) node;
+
+ nodeHandleViewRule((Node **) (&(iter->iterexpr)),
+ rtable, targetlist,
+ rt_index, modified, sublevels_up);
+ }
+ break;
+ case T_ArrayRef:
+ {
+ ArrayRef *ref = (ArrayRef *) node;
+
+ nodeHandleViewRule((Node **) (&(ref->refupperindexpr)),
+ rtable, targetlist,
+ rt_index, modified, sublevels_up);
+ nodeHandleViewRule((Node **) (&(ref->reflowerindexpr)),
+ rtable, targetlist,
+ rt_index, modified, sublevels_up);
+ nodeHandleViewRule((Node **) (&(ref->refexpr)),
+ rtable, targetlist,
+ rt_index, modified, sublevels_up);
+ nodeHandleViewRule((Node **) (&(ref->refassgnexpr)),
+ rtable, targetlist,
+ rt_index, modified, sublevels_up);
+ }
+ break;
case T_Var:
{
Var *var = (Var *) node;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index f9774bf62f5..b3a1db8c069 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
* out of it's tuple
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.3 1998/09/01 04:32:49 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.4 1998/10/02 16:27:51 momjian Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -52,10 +52,23 @@
#include "utils/lsyscache.h"
#include "catalog/pg_class.h"
#include "catalog/pg_type.h"
+#include "catalog/pg_shadow.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_opclass.h"
#include "fmgr.h"
/* ----------
+ * Local data types
+ * ----------
+ */
+typedef struct QryHier {
+ struct QryHier *parent;
+ Query *query;
+} QryHier;
+
+
+/* ----------
* Global data
* ----------
*/
@@ -64,6 +77,10 @@ static void *plan_getrule = NULL;
static char *query_getrule = "SELECT * FROM pg_rewrite WHERE rulename = $1";
static void *plan_getview = NULL;
static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1 or rulename = $2";
+static void *plan_getam = NULL;
+static char *query_getam = "SELECT * FROM pg_am WHERE oid = $1";
+static void *plan_getopclass = NULL;
+static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1";
/* ----------
@@ -72,6 +89,8 @@ static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1 or ru
*/
text *pg_get_ruledef(NameData *rname);
text *pg_get_viewdef(NameData *rname);
+text *pg_get_indexdef(Oid indexrelid);
+NameData *pg_get_userbyid(int4 uid);
/* ----------
@@ -80,15 +99,16 @@ text *pg_get_viewdef(NameData *rname);
*/
static char *make_ruledef(HeapTuple ruletup, TupleDesc rulettc);
static char *make_viewdef(HeapTuple ruletup, TupleDesc rulettc);
-static char *get_query_def(Query *query);
-static char *get_select_query_def(Query *query);
-static char *get_insert_query_def(Query *query);
-static char *get_update_query_def(Query *query);
-static char *get_delete_query_def(Query *query);
-static char *get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix);
-static char *get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix);
-static char *get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix);
+static char *get_query_def(Query *query, QryHier *parentqh);
+static char *get_select_query_def(Query *query, QryHier *qh);
+static char *get_insert_query_def(Query *query, QryHier *qh);
+static char *get_update_query_def(Query *query, QryHier *qh);
+static char *get_delete_query_def(Query *query, QryHier *qh);
+static char *get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix);
+static char *get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix);
+static char *get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix);
static char *get_const_expr(Const *constval);
+static char *get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix);
static char *get_relation_name(Oid relid);
static char *get_attribute_name(Oid relid, int2 attnum);
static bool check_if_rte_used(int rt_index, Node *node, int sup);
@@ -289,6 +309,272 @@ pg_get_viewdef(NameData *rname)
/* ----------
+ * get_viewdef - Mainly the same thing, but we
+ * only return the SELECT part of a view
+ * ----------
+ */
+text *
+pg_get_indexdef(Oid indexrelid)
+{
+ text *indexdef;
+ HeapTuple ht_idx;
+ HeapTuple ht_idxrel;
+ HeapTuple ht_indrel;
+ HeapTuple spi_tup;
+ TupleDesc spi_ttc;
+ int spi_fno;
+ Form_pg_index idxrec;
+ Form_pg_class idxrelrec;
+ Form_pg_class indrelrec;
+ Datum spi_args[1];
+ char spi_nulls[2];
+ int spirc;
+ int len;
+ int keyno;
+ char buf[8192];
+ char keybuf[8192];
+ char *sep;
+
+ /* ----------
+ * Connect to SPI manager
+ * ----------
+ */
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "get_indexdef: cannot connect to SPI manager");
+
+ /* ----------
+ * On the first call prepare the plans to lookup pg_am
+ * and pg_opclass.
+ * ----------
+ */
+ if (plan_getam == NULL)
+ {
+ Oid argtypes[1];
+ void *plan;
+
+ argtypes[0] = OIDOID;
+ plan = SPI_prepare(query_getam, 1, argtypes);
+ if (plan == NULL)
+ elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getam);
+ plan_getam = SPI_saveplan(plan);
+
+ argtypes[0] = OIDOID;
+ plan = SPI_prepare(query_getopclass, 1, argtypes);
+ if (plan == NULL)
+ elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getopclass);
+ plan_getopclass = SPI_saveplan(plan);
+ }
+
+ /* ----------
+ * Fetch the pg_index tuple by the Oid of the index
+ * ----------
+ */
+ ht_idx = SearchSysCacheTuple(INDEXRELID,
+ ObjectIdGetDatum(indexrelid), 0, 0, 0);
+ if (!HeapTupleIsValid(ht_idx))
+ elog(ERROR, "syscache lookup for index %d failed", indexrelid);
+ idxrec = (Form_pg_index)GETSTRUCT(ht_idx);
+
+ /* ----------
+ * Fetch the pg_class tuple of the index relation
+ * ----------
+ */
+ ht_idxrel = SearchSysCacheTuple(RELOID,
+ ObjectIdGetDatum(idxrec->indexrelid), 0, 0, 0);
+ if (!HeapTupleIsValid(ht_idxrel))
+ elog(ERROR, "syscache lookup for relid %d failed", idxrec->indexrelid);
+ idxrelrec = (Form_pg_class)GETSTRUCT(ht_idxrel);
+
+ /* ----------
+ * Fetch the pg_class tuple of the indexed relation
+ * ----------
+ */
+ ht_indrel = SearchSysCacheTuple(RELOID,
+ ObjectIdGetDatum(idxrec->indrelid), 0, 0, 0);
+ if (!HeapTupleIsValid(ht_indrel))
+ elog(ERROR, "syscache lookup for relid %d failed", idxrec->indrelid);
+ indrelrec = (Form_pg_class)GETSTRUCT(ht_indrel);
+
+ /* ----------
+ * Get the am name for the index relation
+ * ----------
+ */
+ spi_args[0] = ObjectIdGetDatum(idxrelrec->relam);
+ spi_nulls[0] = ' ';
+ spi_nulls[1] = '\0';
+ spirc = SPI_execp(plan_getam, spi_args, spi_nulls, 1);
+ if (spirc != SPI_OK_SELECT)
+ elog(ERROR, "failed to get pg_am tuple for index %s", nameout(&(idxrelrec->relname)));
+ if (SPI_processed != 1)
+ elog(ERROR, "failed to get pg_am tuple for index %s", nameout(&(idxrelrec->relname)));
+ spi_tup = SPI_tuptable->vals[0];
+ spi_ttc = SPI_tuptable->tupdesc;
+ spi_fno = SPI_fnumber(spi_ttc, "amname");
+
+ /* ----------
+ * Start the index definition
+ * ----------
+ */
+ sprintf(buf, "CREATE %sINDEX %s ON %s USING %s (",
+ idxrec->indisunique ? "UNIQUE " : "",
+ nameout(&(idxrelrec->relname)),
+ nameout(&(indrelrec->relname)),
+ SPI_getvalue(spi_tup, spi_ttc, spi_fno));
+
+ /* ----------
+ * Collect the indexed attributes
+ * ----------
+ */
+ sep = "";
+ keybuf[0] = '\0';
+ for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
+ {
+ if (idxrec->indkey[keyno] == InvalidAttrNumber)
+ break;
+
+ strcat(keybuf, sep);
+ sep = ", ";
+
+ /* ----------
+ * Add the indexed field name
+ * ----------
+ */
+ if (idxrec->indkey[keyno] == ObjectIdAttributeNumber - 1)
+ strcat(keybuf, "oid");
+ else
+ strcat(keybuf, get_attribute_name(idxrec->indrelid,
+ idxrec->indkey[keyno]));
+
+ /* ----------
+ * If not a functional index, add the operator class name
+ * ----------
+ */
+ if (idxrec->indproc == InvalidOid)
+ {
+ spi_args[0] = ObjectIdGetDatum(idxrec->indclass[keyno]);
+ spi_nulls[0] = ' ';
+ spi_nulls[1] = '\0';
+ spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
+ if (spirc != SPI_OK_SELECT)
+ elog(ERROR, "failed to get pg_opclass tuple %d", idxrec->indclass[keyno]);
+ if (SPI_processed != 1)
+ elog(ERROR, "failed to get pg_opclass tuple %d", idxrec->indclass[keyno]);
+ spi_tup = SPI_tuptable->vals[0];
+ spi_ttc = SPI_tuptable->tupdesc;
+ spi_fno = SPI_fnumber(spi_ttc, "opcname");
+ strcat(keybuf, " ");
+ strcat(keybuf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
+ }
+ }
+
+ /* ----------
+ * For functional index say 'func (attrs) opclass'
+ * ----------
+ */
+ if (idxrec->indproc != InvalidOid)
+ {
+ HeapTuple proctup;
+ Form_pg_proc procStruct;
+
+ proctup = SearchSysCacheTuple(PROOID,
+ ObjectIdGetDatum(idxrec->indproc), 0, 0, 0);
+ if (!HeapTupleIsValid(proctup))
+ elog(ERROR, "cache lookup for proc %d failed", idxrec->indproc);
+
+ procStruct = (Form_pg_proc) GETSTRUCT(proctup);
+ strcat(buf, nameout(&(procStruct->proname)));
+ strcat(buf, " (");
+ strcat(buf, keybuf);
+ strcat(buf, ") ");
+
+ spi_args[0] = ObjectIdGetDatum(idxrec->indclass[0]);
+ spi_nulls[0] = ' ';
+ spi_nulls[1] = '\0';
+ spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
+ if (spirc != SPI_OK_SELECT)
+ elog(ERROR, "failed to get pg_opclass tuple %d", idxrec->indclass[0]);
+ if (SPI_processed != 1)
+ elog(ERROR, "failed to get pg_opclass tuple %d", idxrec->indclass[0]);
+ spi_tup = SPI_tuptable->vals[0];
+ spi_ttc = SPI_tuptable->tupdesc;
+ spi_fno = SPI_fnumber(spi_ttc, "opcname");
+ strcat(buf, SPI_getvalue(spi_tup, spi_ttc, spi_fno));
+ }
+ else
+ /* ----------
+ * For the others say 'attr opclass [, ...]'
+ * ----------
+ */
+ {
+ strcat(buf, keybuf);
+ }
+
+ /* ----------
+ * Finish
+ * ----------
+ */
+ strcat(buf, ")");
+
+ /* ----------
+ * Create the result in upper executor memory
+ * ----------
+ */
+ len = strlen(buf) + VARHDRSZ;
+ indexdef = SPI_palloc(len);
+ VARSIZE(indexdef) = len;
+ memcpy(VARDATA(indexdef), buf, len - VARHDRSZ);
+
+ /* ----------
+ * Disconnect from SPI manager
+ * ----------
+ */
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "get_viewdef: SPI_finish() failed");
+
+ return indexdef;
+}
+
+
+/* ----------
+ * get_userbyid - Get a user name by usesysid and
+ * fallback to 'unknown (UID=n)'
+ * ----------
+ */
+NameData *
+pg_get_userbyid(int4 uid)
+{
+ HeapTuple usertup;
+ Form_pg_shadow user_rec;
+ NameData *result;
+
+ /* ----------
+ * Allocate space for the result
+ * ----------
+ */
+ result = (NameData *) palloc(NAMEDATALEN);
+ memset(result->data, 0, NAMEDATALEN);
+
+ /* ----------
+ * Get the pg_shadow entry and print the result
+ * ----------
+ */
+ usertup = SearchSysCacheTuple(USESYSID,
+ ObjectIdGetDatum(uid), 0, 0, 0);
+ if (HeapTupleIsValid(usertup))
+ {
+ user_rec = (Form_pg_shadow)GETSTRUCT(usertup);
+ StrNCpy(result->data, (&(user_rec->usename))->data, NAMEDATALEN);
+ }
+ else
+ {
+ sprintf((char *)result, "unknown (UID=%d)", uid);
+ }
+
+ return result;
+}
+
+
+/* ----------
* make_ruledef - reconstruct the CREATE RULE command
* for a given pg_rewrite tuple
* ----------
@@ -331,16 +617,13 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
fno = SPI_fnumber(rulettc, "ev_qual");
ev_qual = SPI_getvalue(ruletup, rulettc, fno);
- if (isnull)
- ev_qual = NULL;
fno = SPI_fnumber(rulettc, "ev_action");
ev_action = SPI_getvalue(ruletup, rulettc, fno);
- if (isnull)
- ev_action = NULL;
if (ev_action != NULL)
actions = (List *) stringToNode(ev_action);
+
/* ----------
* Build the rules definition text
* ----------
@@ -391,12 +674,15 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
{
Node *qual;
Query *query;
+ QryHier qh;
qual = stringToNode(ev_qual);
query = (Query *) lfirst(actions);
+ qh.parent = NULL;
+ qh.query = query;
strcat(buf, " WHERE ");
- strcat(buf, get_rule_expr(query->rtable, 0, qual, TRUE));
+ strcat(buf, get_rule_expr(&qh, 0, qual, TRUE));
}
strcat(buf, " DO ");
@@ -415,7 +701,7 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
foreach(action, actions)
{
query = (Query *) lfirst(action);
- strcat(buf, get_query_def(query));
+ strcat(buf, get_query_def(query, NULL));
strcat(buf, "; ");
}
strcat(buf, ");");
@@ -431,7 +717,7 @@ make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
Query *query;
query = (Query *) lfirst(actions);
- strcat(buf, get_query_def(query));
+ strcat(buf, get_query_def(query, NULL));
strcat(buf, ";");
}
}
@@ -482,13 +768,9 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
fno = SPI_fnumber(rulettc, "ev_qual");
ev_qual = SPI_getvalue(ruletup, rulettc, fno);
- if (isnull)
- ev_qual = "";
fno = SPI_fnumber(rulettc, "ev_action");
ev_action = SPI_getvalue(ruletup, rulettc, fno);
- if (isnull)
- ev_action = NULL;
if (ev_action != NULL)
actions = (List *) stringToNode(ev_action);
@@ -500,7 +782,7 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
if (ev_type != '1' || ev_attr >= 0 || !is_instead || strcmp(ev_qual, ""))
return "Not a view";
- strcpy(buf, get_select_query_def(query));
+ strcpy(buf, get_query_def(query, NULL));
strcat(buf, ";");
/* ----------
@@ -518,24 +800,29 @@ make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
* ----------
*/
static char *
-get_query_def(Query *query)
+get_query_def(Query *query, QryHier *parentqh)
{
+ QryHier qh;
+
+ qh.parent = parentqh;
+ qh.query = query;
+
switch (query->commandType)
{
case CMD_SELECT:
- return get_select_query_def(query);
+ return get_select_query_def(query, &qh);
break;
case CMD_UPDATE:
- return get_update_query_def(query);
+ return get_update_query_def(query, &qh);
break;
case CMD_INSERT:
- return get_insert_query_def(query);
+ return get_insert_query_def(query, &qh);
break;
case CMD_DELETE:
- return get_delete_query_def(query);
+ return get_delete_query_def(query, &qh);
break;
case CMD_NOTHING:
@@ -557,7 +844,7 @@ get_query_def(Query *query)
* ----------
*/
static char *
-get_select_query_def(Query *query)
+get_select_query_def(Query *query, QryHier *qh)
{
char buf[8192];
char *sep;
@@ -635,7 +922,7 @@ get_select_query_def(Query *query)
strcat(buf, sep);
sep = ", ";
- strcat(buf, get_tle_expr(query->rtable, 0, tle, (rt_numused > 1)));
+ strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
/* Check if we must say AS ... */
if (nodeTag(tle->expr) != T_Var)
@@ -681,7 +968,7 @@ get_select_query_def(Query *query)
strcat(buf, sep);
sep = ", ";
strcat(buf, rte->relname);
- if (rt_numused > 1)
+ if (strcmp(rte->relname, rte->refname) != 0)
{
strcat(buf, " ");
strcat(buf, rte->refname);
@@ -694,7 +981,7 @@ get_select_query_def(Query *query)
if (query->qual != NULL)
{
strcat(buf, " WHERE ");
- strcat(buf, get_rule_expr(query->rtable, 0, query->qual, (rt_numused > 1)));
+ strcat(buf, get_rule_expr(qh, 0, query->qual, (rt_numused > 1)));
}
/* Add the GROUP BY CLAUSE */
@@ -706,7 +993,7 @@ get_select_query_def(Query *query)
{
strcat(buf, sep);
sep = ", ";
- strcat(buf, get_rule_expr(query->rtable, 0, lfirst(l), (rt_numused > 1)));
+ strcat(buf, get_rule_expr(qh, 0, lfirst(l), (rt_numused > 1)));
}
}
@@ -723,7 +1010,7 @@ get_select_query_def(Query *query)
* ----------
*/
static char *
-get_insert_query_def(Query *query)
+get_insert_query_def(Query *query, QryHier *qh)
{
char buf[8192];
char *sep;
@@ -810,12 +1097,12 @@ get_insert_query_def(Query *query)
strcat(buf, sep);
sep = ", ";
- strcat(buf, get_tle_expr(query->rtable, 0, tle, (rt_numused > 1)));
+ strcat(buf, get_tle_expr(qh, 0, tle, (rt_numused > 1)));
}
strcat(buf, ")");
}
else
- strcat(buf, get_select_query_def(query));
+ strcat(buf, get_query_def(query, qh));
/* ----------
* Copy the query string into allocated space and return it
@@ -830,7 +1117,7 @@ get_insert_query_def(Query *query)
* ----------
*/
static char *
-get_update_query_def(Query *query)
+get_update_query_def(Query *query, QryHier *qh)
{
char buf[8192];
char *sep;
@@ -857,7 +1144,7 @@ get_update_query_def(Query *query)
sep = ", ";
strcat(buf, tle->resdom->resname);
strcat(buf, " = ");
- strcat(buf, get_tle_expr(query->rtable, query->resultRelation,
+ strcat(buf, get_tle_expr(qh, query->resultRelation,
tle, TRUE));
}
@@ -865,7 +1152,7 @@ get_update_query_def(Query *query)
if (query->qual != NULL)
{
strcat(buf, " WHERE ");
- strcat(buf, get_rule_expr(query->rtable, query->resultRelation,
+ strcat(buf, get_rule_expr(qh, query->resultRelation,
query->qual, TRUE));
}
@@ -882,7 +1169,7 @@ get_update_query_def(Query *query)
* ----------
*/
static char *
-get_delete_query_def(Query *query)
+get_delete_query_def(Query *query, QryHier *qh)
{
char buf[8192];
RangeTblEntry *rte;
@@ -899,7 +1186,7 @@ get_delete_query_def(Query *query)
if (query->qual != NULL)
{
strcat(buf, " WHERE ");
- strcat(buf, get_rule_expr(query->rtable, 0, query->qual, FALSE));
+ strcat(buf, get_rule_expr(qh, 0, query->qual, FALSE));
}
/* ----------
@@ -915,7 +1202,7 @@ get_delete_query_def(Query *query)
* ----------
*/
static char *
-get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
+get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
{
char buf[8192];
@@ -936,7 +1223,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
{
TargetEntry *tle = (TargetEntry *) node;
- return get_rule_expr(rtable, rt_index,
+ return get_rule_expr(qh, rt_index,
(Node *) (tle->expr), varprefix);
}
break;
@@ -947,7 +1234,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
strcat(buf, agg->aggname);
strcat(buf, "(");
- strcat(buf, get_rule_expr(rtable, rt_index,
+ strcat(buf, get_rule_expr(qh, rt_index,
(Node *) (agg->target), varprefix));
strcat(buf, ")");
return pstrdup(buf);
@@ -958,7 +1245,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
{
GroupClause *grp = (GroupClause *) node;
- return get_rule_expr(rtable, rt_index,
+ return get_rule_expr(qh, rt_index,
(Node *) (grp->entry), varprefix);
}
break;
@@ -974,13 +1261,13 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
switch (expr->opType)
{
case OP_EXPR:
- strcat(buf, get_rule_expr(rtable, rt_index,
+ strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_leftop(expr),
varprefix));
strcat(buf, " ");
strcat(buf, get_opname(((Oper *) expr->oper)->opno));
strcat(buf, " ");
- strcat(buf, get_rule_expr(rtable, rt_index,
+ strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_rightop(expr),
varprefix));
return pstrdup(buf);
@@ -988,11 +1275,11 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
case OR_EXPR:
strcat(buf, "(");
- strcat(buf, get_rule_expr(rtable, rt_index,
+ strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_leftop(expr),
varprefix));
strcat(buf, ") OR (");
- strcat(buf, get_rule_expr(rtable, rt_index,
+ strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_rightop(expr),
varprefix));
strcat(buf, ")");
@@ -1001,11 +1288,11 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
case AND_EXPR:
strcat(buf, "(");
- strcat(buf, get_rule_expr(rtable, rt_index,
+ strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_leftop(expr),
varprefix));
strcat(buf, ") AND (");
- strcat(buf, get_rule_expr(rtable, rt_index,
+ strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_rightop(expr),
varprefix));
strcat(buf, ")");
@@ -1014,7 +1301,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
case NOT_EXPR:
strcat(buf, "NOT (");
- strcat(buf, get_rule_expr(rtable, rt_index,
+ strcat(buf, get_rule_expr(qh, rt_index,
(Node *) get_leftop(expr),
varprefix));
strcat(buf, ")");
@@ -1022,7 +1309,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
break;
case FUNC_EXPR:
- return get_func_expr(rtable, rt_index,
+ return get_func_expr(qh, rt_index,
(Expr *) node,
varprefix);
break;
@@ -1037,7 +1324,14 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
case T_Var:
{
Var *var = (Var *) node;
- RangeTblEntry *rte = (RangeTblEntry *) nth(var->varno - 1, rtable);
+ RangeTblEntry *rte;
+ int sup = var->varlevelsup;
+
+ while(sup-- > 0) qh = qh->parent;
+ rte = (RangeTblEntry *) nth(var->varno - 1, qh->query->rtable);
+
+ if (qh->parent == NULL && var->varlevelsup > 0)
+ rte = (RangeTblEntry *) nth(var->varno + 1, qh->query->rtable);
if (!strcmp(rte->refname, "*NEW*"))
strcat(buf, "new.");
@@ -1047,7 +1341,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
strcat(buf, "current.");
else
{
- if (varprefix && var->varno != rt_index)
+ if (strcmp(rte->relname, rte->refname) != 0)
{
strcat(buf, rte->refname);
strcat(buf, ".");
@@ -1069,30 +1363,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
case T_SubLink:
{
- SubLink *sublink = (SubLink *) node;
- Query *query = (Query *) (sublink->subselect);
- List *l;
- char *sep;
-
- if (sublink->lefthand != NULL)
- {
- strcat(buf, "(");
- sep = "";
- foreach(l, sublink->lefthand)
- {
- strcat(buf, sep);
- sep = ", ";
- strcat(buf, get_rule_expr(rtable, rt_index,
- lfirst(l), varprefix));
- }
- strcat(buf, ") IN ");
- }
-
- strcat(buf, "(");
- strcat(buf, get_query_def(query));
- strcat(buf, ")");
-
- return pstrdup(buf);
+ return get_sublink_expr(qh, rt_index, node, varprefix);
}
break;
@@ -1116,7 +1387,7 @@ get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
* ----------
*/
static char *
-get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
+get_func_expr(QryHier *qh, int rt_index, Expr *expr, bool varprefix)
{
char buf[8192];
HeapTuple proctup;
@@ -1143,7 +1414,7 @@ get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
if (!strcmp(proname, "nullvalue"))
{
strcpy(buf, "(");
- strcat(buf, get_rule_expr(rtable, rt_index, lfirst(expr->args),
+ strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
varprefix));
strcat(buf, ") ISNULL");
return pstrdup(buf);
@@ -1151,7 +1422,7 @@ get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
if (!strcmp(proname, "nonnullvalue"))
{
strcpy(buf, "(");
- strcat(buf, get_rule_expr(rtable, rt_index, lfirst(expr->args),
+ strcat(buf, get_rule_expr(qh, rt_index, lfirst(expr->args),
varprefix));
strcat(buf, ") NOTNULL");
return pstrdup(buf);
@@ -1169,7 +1440,7 @@ get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
{
strcat(buf, sep);
sep = ", ";
- strcat(buf, get_rule_expr(rtable, rt_index, lfirst(l), varprefix));
+ strcat(buf, get_rule_expr(qh, rt_index, lfirst(l), varprefix));
}
strcat(buf, ")");
@@ -1194,7 +1465,7 @@ get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
* ----------
*/
static char *
-get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
+get_tle_expr(QryHier *qh, int rt_index, TargetEntry *tle, bool varprefix)
{
HeapTuple proctup;
Form_pg_proc procStruct;
@@ -1208,12 +1479,12 @@ get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
* ----------
*/
if (tle->resdom->restypmod < 0)
- return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ return get_rule_expr(qh, rt_index, tle->expr, varprefix);
if (nodeTag(tle->expr) != T_Expr)
- return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ return get_rule_expr(qh, rt_index, tle->expr, varprefix);
expr = (Expr *) (tle->expr);
if (expr->opType != FUNC_EXPR)
- return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ return get_rule_expr(qh, rt_index, tle->expr, varprefix);
func = (Func *) (expr->oper);
@@ -1235,11 +1506,11 @@ get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
* ----------
*/
if (procStruct->pronargs != 2)
- return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ return get_rule_expr(qh, rt_index, tle->expr, varprefix);
if (procStruct->prorettype != procStruct->proargtypes[0])
- return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ return get_rule_expr(qh, rt_index, tle->expr, varprefix);
if (procStruct->proargtypes[1] != INT4OID)
- return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ return get_rule_expr(qh, rt_index, tle->expr, varprefix);
/* ----------
* Finally (to be totally safe) the second argument must be a
@@ -1248,15 +1519,15 @@ get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
*/
second_arg = (Const *) nth(1, expr->args);
if (nodeTag((Node *) second_arg) != T_Const)
- return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ return get_rule_expr(qh, rt_index, tle->expr, varprefix);
if ((int4) (second_arg->constvalue) != tle->resdom->restypmod)
- return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ return get_rule_expr(qh, rt_index, tle->expr, varprefix);
/* ----------
* Whow - got it. Now get rid of the padding function
* ----------
*/
- return get_rule_expr(rtable, rt_index, lfirst(expr->args), varprefix);
+ return get_rule_expr(qh, rt_index, lfirst(expr->args), varprefix);
}
@@ -1274,6 +1545,7 @@ get_const_expr(Const *constval)
char *extval;
bool isnull = FALSE;
char buf[8192];
+ char namebuf[64];
if (constval->constisnull)
return "NULL";
@@ -1289,7 +1561,83 @@ get_const_expr(Const *constval)
extval = (char *) (*fmgr_faddr(&finfo_output)) (constval->constvalue,
&isnull, -1);
- sprintf(buf, "'%s'::%s", extval, nameout(&(typeStruct->typname)));
+ sprintf(namebuf, "::%s", nameout(&(typeStruct->typname)));
+ if (strcmp(namebuf, "::unknown") == 0)
+ namebuf[0] = '\0';
+ sprintf(buf, "'%s'%s", extval, namebuf);
+ return pstrdup(buf);
+}
+
+
+/* ----------
+ * get_sublink_expr - Parse back a sublink
+ * ----------
+ */
+static char *
+get_sublink_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
+{
+ SubLink *sublink = (SubLink *) node;
+ Query *query = (Query *) (sublink->subselect);
+ Expr *expr;
+ List *l;
+ char *sep;
+ char buf[8192];
+
+ buf[0] = '\0';
+
+ if (sublink->lefthand != NULL)
+ {
+ if (length(sublink->lefthand) > 1)
+ strcat(buf, "(");
+
+ sep = "";
+ foreach(l, sublink->lefthand)
+ {
+ strcat(buf, sep);
+ sep = ", ";
+ strcat(buf, get_rule_expr(qh, rt_index,
+ lfirst(l), varprefix));
+ }
+
+ if (length(sublink->lefthand) > 1)
+ strcat(buf, ") ");
+ else
+ strcat(buf, " ");
+ }
+
+ switch (sublink->subLinkType) {
+ case EXISTS_SUBLINK:
+ strcat(buf, "EXISTS ");
+ break;
+
+ case ANY_SUBLINK:
+ expr = (Expr *)lfirst(sublink->oper);
+ strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
+ strcat(buf, " ANY ");
+ break;
+
+ case ALL_SUBLINK:
+ expr = (Expr *)lfirst(sublink->oper);
+ strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
+ strcat(buf, " ALL ");
+ break;
+
+ case EXPR_SUBLINK:
+ expr = (Expr *)lfirst(sublink->oper);
+ strcat(buf, get_opname(((Oper *) (expr->oper))->opno));
+ strcat(buf, " ");
+ break;
+
+ default:
+ elog(ERROR, "unupported sublink type %d",
+ sublink->subLinkType);
+ break;
+ }
+
+ strcat(buf, "(");
+ strcat(buf, get_query_def(query, qh));
+ strcat(buf, ")");
+
return pstrdup(buf);
}
diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh
index 0117885d251..1ca47f40e2f 100644
--- a/src/bin/initdb/initdb.sh
+++ b/src/bin/initdb/initdb.sh
@@ -26,7 +26,7 @@
#
#
# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.55 1998/09/09 18:16:36 momjian Exp $
+# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.56 1998/10/02 16:27:53 momjian Exp $
#
#-------------------------------------------------------------------------
@@ -435,6 +435,7 @@ echo "REVOKE ALL on pg_shadow FROM public" | \
echo "Creating view pg_rules"
echo "CREATE TABLE xpg_rules ( \
+ tablename name, \
rulename name, \
definition text);" | postgres $PGSQL_OPT template1 > /dev/null
#move it into pg_rules
@@ -445,12 +446,18 @@ echo "UPDATE pg_type SET typname = 'pg_rules' WHERE typname = 'xpg_rules';" |\
mv $PGDATA/base/template1/xpg_rules $PGDATA/base/template1/pg_rules
echo "CREATE RULE \"_RETpg_rules\" AS ON SELECT TO pg_rules DO INSTEAD \
- SELECT rulename, pg_get_ruledef(rulename) AS definition \
- FROM pg_rewrite;" | postgres $PGSQL_OPT template1 > /dev/null
+ SELECT C.relname AS tablename, \
+ R.rulename AS rulename, \
+ pg_get_ruledef(R.rulename) AS definition \
+ FROM pg_rewrite R, pg_class C \
+ WHERE R.rulename !~ '^_RET' \
+ AND C.oid = R.ev_class;" | \
+ postgres $PGSQL_OPT template1 > /dev/null
echo "Creating view pg_views"
echo "CREATE TABLE xpg_views ( \
viewname name, \
+ viewowner name, \
definition text);" | postgres $PGSQL_OPT template1 > /dev/null
#move it into pg_views
echo "UPDATE pg_class SET relname = 'pg_views' WHERE relname = 'xpg_views';" |\
@@ -461,11 +468,57 @@ mv $PGDATA/base/template1/xpg_views $PGDATA/base/template1/pg_views
echo "CREATE RULE \"_RETpg_views\" AS ON SELECT TO pg_views DO INSTEAD \
SELECT relname AS viewname, \
+ pg_get_userbyid(relowner) AS viewowner, \
pg_get_viewdef(relname) AS definition \
FROM pg_class WHERE relhasrules AND \
pg_get_viewdef(relname) != 'Not a view';" | \
postgres $PGSQL_OPT template1 > /dev/null
+echo "Creating view pg_tables"
+echo "CREATE TABLE xpg_tables ( \
+ tablename name, \
+ tableowner name, \
+ hasindexes bool, \
+ hasrules bool, \
+ hastriggers bool);" | postgres $PGSQL_OPT template1 > /dev/null
+#move it into pg_tables
+echo "UPDATE pg_class SET relname = 'pg_tables' WHERE relname = 'xpg_tables';" |\
+ postgres $PGSQL_OPT template1 > /dev/null
+echo "UPDATE pg_type SET typname = 'pg_tables' WHERE typname = 'xpg_tables';" |\
+ postgres $PGSQL_OPT template1 > /dev/null
+mv $PGDATA/base/template1/xpg_tables $PGDATA/base/template1/pg_tables
+
+echo "CREATE RULE \"_RETpg_tables\" AS ON SELECT TO pg_tables DO INSTEAD \
+ SELECT relname AS tablename, \
+ pg_get_userbyid(relowner) AS tableowner, \
+ relhasindex AS hasindexes, \
+ relhasrules AS hasrules, \
+ (reltriggers > 0) AS hastriggers \
+ FROM pg_class WHERE relkind IN ('r', 's') \
+ AND pg_get_viewdef(relname) = 'Not a view';" | \
+ postgres $PGSQL_OPT template1 > /dev/null
+
+echo "Creating view pg_indexes"
+echo "CREATE TABLE xpg_indexes ( \
+ tablename name, \
+ indexname name, \
+ indexdef text);" | postgres $PGSQL_OPT template1 > /dev/null
+#move it into pg_indexes
+echo "UPDATE pg_class SET relname = 'pg_indexes' WHERE relname = 'xpg_indexes';" |\
+ postgres $PGSQL_OPT template1 > /dev/null
+echo "UPDATE pg_type SET typname = 'pg_indexes' WHERE typname = 'xpg_indexes';" |\
+ postgres $PGSQL_OPT template1 > /dev/null
+mv $PGDATA/base/template1/xpg_indexes $PGDATA/base/template1/pg_indexes
+
+echo "CREATE RULE \"_RETpg_indexes\" AS ON SELECT TO pg_indexes DO INSTEAD \
+ SELECT C.relname AS tablename, \
+ I.relname AS indexname, \
+ pg_get_indexdef(X.indexrelid) AS indexdef \
+ FROM pg_index X, pg_class C, pg_class I \
+ WHERE C.oid = X.indrelid \
+ AND I.oid = X.indexrelid;" | \
+ postgres $PGSQL_OPT template1 > /dev/null
+
echo "Loading pg_description"
echo "copy pg_description from '$TEMPLATE_DESCR'" | \
postgres $PGSQL_OPT template1 > /dev/null
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 06cb99d0c15..1f8ca515837 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_proc.h,v 1.69 1998/09/01 04:35:10 momjian Exp $
+ * $Id: pg_proc.h,v 1.70 1998/10/02 16:27:55 momjian Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@@ -2040,6 +2040,10 @@ DATA(insert OID = 1640 ( pg_get_ruledef PGUID 11 f t f 1 f 25 "19" 100 0 0 1
DESCR("source text of a rule");
DATA(insert OID = 1641 ( pg_get_viewdef PGUID 11 f t f 1 f 25 "19" 100 0 0 100 foo bar ));
DESCR("select statement of a view");
+DATA(insert OID = 1642 ( pg_get_userbyid PGUID 11 f t f 1 f 19 "23" 100 0 0 100 foo bar ));
+DESCR("user name by UID (with fallback)");
+DATA(insert OID = 1643 ( pg_get_indexdef PGUID 11 f t f 1 f 25 "26" 100 0 0 100 foo bar ));
+DESCR("index description");
/*
* prototypes for functions pg_proc.c
diff --git a/src/include/rewrite/locks.h b/src/include/rewrite/locks.h
index b51b02463ed..ad258d9063e 100644
--- a/src/include/rewrite/locks.h
+++ b/src/include/rewrite/locks.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: locks.h,v 1.9 1998/09/01 04:37:57 momjian Exp $
+ * $Id: locks.h,v 1.10 1998/10/02 16:27:58 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,5 +19,6 @@
extern List *matchLocks(CmdType event, RuleLock *rulelocks, int varno,
Query *parsetree);
+extern void checkLockPerms(List *locks, Query *parsetree, int rt_index);
#endif /* LOCKS_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 447b1499faf..f77a532d6e2 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -671,3 +671,181 @@ QUERY: select * from rtest_nothn3;
200|OK
(2 rows)
+QUERY: create table rtest_view1 (a int4, b text, v bool);
+QUERY: create table rtest_view2 (a int4);
+QUERY: create table rtest_view3 (a int4, b text);
+QUERY: create table rtest_view4 (a int4, b text, c int4);
+QUERY: create view rtest_vview1 as select a, b from rtest_view1 X
+ where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a);
+QUERY: create view rtest_vview2 as select a, b from rtest_view1 where v;
+QUERY: create view rtest_vview3 as select a, b from rtest_vview2 X
+ where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a);
+QUERY: create view rtest_vview4 as select X.a, X.b, count(Y.a) as refcount
+ from rtest_view1 X, rtest_view2 Y
+ where X.a = Y.a
+ group by X.a, X.b;
+QUERY: create function rtest_viewfunc1(int4) returns int4 as
+ 'select count(*) from rtest_view2 where a = $1'
+ language 'sql';
+QUERY: create view rtest_vview5 as select a, b, rtest_viewfunc1(a) as refcount
+ from rtest_view1;
+QUERY: insert into rtest_view1 values (1, 'item 1', 't');
+QUERY: insert into rtest_view1 values (2, 'item 2', 't');
+QUERY: insert into rtest_view1 values (3, 'item 3', 't');
+QUERY: insert into rtest_view1 values (4, 'item 4', 'f');
+QUERY: insert into rtest_view1 values (5, 'item 5', 't');
+QUERY: insert into rtest_view1 values (6, 'item 6', 'f');
+QUERY: insert into rtest_view1 values (7, 'item 7', 't');
+QUERY: insert into rtest_view1 values (8, 'item 8', 't');
+QUERY: insert into rtest_view2 values (2);
+QUERY: insert into rtest_view2 values (2);
+QUERY: insert into rtest_view2 values (4);
+QUERY: insert into rtest_view2 values (5);
+QUERY: insert into rtest_view2 values (7);
+QUERY: insert into rtest_view2 values (7);
+QUERY: insert into rtest_view2 values (7);
+QUERY: insert into rtest_view2 values (7);
+QUERY: select * from rtest_vview1;
+a|b
+-+------
+2|item 2
+4|item 4
+5|item 5
+7|item 7
+(4 rows)
+
+QUERY: select * from rtest_vview2;
+a|b
+-+------
+1|item 1
+2|item 2
+3|item 3
+5|item 5
+7|item 7
+8|item 8
+(6 rows)
+
+QUERY: select * from rtest_vview3;
+a|b
+-+------
+2|item 2
+5|item 5
+7|item 7
+(3 rows)
+
+QUERY: select * from rtest_vview4;
+a|b |refcount
+-+------+--------
+2|item 2| 2
+4|item 4| 1
+5|item 5| 1
+7|item 7| 4
+(4 rows)
+
+QUERY: select * from rtest_vview5;
+a|b |refcount
+-+------+--------
+1|item 1| 0
+2|item 2| 2
+3|item 3| 0
+4|item 4| 1
+5|item 5| 1
+6|item 6| 0
+7|item 7| 4
+8|item 8| 0
+(8 rows)
+
+QUERY: insert into rtest_view3 select * from rtest_vview1 where a < 7;
+QUERY: select * from rtest_view3;
+a|b
+-+------
+2|item 2
+4|item 4
+5|item 5
+(3 rows)
+
+QUERY: delete from rtest_view3;
+QUERY: insert into rtest_view3 select * from rtest_vview2 where a != 5 and b !~ '2';
+QUERY: select * from rtest_view3;
+a|b
+-+------
+1|item 1
+3|item 3
+7|item 7
+8|item 8
+(4 rows)
+
+QUERY: delete from rtest_view3;
+QUERY: insert into rtest_view3 select * from rtest_vview3;
+QUERY: select * from rtest_view3;
+a|b
+-+------
+2|item 2
+5|item 5
+7|item 7
+(3 rows)
+
+QUERY: delete from rtest_view3;
+QUERY: insert into rtest_view4 select * from rtest_vview4 where 3 > refcount;
+QUERY: select * from rtest_view4;
+a|b |c
+-+------+-
+2|item 2|2
+4|item 4|1
+5|item 5|1
+(3 rows)
+
+QUERY: delete from rtest_view4;
+QUERY: insert into rtest_view4 select * from rtest_vview5 where a > 2 and refcount = 0;
+QUERY: select * from rtest_view4;
+a|b |c
+-+------+-
+3|item 3|0
+6|item 6|0
+8|item 8|0
+(3 rows)
+
+QUERY: delete from rtest_view4;
+QUERY: create table rtest_comp (
+ part text,
+ unit char(4),
+ size float
+);
+QUERY: create table rtest_unitfact (
+ unit char(4),
+ factor float
+);
+QUERY: create view rtest_vcomp as
+ select X.part, (X.size * Y.factor) as size_in_cm
+ from rtest_comp X, rtest_unitfact Y
+ where X.unit = Y.unit;
+QUERY: insert into rtest_unitfact values ('m', 100.0);
+QUERY: insert into rtest_unitfact values ('cm', 1.0);
+QUERY: insert into rtest_unitfact values ('inch', 2.54);
+QUERY: insert into rtest_comp values ('p1', 'm', 5.0);
+QUERY: insert into rtest_comp values ('p2', 'm', 3.0);
+QUERY: insert into rtest_comp values ('p3', 'cm', 5.0);
+QUERY: insert into rtest_comp values ('p4', 'cm', 15.0);
+QUERY: insert into rtest_comp values ('p5', 'inch', 7.0);
+QUERY: insert into rtest_comp values ('p6', 'inch', 4.4);
+QUERY: select * from rtest_vcomp order by part;
+part|size_in_cm
+----+----------
+p1 | 500
+p2 | 300
+p3 | 5
+p4 | 15
+p5 | 17.78
+p6 | 11.176
+(6 rows)
+
+QUERY: select * from rtest_vcomp where size_in_cm > 10.0 order by size_in_cm using >;
+part|size_in_cm
+----+----------
+p1 | 500
+p2 | 300
+p5 | 17.78
+p4 | 15
+p6 | 11.176
+(5 rows)
+
diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql
index 6ca18775863..8ffefd5be00 100644
--- a/src/test/regress/sql/rules.sql
+++ b/src/test/regress/sql/rules.sql
@@ -404,3 +404,100 @@ insert into rtest_nothn2 select * from rtest_nothn4;
select * from rtest_nothn2;
select * from rtest_nothn3;
+create table rtest_view1 (a int4, b text, v bool);
+create table rtest_view2 (a int4);
+create table rtest_view3 (a int4, b text);
+create table rtest_view4 (a int4, b text, c int4);
+create view rtest_vview1 as select a, b from rtest_view1 X
+ where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a);
+create view rtest_vview2 as select a, b from rtest_view1 where v;
+create view rtest_vview3 as select a, b from rtest_vview2 X
+ where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a);
+create view rtest_vview4 as select X.a, X.b, count(Y.a) as refcount
+ from rtest_view1 X, rtest_view2 Y
+ where X.a = Y.a
+ group by X.a, X.b;
+create function rtest_viewfunc1(int4) returns int4 as
+ 'select count(*) from rtest_view2 where a = $1'
+ language 'sql';
+create view rtest_vview5 as select a, b, rtest_viewfunc1(a) as refcount
+ from rtest_view1;
+
+insert into rtest_view1 values (1, 'item 1', 't');
+insert into rtest_view1 values (2, 'item 2', 't');
+insert into rtest_view1 values (3, 'item 3', 't');
+insert into rtest_view1 values (4, 'item 4', 'f');
+insert into rtest_view1 values (5, 'item 5', 't');
+insert into rtest_view1 values (6, 'item 6', 'f');
+insert into rtest_view1 values (7, 'item 7', 't');
+insert into rtest_view1 values (8, 'item 8', 't');
+
+insert into rtest_view2 values (2);
+insert into rtest_view2 values (2);
+insert into rtest_view2 values (4);
+insert into rtest_view2 values (5);
+insert into rtest_view2 values (7);
+insert into rtest_view2 values (7);
+insert into rtest_view2 values (7);
+insert into rtest_view2 values (7);
+
+select * from rtest_vview1;
+select * from rtest_vview2;
+select * from rtest_vview3;
+select * from rtest_vview4;
+select * from rtest_vview5;
+
+insert into rtest_view3 select * from rtest_vview1 where a < 7;
+select * from rtest_view3;
+delete from rtest_view3;
+
+insert into rtest_view3 select * from rtest_vview2 where a != 5 and b !~ '2';
+select * from rtest_view3;
+delete from rtest_view3;
+
+insert into rtest_view3 select * from rtest_vview3;
+select * from rtest_view3;
+delete from rtest_view3;
+
+insert into rtest_view4 select * from rtest_vview4 where 3 > refcount;
+select * from rtest_view4;
+delete from rtest_view4;
+
+insert into rtest_view4 select * from rtest_vview5 where a > 2 and refcount = 0;
+select * from rtest_view4;
+delete from rtest_view4;
+--
+-- Test for computations in views
+--
+create table rtest_comp (
+ part text,
+ unit char(4),
+ size float
+);
+
+
+create table rtest_unitfact (
+ unit char(4),
+ factor float
+);
+
+create view rtest_vcomp as
+ select X.part, (X.size * Y.factor) as size_in_cm
+ from rtest_comp X, rtest_unitfact Y
+ where X.unit = Y.unit;
+
+
+insert into rtest_unitfact values ('m', 100.0);
+insert into rtest_unitfact values ('cm', 1.0);
+insert into rtest_unitfact values ('inch', 2.54);
+
+insert into rtest_comp values ('p1', 'm', 5.0);
+insert into rtest_comp values ('p2', 'm', 3.0);
+insert into rtest_comp values ('p3', 'cm', 5.0);
+insert into rtest_comp values ('p4', 'cm', 15.0);
+insert into rtest_comp values ('p5', 'inch', 7.0);
+insert into rtest_comp values ('p6', 'inch', 4.4);
+
+select * from rtest_vcomp order by part;
+
+select * from rtest_vcomp where size_in_cm > 10.0 order by size_in_cm using >;