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