1 /*-------------------------------------------------------------------------
2  *
3  * schemacmds.c
4  *	  schema creation/manipulation commands
5  *
6  * Portions Copyright (c) 1996-2019, 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/table.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 = table_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 	table_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 	Form_pg_namespace nspform;
250 
251 	rel = table_open(NamespaceRelationId, RowExclusiveLock);
252 
253 	tup = SearchSysCacheCopy1(NAMESPACENAME, CStringGetDatum(oldname));
254 	if (!HeapTupleIsValid(tup))
255 		ereport(ERROR,
256 				(errcode(ERRCODE_UNDEFINED_SCHEMA),
257 				 errmsg("schema \"%s\" does not exist", oldname)));
258 
259 	nspform = (Form_pg_namespace) GETSTRUCT(tup);
260 	nspOid = nspform->oid;
261 
262 	/* make sure the new name doesn't exist */
263 	if (OidIsValid(get_namespace_oid(newname, true)))
264 		ereport(ERROR,
265 				(errcode(ERRCODE_DUPLICATE_SCHEMA),
266 				 errmsg("schema \"%s\" already exists", newname)));
267 
268 	/* must be owner */
269 	if (!pg_namespace_ownercheck(nspOid, GetUserId()))
270 		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA,
271 					   oldname);
272 
273 	/* must have CREATE privilege on database */
274 	aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CREATE);
275 	if (aclresult != ACLCHECK_OK)
276 		aclcheck_error(aclresult, OBJECT_DATABASE,
277 					   get_database_name(MyDatabaseId));
278 
279 	if (!allowSystemTableMods && IsReservedName(newname))
280 		ereport(ERROR,
281 				(errcode(ERRCODE_RESERVED_NAME),
282 				 errmsg("unacceptable schema name \"%s\"", newname),
283 				 errdetail("The prefix \"pg_\" is reserved for system schemas.")));
284 
285 	/* rename */
286 	namestrcpy(&nspform->nspname, newname);
287 	CatalogTupleUpdate(rel, &tup->t_self, tup);
288 
289 	InvokeObjectPostAlterHook(NamespaceRelationId, nspOid, 0);
290 
291 	ObjectAddressSet(address, NamespaceRelationId, nspOid);
292 
293 	table_close(rel, NoLock);
294 	heap_freetuple(tup);
295 
296 	return address;
297 }
298 
299 void
AlterSchemaOwner_oid(Oid oid,Oid newOwnerId)300 AlterSchemaOwner_oid(Oid oid, Oid newOwnerId)
301 {
302 	HeapTuple	tup;
303 	Relation	rel;
304 
305 	rel = table_open(NamespaceRelationId, RowExclusiveLock);
306 
307 	tup = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(oid));
308 	if (!HeapTupleIsValid(tup))
309 		elog(ERROR, "cache lookup failed for schema %u", oid);
310 
311 	AlterSchemaOwner_internal(tup, rel, newOwnerId);
312 
313 	ReleaseSysCache(tup);
314 
315 	table_close(rel, RowExclusiveLock);
316 }
317 
318 
319 /*
320  * Change schema owner
321  */
322 ObjectAddress
AlterSchemaOwner(const char * name,Oid newOwnerId)323 AlterSchemaOwner(const char *name, Oid newOwnerId)
324 {
325 	Oid			nspOid;
326 	HeapTuple	tup;
327 	Relation	rel;
328 	ObjectAddress address;
329 	Form_pg_namespace nspform;
330 
331 	rel = table_open(NamespaceRelationId, RowExclusiveLock);
332 
333 	tup = SearchSysCache1(NAMESPACENAME, CStringGetDatum(name));
334 	if (!HeapTupleIsValid(tup))
335 		ereport(ERROR,
336 				(errcode(ERRCODE_UNDEFINED_SCHEMA),
337 				 errmsg("schema \"%s\" does not exist", name)));
338 
339 	nspform = (Form_pg_namespace) GETSTRUCT(tup);
340 	nspOid = nspform->oid;
341 
342 	AlterSchemaOwner_internal(tup, rel, newOwnerId);
343 
344 	ObjectAddressSet(address, NamespaceRelationId, nspOid);
345 
346 	ReleaseSysCache(tup);
347 
348 	table_close(rel, RowExclusiveLock);
349 
350 	return address;
351 }
352 
353 static void
AlterSchemaOwner_internal(HeapTuple tup,Relation rel,Oid newOwnerId)354 AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
355 {
356 	Form_pg_namespace nspForm;
357 
358 	Assert(tup->t_tableOid == NamespaceRelationId);
359 	Assert(RelationGetRelid(rel) == NamespaceRelationId);
360 
361 	nspForm = (Form_pg_namespace) GETSTRUCT(tup);
362 
363 	/*
364 	 * If the new owner is the same as the existing owner, consider the
365 	 * command to have succeeded.  This is for dump restoration purposes.
366 	 */
367 	if (nspForm->nspowner != newOwnerId)
368 	{
369 		Datum		repl_val[Natts_pg_namespace];
370 		bool		repl_null[Natts_pg_namespace];
371 		bool		repl_repl[Natts_pg_namespace];
372 		Acl		   *newAcl;
373 		Datum		aclDatum;
374 		bool		isNull;
375 		HeapTuple	newtuple;
376 		AclResult	aclresult;
377 
378 		/* Otherwise, must be owner of the existing object */
379 		if (!pg_namespace_ownercheck(nspForm->oid, GetUserId()))
380 			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA,
381 						   NameStr(nspForm->nspname));
382 
383 		/* Must be able to become new owner */
384 		check_is_member_of_role(GetUserId(), newOwnerId);
385 
386 		/*
387 		 * must have create-schema rights
388 		 *
389 		 * NOTE: This is different from other alter-owner checks in that the
390 		 * current user is checked for create privileges instead of the
391 		 * destination owner.  This is consistent with the CREATE case for
392 		 * schemas.  Because superusers will always have this right, we need
393 		 * no special case for them.
394 		 */
395 		aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(),
396 										 ACL_CREATE);
397 		if (aclresult != ACLCHECK_OK)
398 			aclcheck_error(aclresult, OBJECT_DATABASE,
399 						   get_database_name(MyDatabaseId));
400 
401 		memset(repl_null, false, sizeof(repl_null));
402 		memset(repl_repl, false, sizeof(repl_repl));
403 
404 		repl_repl[Anum_pg_namespace_nspowner - 1] = true;
405 		repl_val[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(newOwnerId);
406 
407 		/*
408 		 * Determine the modified ACL for the new owner.  This is only
409 		 * necessary when the ACL is non-null.
410 		 */
411 		aclDatum = SysCacheGetAttr(NAMESPACENAME, tup,
412 								   Anum_pg_namespace_nspacl,
413 								   &isNull);
414 		if (!isNull)
415 		{
416 			newAcl = aclnewowner(DatumGetAclP(aclDatum),
417 								 nspForm->nspowner, newOwnerId);
418 			repl_repl[Anum_pg_namespace_nspacl - 1] = true;
419 			repl_val[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(newAcl);
420 		}
421 
422 		newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl);
423 
424 		CatalogTupleUpdate(rel, &newtuple->t_self, newtuple);
425 
426 		heap_freetuple(newtuple);
427 
428 		/* Update owner dependency reference */
429 		changeDependencyOnOwner(NamespaceRelationId, nspForm->oid,
430 								newOwnerId);
431 	}
432 
433 	InvokeObjectPostAlterHook(NamespaceRelationId,
434 							  nspForm->oid, 0);
435 }
436