From 264c0682077fe9ec5c9bd366f756451db397d4fb Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Tue, 12 Sep 2000 04:49:17 +0000 Subject: [PATCH] This patch implements a different "relkind" for views. Views are now have a "relkind" of RELKIND_VIEW instead of RELKIND_RELATION. Also, views no longer have actual heap storage files. The following changes were made 1. CREATE VIEW sets the new relkind 2. The executor complains if a DELETE or INSERT references a view. 3. DROP RULE complains if an attempt is made to delete a view SELECT rule. 4. CREATE RULE "_RETmytable" AS ON SELECT TO mytable DO INSTEAD ... 1. checks to make sure mytable is empty. 2. sets the relkind to RELKIND_VIEW. 3. deletes the heap storage files. 5. LOCK myview is not allowed. :) 6. the regression test type_sanity was changed to account for the new relkind value. 7. CREATE INDEX ON myview ... is not allowed. 8. VACUUM myview is not allowed. VACUUM automatically skips views when do the entire database. 9. TRUNCATE myview is not allowed. THINGS LEFT TO THINK ABOUT o pg_views o pg_dump o pgsql (\d \dv) o Do we really want to be able to inherit from views? o Is 'DROP TABLE myview' OK? -- Mark Hollomon --- src/backend/catalog/heap.c | 8 ++- src/backend/commands/command.c | 29 ++++++--- src/backend/commands/comment.c | 9 +-- src/backend/commands/vacuum.c | 4 +- src/backend/commands/view.c | 4 +- src/backend/executor/execMain.c | 6 +- src/backend/rewrite/rewriteDefine.c | 71 ++++++++++++++++++++++- src/backend/rewrite/rewriteRemove.c | 7 ++- src/backend/tcop/utility.c | 5 +- src/backend/utils/cache/relcache.c | 22 ++++--- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_class.h | 3 +- src/test/regress/expected/type_sanity.out | 2 +- src/test/regress/sql/type_sanity.sql | 2 +- 14 files changed, 139 insertions(+), 37 deletions(-) diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index cf4b86e2e8..68bb827698 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.142 2000/08/03 19:19:08 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.143 2000/09/12 04:49:06 momjian Exp $ * * * INTERFACE ROUTINES @@ -842,7 +842,9 @@ heap_create_with_catalog(char *relname, /* * We create the disk file for this relation here */ - heap_storage_create(new_rel_desc); + if (relkind != RELKIND_VIEW) + heap_storage_create(new_rel_desc); + /* ---------------- * ok, the relation has been cataloged, so close our relations * and return the oid of the newly created relation. @@ -1468,7 +1470,7 @@ heap_drop_with_catalog(const char *relname, * unlink the relation's physical file and finish up. * ---------------- */ - if (! rel->rd_unlinked) + if (rel->rd_rel->relkind != RELKIND_VIEW && ! rel->rd_unlinked) smgrunlink(DEFAULT_SMGR, rel); rel->rd_unlinked = true; diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index d0de9e2e4d..d0faa943cf 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.100 2000/09/12 04:33:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.101 2000/09/12 04:49:06 momjian Exp $ * * NOTES * The PerformAddAttribute() code, like most of the relation @@ -533,6 +533,9 @@ AlterTableAlterColumn(const char *relationName, #endif rel = heap_openr(relationName, AccessExclusiveLock); + if ( rel->rd_rel->relkind == RELKIND_VIEW ) + elog(ERROR, "ALTER TABLE: %s is a view", relationName); + myrelid = RelationGetRelid(rel); heap_close(rel, NoLock); @@ -1133,6 +1136,10 @@ AlterTableAddConstraint(char *relationName, rel = heap_openr(relationName, AccessExclusiveLock); + /* make sure it is not a view */ + if (rel->rd_rel->relkind == RELKIND_VIEW) + elog(ERROR, "ALTER TABLE: cannot add constraint to a view"); + /* * Scan all of the rows, looking for a false match */ @@ -1251,19 +1258,20 @@ AlterTableAddConstraint(char *relationName, elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint."); } - /* check to see if the referenced table is a view. */ - if (is_viewr(fkconstraint->pktable_name)) - elog(ERROR, "ALTER TABLE: Cannot add constraints to views."); - /* * Grab an exclusive lock on the pk table, so that someone * doesn't delete rows out from under us. */ pkrel = heap_openr(fkconstraint->pktable_name, AccessExclusiveLock); - if (pkrel == NULL) - elog(ERROR, "referenced table \"%s\" not found", + if (pkrel == NULL) + elog(ERROR, "referenced table \"%s\" not found", + fkconstraint->pktable_name); + + if (pkrel->rd_rel->relkind != RELKIND_RELATION) + elog(ERROR, "referenced table \"%s\" not a relation", fkconstraint->pktable_name); + /* * Grab an exclusive lock on the fk table, and then scan @@ -1277,6 +1285,9 @@ AlterTableAddConstraint(char *relationName, elog(ERROR, "table \"%s\" not found", relationName); + if (rel->rd_rel->relkind != RELKIND_RELATION) + elog(ERROR, "referencing table \"%s\" not a relation", relationName); + /* First we check for limited correctness of the constraint */ rel_attrs = pkrel->rd_att->attrs; @@ -1503,6 +1514,7 @@ AlterTableCreateToastTable(const char *relationName, bool silent) * allow to create TOAST tables for views. But why not - someone * can insert into a view, so it shouldn't be impossible to hide * huge data there :-) + * Not any more. */ if (((Form_pg_class) GETSTRUCT(reltup))->relkind != RELKIND_RELATION) { @@ -1702,6 +1714,9 @@ LockTableCommand(LockStmt *lockstmt) rel = heap_openr(lockstmt->relname, NoLock); + if (rel->rd_rel->relkind != RELKIND_RELATION) + elog(ERROR, "LOCK TABLE: %s is not a table", lockstmt->relname); + if (is_view(rel)) elog(ERROR, "LOCK TABLE: cannot lock a view"); diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index 87c7d84727..51832bc2c7 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -21,6 +21,7 @@ #include "catalog/pg_shadow.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" +#include "catalog/pg_class.h" #include "commands/comment.h" #include "miscadmin.h" #include "parser/parse.h" @@ -301,19 +302,19 @@ CommentRelation(int reltype, char *relname, char *comment) switch (reltype) { case (INDEX): - if (relkind != 'i') + if (relkind != RELKIND_INDEX) elog(ERROR, "relation '%s' is not an index", relname); break; case (TABLE): - if (relkind != 'r') + if (relkind != RELKIND_RELATION) elog(ERROR, "relation '%s' is not a table", relname); break; case (VIEW): - if (relkind != 'r') + if (relkind != RELKIND_VIEW) elog(ERROR, "relation '%s' is not a view", relname); break; case (SEQUENCE): - if (relkind != 'S') + if (relkind != RELKIND_SEQUENCE) elog(ERROR, "relation '%s' is not a sequence", relname); break; } diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 398d002ffc..7d4005d21d 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.164 2000/09/06 14:15:16 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.165 2000/09/12 04:49:07 momjian Exp $ * *------------------------------------------------------------------------- @@ -306,7 +306,7 @@ getrels(NameData *VacRelP) if (rkind != RELKIND_RELATION) { - elog(NOTICE, "Vacuum: can not process index and certain system tables"); + elog(NOTICE, "Vacuum: can not process indecies, views and certain system tables"); continue; } diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 01e23d1315..af10805b71 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: view.c,v 1.46 2000/09/12 04:15:56 momjian Exp $ + * $Id: view.c,v 1.47 2000/09/12 04:49:07 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -102,7 +102,7 @@ DefineVirtualRelation(char *relname, List *tlist) /* * finally create the relation... */ - DefineRelation(&createStmt, RELKIND_RELATION); + DefineRelation(&createStmt, RELKIND_VIEW); } /*------------------------------------------------------------------ diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index c6571279e1..d25530b44f 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -27,7 +27,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.125 2000/09/06 14:15:17 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.126 2000/09/12 04:49:08 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -720,6 +720,10 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate) elog(ERROR, "You can't change toast relation %s", RelationGetRelationName(resultRelationDesc)); + if (resultRelationDesc->rd_rel->relkind == RELKIND_VIEW) + elog(ERROR, "You can't change view relation %s", + RelationGetRelationName(resultRelationDesc)); + resultRelationInfo = makeNode(RelationInfo); resultRelationInfo->ri_RangeTableIndex = resultRelationIndex; resultRelationInfo->ri_RelationDesc = resultRelationDesc; diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index c9315f6d33..8444bd9138 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.50 2000/09/12 04:15:57 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.51 2000/09/12 04:49:09 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -23,6 +23,8 @@ #include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteSupport.h" +#include "utils/syscache.h" +#include "storage/smgr.h" #include "commands/view.h" @@ -162,6 +164,7 @@ DefineQueryRewrite(RuleStmt *stmt) *event_qualP; List *l; Query *query; + bool RelisBecomingView = false; /* * If we are installing an ON SELECT rule, we had better grab @@ -207,6 +210,30 @@ DefineQueryRewrite(RuleStmt *stmt) elog(ERROR, "rule actions on NEW currently not supported" "\n\tuse triggers instead"); } + + if (event_relation->rd_rel->relkind != RELKIND_VIEW) + { + HeapScanDesc scanDesc; + HeapTuple tuple; + /* + * A relation is about to become a view. + * check that the relation is empty because + * the storage for the relation is going to + * be deleted. + */ + + scanDesc = heap_beginscan(event_relation, 0, SnapshotNow, 0, NULL); + tuple = heap_getnext(scanDesc, 0); + if (HeapTupleIsValid(tuple)) + elog(ERROR, "relation %s is not empty. Cannot convert to view", event_obj->relname); + + /* don't need heap_freetuple because we never got a valid tuple */ + heap_endscan(scanDesc); + + + RelisBecomingView = true; + } + } /* @@ -338,6 +365,10 @@ DefineQueryRewrite(RuleStmt *stmt) /* discard rule if it's null action and not INSTEAD; it's a no-op */ if (action != NULL || is_instead) { + Relation relationRelation; + HeapTuple tuple; + Relation idescs[Num_pg_class_indices]; + event_qualP = nodeToString(event_qual); actionP = nodeToString(action); @@ -351,14 +382,50 @@ DefineQueryRewrite(RuleStmt *stmt) /* * Set pg_class 'relhasrules' field TRUE for event relation. + * Also modify the 'relkind' field to show that the relation is + * now a view. * * Important side effect: an SI notice is broadcast to force all * backends (including me!) to update relcache entries with the new * rule. + * + * NOTE : Used to call setRelhasrulesInRelation. The code + * was inlined so that two updates were not needed. mhh 31-aug-2000 */ - setRelhasrulesInRelation(ev_relid, true); + + /* + * Find the tuple to update in pg_class, using syscache for the lookup. + */ + relationRelation = heap_openr(RelationRelationName, RowExclusiveLock); + tuple = SearchSysCacheTupleCopy(RELOID, + ObjectIdGetDatum(ev_relid), + 0, 0, 0); + Assert(HeapTupleIsValid(tuple)); + + /* Do the update */ + ((Form_pg_class) GETSTRUCT(tuple))->relhasrules = true; + if (RelisBecomingView) + ((Form_pg_class) GETSTRUCT(tuple))->relkind = RELKIND_VIEW; + + heap_update(relationRelation, &tuple->t_self, tuple, NULL); + + /* Keep the catalog indices up to date */ + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple); + CatalogCloseIndices(Num_pg_class_indices, idescs); + + heap_freetuple(tuple); + heap_close(relationRelation, RowExclusiveLock); } + /* + * IF the relation is becoming a view, delete the storage + * files associated with it. + */ + if (RelisBecomingView) + smgrunlink(DEFAULT_SMGR, event_relation); + + /* Close rel, but keep lock till commit... */ heap_close(event_relation, NoLock); } diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c index d50e104909..2af77853c4 100644 --- a/src/backend/rewrite/rewriteRemove.c +++ b/src/backend/rewrite/rewriteRemove.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.38 2000/06/30 07:04:23 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.39 2000/09/12 04:49:09 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -103,6 +103,11 @@ RemoveRewriteRule(char *ruleName) */ event_relation = heap_open(eventRelationOid, AccessExclusiveLock); + /* do not allow the removal of a view's SELECT rule */ + if (event_relation->rd_rel->relkind == RELKIND_VIEW && + ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_type == '1' ) + elog(ERROR, "Cannot remove a view's SELECT rule"); + hasMoreRules = event_relation->rd_rules != NULL && event_relation->rd_rules->numLocks > 1; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 558f678430..d603914c51 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.92 2000/09/06 14:15:21 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.93 2000/09/12 04:49:11 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -228,6 +228,9 @@ ProcessUtility(Node *parsetree, if (rel->rd_rel->relkind == RELKIND_SEQUENCE) elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence", relname); + if (rel->rd_rel->relkind == RELKIND_VIEW) + elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a sequence", + relname); heap_close(rel, NoLock); #ifndef NO_SECURITY diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 013ade7528..e39f1cfd12 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.110 2000/08/30 08:48:55 inoue Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.111 2000/09/12 04:49:13 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -1022,14 +1022,18 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo, * by the storage manager code to rd_fd. * ---------------- */ - fd = smgropen(DEFAULT_SMGR, relation); + if (relation->rd_rel->relkind != RELKIND_VIEW) { + fd = smgropen(DEFAULT_SMGR, relation); - Assert(fd >= -1); - if (fd == -1) - elog(NOTICE, "RelationIdBuildRelation: smgropen(%s): %m", - NameStr(relation->rd_rel->relname)); + Assert(fd >= -1); + if (fd == -1) + elog(NOTICE, "RelationBuildDesc: smgropen(%s): %m", + NameStr(relation->rd_rel->relname)); - relation->rd_fd = fd; + relation->rd_fd = fd; + } else { + relation->rd_fd = -1; + } /* ---------------- * insert newly created relation into proper relcaches, @@ -1279,7 +1283,7 @@ RelationIdCacheGetRelation(Oid relationId) if (RelationIsValid(rd)) { - if (rd->rd_fd == -1) + if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW) { rd->rd_fd = smgropen(DEFAULT_SMGR, rd); Assert(rd->rd_fd != -1 || rd->rd_unlinked); @@ -1313,7 +1317,7 @@ RelationNameCacheGetRelation(const char *relationName) if (RelationIsValid(rd)) { - if (rd->rd_fd == -1) + if (rd->rd_fd == -1 && rd->rd_rel->relkind != RELKIND_VIEW) { rd->rd_fd = smgropen(DEFAULT_SMGR, rd); Assert(rd->rd_fd != -1 || rd->rd_unlinked); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 22b3cb7354..b73ea0e613 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.43 2000/08/23 06:04:43 thomas Exp $ + * $Id: catversion.h,v 1.44 2000/09/12 04:49:15 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200008221 +#define CATALOG_VERSION_NO 200009111 #endif diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 1ea5b7b779..3fc4a7fd76 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_class.h,v 1.39 2000/07/03 23:10:05 wieck Exp $ + * $Id: pg_class.h,v 1.40 2000/09/12 04:49:15 momjian Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -178,5 +178,6 @@ DESCR(""); #define RELKIND_SEQUENCE 'S' /* SEQUENCE relation */ #define RELKIND_UNCATALOGED 'u' /* temporary heap */ #define RELKIND_TOASTVALUE 't' /* moved off huge values */ +#define RELKIND_VIEW 'v' /* view */ #endif /* PG_CLASS_H */ diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 38eaf0b21d..4f388b0e45 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -111,7 +111,7 @@ WHERE p1.typsend = p2.oid AND p1.typtype = 'b' AND -- Look for illegal values in pg_class fields SELECT p1.oid, p1.relname FROM pg_class as p1 -WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 't'); +WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 't', 'v'); oid | relname -----+--------- (0 rows) diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql index 106a86b87c..dbd6d0af40 100644 --- a/src/test/regress/sql/type_sanity.sql +++ b/src/test/regress/sql/type_sanity.sql @@ -99,7 +99,7 @@ WHERE p1.typsend = p2.oid AND p1.typtype = 'b' AND SELECT p1.oid, p1.relname FROM pg_class as p1 -WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 't'); +WHERE p1.relkind NOT IN ('r', 'i', 's', 'S', 't', 'v'); -- Indexes should have an access method, others not.