diff options
author | Robert Haas <rhaas@postgresql.org> | 2012-01-16 09:34:21 -0500 |
---|---|---|
committer | Robert Haas <rhaas@postgresql.org> | 2012-01-16 09:49:34 -0500 |
commit | 1575fbcb795fc331f46588b4520c4bca7e854d5c (patch) | |
tree | 2ff28e174dc1136b699ef8384dbd050a837cb368 /src/backend/parser/parse_utilcmd.c | |
parent | 01d83ffdcae92f75dbfd41de0b4213d241edd394 (diff) | |
download | postgresql-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/parser/parse_utilcmd.c')
-rw-r--r-- | src/backend/parser/parse_utilcmd.c | 30 |
1 files changed, 13 insertions, 17 deletions
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 335bdc6b075..99157c53493 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -146,6 +146,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) List *save_alist; ListCell *elements; Oid namespaceid; + Oid existing_relid; /* * We must not scribble on the passed-in CreateStmt, so copy it. (This is @@ -155,30 +156,25 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) /* * Look up the creation namespace. This also checks permissions on the - * target namespace, so that we throw any permissions error as early as - * possible. + * target namespace, locks it against concurrent drops, checks for a + * preexisting relation in that namespace with the same name, and updates + * stmt->relation->relpersistence if the select namespace is temporary. */ - namespaceid = RangeVarGetAndCheckCreationNamespace(stmt->relation); - RangeVarAdjustRelationPersistence(stmt->relation, namespaceid); + namespaceid = + RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, + &existing_relid); /* * If the relation already exists and the user specified "IF NOT EXISTS", * bail out with a NOTICE. */ - if (stmt->if_not_exists) + if (stmt->if_not_exists && OidIsValid(existing_relid)) { - Oid existing_relid; - - existing_relid = get_relname_relid(stmt->relation->relname, - namespaceid); - if (existing_relid != InvalidOid) - { - ereport(NOTICE, - (errcode(ERRCODE_DUPLICATE_TABLE), - errmsg("relation \"%s\" already exists, skipping", - stmt->relation->relname))); - return NIL; - } + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_TABLE), + errmsg("relation \"%s\" already exists, skipping", + stmt->relation->relname))); + return NIL; } /* |