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