aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/view.c
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2012-01-16 09:34:21 -0500
committerRobert Haas <rhaas@postgresql.org>2012-01-16 09:49:34 -0500
commit1575fbcb795fc331f46588b4520c4bca7e854d5c (patch)
tree2ff28e174dc1136b699ef8384dbd050a837cb368 /src/backend/commands/view.c
parent01d83ffdcae92f75dbfd41de0b4213d241edd394 (diff)
downloadpostgresql-1575fbcb795fc331f46588b4520c4bca7e854d5c.tar.gz
postgresql-1575fbcb795fc331f46588b4520c4bca7e854d5c.zip
Prevent adding relations to a concurrently dropped schema.
In the previous coding, it was possible for a relation to be created via CREATE TABLE, CREATE VIEW, CREATE SEQUENCE, CREATE FOREIGN TABLE, etc. in a schema while that schema was meanwhile being concurrently dropped. This led to a pg_class entry with an invalid relnamespace value. The same problem could occur if a relation was moved using ALTER .. SET SCHEMA while the target schema was being concurrently dropped. This patch prevents both of those scenarios by locking the schema to which the relation is being added using AccessShareLock, which conflicts with the AccessExclusiveLock taken by DROP. As a desirable side effect, this also prevents the use of CREATE OR REPLACE VIEW to queue for an AccessExclusiveLock on a relation on which you have no rights: that will now fail immediately with a permissions error, before trying to obtain a lock. We need similar protection for all other object types, but as everything other than relations uses a slightly different set of code paths, I'm leaving that for a separate commit. Original complaint (as far as I could find) about CREATE by Nikhil Sontakke; risk for ALTER .. SET SCHEMA pointed out by Tom Lane; further details by Dan Farina; patch by me; review by Hitoshi Harada.
Diffstat (limited to 'src/backend/commands/view.c')
-rw-r--r--src/backend/commands/view.c36
1 files changed, 15 insertions, 21 deletions
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index ff9c44908a3..c3520ae03c6 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -98,10 +98,12 @@ isViewOnTempTable_walker(Node *node, void *context)
*---------------------------------------------------------------------
*/
static Oid
-DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace,
- Oid namespaceId, List *options)
+DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
+ List *options)
{
Oid viewOid;
+ Oid namespaceId;
+ LOCKMODE lockmode;
CreateStmt *createStmt = makeNode(CreateStmt);
List *attrList;
ListCell *t;
@@ -159,9 +161,14 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace,
errmsg("view must have at least one column")));
/*
- * Check to see if we want to replace an existing view.
+ * Look up, check permissions on, and lock the creation namespace; also
+ * check for a preexisting view with the same name. This will also set
+ * relation->relpersistence to RELPERSISTENCE_TEMP if the selected
+ * namespace is temporary.
*/
- viewOid = get_relname_relid(relation->relname, namespaceId);
+ lockmode = replace ? AccessExclusiveLock : NoLock;
+ namespaceId =
+ RangeVarGetAndCheckCreationNamespace(relation, lockmode, &viewOid);
if (OidIsValid(viewOid) && replace)
{
@@ -170,24 +177,16 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace,
List *atcmds = NIL;
AlterTableCmd *atcmd;
- /*
- * Yes. Get exclusive lock on the existing view ...
- */
- rel = relation_open(viewOid, AccessExclusiveLock);
+ /* Relation is already locked, but we must build a relcache entry. */
+ rel = relation_open(viewOid, NoLock);
- /*
- * Make sure it *is* a view, and do permissions checks.
- */
+ /* Make sure it *is* a view. */
if (rel->rd_rel->relkind != RELKIND_VIEW)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a view",
RelationGetRelationName(rel))));
- if (!pg_class_ownercheck(viewOid, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
- RelationGetRelationName(rel));
-
/* Also check it's not in use already */
CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW");
@@ -428,7 +427,6 @@ DefineView(ViewStmt *stmt, const char *queryString)
{
Query *viewParse;
Oid viewOid;
- Oid namespaceId;
RangeVar *view;
/*
@@ -514,10 +512,6 @@ DefineView(ViewStmt *stmt, const char *queryString)
view->relname)));
}
- /* Might also need to make it temporary if placed in temp schema. */
- namespaceId = RangeVarGetCreationNamespace(view);
- RangeVarAdjustRelationPersistence(view, namespaceId);
-
/*
* Create the view relation
*
@@ -525,7 +519,7 @@ DefineView(ViewStmt *stmt, const char *queryString)
* aborted.
*/
viewOid = DefineVirtualRelation(view, viewParse->targetList,
- stmt->replace, namespaceId, stmt->options);
+ stmt->replace, stmt->options);
/*
* The relation we have just created is not visible to any other commands