1 /*-------------------------------------------------------------------------
2  *
3  * alter.c
4  *	  Drivers for generic alter commands
5  *
6  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *	  src/backend/commands/alter.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include "access/htup_details.h"
18 #include "access/sysattr.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/namespace.h"
22 #include "catalog/objectaccess.h"
23 #include "catalog/pg_collation.h"
24 #include "catalog/pg_conversion.h"
25 #include "catalog/pg_event_trigger.h"
26 #include "catalog/pg_foreign_data_wrapper.h"
27 #include "catalog/pg_foreign_server.h"
28 #include "catalog/pg_language.h"
29 #include "catalog/pg_largeobject.h"
30 #include "catalog/pg_largeobject_metadata.h"
31 #include "catalog/pg_namespace.h"
32 #include "catalog/pg_opclass.h"
33 #include "catalog/pg_opfamily.h"
34 #include "catalog/pg_proc.h"
35 #include "catalog/pg_subscription.h"
36 #include "catalog/pg_statistic_ext.h"
37 #include "catalog/pg_ts_config.h"
38 #include "catalog/pg_ts_dict.h"
39 #include "catalog/pg_ts_parser.h"
40 #include "catalog/pg_ts_template.h"
41 #include "commands/alter.h"
42 #include "commands/collationcmds.h"
43 #include "commands/conversioncmds.h"
44 #include "commands/dbcommands.h"
45 #include "commands/defrem.h"
46 #include "commands/event_trigger.h"
47 #include "commands/extension.h"
48 #include "commands/policy.h"
49 #include "commands/proclang.h"
50 #include "commands/publicationcmds.h"
51 #include "commands/schemacmds.h"
52 #include "commands/subscriptioncmds.h"
53 #include "commands/tablecmds.h"
54 #include "commands/tablespace.h"
55 #include "commands/trigger.h"
56 #include "commands/typecmds.h"
57 #include "commands/user.h"
58 #include "parser/parse_func.h"
59 #include "miscadmin.h"
60 #include "rewrite/rewriteDefine.h"
61 #include "tcop/utility.h"
62 #include "utils/builtins.h"
63 #include "utils/fmgroids.h"
64 #include "utils/lsyscache.h"
65 #include "utils/rel.h"
66 #include "utils/syscache.h"
67 #include "utils/tqual.h"
68 
69 
70 static Oid	AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid);
71 
72 /*
73  * Raise an error to the effect that an object of the given name is already
74  * present in the given namespace.
75  */
76 static void
report_name_conflict(Oid classId,const char * name)77 report_name_conflict(Oid classId, const char *name)
78 {
79 	char	   *msgfmt;
80 
81 	switch (classId)
82 	{
83 		case EventTriggerRelationId:
84 			msgfmt = gettext_noop("event trigger \"%s\" already exists");
85 			break;
86 		case ForeignDataWrapperRelationId:
87 			msgfmt = gettext_noop("foreign-data wrapper \"%s\" already exists");
88 			break;
89 		case ForeignServerRelationId:
90 			msgfmt = gettext_noop("server \"%s\" already exists");
91 			break;
92 		case LanguageRelationId:
93 			msgfmt = gettext_noop("language \"%s\" already exists");
94 			break;
95 		case PublicationRelationId:
96 			msgfmt = gettext_noop("publication \"%s\" already exists");
97 			break;
98 		case SubscriptionRelationId:
99 			msgfmt = gettext_noop("subscription \"%s\" already exists");
100 			break;
101 		default:
102 			elog(ERROR, "unsupported object class %u", classId);
103 			break;
104 	}
105 
106 	ereport(ERROR,
107 			(errcode(ERRCODE_DUPLICATE_OBJECT),
108 			 errmsg(msgfmt, name)));
109 }
110 
111 static void
report_namespace_conflict(Oid classId,const char * name,Oid nspOid)112 report_namespace_conflict(Oid classId, const char *name, Oid nspOid)
113 {
114 	char	   *msgfmt;
115 
116 	Assert(OidIsValid(nspOid));
117 
118 	switch (classId)
119 	{
120 		case ConversionRelationId:
121 			Assert(OidIsValid(nspOid));
122 			msgfmt = gettext_noop("conversion \"%s\" already exists in schema \"%s\"");
123 			break;
124 		case StatisticExtRelationId:
125 			Assert(OidIsValid(nspOid));
126 			msgfmt = gettext_noop("statistics object \"%s\" already exists in schema \"%s\"");
127 			break;
128 		case TSParserRelationId:
129 			Assert(OidIsValid(nspOid));
130 			msgfmt = gettext_noop("text search parser \"%s\" already exists in schema \"%s\"");
131 			break;
132 		case TSDictionaryRelationId:
133 			Assert(OidIsValid(nspOid));
134 			msgfmt = gettext_noop("text search dictionary \"%s\" already exists in schema \"%s\"");
135 			break;
136 		case TSTemplateRelationId:
137 			Assert(OidIsValid(nspOid));
138 			msgfmt = gettext_noop("text search template \"%s\" already exists in schema \"%s\"");
139 			break;
140 		case TSConfigRelationId:
141 			Assert(OidIsValid(nspOid));
142 			msgfmt = gettext_noop("text search configuration \"%s\" already exists in schema \"%s\"");
143 			break;
144 		default:
145 			elog(ERROR, "unsupported object class %u", classId);
146 			break;
147 	}
148 
149 	ereport(ERROR,
150 			(errcode(ERRCODE_DUPLICATE_OBJECT),
151 			 errmsg(msgfmt, name, get_namespace_name(nspOid))));
152 }
153 
154 /*
155  * AlterObjectRename_internal
156  *
157  * Generic function to rename the given object, for simple cases (won't
158  * work for tables, nor other cases where we need to do more than change
159  * the name column of a single catalog entry).
160  *
161  * rel: catalog relation containing object (RowExclusiveLock'd by caller)
162  * objectId: OID of object to be renamed
163  * new_name: CString representation of new name
164  */
165 static void
AlterObjectRename_internal(Relation rel,Oid objectId,const char * new_name)166 AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
167 {
168 	Oid			classId = RelationGetRelid(rel);
169 	int			oidCacheId = get_object_catcache_oid(classId);
170 	int			nameCacheId = get_object_catcache_name(classId);
171 	AttrNumber	Anum_name = get_object_attnum_name(classId);
172 	AttrNumber	Anum_namespace = get_object_attnum_namespace(classId);
173 	AttrNumber	Anum_owner = get_object_attnum_owner(classId);
174 	AclObjectKind acl_kind = get_object_aclkind(classId);
175 	HeapTuple	oldtup;
176 	HeapTuple	newtup;
177 	Datum		datum;
178 	bool		isnull;
179 	Oid			namespaceId;
180 	Oid			ownerId;
181 	char	   *old_name;
182 	AclResult	aclresult;
183 	Datum	   *values;
184 	bool	   *nulls;
185 	bool	   *replaces;
186 	NameData	nameattrdata;
187 
188 	oldtup = SearchSysCache1(oidCacheId, ObjectIdGetDatum(objectId));
189 	if (!HeapTupleIsValid(oldtup))
190 		elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
191 			 objectId, RelationGetRelationName(rel));
192 
193 	datum = heap_getattr(oldtup, Anum_name,
194 						 RelationGetDescr(rel), &isnull);
195 	Assert(!isnull);
196 	old_name = NameStr(*(DatumGetName(datum)));
197 
198 	/* Get OID of namespace */
199 	if (Anum_namespace > 0)
200 	{
201 		datum = heap_getattr(oldtup, Anum_namespace,
202 							 RelationGetDescr(rel), &isnull);
203 		Assert(!isnull);
204 		namespaceId = DatumGetObjectId(datum);
205 	}
206 	else
207 		namespaceId = InvalidOid;
208 
209 	/* Permission checks ... superusers can always do it */
210 	if (!superuser())
211 	{
212 		/* Fail if object does not have an explicit owner */
213 		if (Anum_owner <= 0)
214 			ereport(ERROR,
215 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
216 					 (errmsg("must be superuser to rename %s",
217 							 getObjectDescriptionOids(classId, objectId)))));
218 
219 		/* Otherwise, must be owner of the existing object */
220 		datum = heap_getattr(oldtup, Anum_owner,
221 							 RelationGetDescr(rel), &isnull);
222 		Assert(!isnull);
223 		ownerId = DatumGetObjectId(datum);
224 
225 		if (!has_privs_of_role(GetUserId(), DatumGetObjectId(ownerId)))
226 			aclcheck_error(ACLCHECK_NOT_OWNER, acl_kind, old_name);
227 
228 		/* User must have CREATE privilege on the namespace */
229 		if (OidIsValid(namespaceId))
230 		{
231 			aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
232 											  ACL_CREATE);
233 			if (aclresult != ACLCHECK_OK)
234 				aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
235 							   get_namespace_name(namespaceId));
236 		}
237 	}
238 
239 	/*
240 	 * Check for duplicate name (more friendly than unique-index failure).
241 	 * Since this is just a friendliness check, we can just skip it in cases
242 	 * where there isn't suitable support.
243 	 */
244 	if (classId == ProcedureRelationId)
245 	{
246 		Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(oldtup);
247 
248 		IsThereFunctionInNamespace(new_name, proc->pronargs,
249 								   &proc->proargtypes, proc->pronamespace);
250 	}
251 	else if (classId == CollationRelationId)
252 	{
253 		Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(oldtup);
254 
255 		IsThereCollationInNamespace(new_name, coll->collnamespace);
256 	}
257 	else if (classId == OperatorClassRelationId)
258 	{
259 		Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(oldtup);
260 
261 		IsThereOpClassInNamespace(new_name, opc->opcmethod,
262 								  opc->opcnamespace);
263 	}
264 	else if (classId == OperatorFamilyRelationId)
265 	{
266 		Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(oldtup);
267 
268 		IsThereOpFamilyInNamespace(new_name, opf->opfmethod,
269 								   opf->opfnamespace);
270 	}
271 	else if (classId == SubscriptionRelationId)
272 	{
273 		if (SearchSysCacheExists2(SUBSCRIPTIONNAME, MyDatabaseId,
274 								  CStringGetDatum(new_name)))
275 			report_name_conflict(classId, new_name);
276 	}
277 	else if (nameCacheId >= 0)
278 	{
279 		if (OidIsValid(namespaceId))
280 		{
281 			if (SearchSysCacheExists2(nameCacheId,
282 									  CStringGetDatum(new_name),
283 									  ObjectIdGetDatum(namespaceId)))
284 				report_namespace_conflict(classId, new_name, namespaceId);
285 		}
286 		else
287 		{
288 			if (SearchSysCacheExists1(nameCacheId,
289 									  CStringGetDatum(new_name)))
290 				report_name_conflict(classId, new_name);
291 		}
292 	}
293 
294 	/* Build modified tuple */
295 	values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
296 	nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
297 	replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
298 	namestrcpy(&nameattrdata, new_name);
299 	values[Anum_name - 1] = NameGetDatum(&nameattrdata);
300 	replaces[Anum_name - 1] = true;
301 	newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
302 							   values, nulls, replaces);
303 
304 	/* Perform actual update */
305 	CatalogTupleUpdate(rel, &oldtup->t_self, newtup);
306 
307 	InvokeObjectPostAlterHook(classId, objectId, 0);
308 
309 	/* Release memory */
310 	pfree(values);
311 	pfree(nulls);
312 	pfree(replaces);
313 	heap_freetuple(newtup);
314 
315 	ReleaseSysCache(oldtup);
316 }
317 
318 /*
319  * Executes an ALTER OBJECT / RENAME TO statement.  Based on the object
320  * type, the function appropriate to that type is executed.
321  *
322  * Return value is the address of the renamed object.
323  */
324 ObjectAddress
ExecRenameStmt(RenameStmt * stmt)325 ExecRenameStmt(RenameStmt *stmt)
326 {
327 	switch (stmt->renameType)
328 	{
329 		case OBJECT_TABCONSTRAINT:
330 		case OBJECT_DOMCONSTRAINT:
331 			return RenameConstraint(stmt);
332 
333 		case OBJECT_DATABASE:
334 			return RenameDatabase(stmt->subname, stmt->newname);
335 
336 		case OBJECT_ROLE:
337 			return RenameRole(stmt->subname, stmt->newname);
338 
339 		case OBJECT_SCHEMA:
340 			return RenameSchema(stmt->subname, stmt->newname);
341 
342 		case OBJECT_TABLESPACE:
343 			return RenameTableSpace(stmt->subname, stmt->newname);
344 
345 		case OBJECT_TABLE:
346 		case OBJECT_SEQUENCE:
347 		case OBJECT_VIEW:
348 		case OBJECT_MATVIEW:
349 		case OBJECT_INDEX:
350 		case OBJECT_FOREIGN_TABLE:
351 			return RenameRelation(stmt);
352 
353 		case OBJECT_COLUMN:
354 		case OBJECT_ATTRIBUTE:
355 			return renameatt(stmt);
356 
357 		case OBJECT_RULE:
358 			return RenameRewriteRule(stmt->relation, stmt->subname,
359 									 stmt->newname);
360 
361 		case OBJECT_TRIGGER:
362 			return renametrig(stmt);
363 
364 		case OBJECT_POLICY:
365 			return rename_policy(stmt);
366 
367 		case OBJECT_DOMAIN:
368 		case OBJECT_TYPE:
369 			return RenameType(stmt);
370 
371 		case OBJECT_AGGREGATE:
372 		case OBJECT_COLLATION:
373 		case OBJECT_CONVERSION:
374 		case OBJECT_EVENT_TRIGGER:
375 		case OBJECT_FDW:
376 		case OBJECT_FOREIGN_SERVER:
377 		case OBJECT_FUNCTION:
378 		case OBJECT_OPCLASS:
379 		case OBJECT_OPFAMILY:
380 		case OBJECT_LANGUAGE:
381 		case OBJECT_STATISTIC_EXT:
382 		case OBJECT_TSCONFIGURATION:
383 		case OBJECT_TSDICTIONARY:
384 		case OBJECT_TSPARSER:
385 		case OBJECT_TSTEMPLATE:
386 		case OBJECT_PUBLICATION:
387 		case OBJECT_SUBSCRIPTION:
388 			{
389 				ObjectAddress address;
390 				Relation	catalog;
391 				Relation	relation;
392 
393 				address = get_object_address(stmt->renameType,
394 											 stmt->object,
395 											 &relation,
396 											 AccessExclusiveLock, false);
397 				Assert(relation == NULL);
398 
399 				catalog = heap_open(address.classId, RowExclusiveLock);
400 				AlterObjectRename_internal(catalog,
401 										   address.objectId,
402 										   stmt->newname);
403 				heap_close(catalog, RowExclusiveLock);
404 
405 				return address;
406 			}
407 
408 		default:
409 			elog(ERROR, "unrecognized rename stmt type: %d",
410 				 (int) stmt->renameType);
411 			return InvalidObjectAddress;	/* keep compiler happy */
412 	}
413 }
414 
415 /*
416  * Executes an ALTER OBJECT / DEPENDS ON [EXTENSION] statement.
417  *
418  * Return value is the address of the altered object.  refAddress is an output
419  * argument which, if not null, receives the address of the object that the
420  * altered object now depends on.
421  */
422 ObjectAddress
ExecAlterObjectDependsStmt(AlterObjectDependsStmt * stmt,ObjectAddress * refAddress)423 ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddress)
424 {
425 	ObjectAddress address;
426 	ObjectAddress refAddr;
427 	Relation	rel;
428 	List   *currexts;
429 
430 	address =
431 		get_object_address_rv(stmt->objectType, stmt->relation, (List *) stmt->object,
432 							  &rel, AccessExclusiveLock, false);
433 
434 	/*
435 	 * Verify that the user is entitled to run the command.
436 	 *
437 	 * We don't check any privileges on the extension, because that's not
438 	 * needed.  The object owner is stipulating, by running this command, that
439 	 * the extension owner can drop the object whenever they feel like it,
440 	 * which is not considered a problem.
441 	 */
442 	check_object_ownership(GetUserId(),
443 						   stmt->objectType, address, stmt->object, rel);
444 
445 	/*
446 	 * If a relation was involved, it would have been opened and locked. We
447 	 * don't need the relation here, but we'll retain the lock until commit.
448 	 */
449 	if (rel)
450 		heap_close(rel, NoLock);
451 
452 	refAddr = get_object_address(OBJECT_EXTENSION, (Node *) stmt->extname,
453 								 &rel, AccessExclusiveLock, false);
454 	Assert(rel == NULL);
455 	if (refAddress)
456 		*refAddress = refAddr;
457 
458 	/* Avoid duplicates */
459 	currexts = getAutoExtensionsOfObject(address.classId,
460 										 address.objectId);
461 	if (!list_member_oid(currexts, refAddr.objectId))
462 		recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
463 
464 	return address;
465 }
466 
467 /*
468  * Executes an ALTER OBJECT / SET SCHEMA statement.  Based on the object
469  * type, the function appropriate to that type is executed.
470  *
471  * Return value is that of the altered object.
472  *
473  * oldSchemaAddr is an output argument which, if not NULL, is set to the object
474  * address of the original schema.
475  */
476 ObjectAddress
ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt * stmt,ObjectAddress * oldSchemaAddr)477 ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
478 						  ObjectAddress *oldSchemaAddr)
479 {
480 	ObjectAddress address;
481 	Oid			oldNspOid;
482 
483 	switch (stmt->objectType)
484 	{
485 		case OBJECT_EXTENSION:
486 			address = AlterExtensionNamespace(strVal((Value *) stmt->object), stmt->newschema,
487 											  oldSchemaAddr ? &oldNspOid : NULL);
488 			break;
489 
490 		case OBJECT_FOREIGN_TABLE:
491 		case OBJECT_SEQUENCE:
492 		case OBJECT_TABLE:
493 		case OBJECT_VIEW:
494 		case OBJECT_MATVIEW:
495 			address = AlterTableNamespace(stmt,
496 										  oldSchemaAddr ? &oldNspOid : NULL);
497 			break;
498 
499 		case OBJECT_DOMAIN:
500 		case OBJECT_TYPE:
501 			address = AlterTypeNamespace(castNode(List, stmt->object), stmt->newschema,
502 										 stmt->objectType,
503 										 oldSchemaAddr ? &oldNspOid : NULL);
504 			break;
505 
506 			/* generic code path */
507 		case OBJECT_AGGREGATE:
508 		case OBJECT_COLLATION:
509 		case OBJECT_CONVERSION:
510 		case OBJECT_FUNCTION:
511 		case OBJECT_OPERATOR:
512 		case OBJECT_OPCLASS:
513 		case OBJECT_OPFAMILY:
514 		case OBJECT_STATISTIC_EXT:
515 		case OBJECT_TSCONFIGURATION:
516 		case OBJECT_TSDICTIONARY:
517 		case OBJECT_TSPARSER:
518 		case OBJECT_TSTEMPLATE:
519 			{
520 				Relation	catalog;
521 				Relation	relation;
522 				Oid			classId;
523 				Oid			nspOid;
524 
525 				address = get_object_address(stmt->objectType,
526 											 stmt->object,
527 											 &relation,
528 											 AccessExclusiveLock,
529 											 false);
530 				Assert(relation == NULL);
531 				classId = address.classId;
532 				catalog = heap_open(classId, RowExclusiveLock);
533 				nspOid = LookupCreationNamespace(stmt->newschema);
534 
535 				oldNspOid = AlterObjectNamespace_internal(catalog, address.objectId,
536 														  nspOid);
537 				heap_close(catalog, RowExclusiveLock);
538 			}
539 			break;
540 
541 		default:
542 			elog(ERROR, "unrecognized AlterObjectSchemaStmt type: %d",
543 				 (int) stmt->objectType);
544 			return InvalidObjectAddress;	/* keep compiler happy */
545 	}
546 
547 	if (oldSchemaAddr)
548 		ObjectAddressSet(*oldSchemaAddr, NamespaceRelationId, oldNspOid);
549 
550 	return address;
551 }
552 
553 /*
554  * Change an object's namespace given its classOid and object Oid.
555  *
556  * Objects that don't have a namespace should be ignored.
557  *
558  * This function is currently used only by ALTER EXTENSION SET SCHEMA,
559  * so it only needs to cover object types that can be members of an
560  * extension, and it doesn't have to deal with certain special cases
561  * such as not wanting to process array types --- those should never
562  * be direct members of an extension anyway.  Nonetheless, we insist
563  * on listing all OCLASS types in the switch.
564  *
565  * Returns the OID of the object's previous namespace, or InvalidOid if
566  * object doesn't have a schema.
567  */
568 Oid
AlterObjectNamespace_oid(Oid classId,Oid objid,Oid nspOid,ObjectAddresses * objsMoved)569 AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
570 						 ObjectAddresses *objsMoved)
571 {
572 	Oid			oldNspOid = InvalidOid;
573 	ObjectAddress dep;
574 
575 	dep.classId = classId;
576 	dep.objectId = objid;
577 	dep.objectSubId = 0;
578 
579 	switch (getObjectClass(&dep))
580 	{
581 		case OCLASS_CLASS:
582 			{
583 				Relation	rel;
584 
585 				rel = relation_open(objid, AccessExclusiveLock);
586 				oldNspOid = RelationGetNamespace(rel);
587 
588 				AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
589 
590 				relation_close(rel, NoLock);
591 				break;
592 			}
593 
594 		case OCLASS_TYPE:
595 			oldNspOid = AlterTypeNamespace_oid(objid, nspOid, objsMoved);
596 			break;
597 
598 		case OCLASS_PROC:
599 		case OCLASS_COLLATION:
600 		case OCLASS_CONVERSION:
601 		case OCLASS_OPERATOR:
602 		case OCLASS_OPCLASS:
603 		case OCLASS_OPFAMILY:
604 		case OCLASS_STATISTIC_EXT:
605 		case OCLASS_TSPARSER:
606 		case OCLASS_TSDICT:
607 		case OCLASS_TSTEMPLATE:
608 		case OCLASS_TSCONFIG:
609 			{
610 				Relation	catalog;
611 
612 				catalog = heap_open(classId, RowExclusiveLock);
613 
614 				oldNspOid = AlterObjectNamespace_internal(catalog, objid,
615 														  nspOid);
616 
617 				heap_close(catalog, RowExclusiveLock);
618 			}
619 			break;
620 
621 		case OCLASS_CAST:
622 		case OCLASS_CONSTRAINT:
623 		case OCLASS_DEFAULT:
624 		case OCLASS_LANGUAGE:
625 		case OCLASS_LARGEOBJECT:
626 		case OCLASS_AM:
627 		case OCLASS_AMOP:
628 		case OCLASS_AMPROC:
629 		case OCLASS_REWRITE:
630 		case OCLASS_TRIGGER:
631 		case OCLASS_SCHEMA:
632 		case OCLASS_ROLE:
633 		case OCLASS_DATABASE:
634 		case OCLASS_TBLSPACE:
635 		case OCLASS_FDW:
636 		case OCLASS_FOREIGN_SERVER:
637 		case OCLASS_USER_MAPPING:
638 		case OCLASS_DEFACL:
639 		case OCLASS_EXTENSION:
640 		case OCLASS_EVENT_TRIGGER:
641 		case OCLASS_POLICY:
642 		case OCLASS_PUBLICATION:
643 		case OCLASS_PUBLICATION_REL:
644 		case OCLASS_SUBSCRIPTION:
645 		case OCLASS_TRANSFORM:
646 			/* ignore object types that don't have schema-qualified names */
647 			break;
648 
649 			/*
650 			 * There's intentionally no default: case here; we want the
651 			 * compiler to warn if a new OCLASS hasn't been handled above.
652 			 */
653 	}
654 
655 	return oldNspOid;
656 }
657 
658 /*
659  * Generic function to change the namespace of a given object, for simple
660  * cases (won't work for tables, nor other cases where we need to do more
661  * than change the namespace column of a single catalog entry).
662  *
663  * rel: catalog relation containing object (RowExclusiveLock'd by caller)
664  * objid: OID of object to change the namespace of
665  * nspOid: OID of new namespace
666  *
667  * Returns the OID of the object's previous namespace.
668  */
669 static Oid
AlterObjectNamespace_internal(Relation rel,Oid objid,Oid nspOid)670 AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
671 {
672 	Oid			classId = RelationGetRelid(rel);
673 	int			oidCacheId = get_object_catcache_oid(classId);
674 	int			nameCacheId = get_object_catcache_name(classId);
675 	AttrNumber	Anum_name = get_object_attnum_name(classId);
676 	AttrNumber	Anum_namespace = get_object_attnum_namespace(classId);
677 	AttrNumber	Anum_owner = get_object_attnum_owner(classId);
678 	AclObjectKind acl_kind = get_object_aclkind(classId);
679 	Oid			oldNspOid;
680 	Datum		name,
681 				namespace;
682 	bool		isnull;
683 	HeapTuple	tup,
684 				newtup;
685 	Datum	   *values;
686 	bool	   *nulls;
687 	bool	   *replaces;
688 
689 	tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid));
690 	if (!HeapTupleIsValid(tup)) /* should not happen */
691 		elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
692 			 objid, RelationGetRelationName(rel));
693 
694 	name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
695 	Assert(!isnull);
696 	namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel),
697 							 &isnull);
698 	Assert(!isnull);
699 	oldNspOid = DatumGetObjectId(namespace);
700 
701 	/*
702 	 * If the object is already in the correct namespace, we don't need to do
703 	 * anything except fire the object access hook.
704 	 */
705 	if (oldNspOid == nspOid)
706 	{
707 		InvokeObjectPostAlterHook(classId, objid, 0);
708 		return oldNspOid;
709 	}
710 
711 	/* Check basic namespace related issues */
712 	CheckSetNamespace(oldNspOid, nspOid);
713 
714 	/* Permission checks ... superusers can always do it */
715 	if (!superuser())
716 	{
717 		Datum		owner;
718 		Oid			ownerId;
719 		AclResult	aclresult;
720 
721 		/* Fail if object does not have an explicit owner */
722 		if (Anum_owner <= 0)
723 			ereport(ERROR,
724 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
725 					 (errmsg("must be superuser to set schema of %s",
726 							 getObjectDescriptionOids(classId, objid)))));
727 
728 		/* Otherwise, must be owner of the existing object */
729 		owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull);
730 		Assert(!isnull);
731 		ownerId = DatumGetObjectId(owner);
732 
733 		if (!has_privs_of_role(GetUserId(), ownerId))
734 			aclcheck_error(ACLCHECK_NOT_OWNER, acl_kind,
735 						   NameStr(*(DatumGetName(name))));
736 
737 		/* User must have CREATE privilege on new namespace */
738 		aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE);
739 		if (aclresult != ACLCHECK_OK)
740 			aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
741 						   get_namespace_name(nspOid));
742 	}
743 
744 	/*
745 	 * Check for duplicate name (more friendly than unique-index failure).
746 	 * Since this is just a friendliness check, we can just skip it in cases
747 	 * where there isn't suitable support.
748 	 */
749 	if (classId == ProcedureRelationId)
750 	{
751 		Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tup);
752 
753 		IsThereFunctionInNamespace(NameStr(proc->proname), proc->pronargs,
754 								   &proc->proargtypes, nspOid);
755 	}
756 	else if (classId == CollationRelationId)
757 	{
758 		Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(tup);
759 
760 		IsThereCollationInNamespace(NameStr(coll->collname), nspOid);
761 	}
762 	else if (classId == OperatorClassRelationId)
763 	{
764 		Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(tup);
765 
766 		IsThereOpClassInNamespace(NameStr(opc->opcname),
767 								  opc->opcmethod, nspOid);
768 	}
769 	else if (classId == OperatorFamilyRelationId)
770 	{
771 		Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(tup);
772 
773 		IsThereOpFamilyInNamespace(NameStr(opf->opfname),
774 								   opf->opfmethod, nspOid);
775 	}
776 	else if (nameCacheId >= 0 &&
777 			 SearchSysCacheExists2(nameCacheId, name,
778 								   ObjectIdGetDatum(nspOid)))
779 		report_namespace_conflict(classId,
780 								  NameStr(*(DatumGetName(name))),
781 								  nspOid);
782 
783 	/* Build modified tuple */
784 	values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
785 	nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
786 	replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
787 	values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid);
788 	replaces[Anum_namespace - 1] = true;
789 	newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
790 							   values, nulls, replaces);
791 
792 	/* Perform actual update */
793 	CatalogTupleUpdate(rel, &tup->t_self, newtup);
794 
795 	/* Release memory */
796 	pfree(values);
797 	pfree(nulls);
798 	pfree(replaces);
799 
800 	/* update dependencies to point to the new schema */
801 	changeDependencyFor(classId, objid,
802 						NamespaceRelationId, oldNspOid, nspOid);
803 
804 	InvokeObjectPostAlterHook(classId, objid, 0);
805 
806 	return oldNspOid;
807 }
808 
809 /*
810  * Executes an ALTER OBJECT / OWNER TO statement.  Based on the object
811  * type, the function appropriate to that type is executed.
812  */
813 ObjectAddress
ExecAlterOwnerStmt(AlterOwnerStmt * stmt)814 ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
815 {
816 	Oid			newowner = get_rolespec_oid(stmt->newowner, false);
817 
818 	switch (stmt->objectType)
819 	{
820 		case OBJECT_DATABASE:
821 			return AlterDatabaseOwner(strVal((Value *) stmt->object), newowner);
822 
823 		case OBJECT_SCHEMA:
824 			return AlterSchemaOwner(strVal((Value *) stmt->object), newowner);
825 
826 		case OBJECT_TYPE:
827 		case OBJECT_DOMAIN:		/* same as TYPE */
828 			return AlterTypeOwner(castNode(List, stmt->object), newowner, stmt->objectType);
829 			break;
830 
831 		case OBJECT_FDW:
832 			return AlterForeignDataWrapperOwner(strVal((Value *) stmt->object),
833 												newowner);
834 
835 		case OBJECT_FOREIGN_SERVER:
836 			return AlterForeignServerOwner(strVal((Value *) stmt->object),
837 										   newowner);
838 
839 		case OBJECT_EVENT_TRIGGER:
840 			return AlterEventTriggerOwner(strVal((Value *) stmt->object),
841 										  newowner);
842 
843 		case OBJECT_PUBLICATION:
844 			return AlterPublicationOwner(strVal((Value *) stmt->object),
845 										 newowner);
846 
847 		case OBJECT_SUBSCRIPTION:
848 			return AlterSubscriptionOwner(strVal((Value *) stmt->object),
849 										  newowner);
850 
851 			/* Generic cases */
852 		case OBJECT_AGGREGATE:
853 		case OBJECT_COLLATION:
854 		case OBJECT_CONVERSION:
855 		case OBJECT_FUNCTION:
856 		case OBJECT_LANGUAGE:
857 		case OBJECT_LARGEOBJECT:
858 		case OBJECT_OPERATOR:
859 		case OBJECT_OPCLASS:
860 		case OBJECT_OPFAMILY:
861 		case OBJECT_STATISTIC_EXT:
862 		case OBJECT_TABLESPACE:
863 		case OBJECT_TSDICTIONARY:
864 		case OBJECT_TSCONFIGURATION:
865 			{
866 				Relation	catalog;
867 				Relation	relation;
868 				Oid			classId;
869 				ObjectAddress address;
870 
871 				address = get_object_address(stmt->objectType,
872 											 stmt->object,
873 											 &relation,
874 											 AccessExclusiveLock,
875 											 false);
876 				Assert(relation == NULL);
877 				classId = address.classId;
878 
879 				/*
880 				 * XXX - get_object_address returns Oid of pg_largeobject
881 				 * catalog for OBJECT_LARGEOBJECT because of historical
882 				 * reasons.  Fix up it here.
883 				 */
884 				if (classId == LargeObjectRelationId)
885 					classId = LargeObjectMetadataRelationId;
886 
887 				catalog = heap_open(classId, RowExclusiveLock);
888 
889 				AlterObjectOwner_internal(catalog, address.objectId, newowner);
890 				heap_close(catalog, RowExclusiveLock);
891 
892 				return address;
893 			}
894 			break;
895 
896 		default:
897 			elog(ERROR, "unrecognized AlterOwnerStmt type: %d",
898 				 (int) stmt->objectType);
899 			return InvalidObjectAddress;	/* keep compiler happy */
900 	}
901 }
902 
903 /*
904  * Generic function to change the ownership of a given object, for simple
905  * cases (won't work for tables, nor other cases where we need to do more than
906  * change the ownership column of a single catalog entry).
907  *
908  * rel: catalog relation containing object (RowExclusiveLock'd by caller)
909  * objectId: OID of object to change the ownership of
910  * new_ownerId: OID of new object owner
911  */
912 void
AlterObjectOwner_internal(Relation rel,Oid objectId,Oid new_ownerId)913 AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId)
914 {
915 	Oid			classId = RelationGetRelid(rel);
916 	AttrNumber	Anum_owner = get_object_attnum_owner(classId);
917 	AttrNumber	Anum_namespace = get_object_attnum_namespace(classId);
918 	AttrNumber	Anum_acl = get_object_attnum_acl(classId);
919 	AttrNumber	Anum_name = get_object_attnum_name(classId);
920 	HeapTuple	oldtup;
921 	Datum		datum;
922 	bool		isnull;
923 	Oid			old_ownerId;
924 	Oid			namespaceId = InvalidOid;
925 
926 	oldtup = get_catalog_object_by_oid(rel, objectId);
927 	if (oldtup == NULL)
928 		elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
929 			 objectId, RelationGetRelationName(rel));
930 
931 	datum = heap_getattr(oldtup, Anum_owner,
932 						 RelationGetDescr(rel), &isnull);
933 	Assert(!isnull);
934 	old_ownerId = DatumGetObjectId(datum);
935 
936 	if (Anum_namespace != InvalidAttrNumber)
937 	{
938 		datum = heap_getattr(oldtup, Anum_namespace,
939 							 RelationGetDescr(rel), &isnull);
940 		Assert(!isnull);
941 		namespaceId = DatumGetObjectId(datum);
942 	}
943 
944 	if (old_ownerId != new_ownerId)
945 	{
946 		AttrNumber	nattrs;
947 		HeapTuple	newtup;
948 		Datum	   *values;
949 		bool	   *nulls;
950 		bool	   *replaces;
951 
952 		/* Superusers can bypass permission checks */
953 		if (!superuser())
954 		{
955 			AclObjectKind aclkind = get_object_aclkind(classId);
956 
957 			/* must be owner */
958 			if (!has_privs_of_role(GetUserId(), old_ownerId))
959 			{
960 				char	   *objname;
961 				char		namebuf[NAMEDATALEN];
962 
963 				if (Anum_name != InvalidAttrNumber)
964 				{
965 					datum = heap_getattr(oldtup, Anum_name,
966 										 RelationGetDescr(rel), &isnull);
967 					Assert(!isnull);
968 					objname = NameStr(*DatumGetName(datum));
969 				}
970 				else
971 				{
972 					snprintf(namebuf, sizeof(namebuf), "%u",
973 							 HeapTupleGetOid(oldtup));
974 					objname = namebuf;
975 				}
976 				aclcheck_error(ACLCHECK_NOT_OWNER, aclkind, objname);
977 			}
978 			/* Must be able to become new owner */
979 			check_is_member_of_role(GetUserId(), new_ownerId);
980 
981 			/* New owner must have CREATE privilege on namespace */
982 			if (OidIsValid(namespaceId))
983 			{
984 				AclResult	aclresult;
985 
986 				aclresult = pg_namespace_aclcheck(namespaceId, new_ownerId,
987 												  ACL_CREATE);
988 				if (aclresult != ACLCHECK_OK)
989 					aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
990 								   get_namespace_name(namespaceId));
991 			}
992 		}
993 
994 		/* Build a modified tuple */
995 		nattrs = RelationGetNumberOfAttributes(rel);
996 		values = palloc0(nattrs * sizeof(Datum));
997 		nulls = palloc0(nattrs * sizeof(bool));
998 		replaces = palloc0(nattrs * sizeof(bool));
999 		values[Anum_owner - 1] = ObjectIdGetDatum(new_ownerId);
1000 		replaces[Anum_owner - 1] = true;
1001 
1002 		/*
1003 		 * Determine the modified ACL for the new owner.  This is only
1004 		 * necessary when the ACL is non-null.
1005 		 */
1006 		if (Anum_acl != InvalidAttrNumber)
1007 		{
1008 			datum = heap_getattr(oldtup,
1009 								 Anum_acl, RelationGetDescr(rel), &isnull);
1010 			if (!isnull)
1011 			{
1012 				Acl		   *newAcl;
1013 
1014 				newAcl = aclnewowner(DatumGetAclP(datum),
1015 									 old_ownerId, new_ownerId);
1016 				values[Anum_acl - 1] = PointerGetDatum(newAcl);
1017 				replaces[Anum_acl - 1] = true;
1018 			}
1019 		}
1020 
1021 		newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
1022 								   values, nulls, replaces);
1023 
1024 		/* Perform actual update */
1025 		CatalogTupleUpdate(rel, &newtup->t_self, newtup);
1026 
1027 		/* Update owner dependency reference */
1028 		if (classId == LargeObjectMetadataRelationId)
1029 			classId = LargeObjectRelationId;
1030 		changeDependencyOnOwner(classId, HeapTupleGetOid(newtup), new_ownerId);
1031 
1032 		/* Release memory */
1033 		pfree(values);
1034 		pfree(nulls);
1035 		pfree(replaces);
1036 	}
1037 
1038 	InvokeObjectPostAlterHook(classId, objectId, 0);
1039 }
1040