aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/heap.c6
-rw-r--r--src/backend/catalog/pg_proc.c4
-rw-r--r--src/backend/executor/functions.c4
-rw-r--r--src/backend/executor/spi.c2
-rw-r--r--src/backend/libpq/be-pqexec.c4
-rw-r--r--src/backend/optimizer/path/xfunc.c4
-rw-r--r--src/backend/parser/keywords.c10
-rw-r--r--src/backend/rewrite/locks.c103
-rw-r--r--src/backend/rewrite/rewriteDefine.c55
-rw-r--r--src/backend/rewrite/rewriteHandler.c119
-rw-r--r--src/backend/rewrite/rewriteSupport.c6
-rw-r--r--src/backend/tcop/postgres.c94
-rw-r--r--src/backend/utils/adt/Makefile6
-rw-r--r--src/backend/utils/adt/ruleutils.c1376
-rw-r--r--src/bin/initdb/initdb.sh35
-rw-r--r--src/include/catalog/pg_proc.h8
-rw-r--r--src/include/tcop/tcopprot.h8
17 files changed, 1719 insertions, 125 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 84f6849357f..99c3b485aaa 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.59 1998/08/20 22:07:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.60 1998/08/24 01:37:46 momjian Exp $
*
* INTERFACE ROUTINES
* heap_create() - Create an uncataloged heap relation
@@ -1448,7 +1448,7 @@ start:;
sprintf(str, "select %s%s from %.*s", attrdef->adsrc, cast,
NAMEDATALEN, rel->rd_rel->relname.data);
setheapoverride(true);
- planTree_list = (List *) pg_parse_and_plan(str, NULL, 0, &queryTree_list, None);
+ planTree_list = (List *) pg_parse_and_plan(str, NULL, 0, &queryTree_list, None, FALSE);
setheapoverride(false);
query = (Query *) (queryTree_list->qtrees[0]);
@@ -1519,7 +1519,7 @@ StoreRelCheck(Relation rel, ConstrCheck *check)
sprintf(str, "select 1 from %.*s where %s",
NAMEDATALEN, rel->rd_rel->relname.data, check->ccsrc);
setheapoverride(true);
- planTree_list = (List *) pg_parse_and_plan(str, NULL, 0, &queryTree_list, None);
+ planTree_list = (List *) pg_parse_and_plan(str, NULL, 0, &queryTree_list, None, FALSE);
setheapoverride(false);
query = (Query *) (queryTree_list->qtrees[0]);
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index ecebeb84d19..5cd560907b5 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.19 1998/08/19 02:01:37 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.20 1998/08/24 01:37:47 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -214,7 +214,7 @@ ProcedureCreate(char *procedureName,
if (strcmp(languageName, "sql") == 0)
{
plan_list = pg_parse_and_plan(prosrc, typev, parameterCount,
- &querytree_list, dest);
+ &querytree_list, dest, FALSE);
/* typecheck return value */
pg_checkretval(typeObjectId, querytree_list);
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index fe14f3df728..645a312fbf8 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.17 1998/06/15 19:28:20 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.18 1998/08/24 01:37:48 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -113,7 +113,7 @@ init_execution_state(FunctionCachePtr fcache,
planTree_list = (List *)
- pg_parse_and_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None);
+ pg_parse_and_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None, FALSE);
for (i = 0; i < queryTree_list->len; i++)
{
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index e2e87d88e06..0f559e1bcdf 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -642,7 +642,7 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
argtypes = plan->argtypes;
}
ptlist = planTree_list = (List *)
- pg_parse_and_plan(src, argtypes, nargs, &queryTree_list, None);
+ pg_parse_and_plan(src, argtypes, nargs, &queryTree_list, None, FALSE);
_SPI_current->qtlist = queryTree_list;
diff --git a/src/backend/libpq/be-pqexec.c b/src/backend/libpq/be-pqexec.c
index 38b4536ccf9..2d3952bebad 100644
--- a/src/backend/libpq/be-pqexec.c
+++ b/src/backend/libpq/be-pqexec.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.17 1998/06/15 19:28:25 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.18 1998/08/24 01:37:52 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -137,7 +137,7 @@ PQexec(char *query)
* end up on the top of the portal stack.
* ----------------
*/
- pg_exec_query_dest(query, Local);
+ pg_exec_query_dest(query, Local, FALSE);
/* ----------------
* pop the portal off the portal stack and return the
diff --git a/src/backend/optimizer/path/xfunc.c b/src/backend/optimizer/path/xfunc.c
index eb66a4f6e2e..0346dcf4720 100644
--- a/src/backend/optimizer/path/xfunc.c
+++ b/src/backend/optimizer/path/xfunc.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/xfunc.c,v 1.19 1998/08/19 02:02:13 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/xfunc.c,v 1.20 1998/08/24 01:37:53 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -528,7 +528,7 @@ xfunc_func_expense(LispValue node, LispValue args)
if (nargs > 0)
argOidVect = proc->proargtypes;
planlist = (List) pg_parse_and_plan(pq_src, argOidVect, nargs,
- &parseTree_list, None);
+ &parseTree_list, None, FALSE);
if (IsA(node, Func))
set_func_planlist((Func) node, planlist);
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index c3b53722a31..24cd4e82721 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.38 1998/07/24 03:31:24 scrappy Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.39 1998/08/24 01:37:55 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -66,7 +66,12 @@ static ScanKeyword ScanKeywords[] = {
{"createdb", CREATEDB},
{"createuser", CREATEUSER},
{"cross", CROSS},
- {"current", CURRENT},
+ {"current", CURRENT}, /*
+ * 6.4 to 6.5 is migration time!
+ * CURRENT will be removed in 6.5!
+ * Use OLD keyword in rules.
+ * Jan
+ */
{"current_date", CURRENT_DATE},
{"current_time", CURRENT_TIME},
{"current_timestamp", CURRENT_TIMESTAMP},
@@ -152,6 +157,7 @@ static ScanKeyword ScanKeywords[] = {
{"null", NULL_P},
{"numeric", NUMERIC},
{"oids", OIDS},
+ {"old", OLD},
{"on", ON},
{"operator", OPERATOR},
{"option", OPTION},
diff --git a/src/backend/rewrite/locks.c b/src/backend/rewrite/locks.c
index 04698be13e3..46727d94c6e 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.10 1998/06/15 19:29:06 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.11 1998/08/24 01:37:56 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,6 +18,14 @@
#include "utils/syscache.h" /* for SearchSysCache */
#include "rewrite/locks.h" /* for rewrite specific lock defns */
+#include "access/heapam.h" /* for ACL checking */
+#include "utils/syscache.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "catalog/pg_shadow.h"
+
+static void checkLockPerms(List *locks, Query *parsetree, int rt_index);
+
/*
* ThisLockWasTriggered
*
@@ -156,5 +164,98 @@ matchLocks(CmdType event,
}
}
+ checkLockPerms(real_locks, parsetree, varno);
+
return (real_locks);
}
+
+
+static void
+checkLockPerms(List *locks, Query *parsetree, int rt_index)
+{
+ Relation ev_rel;
+ HeapTuple usertup;
+ char *evowner;
+ RangeTblEntry *rte;
+ int32 reqperm;
+ int32 aclcheck_res;
+ int i;
+ List *l;
+
+ if (locks == NIL)
+ return;
+
+ /*
+ * Get the usename of the rules event relation owner
+ */
+ rte = (RangeTblEntry *)nth(rt_index - 1, parsetree->rtable);
+ ev_rel = heap_openr(rte->relname);
+ usertup = SearchSysCacheTuple(USESYSID,
+ ObjectIdGetDatum(ev_rel->rd_rel->relowner),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(usertup))
+ {
+ elog(ERROR, "cache lookup for userid %d failed",
+ ev_rel->rd_rel->relowner);
+ }
+ heap_close(ev_rel);
+ evowner = nameout(&(((Form_pg_shadow) GETSTRUCT(usertup))->usename));
+
+ /*
+ * Check all the locks, that should get fired on this query
+ */
+ foreach (l, locks) {
+ RewriteRule *onelock = (RewriteRule *)lfirst(l);
+ List *action;
+
+ /*
+ * In each lock check every action
+ */
+ foreach (action, onelock->actions) {
+ Query *query = (Query *)lfirst(action);
+
+ /*
+ * In each action check every rangetable entry
+ * for read/write permission of the event relations
+ * owner depending on if it's the result relation
+ * (write) or not (read)
+ */
+ for (i = 2; i < length(query->rtable); i++) {
+ if (i + 1 == query->resultRelation)
+ switch (query->resultRelation) {
+ case CMD_INSERT:
+ reqperm = ACL_AP;
+ break;
+ default:
+ reqperm = ACL_WR;
+ break;
+ }
+ else
+ reqperm = ACL_RD;
+
+ rte = (RangeTblEntry *)nth(i, query->rtable);
+ aclcheck_res = pg_aclcheck(rte->relname,
+ evowner, reqperm);
+ if (aclcheck_res != ACLCHECK_OK) {
+ elog(ERROR, "%s: %s",
+ rte->relname,
+ aclcheck_error_strings[aclcheck_res]);
+ }
+
+ /*
+ * So this is allowed due to the permissions
+ * of the rules event relation owner. But
+ * let's see if the next one too
+ */
+ rte->skipAcl = TRUE;
+ }
+ }
+ }
+
+ /*
+ * Phew, that was close
+ */
+ return;
+}
+
+
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 4d8ecb8b1e9..c5c3b887867 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.18 1998/08/19 02:02:29 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.19 1998/08/24 01:37:58 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -131,7 +131,7 @@ InsertRule(char *rulname,
rulname, evtype, eventrel_oid, evslot_index, actionbuf,
qualbuf, is_instead);
- pg_exec_query(rulebuf);
+ pg_exec_query_acl_override(rulebuf);
return (LastOidProcessed);
}
@@ -192,12 +192,61 @@ DefineQueryRewrite(RuleStmt *stmt)
Oid event_attype = 0;
char *actionP,
*event_qualP;
-
+ List *l;
+ Query *query;
+
+ /* ----------
+ * 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.
+ *
+ * Jan
+ * ----------
+ */
if (event_obj->attrs)
+ elog(ERROR, "attribute level rules currently not supported");
+ /*
eslot_string = strVal(lfirst(event_obj->attrs));
+ */
else
eslot_string = NULL;
+ if (action != NIL)
+ foreach (l, action) {
+ query = (Query *)lfirst(l);
+ if (query->resultRelation == 1) {
+ elog(NOTICE, "rule actions on OLD currently not supported");
+ elog(ERROR, " use views or triggers instead");
+ }
+ if (query->resultRelation == 2) {
+ elog(NOTICE, "rule actions on NEW currently not supported");
+ elog(ERROR, " use triggers instead");
+ }
+ }
+
+ if (event_type == CMD_SELECT) {
+ if (length(action) == 0) {
+ elog(NOTICE, "instead nothing rules on select currently not supported");
+ elog(ERROR, " use views instead");
+ }
+ if (length(action) > 1) {
+ elog(ERROR, "multiple action rules on select currently not supported");
+ }
+ query = (Query *)lfirst(action);
+ if (!is_instead || query->commandType != CMD_SELECT) {
+ elog(ERROR, "only instead-select rules currently supported on select");
+ }
+ }
+ /*
+ * This rule is currently allowed - too restricted I know -
+ * but women and children first
+ * Jan
+ */
+
event_relation = heap_openr(event_obj->relname);
if (event_relation == NULL)
elog(ERROR, "virtual relations not supported yet");
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 68169cbf2d2..02bbc69a8ca 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.19 1998/08/19 02:02:30 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.20 1998/08/24 01:37:59 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -45,7 +45,6 @@ fireRules(Query *parsetree, int rt_index, CmdType event,
static void QueryRewriteSubLink(Node *node);
static List *QueryRewriteOne(Query *parsetree);
static List *deepRewriteQuery(Query *parsetree);
-static void CheckViewPerms(Relation view, List *rtable);
static void RewritePreprocessQuery(Query *parsetree);
static Query *RewritePostprocessNonSelect(Query *parsetree);
@@ -273,7 +272,6 @@ ApplyRetrieveRule(Query *parsetree,
int nothing,
rt_length;
int badsql = FALSE;
- int viewAclOverride = FALSE;
rule_qual = rule->qual;
if (rule->actions)
@@ -291,19 +289,6 @@ ApplyRetrieveRule(Query *parsetree,
return;
rule_action = copyObject(lfirst(rule->actions));
nothing = FALSE;
-
- /*
- * If this rule is on the relation level, the rule action is a
- * select and the rule is instead then it must be a view.
- * Permissions for views now follow the owner of the view, not the
- * current user.
- */
- if (relation_level && rule_action->commandType == CMD_SELECT
- && rule->isInstead)
- {
- CheckViewPerms(relation, rule_action->rtable);
- viewAclOverride = TRUE;
- }
}
else
nothing = TRUE;
@@ -321,28 +306,7 @@ ApplyRetrieveRule(Query *parsetree,
}
rt_length = length(rtable);
- if (viewAclOverride)
- {
- List *rule_rtable,
- *rule_rt;
- RangeTblEntry *rte;
-
- rule_rtable = copyObject(rule_action->rtable);
- foreach(rule_rt, rule_rtable)
- {
- rte = lfirst(rule_rt);
-
- /*
- * tell the executor that the ACL check on this range table
- * entry is already done
- */
- rte->skipAcl = true;
- }
-
- rtable = nconc(rtable, rule_rtable);
- }
- else
- rtable = nconc(rtable, copyObject(rule_action->rtable));
+ rtable = nconc(rtable, copyObject(rule_action->rtable));
parsetree->rtable = rtable;
rule_action->rtable = rtable;
@@ -425,6 +389,8 @@ ProcessRetrieveQuery(Query *parsetree,
if (rule)
return NIL;
+ rt_index = 0;
+
foreach(rt, rtable)
{
RangeTblEntry *rt_entry = lfirst(rt);
@@ -537,6 +503,44 @@ fireRules(Query *parsetree,
List *r;
bool orig_instead_flag = *instead_flag;
+ /*
+ * Instead rules change the resultRelation of the
+ * query. So the permission checks on the initial
+ * resultRelation would never be done (this is
+ * normally done in the executor deep down). So
+ * we must do it here. The result relations resulting
+ * from earlier rewrites are already checked against
+ * the rules eventrelation owner (during matchLocks)
+ * and have the skipAcl flag set.
+ */
+ if (rule_lock->isInstead &&
+ parsetree->commandType != CMD_SELECT) {
+ RangeTblEntry *rte;
+ int32 acl_rc;
+ int32 reqperm;
+
+ switch (parsetree->commandType) {
+ case CMD_INSERT:
+ reqperm = ACL_AP;
+ break;
+ default:
+ reqperm = ACL_WR;
+ break;
+ }
+
+ rte = (RangeTblEntry *)nth(parsetree->resultRelation - 1,
+ parsetree->rtable);
+ if (!rte->skipAcl) {
+ acl_rc = pg_aclcheck(rte->relname,
+ GetPgUserName(), reqperm);
+ if (acl_rc != ACLCHECK_OK) {
+ elog(ERROR, "%s: %s",
+ rte->relname,
+ aclcheck_error_strings[acl_rc]);
+ }
+ }
+ }
+
/* multiple rule action time */
*instead_flag = rule_lock->isInstead;
event_qual = rule_lock->qual;
@@ -1024,42 +1028,3 @@ deepRewriteQuery(Query *parsetree)
return rewritten;
}
-
-
-static void
-CheckViewPerms(Relation view, List *rtable)
-{
- HeapTuple utup;
- NameData uname;
- List *rt;
- RangeTblEntry *rte;
- int32 aclcheck_res;
-
- /*
- * get the usename of the view's owner
- */
- utup = SearchSysCacheTuple(USESYSID,
- ObjectIdGetDatum(view->rd_rel->relowner),
- 0, 0, 0);
- if (!HeapTupleIsValid(utup))
- {
- elog(ERROR, "cache lookup for userid %d failed",
- view->rd_rel->relowner);
- }
- StrNCpy(uname.data,
- ((Form_pg_shadow) GETSTRUCT(utup))->usename.data,
- NAMEDATALEN);
-
- /*
- * check that we have read access to all the classes in the range
- * table of the view
- */
- foreach(rt, rtable)
- {
- rte = (RangeTblEntry *) lfirst(rt);
-
- aclcheck_res = pg_aclcheck(rte->relname, uname.data, ACL_RD);
- if (aclcheck_res != ACLCHECK_OK)
- elog(ERROR, "%s: %s", rte->relname, aclcheck_error_strings[aclcheck_res]);
- }
-}
diff --git a/src/backend/rewrite/rewriteSupport.c b/src/backend/rewrite/rewriteSupport.c
index dae10410809..4c17cccb7f7 100644
--- a/src/backend/rewrite/rewriteSupport.c
+++ b/src/backend/rewrite/rewriteSupport.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.25 1998/08/19 02:02:33 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.26 1998/08/24 01:38:01 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -158,6 +158,10 @@ prs2_addToRelation(Oid relid,
*/
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
thisRule = (RewriteRule *) palloc(sizeof(RewriteRule));
+ if (qual != NULL)
+ qual = copyObject(qual);
+ if (actions != NIL)
+ actions = copyObject(actions);
MemoryContextSwitchTo(oldcxt);
thisRule->ruleId = ruleId;
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index f158a0f1003..51af2f84a5a 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.82 1998/08/04 16:44:20 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.83 1998/08/24 01:38:02 momjian Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@@ -380,7 +380,8 @@ pg_parse_and_plan(char *query_string, /* string to execute */
int nargs, /* number of arguments */
QueryTreeList **queryListP, /* pointer to the parse
* trees */
- CommandDest dest) /* where results should go */
+ CommandDest dest, /* where results should go */
+ bool aclOverride)
{
QueryTreeList *querytree_list;
int i;
@@ -451,24 +452,24 @@ pg_parse_and_plan(char *query_string, /* string to execute */
/* rewrite queries (retrieve, append, delete, replace) */
rewritten = QueryRewrite(querytree);
- /*
- * Rewrite the UNIONS.
- */
- foreach(rewritten_list, rewritten)
- {
- Query *qry = (Query *) lfirst(rewritten_list);
-
- union_result = NIL;
- foreach(union_list, qry->unionClause)
- union_result = nconc(union_result, QueryRewrite((Query *) lfirst(union_list)));
- qry->unionClause = union_result;
- }
-
- if (rewritten != NULL)
+ if (rewritten != NIL)
{
int len,
k;
+ /*
+ * Rewrite the UNIONS.
+ */
+ foreach(rewritten_list, rewritten)
+ {
+ Query *qry = (Query *) lfirst(rewritten_list);
+
+ union_result = NIL;
+ foreach(union_list, qry->unionClause)
+ union_result = nconc(union_result, QueryRewrite((Query *) lfirst(union_list)));
+ qry->unionClause = union_result;
+ }
+
len = length(rewritten);
if (len == 1)
new_list->qtrees[j++] = (Query *) lfirst(rewritten);
@@ -487,12 +488,40 @@ pg_parse_and_plan(char *query_string, /* string to execute */
}
}
+ /* ----------
+ * Due to rewriting, the new list could also have been
+ * shrunk (do instead nothing). Forget obsolete queries
+ * at the end.
+ * ----------
+ */
+ new_list->len = j;
+
/* we're done with the original lists, free it */
free(querytree_list->qtrees);
free(querytree_list);
querytree_list = new_list;
+ /*
+ * Override ACL checking if requested
+ */
+ if (aclOverride) {
+ for (i = 0; i < querytree_list->len; i++) {
+ RangeTblEntry *rte;
+ List *l;
+
+ querytree = querytree_list->qtrees[i];
+ if (querytree->commandType == CMD_UTILITY)
+ continue;
+
+ foreach (l, querytree->rtable) {
+ rte = (RangeTblEntry *)lfirst(l);
+
+ rte->skipAcl = TRUE;
+ }
+ }
+ }
+
if (DebugPrintRewrittenParsetree == true)
{
printf("\n---- \tafter rewriting:\n");
@@ -530,7 +559,10 @@ pg_parse_and_plan(char *query_string, /* string to execute */
elog(NOTICE, "(transaction aborted): %s",
"queries ignored until END");
- *queryListP = (QueryTreeList *) NULL;
+ free(querytree_list->qtrees);
+ free(querytree_list);
+ if (queryListP)
+ *queryListP = (QueryTreeList *) NULL;
return (List *) NULL;
}
@@ -573,6 +605,16 @@ pg_parse_and_plan(char *query_string, /* string to execute */
#endif
}
+ /* ----------
+ * Check if the rewriting had thrown away anything
+ * ----------
+ */
+ if (querytree_list->len == 0) {
+ free(querytree_list->qtrees);
+ free(querytree_list);
+ querytree_list = NULL;
+ }
+
if (queryListP)
*queryListP = querytree_list;
@@ -599,12 +641,22 @@ pg_parse_and_plan(char *query_string, /* string to execute */
void
pg_exec_query(char *query_string)
{
- pg_exec_query_dest(query_string, whereToSendOutput);
+ pg_exec_query_dest(query_string, whereToSendOutput, FALSE);
+}
+
+void
+pg_exec_query_acl_override(char *query_string)
+{
+ pg_exec_query_dest(query_string, whereToSendOutput, TRUE);
}
void
pg_exec_query_dest(char *query_string, /* string to execute */
- CommandDest dest) /* where results should go */
+ CommandDest dest, /* where results should go */
+ bool aclOverride) /* to give utility
+ * commands power of
+ * superusers
+ */
{
List *plan_list;
Plan *plan;
@@ -614,7 +666,7 @@ pg_exec_query_dest(char *query_string, /* string to execute */
QueryTreeList *querytree_list;
/* plan the queries */
- plan_list = pg_parse_and_plan(query_string, NULL, 0, &querytree_list, dest);
+ plan_list = pg_parse_and_plan(query_string, NULL, 0, &querytree_list, dest, aclOverride);
if (QueryCancel)
CancelQuery();
@@ -1339,7 +1391,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface");
- puts("$Revision: 1.82 $ $Date: 1998/08/04 16:44:20 $");
+ puts("$Revision: 1.83 $ $Date: 1998/08/24 01:38:02 $");
}
/* ----------------
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 9d2f4df3e3c..d9812a52367 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -4,7 +4,7 @@
# Makefile for utils/adt
#
# IDENTIFICATION
-# $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.16 1998/08/19 02:02:52 momjian Exp $
+# $Header: /cvsroot/pgsql/src/backend/utils/adt/Makefile,v 1.17 1998/08/24 01:38:04 momjian Exp $
#
#-------------------------------------------------------------------------
@@ -22,8 +22,8 @@ OBJS = acl.o arrayfuncs.o arrayutils.o bool.o cash.o char.o chunk.o \
geo_ops.o geo_selfuncs.o int.o int8.o like.o \
misc.o nabstime.o name.o not_in.o numutils.o \
oid.o oracle_compat.o \
- regexp.o regproc.o selfuncs.o sets.o tid.o timestamp.o \
- varchar.o varlena.o version.o
+ regexp.o regproc.o ruleutils.o selfuncs.o sets.o \
+ tid.o timestamp.o varchar.o varlena.o version.o
all: SUBSYS.o
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
new file mode 100644
index 00000000000..3b7c3e98fa2
--- /dev/null
+++ b/src/backend/utils/adt/ruleutils.c
@@ -0,0 +1,1376 @@
+/**********************************************************************
+ * get_ruledef.c - Function to get a rules definition text
+ * out of it's tuple
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.1 1998/08/24 01:38:05 momjian Exp $
+ *
+ * This software is copyrighted by Jan Wieck - Hamburg.
+ *
+ * The author hereby grants permission to use, copy, modify,
+ * distribute, and license this software and its documentation
+ * for any purpose, provided that existing copyright notices are
+ * retained in all copies and that this notice is included
+ * verbatim in any distributions. No written agreement, license,
+ * or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their
+ * author and need not follow the licensing terms described
+ * here, provided that the new terms are clearly indicated on
+ * the first page of each file where they apply.
+ *
+ * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
+ * SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
+ * IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON
+ * AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
+ * ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ **********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "executor/spi.h"
+#include "commands/trigger.h"
+#include "utils/elog.h"
+#include "utils/builtins.h"
+#include "nodes/nodes.h"
+#include "optimizer/clauses.h"
+#include "utils/syscache.h"
+#include "utils/lsyscache.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_type.h"
+#include "fmgr.h"
+
+
+/* ----------
+ * Global data
+ * ----------
+ */
+static char *rulename;
+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";
+
+
+/* ----------
+ * Global functions
+ * ----------
+ */
+text *pg_get_ruledef(NameData *rname);
+text *pg_get_viewdef(NameData *rname);
+
+
+/* ----------
+ * Local functions
+ * ----------
+ */
+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_const_expr(Const *constval);
+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);
+
+
+/* ----------
+ * get_ruledef - Do it all and return a text
+ * that could be used as a statement
+ * to recreate the rule
+ * ----------
+ */
+text *
+pg_get_ruledef(NameData *rname)
+{
+ text *ruledef;
+ Datum args[1];
+ char nulls[2];
+ int spirc;
+ HeapTuple ruletup;
+ TupleDesc rulettc;
+ char *tmp;
+ int len;
+
+ /* ----------
+ * We need the rules name somewhere deep down
+ * ----------
+ */
+ rulename = nameout(rname);
+
+ /* ----------
+ * Connect to SPI manager
+ * ----------
+ */
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "get_ruledef: cannot connect to SPI manager");
+
+ /* ----------
+ * On the first call prepare the plan to lookup pg_proc.
+ * We read pg_proc over the SPI manager instead of using
+ * the syscache to be checked for read access on pg_proc.
+ * ----------
+ */
+ if (plan_getrule == NULL) {
+ Oid argtypes[1];
+ void *plan;
+
+ argtypes[0] = NAMEOID;
+ plan = SPI_prepare(query_getrule, 1, argtypes);
+ if (plan == NULL)
+ elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getrule);
+ plan_getrule = SPI_saveplan(plan);
+ }
+
+ /* ----------
+ * Get the pg_rewrite tuple for this rule
+ * ----------
+ */
+ args[0] = PointerGetDatum(rulename);
+ nulls[0] = (rulename == NULL) ? 'n' : ' ';
+ nulls[1] = '\0';
+ spirc = SPI_execp(plan_getrule, args, nulls, 1);
+ if (spirc != SPI_OK_SELECT) {
+ elog(ERROR, "failed to get pg_rewrite tuple for %s", rulename);
+ }
+ if (SPI_processed != 1) {
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "get_ruledef: SPI_finish() failed");
+ ruledef = SPI_palloc(VARHDRSZ + 1);
+ VARSIZE(ruledef) = VARHDRSZ + 1;
+ VARDATA(ruledef)[0] = '-';
+ return ruledef;
+ }
+
+ ruletup = SPI_tuptable->vals[0];
+ rulettc = SPI_tuptable->tupdesc;
+
+ /* ----------
+ * Get the rules definition and put it into executors memory
+ * ----------
+ */
+ tmp = make_ruledef(ruletup, rulettc);
+ len = strlen(tmp) + VARHDRSZ;
+ ruledef = SPI_palloc(len);
+ VARSIZE(ruledef) = len;
+ memcpy(VARDATA(ruledef), tmp, len - VARHDRSZ);
+
+ /* ----------
+ * Disconnect from SPI manager
+ * ----------
+ */
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "get_ruledef: SPI_finish() failed");
+
+ /* ----------
+ * Easy - isn't it?
+ * ----------
+ */
+ return ruledef;
+}
+
+
+/* ----------
+ * get_viewdef - Mainly the same thing, but we
+ * only return the SELECT part of a view
+ * ----------
+ */
+text *
+pg_get_viewdef(NameData *rname)
+{
+ text *ruledef;
+ Datum args[2];
+ char nulls[3];
+ int spirc;
+ HeapTuple ruletup;
+ TupleDesc rulettc;
+ char *tmp;
+ int len;
+ char name1[NAMEDATALEN + 5];
+ char name2[NAMEDATALEN + 5];
+
+ /* ----------
+ * We need the rules name somewhere deep down
+ * ----------
+ */
+ rulename = nameout(rname);
+
+ /* ----------
+ * Connect to SPI manager
+ * ----------
+ */
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "get_viewdef: cannot connect to SPI manager");
+
+ /* ----------
+ * On the first call prepare the plan to lookup pg_proc.
+ * We read pg_proc over the SPI manager instead of using
+ * the syscache to be checked for read access on pg_proc.
+ * ----------
+ */
+ if (plan_getview == NULL) {
+ Oid argtypes[2];
+ void *plan;
+
+ argtypes[0] = NAMEOID;
+ argtypes[1] = NAMEOID;
+ plan = SPI_prepare(query_getview, 2, argtypes);
+ if (plan == NULL)
+ elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getview);
+ plan_getview = SPI_saveplan(plan);
+ }
+
+ /* ----------
+ * Get the pg_rewrite tuple for this rule
+ * ----------
+ */
+ sprintf(name1, "_RET%s", rulename);
+ sprintf(name2, "_ret%s", rulename);
+ args[0] = PointerGetDatum(name1);
+ args[1] = PointerGetDatum(name2);
+ nulls[0] = ' ';
+ nulls[1] = ' ';
+ nulls[2] = '\0';
+ spirc = SPI_execp(plan_getview, args, nulls, 1);
+ if (spirc != SPI_OK_SELECT) {
+ elog(ERROR, "failed to get pg_rewrite tuple for view %s", rulename);
+ }
+ if (SPI_processed != 1) {
+ tmp = "Not a view";
+ } else {
+ /* ----------
+ * Get the rules definition and put it into executors memory
+ * ----------
+ */
+ ruletup = SPI_tuptable->vals[0];
+ rulettc = SPI_tuptable->tupdesc;
+ tmp = make_viewdef(ruletup, rulettc);
+ }
+ len = strlen(tmp) + VARHDRSZ;
+ ruledef = SPI_palloc(len);
+ VARSIZE(ruledef) = len;
+ memcpy(VARDATA(ruledef), tmp, len - VARHDRSZ);
+
+ /* ----------
+ * Disconnect from SPI manager
+ * ----------
+ */
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "get_viewdef: SPI_finish() failed");
+
+ /* ----------
+ * Easy - isn't it?
+ * ----------
+ */
+ return ruledef;
+}
+
+
+/* ----------
+ * make_ruledef - reconstruct the CREATE RULE command
+ * for a given pg_rewrite tuple
+ * ----------
+ */
+static char *
+make_ruledef(HeapTuple ruletup, TupleDesc rulettc)
+{
+ char *buf;
+ char ev_type;
+ Oid ev_class;
+ int2 ev_attr;
+ bool is_instead;
+ char *ev_qual;
+ char *ev_action;
+ List *actions = NIL;
+ int fno;
+ bool isnull;
+
+ /* ----------
+ * Allocate space for the returned rule definition text
+ * ----------
+ */
+ buf = palloc(8192);
+
+ /* ----------
+ * Get the attribute values from the rules tuple
+ * ----------
+ */
+ fno = SPI_fnumber(rulettc, "ev_type");
+ ev_type = (char)SPI_getbinval(ruletup, rulettc, fno, &isnull);
+
+ fno = SPI_fnumber(rulettc, "ev_class");
+ ev_class = (Oid)SPI_getbinval(ruletup, rulettc, fno, &isnull);
+
+ fno = SPI_fnumber(rulettc, "ev_attr");
+ ev_attr = (int2)SPI_getbinval(ruletup, rulettc, fno, &isnull);
+
+ fno = SPI_fnumber(rulettc, "is_instead");
+ is_instead = (bool)SPI_getbinval(ruletup, rulettc, fno, &isnull);
+
+ 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
+ * ----------
+ */
+ strcpy(buf, "CREATE RULE ");
+
+ /* The rule name */
+ strcat(buf, rulename);
+ strcat(buf, " AS ON ");
+
+ /* The event the rule is fired for */
+ switch (ev_type) {
+ case '1': strcat(buf, "SELECT TO ");
+ break;
+
+ case '2': strcat(buf, "UPDATE TO ");
+ break;
+
+ case '3': strcat(buf, "INSERT TO ");
+ break;
+
+ case '4': strcat(buf, "DELETE TO ");
+ break;
+
+ default:
+ elog(ERROR, "get_ruledef: rule %s has unsupported event type %d",
+ rulename, ev_type);
+ break;
+ }
+
+ /* The relation the rule is fired on */
+ strcat(buf, get_relation_name(ev_class));
+ if (ev_attr > 0) {
+ strcat(buf, ".");
+ strcat(buf, get_attribute_name(ev_class, ev_attr));
+ }
+
+ /* If the rule has an event qualification, add it */
+ if (ev_qual == NULL) ev_qual = "";
+ if (strlen(ev_qual) > 0) {
+ Node *qual;
+ Query *query;
+
+ qual = stringToNode(ev_qual);
+ query = (Query *)lfirst(actions);
+
+ strcat(buf, " WHERE ");
+ strcat(buf, get_rule_expr(query->rtable, 0, qual, TRUE));
+ }
+
+ strcat(buf, " DO ");
+
+ /* The INSTEAD keyword (if so) */
+ if (is_instead)
+ strcat(buf, "INSTEAD ");
+
+ /* Finally the rules actions */
+ if (length(actions) > 1) {
+ List *action;
+ Query *query;
+
+ strcat(buf, "(");
+ foreach (action, actions) {
+ query = (Query *)lfirst(action);
+ strcat(buf, get_query_def(query));
+ strcat(buf, "; ");
+ }
+ strcat(buf, ");");
+ } else {
+ if (length(actions) == 0) {
+ strcat(buf, "NOTHING;");
+ } else {
+ Query *query;
+
+ query = (Query *)lfirst(actions);
+ strcat(buf, get_query_def(query));
+ strcat(buf, ";");
+ }
+ }
+
+ /* ----------
+ * That's it
+ * ----------
+ */
+ return buf;
+}
+
+
+/* ----------
+ * make_viewdef - reconstruct the SELECT part of a
+ * view rewrite rule
+ * ----------
+ */
+static char *
+make_viewdef(HeapTuple ruletup, TupleDesc rulettc)
+{
+ char buf[8192];
+ Query *query;
+ char ev_type;
+ Oid ev_class;
+ int2 ev_attr;
+ bool is_instead;
+ char *ev_qual;
+ char *ev_action;
+ List *actions = NIL;
+ int fno;
+ bool isnull;
+
+ /* ----------
+ * Get the attribute values from the rules tuple
+ * ----------
+ */
+ fno = SPI_fnumber(rulettc, "ev_type");
+ ev_type = (char)SPI_getbinval(ruletup, rulettc, fno, &isnull);
+
+ fno = SPI_fnumber(rulettc, "ev_class");
+ ev_class = (Oid)SPI_getbinval(ruletup, rulettc, fno, &isnull);
+
+ fno = SPI_fnumber(rulettc, "ev_attr");
+ ev_attr = (int2)SPI_getbinval(ruletup, rulettc, fno, &isnull);
+
+ fno = SPI_fnumber(rulettc, "is_instead");
+ is_instead = (bool)SPI_getbinval(ruletup, rulettc, fno, &isnull);
+
+ 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);
+ }
+
+ if (length(actions) != 1)
+ return "Not a view";
+
+ query = (Query *)lfirst(actions);
+
+ if (ev_type != '1' || ev_attr >= 0 || !is_instead || strcmp(ev_qual, ""))
+ return "Not a view";
+
+ strcpy(buf, get_select_query_def(query));
+ strcat(buf, ";");
+
+ /* ----------
+ * That's it
+ * ----------
+ */
+ return pstrdup(buf);
+}
+
+
+/* ----------
+ * get_query_def - Parse back one action from
+ * the parsetree in the actions
+ * list
+ * ----------
+ */
+static char *
+get_query_def(Query *query)
+{
+ switch (query->commandType) {
+ case CMD_SELECT:
+ return get_select_query_def(query);
+ break;
+
+ case CMD_UPDATE:
+ return get_update_query_def(query);
+ break;
+
+ case CMD_INSERT:
+ return get_insert_query_def(query);
+ break;
+
+ case CMD_DELETE:
+ return get_delete_query_def(query);
+ break;
+
+ case CMD_NOTHING:
+ return "NOTHING";
+ break;
+
+ default:
+ elog(ERROR, "get_ruledef of %s: query command type %d not implemented yet",
+ rulename, query->commandType);
+ break;
+ }
+
+ return NULL;
+}
+
+
+/* ----------
+ * get_select_query_def - Parse back a SELECT parsetree
+ * ----------
+ */
+static char *
+get_select_query_def(Query *query)
+{
+ char buf[8192];
+ char *sep;
+ TargetEntry *tle;
+ RangeTblEntry *rte;
+ bool *rt_used;
+ int rt_length;
+ int rt_numused = 0;
+ bool rt_constonly = TRUE;
+ int i;
+ List *l;
+
+ /* ----------
+ * First we need need to know which and how many of the
+ * range table entries in the query are used in the target list
+ * or queries qualification
+ * ----------
+ */
+ rt_length = length(query->rtable);
+ rt_used = palloc(sizeof(bool) * rt_length);
+ for (i = 0; i < rt_length; i++) {
+ if (check_if_rte_used(i + 1, (Node *)(query->targetList), 0)) {
+ rt_used[i] = TRUE;
+ rt_numused++;
+ } else {
+ if (check_if_rte_used(i + 1, (Node *)(query->qual), 0)) {
+ rt_used[i] = TRUE;
+ rt_numused++;
+ } else {
+ rt_used[i] = FALSE;
+ }
+ }
+ }
+
+ /* ----------
+ * Now check if any of the used rangetable entries is different
+ * from *NEW* and *CURRENT*. If so we must omit the FROM clause
+ * later.
+ * ----------
+ */
+ i = 0;
+ foreach (l, query->rtable) {
+ if (!rt_used[i++])
+ continue;
+
+ rte = (RangeTblEntry *)lfirst(l);
+ if (!strcmp(rte->refname, "*NEW*"))
+ continue;
+ if (!strcmp(rte->refname, "*CURRENT*"))
+ continue;
+
+ rt_constonly = FALSE;
+ break;
+ }
+
+ /* ----------
+ * Build up the query string - first we say SELECT
+ * ----------
+ */
+ strcpy(buf, "SELECT");
+
+ /* Then we tell what to select (the targetlist) */
+ sep = " ";
+ foreach (l, query->targetList) {
+ bool tell_as = FALSE;
+
+ tle = (TargetEntry *)lfirst(l);
+ strcat(buf, sep);
+ sep = ", ";
+
+ strcat(buf, get_tle_expr(query->rtable, 0, tle, (rt_numused > 1)));
+
+ /* Check if we must say AS ... */
+ if (nodeTag(tle->expr) != T_Var) {
+ tell_as = strcmp(tle->resdom->resname, "?column?");
+ } else {
+ Var *var = (Var *)(tle->expr);
+ char *attname;
+
+ rte = (RangeTblEntry *)nth(var->varno - 1, query->rtable);
+ attname = get_attribute_name(rte->relid, var->varattno);
+ if (strcmp(attname, tle->resdom->resname))
+ tell_as = TRUE;
+ }
+
+ /* and do if so */
+ if (tell_as) {
+ strcat(buf, " AS ");
+ strcat(buf, tle->resdom->resname);
+ }
+ }
+
+ /* If we need other tables that *NEW* or *CURRENT* add the FROM clause */
+ if (!rt_constonly && rt_numused > 0) {
+ strcat(buf, " FROM");
+
+ i = 0;
+ sep = " ";
+ foreach (l, query->rtable) {
+ if (rt_used[i++]) {
+ rte = (RangeTblEntry *)lfirst(l);
+
+ if (!strcmp(rte->refname, "*NEW*"))
+ continue;
+
+ if (!strcmp(rte->refname, "*CURRENT*"))
+ continue;
+
+ strcat(buf, sep); sep = ", ";
+ strcat(buf, rte->relname);
+ if (rt_numused > 1) {
+ strcat(buf, " ");
+ strcat(buf, rte->refname);
+ }
+ }
+ }
+ }
+
+ /* Add the WHERE clause if given */
+ if (query->qual != NULL) {
+ strcat(buf, " WHERE ");
+ strcat(buf, get_rule_expr(query->rtable, 0, query->qual, (rt_numused > 1)));
+ }
+
+ /* Add the GROUP BY CLAUSE */
+ if (query->groupClause != NULL) {
+ strcat(buf, " GROUP BY ");
+ sep = "";
+ foreach (l, query->groupClause) {
+ strcat(buf, sep); sep = ", ";
+ strcat(buf, get_rule_expr(query->rtable, 0, lfirst(l), (rt_numused > 1)));
+ }
+ }
+
+ /* ----------
+ * Copy the query string into allocated space and return it
+ * ----------
+ */
+ return pstrdup(buf);
+}
+
+
+/* ----------
+ * get_insert_query_def - Parse back an INSERT parsetree
+ * ----------
+ */
+static char *
+get_insert_query_def(Query *query)
+{
+ char buf[8192];
+ char *sep;
+ TargetEntry *tle;
+ RangeTblEntry *rte;
+ bool *rt_used;
+ int rt_length;
+ int rt_numused = 0;
+ bool rt_constonly = TRUE;
+ int i;
+ List *l;
+
+ /* ----------
+ * We need to know if other tables than *NEW* or *CURRENT*
+ * are used in the query. If not, it's an INSERT ... VALUES,
+ * otherwise an INSERT ... SELECT.
+ * ----------
+ */
+ rt_length = length(query->rtable);
+ rt_used = palloc(sizeof(bool) * rt_length);
+ for (i = 0; i < rt_length; i++) {
+ if (check_if_rte_used(i + 1, (Node *)(query->targetList), 0)) {
+ rt_used[i] = TRUE;
+ rt_numused++;
+ } else {
+ if (check_if_rte_used(i + 1, (Node *)(query->qual), 0)) {
+ rt_used[i] = TRUE;
+ rt_numused++;
+ } else {
+ rt_used[i] = FALSE;
+ }
+ }
+ }
+
+ i = 0;
+ foreach (l, query->rtable) {
+ if (!rt_used[i++])
+ continue;
+
+ rte = (RangeTblEntry *)lfirst(l);
+ if (!strcmp(rte->refname, "*NEW*"))
+ continue;
+ if (!strcmp(rte->refname, "*CURRENT*"))
+ continue;
+
+ rt_constonly = FALSE;
+ break;
+ }
+
+ /* ----------
+ * Start the query with INSERT INTO relname
+ * ----------
+ */
+ rte = (RangeTblEntry *)nth(query->resultRelation - 1, query->rtable);
+ strcpy(buf, "INSERT INTO ");
+ strcat(buf, rte->relname);
+
+ /* Add the target list */
+ sep = " (";
+ foreach (l, query->targetList) {
+ tle = (TargetEntry *)lfirst(l);
+
+ strcat(buf, sep); sep = ", ";
+ strcat(buf, tle->resdom->resname);
+ }
+ strcat(buf, ") ");
+
+ /* Add the VALUES or the SELECT */
+ if (rt_constonly && query->qual == NULL) {
+ strcat(buf, "VALUES (");
+ sep = "";
+ foreach (l, query->targetList) {
+ tle = (TargetEntry *)lfirst(l);
+
+ strcat(buf, sep); sep = ", ";
+ strcat(buf, get_tle_expr(query->rtable, 0, tle, (rt_numused > 1)));
+ }
+ strcat(buf, ")");
+ } else {
+ strcat(buf, get_select_query_def(query));
+ }
+
+ /* ----------
+ * Copy the query string into allocated space and return it
+ * ----------
+ */
+ return pstrdup(buf);
+}
+
+
+/* ----------
+ * get_update_query_def - Parse back an UPDATE parsetree
+ * ----------
+ */
+static char *
+get_update_query_def(Query *query)
+{
+ char buf[8192];
+ char *sep;
+ TargetEntry *tle;
+ RangeTblEntry *rte;
+ List *l;
+
+ /* ----------
+ * Start the query with UPDATE relname SET
+ * ----------
+ */
+ rte = (RangeTblEntry *)nth(query->resultRelation - 1, query->rtable);
+ strcpy(buf, "UPDATE ");
+ strcat(buf, rte->relname);
+ strcat(buf, " SET ");
+
+ /* Add the comma separated list of 'attname = value' */
+ sep = "";
+ foreach (l, query->targetList) {
+ tle = (TargetEntry *)lfirst(l);
+
+ strcat(buf, sep); sep = ", ";
+ strcat(buf, tle->resdom->resname);
+ strcat(buf, " = ");
+ strcat(buf, get_tle_expr(query->rtable, query->resultRelation,
+ tle, TRUE));
+ }
+
+ /* Finally add a WHERE clause if given */
+ if (query->qual != NULL) {
+ strcat(buf, " WHERE ");
+ strcat(buf, get_rule_expr(query->rtable, query->resultRelation,
+ query->qual, TRUE));
+ }
+
+ /* ----------
+ * Copy the query string into allocated space and return it
+ * ----------
+ */
+ return pstrdup(buf);
+}
+
+
+/* ----------
+ * get_delete_query_def - Parse back a DELETE parsetree
+ * ----------
+ */
+static char *
+get_delete_query_def(Query *query)
+{
+ char buf[8192];
+ RangeTblEntry *rte;
+
+ /* ----------
+ * Start the query with DELETE FROM relname
+ * ----------
+ */
+ rte = (RangeTblEntry *)nth(query->resultRelation - 1, query->rtable);
+ strcpy(buf, "DELETE FROM ");
+ strcat(buf, rte->relname);
+
+ /* Add a WHERE clause if given */
+ if (query->qual != NULL) {
+ strcat(buf, " WHERE ");
+ strcat(buf, get_rule_expr(query->rtable, 0, query->qual, FALSE));
+ }
+
+ /* ----------
+ * Copy the query string into allocated space and return it
+ * ----------
+ */
+ return pstrdup(buf);
+}
+
+
+/* ----------
+ * get_rule_expr - Parse back an expression
+ * ----------
+ */
+static char *
+get_rule_expr(List *rtable, int rt_index, Node *node, bool varprefix)
+{
+ char buf[8192];
+
+ if (node == NULL)
+ return pstrdup("");
+
+ buf[0] = '\0';
+
+ /* ----------
+ * Up to now I don't know if all the node types below
+ * can really occur in rules actions and qualifications.
+ * There might be some work left.
+ * ----------
+ */
+ switch(nodeTag(node)) {
+ case T_TargetEntry:
+ {
+ TargetEntry *tle = (TargetEntry *)node;
+
+ return get_rule_expr(rtable, rt_index,
+ (Node *)(tle->expr), varprefix);
+ }
+ break;
+
+ case T_Aggreg:
+ {
+ Aggreg *agg = (Aggreg *)node;
+
+ strcat(buf, agg->aggname);
+ strcat(buf, "(");
+ strcat(buf, get_rule_expr(rtable, rt_index,
+ (Node *)(agg->target), varprefix));
+ strcat(buf, ")");
+ return pstrdup(buf);
+ }
+ break;
+
+ case T_GroupClause:
+ {
+ GroupClause *grp = (GroupClause *)node;
+
+ return get_rule_expr(rtable, rt_index,
+ (Node *)(grp->entry), varprefix);
+ }
+ break;
+
+ case T_Expr:
+ {
+ Expr *expr = (Expr *)node;
+
+ /* ----------
+ * Expr nodes have to be handled a bit detailed
+ * ----------
+ */
+ switch (expr->opType) {
+ case OP_EXPR:
+ strcat(buf, get_rule_expr(rtable, 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,
+ (Node *)get_rightop(expr),
+ varprefix));
+ return pstrdup(buf);
+ break;
+
+ case OR_EXPR:
+ strcat(buf, "(");
+ strcat(buf, get_rule_expr(rtable, rt_index,
+ (Node *)get_leftop(expr),
+ varprefix));
+ strcat(buf, ") OR (");
+ strcat(buf, get_rule_expr(rtable, rt_index,
+ (Node *)get_rightop(expr),
+ varprefix));
+ strcat(buf, ")");
+ return pstrdup(buf);
+ break;
+
+ case AND_EXPR:
+ strcat(buf, "(");
+ strcat(buf, get_rule_expr(rtable, rt_index,
+ (Node *)get_leftop(expr),
+ varprefix));
+ strcat(buf, ") AND (");
+ strcat(buf, get_rule_expr(rtable, rt_index,
+ (Node *)get_rightop(expr),
+ varprefix));
+ strcat(buf, ")");
+ return pstrdup(buf);
+ break;
+
+ case NOT_EXPR:
+ strcat(buf, "NOT (");
+ strcat(buf, get_rule_expr(rtable, rt_index,
+ (Node *)get_leftop(expr),
+ varprefix));
+ strcat(buf, ")");
+ return pstrdup(buf);
+ break;
+
+ case FUNC_EXPR:
+ return get_func_expr(rtable, rt_index,
+ (Expr *)node,
+ varprefix);
+ break;
+
+ default:
+ printf("\n%s\n", nodeToString(node));
+ elog(ERROR, "Expr not yet supported");
+ }
+ }
+ break;
+
+ case T_Var:
+ {
+ Var *var = (Var *)node;
+ RangeTblEntry *rte = (RangeTblEntry *)nth(var->varno - 1, rtable);
+
+ if (!strcmp(rte->refname, "*NEW*")) {
+ strcat(buf, "new.");
+ } else {
+ if (!strcmp(rte->refname, "*CURRENT*")) {
+ strcat(buf, "current.");
+ } else {
+ if (varprefix && var->varno != rt_index) {
+ strcat(buf, rte->refname);
+ strcat(buf, ".");
+ }
+ }
+ }
+ strcat(buf, get_attribute_name(rte->relid, var->varattno));
+
+ return pstrdup(buf);
+ }
+ break;
+
+ case T_List:
+ {
+ printf("\n%s\n", nodeToString(node));
+ elog(ERROR, "List not yet supported");
+ }
+ break;
+
+ 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);
+ }
+ break;
+
+ case T_Const:
+ {
+ return get_const_expr((Const *)node);
+ }
+ break;
+
+ default:
+ printf("\n%s\n", nodeToString(node));
+ elog(ERROR, "get_ruledef of %s: unknown node type %d get_rule_expr()",
+ rulename, nodeTag(node));
+ break;
+ }
+
+ return FALSE;
+}
+
+
+/* ----------
+ * get_func_expr - Parse back a Func node
+ * ----------
+ */
+static char *
+get_func_expr(List *rtable, int rt_index, Expr *expr, bool varprefix)
+{
+ char buf[8192];
+ HeapTuple proctup;
+ Form_pg_proc procStruct;
+ List *l;
+ char *sep;
+ Func *func = (Func *)(expr->oper);
+ char *proname;
+
+ /* ----------
+ * Get the functions pg_proc tuple
+ * ----------
+ */
+ proctup = SearchSysCacheTuple(PROOID,
+ ObjectIdGetDatum(func->funcid), 0, 0, 0);
+ if (!HeapTupleIsValid(proctup))
+ elog(ERROR, "cache lookup for proc %d failed", func->funcid);
+
+ procStruct = (Form_pg_proc) GETSTRUCT(proctup);
+ proname = nameout(&(procStruct->proname));
+
+ if (procStruct->pronargs == 1 && procStruct->proargtypes[0] == InvalidOid) {
+ if (!strcmp(proname, "nullvalue")) {
+ strcpy(buf, "(");
+ strcat(buf, get_rule_expr(rtable, rt_index, lfirst(expr->args),
+ varprefix));
+ strcat(buf, ") ISNULL");
+ return pstrdup(buf);
+ }
+ if (!strcmp(proname, "nonnullvalue")) {
+ strcpy(buf, "(");
+ strcat(buf, get_rule_expr(rtable, rt_index, lfirst(expr->args),
+ varprefix));
+ strcat(buf, ") NOTNULL");
+ return pstrdup(buf);
+ }
+ }
+
+ /* ----------
+ * Build a string of proname(args)
+ * ----------
+ */
+ strcpy(buf, proname);
+ strcat(buf, "(");
+ sep = "";
+ foreach (l, expr->args) {
+ strcat(buf, sep); sep = ", ";
+ strcat(buf, get_rule_expr(rtable, rt_index, lfirst(l), varprefix));
+ }
+ strcat(buf, ")");
+
+ /* ----------
+ * Copy the function call string into allocated space and return it
+ * ----------
+ */
+ return pstrdup(buf);
+}
+
+
+/* ----------
+ * get_tle_expr - A target list expression is a bit
+ * different from a normal expression.
+ * If the target column has an
+ * an atttypmod, the parser usually
+ * puts a padding-/cut-function call
+ * around the expression itself. We
+ * we must get rid of it, otherwise
+ * dump/reload/dump... would blow up
+ * the expressions.
+ * ----------
+ */
+static char *
+get_tle_expr(List *rtable, int rt_index, TargetEntry *tle, bool varprefix)
+{
+ HeapTuple proctup;
+ Form_pg_proc procStruct;
+ Expr *expr;
+ Func *func;
+ Const *second_arg;
+
+ /* ----------
+ * Check if the result has an atttypmod and if the
+ * expression in the targetlist entry is a function call
+ * ----------
+ */
+ if (tle->resdom->restypmod < 0) {
+ return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ }
+ if (nodeTag(tle->expr) != T_Expr) {
+ return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ }
+ expr = (Expr *)(tle->expr);
+ if (expr->opType != FUNC_EXPR) {
+ return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ }
+
+ func = (Func *)(expr->oper);
+
+ /* ----------
+ * Get the functions pg_proc tuple
+ * ----------
+ */
+ proctup = SearchSysCacheTuple(PROOID,
+ ObjectIdGetDatum(func->funcid), 0, 0, 0);
+ if (!HeapTupleIsValid(proctup))
+ elog(ERROR, "cache lookup for proc %d failed", func->funcid);
+
+ procStruct = (Form_pg_proc) GETSTRUCT(proctup);
+
+ /* ----------
+ * It must be a function with two arguments where the first
+ * is of the same type as the return value and the second is
+ * an int4.
+ * ----------
+ */
+ if (procStruct->pronargs != 2) {
+ return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ }
+ if (procStruct->prorettype != procStruct->proargtypes[0]) {
+ return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ }
+ if (procStruct->proargtypes[1] != INT4OID) {
+ return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ }
+
+ /* ----------
+ * Finally (to be totally safe) the second argument must be a
+ * const and match the value in the results atttypmod.
+ * ----------
+ */
+ second_arg = (Const *)nth(1, expr->args);
+ if (nodeTag((Node *)second_arg) != T_Const) {
+ return get_rule_expr(rtable, rt_index, tle->expr, varprefix);
+ }
+ if ((int4)(second_arg->constvalue) != tle->resdom->restypmod) {
+ return get_rule_expr(rtable, 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);
+}
+
+
+/* ----------
+ * get_const_expr - Make a string representation
+ * with the type cast out of a Const
+ * ----------
+ */
+char *
+get_const_expr(Const *constval)
+{
+ HeapTuple typetup;
+ TypeTupleForm typeStruct;
+ FmgrInfo finfo_output;
+ char *extval;
+ bool isnull = FALSE;
+ char buf[8192];
+
+ if (constval->constisnull)
+ return "NULL";
+
+ typetup = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(constval->consttype), 0, 0, 0);
+ if (!HeapTupleIsValid(typetup))
+ elog(ERROR, "cache lookup of type %d failed", constval->consttype);
+
+ typeStruct = (TypeTupleForm) GETSTRUCT(typetup);
+
+ fmgr_info(typeStruct->typoutput, &finfo_output);
+ extval = (char *)(*fmgr_faddr(&finfo_output))(constval->constvalue,
+ &isnull, -1);
+
+ sprintf(buf, "'%s'::%s", extval, nameout(&(typeStruct->typname)));
+ return pstrdup(buf);
+}
+
+
+/* ----------
+ * get_relation_name - Get a relation name by Oid
+ * ----------
+ */
+static char *
+get_relation_name(Oid relid)
+{
+ HeapTuple classtup;
+ Form_pg_class classStruct;
+
+ classtup = SearchSysCacheTuple(RELOID,
+ ObjectIdGetDatum(relid), 0, 0, 0);
+ if (!HeapTupleIsValid(classtup))
+ elog(ERROR, "cache lookup of relation %d failed", relid);
+
+ classStruct = (Form_pg_class) GETSTRUCT(classtup);
+ return nameout(&(classStruct->relname));
+}
+
+
+/* ----------
+ * get_attribute_name - Get an attribute name by it's
+ * relations Oid and it's attnum
+ * ----------
+ */
+static char *
+get_attribute_name(Oid relid, int2 attnum)
+{
+ HeapTuple atttup;
+ AttributeTupleForm attStruct;
+
+ atttup = SearchSysCacheTuple(ATTNUM,
+ ObjectIdGetDatum(relid), (Datum)attnum, 0, 0);
+ if (!HeapTupleIsValid(atttup))
+ elog(ERROR, "cache lookup of attribute %d in relation %d failed",
+ attnum, relid);
+
+ attStruct = (AttributeTupleForm) GETSTRUCT(atttup);
+ return nameout(&(attStruct->attname));
+}
+
+
+/* ----------
+ * check_if_rte_used - Check a targetlist or qual
+ * if a given rangetable entry
+ * is used in it
+ * ----------
+ */
+static bool
+check_if_rte_used(int rt_index, Node *node, int sup)
+{
+ if (node == NULL)
+ return FALSE;
+
+ switch(nodeTag(node)) {
+ case T_TargetEntry:
+ {
+ TargetEntry *tle = (TargetEntry *)node;
+
+ return check_if_rte_used(rt_index,
+ (Node *)(tle->expr), sup);
+ }
+ break;
+
+ case T_Aggreg:
+ {
+ Aggreg *agg = (Aggreg *)node;
+ return check_if_rte_used(rt_index,
+ (Node *)(agg->target), sup);
+ }
+ break;
+
+ case T_GroupClause:
+ {
+ GroupClause *grp = (GroupClause *)node;
+ return check_if_rte_used(rt_index,
+ (Node *)(grp->entry), sup);
+ }
+ break;
+
+ case T_Expr:
+ {
+ Expr *expr = (Expr *)node;
+ return check_if_rte_used(rt_index,
+ (Node *)(expr->args), sup);
+ }
+ break;
+
+ case T_Var:
+ {
+ Var *var = (Var *)node;
+ return (var->varno == rt_index && var->varlevelsup == sup);
+ }
+ break;
+
+ case T_List:
+ {
+ List *l;
+
+ foreach (l, (List *)node) {
+ if (check_if_rte_used(rt_index, lfirst(l), sup))
+ return TRUE;
+ }
+ return FALSE;
+ }
+ break;
+
+ case T_SubLink:
+ {
+ SubLink *sublink = (SubLink *)node;
+ Query *query = (Query *)sublink->subselect;
+
+ if (check_if_rte_used(rt_index, (Node *)(query->qual), sup + 1))
+ return TRUE;
+
+ if (check_if_rte_used(rt_index, (Node *)(sublink->lefthand), sup))
+ return TRUE;
+
+ return FALSE;
+ }
+ break;
+
+ case T_Const:
+ return FALSE;
+ break;
+
+ default:
+ elog(ERROR, "get_ruledef of %s: unknown node type %d in check_if_rte_used()",
+ rulename, nodeTag(node));
+ break;
+ }
+
+ return FALSE;
+}
+
+
diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh
index df645dcf172..c9cf8d8a60b 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.53 1998/08/24 01:14:04 momjian Exp $
+# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.54 1998/08/24 01:38:06 momjian Exp $
#
#-------------------------------------------------------------------------
@@ -433,6 +433,39 @@ echo "CREATE RULE _RETpg_user AS ON SELECT TO pg_user DO INSTEAD \
echo "REVOKE ALL on pg_shadow FROM public" | \
postgres $PGSQL_OPT template1 > /dev/null
+echo "Creating view pg_rule"
+echo "CREATE TABLE xpg_rule ( \
+ rulename name, \
+ definition text);" | postgres $PGSQL_OPT template1 > /dev/null
+#move it into pg_rule
+echo "UPDATE pg_class SET relname = 'pg_rule' WHERE relname = 'xpg_rule';" |\
+ postgres $PGSQL_OPT template1 > /dev/null
+echo "UPDATE pg_type SET typname = 'pg_rule' WHERE typname = 'xpg_rule';" |\
+ postgres $PGSQL_OPT template1 > /dev/null
+mv $PGDATA/base/template1/xpg_rule $PGDATA/base/template1/pg_rule
+
+echo "CREATE RULE _RETpg_rule AS ON SELECT TO pg_rule DO INSTEAD \
+ SELECT rulename, pg_get_ruledef(rulename) AS definition \
+ FROM pg_rewrite;" | postgres $PGSQL_OPT template1 > /dev/null
+
+echo "Creating view pg_view"
+echo "CREATE TABLE xpg_view ( \
+ viewname name, \
+ definition text);" | postgres $PGSQL_OPT template1 > /dev/null
+#move it into pg_view
+echo "UPDATE pg_class SET relname = 'pg_view' WHERE relname = 'xpg_view';" |\
+ postgres $PGSQL_OPT template1 > /dev/null
+echo "UPDATE pg_type SET typname = 'pg_view' WHERE typname = 'xpg_view';" |\
+ postgres $PGSQL_OPT template1 > /dev/null
+mv $PGDATA/base/template1/xpg_view $PGDATA/base/template1/pg_view
+
+echo "CREATE RULE _RETpg_view AS ON SELECT TO pg_view DO INSTEAD \
+ SELECT relname AS viewname, \
+ 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 "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 94f74396790..902b057f172 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.66 1998/08/19 02:03:54 momjian Exp $
+ * $Id: pg_proc.h,v 1.67 1998/08/24 01:38:08 momjian Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@@ -2033,6 +2033,12 @@ DESCR("sequence current value");
/* for multi-byte support */
DATA(insert OID = 1039 ( getdatabaseencoding PGUID 11 f t f 0 f 19 "0" 100 0 0 100 foo bar ));
+/* System-view support functions */
+DATA(insert OID = 1640 ( pg_get_ruledef PGUID 11 f t f 1 f 25 "19" 100 0 0 100 foo bar ));
+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");
+
/*
* prototypes for functions pg_proc.c
*/
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 4f4b60e867f..feed580e692 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: tcopprot.h,v 1.14 1998/06/04 17:26:49 momjian Exp $
+ * $Id: tcopprot.h,v 1.15 1998/08/24 01:38:11 momjian Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
@@ -24,10 +24,12 @@
#ifndef BOOTSTRAP_INCLUDE
extern List *
pg_parse_and_plan(char *query_string, Oid *typev, int nargs,
- QueryTreeList **queryListP, CommandDest dest);
+ QueryTreeList **queryListP, CommandDest dest,
+ bool aclOverride);
extern void pg_exec_query(char *query_string);
+extern void pg_exec_query_acl_override(char *query_string);
extern void
-pg_exec_query_dest(char *query_string, CommandDest dest);
+pg_exec_query_dest(char *query_string, CommandDest dest, bool aclOverride);
#endif /* BOOTSTRAP_HEADER */