]> granicus.if.org Git - postgresql/blob - src/backend/commands/schemacmds.c
Minor correction: cause ALTER ROLE role ROLE rolenames to behave
[postgresql] / src / backend / commands / schemacmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * schemacmds.c
4  *        schema creation/manipulation commands
5  *
6  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.33 2005/07/14 21:46:29 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
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"
32
33
34 /*
35  * CREATE SCHEMA
36  */
37 void
38 CreateSchemaCommand(CreateSchemaStmt *stmt)
39 {
40         const char *schemaName = stmt->schemaname;
41         const char *authId = stmt->authid;
42         Oid                     namespaceId;
43         List       *parsetree_list;
44         ListCell   *parsetree_item;
45         Oid             owner_uid;
46         Oid             saved_uid;
47         AclResult       aclresult;
48
49         saved_uid = GetUserId();
50
51         /*
52          * Who is supposed to own the new schema?
53          */
54         if (authId)
55                 owner_uid = get_roleid_checked(authId);
56         else
57                 owner_uid = saved_uid;
58
59         /*
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.
65          */
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));
70
71         check_is_member_of_role(saved_uid, owner_uid);
72
73         /* Additional check to protect reserved schema names */
74         if (!allowSystemTableMods && IsReservedName(schemaName))
75                 ereport(ERROR,
76                                 (errcode(ERRCODE_RESERVED_NAME),
77                                  errmsg("unacceptable schema name \"%s\"", schemaName),
78                 errdetail("The prefix \"pg_\" is reserved for system schemas.")));
79
80         /*
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.
84          *
85          * (The setting will revert to session user on error or at the end of
86          * this routine.)
87          */
88         if (saved_uid != owner_uid)
89                 SetUserId(owner_uid);
90
91         /* Create the schema's namespace */
92         namespaceId = NamespaceCreate(schemaName, owner_uid);
93
94         /* Advance cmd counter to make the namespace visible */
95         CommandCounterIncrement();
96
97         /*
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.
101          */
102         PushSpecialNamespace(namespaceId);
103
104         /*
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
110          * prior ones.
111          */
112         parsetree_list = analyzeCreateSchemaStmt(stmt);
113
114         /*
115          * Analyze and execute each command contained in the CREATE SCHEMA
116          */
117         foreach(parsetree_item, parsetree_list)
118         {
119                 Node       *parsetree = (Node *) lfirst(parsetree_item);
120                 List       *querytree_list;
121                 ListCell   *querytree_item;
122
123                 querytree_list = parse_analyze(parsetree, NULL, 0);
124
125                 foreach(querytree_item, querytree_list)
126                 {
127                         Query      *querytree = (Query *) lfirst(querytree_item);
128
129                         /* schemas should contain only utility stmts */
130                         Assert(querytree->commandType == CMD_UTILITY);
131                         /* do this step */
132                         ProcessUtility(querytree->utilityStmt, NULL, None_Receiver, NULL);
133                         /* make sure later steps can see the object created here */
134                         CommandCounterIncrement();
135                 }
136         }
137
138         /* Reset search path to normal state */
139         PopSpecialNamespace(namespaceId);
140
141         /* Reset current user */
142         SetUserId(saved_uid);
143 }
144
145
146 /*
147  *      RemoveSchema
148  *              Removes a schema.
149  */
150 void
151 RemoveSchema(List *names, DropBehavior behavior)
152 {
153         char       *namespaceName;
154         Oid                     namespaceId;
155         ObjectAddress object;
156
157         if (list_length(names) != 1)
158                 ereport(ERROR,
159                                 (errcode(ERRCODE_SYNTAX_ERROR),
160                                  errmsg("schema name may not be qualified")));
161         namespaceName = strVal(linitial(names));
162
163         namespaceId = GetSysCacheOid(NAMESPACENAME,
164                                                                  CStringGetDatum(namespaceName),
165                                                                  0, 0, 0);
166         if (!OidIsValid(namespaceId))
167                 ereport(ERROR,
168                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
169                                  errmsg("schema \"%s\" does not exist", namespaceName)));
170
171         /* Permission check */
172         if (!pg_namespace_ownercheck(namespaceId, GetUserId()))
173                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
174                                            namespaceName);
175
176         /*
177          * Do the deletion.  Objects contained in the schema are removed by
178          * means of their dependency links to the schema.
179          */
180         object.classId = NamespaceRelationId;
181         object.objectId = namespaceId;
182         object.objectSubId = 0;
183
184         performDeletion(&object, behavior);
185 }
186
187
188 /*
189  * Guts of schema deletion.
190  */
191 void
192 RemoveSchemaById(Oid schemaOid)
193 {
194         Relation        relation;
195         HeapTuple       tup;
196
197         relation = heap_open(NamespaceRelationId, RowExclusiveLock);
198
199         tup = SearchSysCache(NAMESPACEOID,
200                                                  ObjectIdGetDatum(schemaOid),
201                                                  0, 0, 0);
202         if (!HeapTupleIsValid(tup)) /* should not happen */
203                 elog(ERROR, "cache lookup failed for namespace %u", schemaOid);
204
205         simple_heap_delete(relation, &tup->t_self);
206
207         ReleaseSysCache(tup);
208
209         heap_close(relation, RowExclusiveLock);
210 }
211
212
213 /*
214  * Rename schema
215  */
216 void
217 RenameSchema(const char *oldname, const char *newname)
218 {
219         HeapTuple       tup;
220         Relation        rel;
221         AclResult       aclresult;
222
223         rel = heap_open(NamespaceRelationId, RowExclusiveLock);
224
225         tup = SearchSysCacheCopy(NAMESPACENAME,
226                                                          CStringGetDatum(oldname),
227                                                          0, 0, 0);
228         if (!HeapTupleIsValid(tup))
229                 ereport(ERROR,
230                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
231                                  errmsg("schema \"%s\" does not exist", oldname)));
232
233         /* make sure the new name doesn't exist */
234         if (HeapTupleIsValid(
235                                                  SearchSysCache(NAMESPACENAME,
236                                                                                 CStringGetDatum(newname),
237                                                                                 0, 0, 0)))
238                 ereport(ERROR,
239                                 (errcode(ERRCODE_DUPLICATE_SCHEMA),
240                                  errmsg("schema \"%s\" already exists", newname)));
241
242         /* must be owner */
243         if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
244                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
245                                            oldname);
246
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));
252
253         if (!allowSystemTableMods && IsReservedName(newname))
254                 ereport(ERROR,
255                                 (errcode(ERRCODE_RESERVED_NAME),
256                                  errmsg("unacceptable schema name \"%s\"", newname),
257                 errdetail("The prefix \"pg_\" is reserved for system schemas.")));
258
259         /* rename */
260         namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname);
261         simple_heap_update(rel, &tup->t_self, tup);
262         CatalogUpdateIndexes(rel, tup);
263
264         heap_close(rel, NoLock);
265         heap_freetuple(tup);
266 }
267
268 /*
269  * Change schema owner
270  */
271 void
272 AlterSchemaOwner(const char *name, Oid newOwnerId)
273 {
274         HeapTuple       tup;
275         Relation        rel;
276         Form_pg_namespace nspForm;
277
278         rel = heap_open(NamespaceRelationId, RowExclusiveLock);
279
280         tup = SearchSysCache(NAMESPACENAME,
281                                                  CStringGetDatum(name),
282                                                  0, 0, 0);
283         if (!HeapTupleIsValid(tup))
284                 ereport(ERROR,
285                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
286                                  errmsg("schema \"%s\" does not exist", name)));
287         nspForm = (Form_pg_namespace) GETSTRUCT(tup);
288
289         /*
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.
292          */
293         if (nspForm->nspowner != newOwnerId)
294         {
295                 Datum           repl_val[Natts_pg_namespace];
296                 char            repl_null[Natts_pg_namespace];
297                 char            repl_repl[Natts_pg_namespace];
298                 Acl                *newAcl;
299                 Datum           aclDatum;
300                 bool            isNull;
301                 HeapTuple       newtuple;
302                 AclResult       aclresult;
303
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,
307                                                    name);
308
309                 /* Must be able to become new owner */
310                 check_is_member_of_role(GetUserId(),newOwnerId);
311
312                 /*
313                  * must have create-schema rights
314                  *
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.
319                  */
320                 aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(),
321                                                                                  ACL_CREATE);
322                 if (aclresult != ACLCHECK_OK)
323                         aclcheck_error(aclresult, ACL_KIND_DATABASE,
324                                                    get_database_name(MyDatabaseId));
325
326                 memset(repl_null, ' ', sizeof(repl_null));
327                 memset(repl_repl, ' ', sizeof(repl_repl));
328
329                 repl_repl[Anum_pg_namespace_nspowner - 1] = 'r';
330                 repl_val[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(newOwnerId);
331
332                 /*
333                  * Determine the modified ACL for the new owner.  This is only
334                  * necessary when the ACL is non-null.
335                  */
336                 aclDatum = SysCacheGetAttr(NAMESPACENAME, tup,
337                                                                    Anum_pg_namespace_nspacl,
338                                                                    &isNull);
339                 if (!isNull)
340                 {
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);
345                 }
346
347                 newtuple = heap_modifytuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl);
348
349                 simple_heap_update(rel, &newtuple->t_self, newtuple);
350                 CatalogUpdateIndexes(rel, newtuple);
351
352                 heap_freetuple(newtuple);
353
354                 /* Update owner dependency reference */
355                 changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup),
356                                                                 newOwnerId);
357         }
358
359         ReleaseSysCache(tup);
360         heap_close(rel, NoLock);
361 }