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