1 /*-------------------------------------------------------------------------
4 * schema creation/manipulation commands
6 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.33 2005/07/14 21:46:29 tgl Exp $
13 *-------------------------------------------------------------------------
17 #include "access/heapam.h"
18 #include "catalog/catalog.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/namespace.h"
22 #include "catalog/pg_namespace.h"
23 #include "commands/dbcommands.h"
24 #include "commands/schemacmds.h"
25 #include "miscadmin.h"
26 #include "parser/analyze.h"
27 #include "tcop/utility.h"
28 #include "utils/acl.h"
29 #include "utils/builtins.h"
30 #include "utils/lsyscache.h"
31 #include "utils/syscache.h"
38 CreateSchemaCommand(CreateSchemaStmt *stmt)
40 const char *schemaName = stmt->schemaname;
41 const char *authId = stmt->authid;
44 ListCell *parsetree_item;
49 saved_uid = GetUserId();
52 * Who is supposed to own the new schema?
55 owner_uid = get_roleid_checked(authId);
57 owner_uid = saved_uid;
60 * To create a schema, must have schema-create privilege on the current
61 * database and must be able to become the target role (this does not
62 * imply that the target role itself must have create-schema privilege).
63 * The latter provision guards against "giveaway" attacks. Note that
64 * a superuser will always have both of these privileges a fortiori.
66 aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE);
67 if (aclresult != ACLCHECK_OK)
68 aclcheck_error(aclresult, ACL_KIND_DATABASE,
69 get_database_name(MyDatabaseId));
71 check_is_member_of_role(saved_uid, owner_uid);
73 /* Additional check to protect reserved schema names */
74 if (!allowSystemTableMods && IsReservedName(schemaName))
76 (errcode(ERRCODE_RESERVED_NAME),
77 errmsg("unacceptable schema name \"%s\"", schemaName),
78 errdetail("The prefix \"pg_\" is reserved for system schemas.")));
81 * If the requested authorization is different from the current user,
82 * temporarily set the current user so that the object(s) will be
83 * created with the correct ownership.
85 * (The setting will revert to session user on error or at the end of
88 if (saved_uid != owner_uid)
91 /* Create the schema's namespace */
92 namespaceId = NamespaceCreate(schemaName, owner_uid);
94 /* Advance cmd counter to make the namespace visible */
95 CommandCounterIncrement();
98 * Temporarily make the new namespace be the front of the search path,
99 * as well as the default creation target namespace. This will be
100 * undone at the end of this routine, or upon error.
102 PushSpecialNamespace(namespaceId);
105 * Examine the list of commands embedded in the CREATE SCHEMA command,
106 * and reorganize them into a sequentially executable order with no
107 * forward references. Note that the result is still a list of raw
108 * parsetrees in need of parse analysis --- we cannot, in general, run
109 * analyze.c on one statement until we have actually executed the
112 parsetree_list = analyzeCreateSchemaStmt(stmt);
115 * Analyze and execute each command contained in the CREATE SCHEMA
117 foreach(parsetree_item, parsetree_list)
119 Node *parsetree = (Node *) lfirst(parsetree_item);
120 List *querytree_list;
121 ListCell *querytree_item;
123 querytree_list = parse_analyze(parsetree, NULL, 0);
125 foreach(querytree_item, querytree_list)
127 Query *querytree = (Query *) lfirst(querytree_item);
129 /* schemas should contain only utility stmts */
130 Assert(querytree->commandType == CMD_UTILITY);
132 ProcessUtility(querytree->utilityStmt, NULL, None_Receiver, NULL);
133 /* make sure later steps can see the object created here */
134 CommandCounterIncrement();
138 /* Reset search path to normal state */
139 PopSpecialNamespace(namespaceId);
141 /* Reset current user */
142 SetUserId(saved_uid);
151 RemoveSchema(List *names, DropBehavior behavior)
155 ObjectAddress object;
157 if (list_length(names) != 1)
159 (errcode(ERRCODE_SYNTAX_ERROR),
160 errmsg("schema name may not be qualified")));
161 namespaceName = strVal(linitial(names));
163 namespaceId = GetSysCacheOid(NAMESPACENAME,
164 CStringGetDatum(namespaceName),
166 if (!OidIsValid(namespaceId))
168 (errcode(ERRCODE_UNDEFINED_SCHEMA),
169 errmsg("schema \"%s\" does not exist", namespaceName)));
171 /* Permission check */
172 if (!pg_namespace_ownercheck(namespaceId, GetUserId()))
173 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
177 * Do the deletion. Objects contained in the schema are removed by
178 * means of their dependency links to the schema.
180 object.classId = NamespaceRelationId;
181 object.objectId = namespaceId;
182 object.objectSubId = 0;
184 performDeletion(&object, behavior);
189 * Guts of schema deletion.
192 RemoveSchemaById(Oid schemaOid)
197 relation = heap_open(NamespaceRelationId, RowExclusiveLock);
199 tup = SearchSysCache(NAMESPACEOID,
200 ObjectIdGetDatum(schemaOid),
202 if (!HeapTupleIsValid(tup)) /* should not happen */
203 elog(ERROR, "cache lookup failed for namespace %u", schemaOid);
205 simple_heap_delete(relation, &tup->t_self);
207 ReleaseSysCache(tup);
209 heap_close(relation, RowExclusiveLock);
217 RenameSchema(const char *oldname, const char *newname)
223 rel = heap_open(NamespaceRelationId, RowExclusiveLock);
225 tup = SearchSysCacheCopy(NAMESPACENAME,
226 CStringGetDatum(oldname),
228 if (!HeapTupleIsValid(tup))
230 (errcode(ERRCODE_UNDEFINED_SCHEMA),
231 errmsg("schema \"%s\" does not exist", oldname)));
233 /* make sure the new name doesn't exist */
234 if (HeapTupleIsValid(
235 SearchSysCache(NAMESPACENAME,
236 CStringGetDatum(newname),
239 (errcode(ERRCODE_DUPLICATE_SCHEMA),
240 errmsg("schema \"%s\" already exists", newname)));
243 if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
244 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
247 /* must have CREATE privilege on database */
248 aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE);
249 if (aclresult != ACLCHECK_OK)
250 aclcheck_error(aclresult, ACL_KIND_DATABASE,
251 get_database_name(MyDatabaseId));
253 if (!allowSystemTableMods && IsReservedName(newname))
255 (errcode(ERRCODE_RESERVED_NAME),
256 errmsg("unacceptable schema name \"%s\"", newname),
257 errdetail("The prefix \"pg_\" is reserved for system schemas.")));
260 namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname);
261 simple_heap_update(rel, &tup->t_self, tup);
262 CatalogUpdateIndexes(rel, tup);
264 heap_close(rel, NoLock);
269 * Change schema owner
272 AlterSchemaOwner(const char *name, Oid newOwnerId)
276 Form_pg_namespace nspForm;
278 rel = heap_open(NamespaceRelationId, RowExclusiveLock);
280 tup = SearchSysCache(NAMESPACENAME,
281 CStringGetDatum(name),
283 if (!HeapTupleIsValid(tup))
285 (errcode(ERRCODE_UNDEFINED_SCHEMA),
286 errmsg("schema \"%s\" does not exist", name)));
287 nspForm = (Form_pg_namespace) GETSTRUCT(tup);
290 * If the new owner is the same as the existing owner, consider the
291 * command to have succeeded. This is for dump restoration purposes.
293 if (nspForm->nspowner != newOwnerId)
295 Datum repl_val[Natts_pg_namespace];
296 char repl_null[Natts_pg_namespace];
297 char repl_repl[Natts_pg_namespace];
304 /* Otherwise, must be owner of the existing object */
305 if (!pg_namespace_ownercheck(HeapTupleGetOid(tup),GetUserId()))
306 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
309 /* Must be able to become new owner */
310 check_is_member_of_role(GetUserId(),newOwnerId);
313 * must have create-schema rights
315 * NOTE: This is different from other alter-owner checks in
316 * that the current user is checked for create privileges
317 * instead of the destination owner. This is consistent
318 * with the CREATE case for schemas.
320 aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(),
322 if (aclresult != ACLCHECK_OK)
323 aclcheck_error(aclresult, ACL_KIND_DATABASE,
324 get_database_name(MyDatabaseId));
326 memset(repl_null, ' ', sizeof(repl_null));
327 memset(repl_repl, ' ', sizeof(repl_repl));
329 repl_repl[Anum_pg_namespace_nspowner - 1] = 'r';
330 repl_val[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(newOwnerId);
333 * Determine the modified ACL for the new owner. This is only
334 * necessary when the ACL is non-null.
336 aclDatum = SysCacheGetAttr(NAMESPACENAME, tup,
337 Anum_pg_namespace_nspacl,
341 newAcl = aclnewowner(DatumGetAclP(aclDatum),
342 nspForm->nspowner, newOwnerId);
343 repl_repl[Anum_pg_namespace_nspacl - 1] = 'r';
344 repl_val[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(newAcl);
347 newtuple = heap_modifytuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl);
349 simple_heap_update(rel, &newtuple->t_self, newtuple);
350 CatalogUpdateIndexes(rel, newtuple);
352 heap_freetuple(newtuple);
354 /* Update owner dependency reference */
355 changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup),
359 ReleaseSysCache(tup);
360 heap_close(rel, NoLock);