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