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