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