1 /*-------------------------------------------------------------------------
2 *
3 * schemacmds.c
4 * schema creation/manipulation commands
5 *
6 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/commands/schemacmds.c
12 *
13 *-------------------------------------------------------------------------
14 */
15 #include "postgres.h"
16
17 #include "access/htup_details.h"
18 #include "access/heapam.h"
19 #include "access/xact.h"
20 #include "catalog/catalog.h"
21 #include "catalog/dependency.h"
22 #include "catalog/indexing.h"
23 #include "catalog/namespace.h"
24 #include "catalog/pg_authid.h"
25 #include "catalog/objectaccess.h"
26 #include "catalog/pg_namespace.h"
27 #include "commands/dbcommands.h"
28 #include "commands/event_trigger.h"
29 #include "commands/schemacmds.h"
30 #include "miscadmin.h"
31 #include "parser/parse_utilcmd.h"
32 #include "tcop/utility.h"
33 #include "utils/acl.h"
34 #include "utils/builtins.h"
35 #include "utils/rel.h"
36 #include "utils/syscache.h"
37
38
39 static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId);
40
41 /*
42 * CREATE SCHEMA
43 */
44 Oid
CreateSchemaCommand(CreateSchemaStmt * stmt,const char * queryString)45 CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
46 {
47 const char *schemaName = stmt->schemaname;
48 Oid namespaceId;
49 OverrideSearchPath *overridePath;
50 List *parsetree_list;
51 ListCell *parsetree_item;
52 Oid owner_uid;
53 Oid saved_uid;
54 int save_sec_context;
55 AclResult aclresult;
56 ObjectAddress address;
57
58 GetUserIdAndSecContext(&saved_uid, &save_sec_context);
59
60 /*
61 * Who is supposed to own the new schema?
62 */
63 if (stmt->authrole)
64 owner_uid = get_rolespec_oid(stmt->authrole, false);
65 else
66 owner_uid = saved_uid;
67
68 /* fill schema name with the user name if not specified */
69 if (!schemaName)
70 {
71 HeapTuple tuple;
72
73 tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(owner_uid));
74 if (!HeapTupleIsValid(tuple))
75 elog(ERROR, "cache lookup failed for role %u", owner_uid);
76 schemaName =
77 pstrdup(NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname));
78 ReleaseSysCache(tuple);
79 }
80
81 /*
82 * To create a schema, must have schema-create privilege on the current
83 * database and must be able to become the target role (this does not
84 * imply that the target role itself must have create-schema privilege).
85 * The latter provision guards against "giveaway" attacks. Note that a
86 * superuser will always have both of these privileges a fortiori.
87 */
88 aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE);
89 if (aclresult != ACLCHECK_OK)
90 aclcheck_error(aclresult, ACL_KIND_DATABASE,
91 get_database_name(MyDatabaseId));
92
93 check_is_member_of_role(saved_uid, owner_uid);
94
95 /* Additional check to protect reserved schema names */
96 if (!allowSystemTableMods && IsReservedName(schemaName))
97 ereport(ERROR,
98 (errcode(ERRCODE_RESERVED_NAME),
99 errmsg("unacceptable schema name \"%s\"", schemaName),
100 errdetail("The prefix \"pg_\" is reserved for system schemas.")));
101
102 /*
103 * If if_not_exists was given and the schema already exists, bail out.
104 * (Note: we needn't check this when not if_not_exists, because
105 * NamespaceCreate will complain anyway.) We could do this before making
106 * the permissions checks, but since CREATE TABLE IF NOT EXISTS makes its
107 * creation-permission check first, we do likewise.
108 */
109 if (stmt->if_not_exists &&
110 SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(schemaName)))
111 {
112 ereport(NOTICE,
113 (errcode(ERRCODE_DUPLICATE_SCHEMA),
114 errmsg("schema \"%s\" already exists, skipping",
115 schemaName)));
116 return InvalidOid;
117 }
118
119 /*
120 * If the requested authorization is different from the current user,
121 * temporarily set the current user so that the object(s) will be created
122 * with the correct ownership.
123 *
124 * (The setting will be restored at the end of this routine, or in case of
125 * error, transaction abort will clean things up.)
126 */
127 if (saved_uid != owner_uid)
128 SetUserIdAndSecContext(owner_uid,
129 save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
130
131 /* Create the schema's namespace */
132 namespaceId = NamespaceCreate(schemaName, owner_uid, false);
133
134 /* Advance cmd counter to make the namespace visible */
135 CommandCounterIncrement();
136
137 /*
138 * Temporarily make the new namespace be the front of the search path, as
139 * well as the default creation target namespace. This will be undone at
140 * the end of this routine, or upon error.
141 */
142 overridePath = GetOverrideSearchPath(CurrentMemoryContext);
143 overridePath->schemas = lcons_oid(namespaceId, overridePath->schemas);
144 /* XXX should we clear overridePath->useTemp? */
145 PushOverrideSearchPath(overridePath);
146
147 /*
148 * Report the new schema to possibly interested event triggers. Note we
149 * must do this here and not in ProcessUtilitySlow because otherwise the
150 * objects created below are reported before the schema, which would be
151 * wrong.
152 */
153 ObjectAddressSet(address, NamespaceRelationId, namespaceId);
154 EventTriggerCollectSimpleCommand(address, InvalidObjectAddress,
155 (Node *) stmt);
156
157 /*
158 * Examine the list of commands embedded in the CREATE SCHEMA command, and
159 * reorganize them into a sequentially executable order with no forward
160 * references. Note that the result is still a list of raw parsetrees ---
161 * we cannot, in general, run parse analysis on one statement until we
162 * have actually executed the prior ones.
163 */
164 parsetree_list = transformCreateSchemaStmt(stmt);
165
166 /*
167 * Execute each command contained in the CREATE SCHEMA. Since the grammar
168 * allows only utility commands in CREATE SCHEMA, there is no need to pass
169 * them through parse_analyze() or the rewriter; we can just hand them
170 * straight to ProcessUtility.
171 */
172 foreach(parsetree_item, parsetree_list)
173 {
174 Node *stmt = (Node *) lfirst(parsetree_item);
175
176 /* do this step */
177 ProcessUtility(stmt,
178 queryString,
179 PROCESS_UTILITY_SUBCOMMAND,
180 NULL,
181 None_Receiver,
182 NULL);
183 /* make sure later steps can see the object created here */
184 CommandCounterIncrement();
185 }
186
187 /* Reset search path to normal state */
188 PopOverrideSearchPath();
189
190 /* Reset current user and security context */
191 SetUserIdAndSecContext(saved_uid, save_sec_context);
192
193 return namespaceId;
194 }
195
196 /*
197 * Guts of schema deletion.
198 */
199 void
RemoveSchemaById(Oid schemaOid)200 RemoveSchemaById(Oid schemaOid)
201 {
202 Relation relation;
203 HeapTuple tup;
204
205 relation = heap_open(NamespaceRelationId, RowExclusiveLock);
206
207 tup = SearchSysCache1(NAMESPACEOID,
208 ObjectIdGetDatum(schemaOid));
209 if (!HeapTupleIsValid(tup)) /* should not happen */
210 elog(ERROR, "cache lookup failed for namespace %u", schemaOid);
211
212 simple_heap_delete(relation, &tup->t_self);
213
214 ReleaseSysCache(tup);
215
216 heap_close(relation, RowExclusiveLock);
217 }
218
219
220 /*
221 * Rename schema
222 */
223 ObjectAddress
RenameSchema(const char * oldname,const char * newname)224 RenameSchema(const char *oldname, const char *newname)
225 {
226 Oid nspOid;
227 HeapTuple tup;
228 Relation rel;
229 AclResult aclresult;
230 ObjectAddress address;
231
232 rel = heap_open(NamespaceRelationId, RowExclusiveLock);
233
234 tup = SearchSysCacheCopy1(NAMESPACENAME, CStringGetDatum(oldname));
235 if (!HeapTupleIsValid(tup))
236 ereport(ERROR,
237 (errcode(ERRCODE_UNDEFINED_SCHEMA),
238 errmsg("schema \"%s\" does not exist", oldname)));
239
240 nspOid = HeapTupleGetOid(tup);
241
242 /* make sure the new name doesn't exist */
243 if (OidIsValid(get_namespace_oid(newname, true)))
244 ereport(ERROR,
245 (errcode(ERRCODE_DUPLICATE_SCHEMA),
246 errmsg("schema \"%s\" already exists", newname)));
247
248 /* must be owner */
249 if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
250 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
251 oldname);
252
253 /* must have CREATE privilege on database */
254 aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE);
255 if (aclresult != ACLCHECK_OK)
256 aclcheck_error(aclresult, ACL_KIND_DATABASE,
257 get_database_name(MyDatabaseId));
258
259 if (!allowSystemTableMods && IsReservedName(newname))
260 ereport(ERROR,
261 (errcode(ERRCODE_RESERVED_NAME),
262 errmsg("unacceptable schema name \"%s\"", newname),
263 errdetail("The prefix \"pg_\" is reserved for system schemas.")));
264
265 /* rename */
266 namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname);
267 simple_heap_update(rel, &tup->t_self, tup);
268 CatalogUpdateIndexes(rel, tup);
269
270 InvokeObjectPostAlterHook(NamespaceRelationId, HeapTupleGetOid(tup), 0);
271
272 ObjectAddressSet(address, NamespaceRelationId, nspOid);
273
274 heap_close(rel, NoLock);
275 heap_freetuple(tup);
276
277 return address;
278 }
279
280 void
AlterSchemaOwner_oid(Oid oid,Oid newOwnerId)281 AlterSchemaOwner_oid(Oid oid, Oid newOwnerId)
282 {
283 HeapTuple tup;
284 Relation rel;
285
286 rel = heap_open(NamespaceRelationId, RowExclusiveLock);
287
288 tup = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(oid));
289 if (!HeapTupleIsValid(tup))
290 elog(ERROR, "cache lookup failed for schema %u", oid);
291
292 AlterSchemaOwner_internal(tup, rel, newOwnerId);
293
294 ReleaseSysCache(tup);
295
296 heap_close(rel, RowExclusiveLock);
297 }
298
299
300 /*
301 * Change schema owner
302 */
303 ObjectAddress
AlterSchemaOwner(const char * name,Oid newOwnerId)304 AlterSchemaOwner(const char *name, Oid newOwnerId)
305 {
306 Oid nspOid;
307 HeapTuple tup;
308 Relation rel;
309 ObjectAddress address;
310
311 rel = heap_open(NamespaceRelationId, RowExclusiveLock);
312
313 tup = SearchSysCache1(NAMESPACENAME, CStringGetDatum(name));
314 if (!HeapTupleIsValid(tup))
315 ereport(ERROR,
316 (errcode(ERRCODE_UNDEFINED_SCHEMA),
317 errmsg("schema \"%s\" does not exist", name)));
318
319 nspOid = HeapTupleGetOid(tup);
320
321 AlterSchemaOwner_internal(tup, rel, newOwnerId);
322
323 ObjectAddressSet(address, NamespaceRelationId, nspOid);
324
325 ReleaseSysCache(tup);
326
327 heap_close(rel, RowExclusiveLock);
328
329 return address;
330 }
331
332 static void
AlterSchemaOwner_internal(HeapTuple tup,Relation rel,Oid newOwnerId)333 AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
334 {
335 Form_pg_namespace nspForm;
336
337 Assert(tup->t_tableOid == NamespaceRelationId);
338 Assert(RelationGetRelid(rel) == NamespaceRelationId);
339
340 nspForm = (Form_pg_namespace) GETSTRUCT(tup);
341
342 /*
343 * If the new owner is the same as the existing owner, consider the
344 * command to have succeeded. This is for dump restoration purposes.
345 */
346 if (nspForm->nspowner != newOwnerId)
347 {
348 Datum repl_val[Natts_pg_namespace];
349 bool repl_null[Natts_pg_namespace];
350 bool repl_repl[Natts_pg_namespace];
351 Acl *newAcl;
352 Datum aclDatum;
353 bool isNull;
354 HeapTuple newtuple;
355 AclResult aclresult;
356
357 /* Otherwise, must be owner of the existing object */
358 if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
359 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
360 NameStr(nspForm->nspname));
361
362 /* Must be able to become new owner */
363 check_is_member_of_role(GetUserId(), newOwnerId);
364
365 /*
366 * must have create-schema rights
367 *
368 * NOTE: This is different from other alter-owner checks in that the
369 * current user is checked for create privileges instead of the
370 * destination owner. This is consistent with the CREATE case for
371 * schemas. Because superusers will always have this right, we need
372 * no special case for them.
373 */
374 aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(),
375 ACL_CREATE);
376 if (aclresult != ACLCHECK_OK)
377 aclcheck_error(aclresult, ACL_KIND_DATABASE,
378 get_database_name(MyDatabaseId));
379
380 memset(repl_null, false, sizeof(repl_null));
381 memset(repl_repl, false, sizeof(repl_repl));
382
383 repl_repl[Anum_pg_namespace_nspowner - 1] = true;
384 repl_val[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(newOwnerId);
385
386 /*
387 * Determine the modified ACL for the new owner. This is only
388 * necessary when the ACL is non-null.
389 */
390 aclDatum = SysCacheGetAttr(NAMESPACENAME, tup,
391 Anum_pg_namespace_nspacl,
392 &isNull);
393 if (!isNull)
394 {
395 newAcl = aclnewowner(DatumGetAclP(aclDatum),
396 nspForm->nspowner, newOwnerId);
397 repl_repl[Anum_pg_namespace_nspacl - 1] = true;
398 repl_val[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(newAcl);
399 }
400
401 newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl);
402
403 simple_heap_update(rel, &newtuple->t_self, newtuple);
404 CatalogUpdateIndexes(rel, newtuple);
405
406 heap_freetuple(newtuple);
407
408 /* Update owner dependency reference */
409 changeDependencyOnOwner(NamespaceRelationId, HeapTupleGetOid(tup),
410 newOwnerId);
411 }
412
413 InvokeObjectPostAlterHook(NamespaceRelationId,
414 HeapTupleGetOid(tup), 0);
415 }
416