Skip site navigation (1) Skip section navigation (2)

Patch adding pg_rule and pg_view

From: jwieck(at)debis(dot)com (Jan Wieck)
To: jwieck(at)debis(dot)com
Cc: maillist(at)candle(dot)pha(dot)pa(dot)us, pgsql-hackers(at)postgreSQL(dot)org
Subject: Patch adding pg_rule and pg_view
Date: 1998-08-19 20:10:28
Message-ID: m0z9EYv-000EBPC@orion.SAPserv.Hamburg.dsh.de (view raw or flat)
Thread:
Lists: pgsql-hackers
>     You'll  get  it  after the currently running clean compile of
>     the initial source tree (with the patch applied) and the rest
>     went through.

    Here they are.


Jan

-- 

#======================================================================#
# It's easier to get forgiveness for being wrong than for being right. #
# Let's break this rule - forgive me.                                  #
#======================================== jwieck(at)debis(dot)com (Jan Wieck) #

diff -crN src.orig/backend/utils/adt/Makefile src/backend/utils/adt/Makefile
*** src.orig/backend/utils/adt/Makefile	Wed Aug 19 10:36:19 1998
--- src/backend/utils/adt/Makefile	Wed Aug 19 20:21:30 1998
***************
*** 22,29 ****
  	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
  
  all: SUBSYS.o
  
--- 22,29 ----
  	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 ruleutils.o selfuncs.o sets.o \
! 	tid.o timestamp.o varchar.o varlena.o version.o
  
  all: SUBSYS.o
  
diff -crN src.orig/backend/utils/adt/ruleutils.c src/backend/utils/adt/ruleutils.c
*** src.orig/backend/utils/adt/ruleutils.c	Thu Jan  1 01:00:00 1970
--- src/backend/utils/adt/ruleutils.c	Wed Aug 19 20:20:29 1998
***************
*** 0 ****
--- 1,1376 ----
+ /**********************************************************************
+  * get_ruledef.c	- Function to get a rules definition text
+  *			  out of it's tuple
+  *
+  * IDENTIFICATION
+  *    $Header: $
+  *
+  *    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 -crN src.orig/bin/initdb/initdb.sh src/bin/initdb/initdb.sh
*** src.orig/bin/initdb/initdb.sh	Fri Aug 14 19:09:08 1998
--- src/bin/initdb/initdb.sh	Wed Aug 19 20:52:38 1998
***************
*** 436,441 ****
--- 436,474 ----
  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 -crN src.orig/include/catalog/pg_proc.h src/include/catalog/pg_proc.h
*** src.orig/include/catalog/pg_proc.h	Wed Aug 19 10:36:33 1998
--- src/include/catalog/pg_proc.h	Wed Aug 19 20:17:56 1998
***************
*** 2033,2038 ****
--- 2033,2044 ----
  /* 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
   */

In response to

pgsql-hackers by date

Next:From: Garrett WollmanDate: 1998-08-19 21:17:49
Subject: More complete indexing for geometric types?
Previous:From: Jan WieckDate: 1998-08-19 19:54:32
Subject: Re: Rules and views (was Re: [HACKERS] Rules: 2nd patch)

Privacy Policy | About PostgreSQL
Copyright © 1996-2014 The PostgreSQL Global Development Group