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