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
71 static 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 */
77 static void
report_name_conflict(Oid classId,const char * name)78 report_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
112 static void
report_namespace_conflict(Oid classId,const char * name,Oid nspOid)113 report_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 */
166 static void
AlterObjectRename_internal(Relation rel,Oid objectId,const char * new_name)167 AlterObjectRename_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 HeapTuple oldtup;
176 HeapTuple newtup;
177 Datum datum;
178 bool isnull;
179 Oid namespaceId;
180 Oid ownerId;
181 char *old_name;
182 AclResult aclresult;
183 Datum *values;
184 bool *nulls;
185 bool *replaces;
186 NameData nameattrdata;
187
188 oldtup = SearchSysCache1(oidCacheId, ObjectIdGetDatum(objectId));
189 if (!HeapTupleIsValid(oldtup))
190 elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
191 objectId, RelationGetRelationName(rel));
192
193 datum = heap_getattr(oldtup, Anum_name,
194 RelationGetDescr(rel), &isnull);
195 Assert(!isnull);
196 old_name = NameStr(*(DatumGetName(datum)));
197
198 /* Get OID of namespace */
199 if (Anum_namespace > 0)
200 {
201 datum = heap_getattr(oldtup, Anum_namespace,
202 RelationGetDescr(rel), &isnull);
203 Assert(!isnull);
204 namespaceId = DatumGetObjectId(datum);
205 }
206 else
207 namespaceId = InvalidOid;
208
209 /* Permission checks ... superusers can always do it */
210 if (!superuser())
211 {
212 /* Fail if object does not have an explicit owner */
213 if (Anum_owner <= 0)
214 ereport(ERROR,
215 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
216 (errmsg("must be superuser to rename %s",
217 getObjectDescriptionOids(classId, objectId)))));
218
219 /* Otherwise, must be owner of the existing object */
220 datum = heap_getattr(oldtup, Anum_owner,
221 RelationGetDescr(rel), &isnull);
222 Assert(!isnull);
223 ownerId = DatumGetObjectId(datum);
224
225 if (!has_privs_of_role(GetUserId(), DatumGetObjectId(ownerId)))
226 aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objectId),
227 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 */
331 ObjectAddress
ExecRenameStmt(RenameStmt * stmt)332 ExecRenameStmt(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 */
431 ObjectAddress
ExecAlterObjectDependsStmt(AlterObjectDependsStmt * stmt,ObjectAddress * refAddress)432 ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddress)
433 {
434 ObjectAddress address;
435 ObjectAddress refAddr;
436 Relation rel;
437 List *currexts;
438
439 address =
440 get_object_address_rv(stmt->objectType, stmt->relation, (List *) stmt->object,
441 &rel, AccessExclusiveLock, false);
442
443 /*
444 * Verify that the user is entitled to run the command.
445 *
446 * We don't check any privileges on the extension, because that's not
447 * needed. The object owner is stipulating, by running this command, that
448 * the extension owner can drop the object whenever they feel like it,
449 * which is not considered a problem.
450 */
451 check_object_ownership(GetUserId(),
452 stmt->objectType, address, stmt->object, rel);
453
454 /*
455 * If a relation was involved, it would have been opened and locked. We
456 * don't need the relation here, but we'll retain the lock until commit.
457 */
458 if (rel)
459 table_close(rel, NoLock);
460
461 refAddr = get_object_address(OBJECT_EXTENSION, (Node *) stmt->extname,
462 &rel, AccessExclusiveLock, false);
463 Assert(rel == NULL);
464 if (refAddress)
465 *refAddress = refAddr;
466
467 /* Avoid duplicates */
468 currexts = getAutoExtensionsOfObject(address.classId,
469 address.objectId);
470 if (!list_member_oid(currexts, refAddr.objectId))
471 recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
472
473 return address;
474 }
475
476 /*
477 * Executes an ALTER OBJECT / SET SCHEMA statement. Based on the object
478 * type, the function appropriate to that type is executed.
479 *
480 * Return value is that of the altered object.
481 *
482 * oldSchemaAddr is an output argument which, if not NULL, is set to the object
483 * address of the original schema.
484 */
485 ObjectAddress
ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt * stmt,ObjectAddress * oldSchemaAddr)486 ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
487 ObjectAddress *oldSchemaAddr)
488 {
489 ObjectAddress address;
490 Oid oldNspOid;
491
492 switch (stmt->objectType)
493 {
494 case OBJECT_EXTENSION:
495 address = AlterExtensionNamespace(strVal((Value *) stmt->object), stmt->newschema,
496 oldSchemaAddr ? &oldNspOid : NULL);
497 break;
498
499 case OBJECT_FOREIGN_TABLE:
500 case OBJECT_SEQUENCE:
501 case OBJECT_TABLE:
502 case OBJECT_VIEW:
503 case OBJECT_MATVIEW:
504 address = AlterTableNamespace(stmt,
505 oldSchemaAddr ? &oldNspOid : NULL);
506 break;
507
508 case OBJECT_DOMAIN:
509 case OBJECT_TYPE:
510 address = AlterTypeNamespace(castNode(List, stmt->object), stmt->newschema,
511 stmt->objectType,
512 oldSchemaAddr ? &oldNspOid : NULL);
513 break;
514
515 /* generic code path */
516 case OBJECT_AGGREGATE:
517 case OBJECT_COLLATION:
518 case OBJECT_CONVERSION:
519 case OBJECT_FUNCTION:
520 case OBJECT_OPERATOR:
521 case OBJECT_OPCLASS:
522 case OBJECT_OPFAMILY:
523 case OBJECT_PROCEDURE:
524 case OBJECT_ROUTINE:
525 case OBJECT_STATISTIC_EXT:
526 case OBJECT_TSCONFIGURATION:
527 case OBJECT_TSDICTIONARY:
528 case OBJECT_TSPARSER:
529 case OBJECT_TSTEMPLATE:
530 {
531 Relation catalog;
532 Relation relation;
533 Oid classId;
534 Oid nspOid;
535
536 address = get_object_address(stmt->objectType,
537 stmt->object,
538 &relation,
539 AccessExclusiveLock,
540 false);
541 Assert(relation == NULL);
542 classId = address.classId;
543 catalog = table_open(classId, RowExclusiveLock);
544 nspOid = LookupCreationNamespace(stmt->newschema);
545
546 oldNspOid = AlterObjectNamespace_internal(catalog, address.objectId,
547 nspOid);
548 table_close(catalog, RowExclusiveLock);
549 }
550 break;
551
552 default:
553 elog(ERROR, "unrecognized AlterObjectSchemaStmt type: %d",
554 (int) stmt->objectType);
555 return InvalidObjectAddress; /* keep compiler happy */
556 }
557
558 if (oldSchemaAddr)
559 ObjectAddressSet(*oldSchemaAddr, NamespaceRelationId, oldNspOid);
560
561 return address;
562 }
563
564 /*
565 * Change an object's namespace given its classOid and object Oid.
566 *
567 * Objects that don't have a namespace should be ignored.
568 *
569 * This function is currently used only by ALTER EXTENSION SET SCHEMA,
570 * so it only needs to cover object types that can be members of an
571 * extension, and it doesn't have to deal with certain special cases
572 * such as not wanting to process array types --- those should never
573 * be direct members of an extension anyway. Nonetheless, we insist
574 * on listing all OCLASS types in the switch.
575 *
576 * Returns the OID of the object's previous namespace, or InvalidOid if
577 * object doesn't have a schema.
578 */
579 Oid
AlterObjectNamespace_oid(Oid classId,Oid objid,Oid nspOid,ObjectAddresses * objsMoved)580 AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
581 ObjectAddresses *objsMoved)
582 {
583 Oid oldNspOid = InvalidOid;
584 ObjectAddress dep;
585
586 dep.classId = classId;
587 dep.objectId = objid;
588 dep.objectSubId = 0;
589
590 switch (getObjectClass(&dep))
591 {
592 case OCLASS_CLASS:
593 {
594 Relation rel;
595
596 rel = relation_open(objid, AccessExclusiveLock);
597 oldNspOid = RelationGetNamespace(rel);
598
599 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
600
601 relation_close(rel, NoLock);
602 break;
603 }
604
605 case OCLASS_TYPE:
606 oldNspOid = AlterTypeNamespace_oid(objid, nspOid, objsMoved);
607 break;
608
609 case OCLASS_PROC:
610 case OCLASS_COLLATION:
611 case OCLASS_CONVERSION:
612 case OCLASS_OPERATOR:
613 case OCLASS_OPCLASS:
614 case OCLASS_OPFAMILY:
615 case OCLASS_STATISTIC_EXT:
616 case OCLASS_TSPARSER:
617 case OCLASS_TSDICT:
618 case OCLASS_TSTEMPLATE:
619 case OCLASS_TSCONFIG:
620 {
621 Relation catalog;
622
623 catalog = table_open(classId, RowExclusiveLock);
624
625 oldNspOid = AlterObjectNamespace_internal(catalog, objid,
626 nspOid);
627
628 table_close(catalog, RowExclusiveLock);
629 }
630 break;
631
632 case OCLASS_CAST:
633 case OCLASS_CONSTRAINT:
634 case OCLASS_DEFAULT:
635 case OCLASS_LANGUAGE:
636 case OCLASS_LARGEOBJECT:
637 case OCLASS_AM:
638 case OCLASS_AMOP:
639 case OCLASS_AMPROC:
640 case OCLASS_REWRITE:
641 case OCLASS_TRIGGER:
642 case OCLASS_SCHEMA:
643 case OCLASS_ROLE:
644 case OCLASS_DATABASE:
645 case OCLASS_TBLSPACE:
646 case OCLASS_FDW:
647 case OCLASS_FOREIGN_SERVER:
648 case OCLASS_USER_MAPPING:
649 case OCLASS_DEFACL:
650 case OCLASS_EXTENSION:
651 case OCLASS_EVENT_TRIGGER:
652 case OCLASS_POLICY:
653 case OCLASS_PUBLICATION:
654 case OCLASS_PUBLICATION_REL:
655 case OCLASS_SUBSCRIPTION:
656 case OCLASS_TRANSFORM:
657 /* ignore object types that don't have schema-qualified names */
658 break;
659
660 /*
661 * There's intentionally no default: case here; we want the
662 * compiler to warn if a new OCLASS hasn't been handled above.
663 */
664 }
665
666 return oldNspOid;
667 }
668
669 /*
670 * Generic function to change the namespace of a given object, for simple
671 * cases (won't work for tables, nor other cases where we need to do more
672 * than change the namespace column of a single catalog entry).
673 *
674 * rel: catalog relation containing object (RowExclusiveLock'd by caller)
675 * objid: OID of object to change the namespace of
676 * nspOid: OID of new namespace
677 *
678 * Returns the OID of the object's previous namespace.
679 */
680 static Oid
AlterObjectNamespace_internal(Relation rel,Oid objid,Oid nspOid)681 AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
682 {
683 Oid classId = RelationGetRelid(rel);
684 int oidCacheId = get_object_catcache_oid(classId);
685 int nameCacheId = get_object_catcache_name(classId);
686 AttrNumber Anum_name = get_object_attnum_name(classId);
687 AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
688 AttrNumber Anum_owner = get_object_attnum_owner(classId);
689 Oid oldNspOid;
690 Datum name,
691 namespace;
692 bool isnull;
693 HeapTuple tup,
694 newtup;
695 Datum *values;
696 bool *nulls;
697 bool *replaces;
698
699 tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid));
700 if (!HeapTupleIsValid(tup)) /* should not happen */
701 elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
702 objid, RelationGetRelationName(rel));
703
704 name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
705 Assert(!isnull);
706 namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel),
707 &isnull);
708 Assert(!isnull);
709 oldNspOid = DatumGetObjectId(namespace);
710
711 /*
712 * If the object is already in the correct namespace, we don't need to do
713 * anything except fire the object access hook.
714 */
715 if (oldNspOid == nspOid)
716 {
717 InvokeObjectPostAlterHook(classId, objid, 0);
718 return oldNspOid;
719 }
720
721 /* Check basic namespace related issues */
722 CheckSetNamespace(oldNspOid, nspOid);
723
724 /* Permission checks ... superusers can always do it */
725 if (!superuser())
726 {
727 Datum owner;
728 Oid ownerId;
729 AclResult aclresult;
730
731 /* Fail if object does not have an explicit owner */
732 if (Anum_owner <= 0)
733 ereport(ERROR,
734 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
735 (errmsg("must be superuser to set schema of %s",
736 getObjectDescriptionOids(classId, objid)))));
737
738 /* Otherwise, must be owner of the existing object */
739 owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull);
740 Assert(!isnull);
741 ownerId = DatumGetObjectId(owner);
742
743 if (!has_privs_of_role(GetUserId(), ownerId))
744 aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objid),
745 NameStr(*(DatumGetName(name))));
746
747 /* User must have CREATE privilege on new namespace */
748 aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE);
749 if (aclresult != ACLCHECK_OK)
750 aclcheck_error(aclresult, OBJECT_SCHEMA,
751 get_namespace_name(nspOid));
752 }
753
754 /*
755 * Check for duplicate name (more friendly than unique-index failure).
756 * Since this is just a friendliness check, we can just skip it in cases
757 * where there isn't suitable support.
758 */
759 if (classId == ProcedureRelationId)
760 {
761 Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tup);
762
763 IsThereFunctionInNamespace(NameStr(proc->proname), proc->pronargs,
764 &proc->proargtypes, nspOid);
765 }
766 else if (classId == CollationRelationId)
767 {
768 Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(tup);
769
770 IsThereCollationInNamespace(NameStr(coll->collname), nspOid);
771 }
772 else if (classId == OperatorClassRelationId)
773 {
774 Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(tup);
775
776 IsThereOpClassInNamespace(NameStr(opc->opcname),
777 opc->opcmethod, nspOid);
778 }
779 else if (classId == OperatorFamilyRelationId)
780 {
781 Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(tup);
782
783 IsThereOpFamilyInNamespace(NameStr(opf->opfname),
784 opf->opfmethod, nspOid);
785 }
786 else if (nameCacheId >= 0 &&
787 SearchSysCacheExists2(nameCacheId, name,
788 ObjectIdGetDatum(nspOid)))
789 report_namespace_conflict(classId,
790 NameStr(*(DatumGetName(name))),
791 nspOid);
792
793 /* Build modified tuple */
794 values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
795 nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
796 replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
797 values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid);
798 replaces[Anum_namespace - 1] = true;
799 newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
800 values, nulls, replaces);
801
802 /* Perform actual update */
803 CatalogTupleUpdate(rel, &tup->t_self, newtup);
804
805 /* Release memory */
806 pfree(values);
807 pfree(nulls);
808 pfree(replaces);
809
810 /* update dependencies to point to the new schema */
811 changeDependencyFor(classId, objid,
812 NamespaceRelationId, oldNspOid, nspOid);
813
814 InvokeObjectPostAlterHook(classId, objid, 0);
815
816 return oldNspOid;
817 }
818
819 /*
820 * Executes an ALTER OBJECT / OWNER TO statement. Based on the object
821 * type, the function appropriate to that type is executed.
822 */
823 ObjectAddress
ExecAlterOwnerStmt(AlterOwnerStmt * stmt)824 ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
825 {
826 Oid newowner = get_rolespec_oid(stmt->newowner, false);
827
828 switch (stmt->objectType)
829 {
830 case OBJECT_DATABASE:
831 return AlterDatabaseOwner(strVal((Value *) stmt->object), newowner);
832
833 case OBJECT_SCHEMA:
834 return AlterSchemaOwner(strVal((Value *) stmt->object), newowner);
835
836 case OBJECT_TYPE:
837 case OBJECT_DOMAIN: /* same as TYPE */
838 return AlterTypeOwner(castNode(List, stmt->object), newowner, stmt->objectType);
839 break;
840
841 case OBJECT_FDW:
842 return AlterForeignDataWrapperOwner(strVal((Value *) stmt->object),
843 newowner);
844
845 case OBJECT_FOREIGN_SERVER:
846 return AlterForeignServerOwner(strVal((Value *) stmt->object),
847 newowner);
848
849 case OBJECT_EVENT_TRIGGER:
850 return AlterEventTriggerOwner(strVal((Value *) stmt->object),
851 newowner);
852
853 case OBJECT_PUBLICATION:
854 return AlterPublicationOwner(strVal((Value *) stmt->object),
855 newowner);
856
857 case OBJECT_SUBSCRIPTION:
858 return AlterSubscriptionOwner(strVal((Value *) stmt->object),
859 newowner);
860
861 /* Generic cases */
862 case OBJECT_AGGREGATE:
863 case OBJECT_COLLATION:
864 case OBJECT_CONVERSION:
865 case OBJECT_FUNCTION:
866 case OBJECT_LANGUAGE:
867 case OBJECT_LARGEOBJECT:
868 case OBJECT_OPERATOR:
869 case OBJECT_OPCLASS:
870 case OBJECT_OPFAMILY:
871 case OBJECT_PROCEDURE:
872 case OBJECT_ROUTINE:
873 case OBJECT_STATISTIC_EXT:
874 case OBJECT_TABLESPACE:
875 case OBJECT_TSDICTIONARY:
876 case OBJECT_TSCONFIGURATION:
877 {
878 Relation catalog;
879 Relation relation;
880 Oid classId;
881 ObjectAddress address;
882
883 address = get_object_address(stmt->objectType,
884 stmt->object,
885 &relation,
886 AccessExclusiveLock,
887 false);
888 Assert(relation == NULL);
889 classId = address.classId;
890
891 /*
892 * XXX - get_object_address returns Oid of pg_largeobject
893 * catalog for OBJECT_LARGEOBJECT because of historical
894 * reasons. Fix up it here.
895 */
896 if (classId == LargeObjectRelationId)
897 classId = LargeObjectMetadataRelationId;
898
899 catalog = table_open(classId, RowExclusiveLock);
900
901 AlterObjectOwner_internal(catalog, address.objectId, newowner);
902 table_close(catalog, RowExclusiveLock);
903
904 return address;
905 }
906 break;
907
908 default:
909 elog(ERROR, "unrecognized AlterOwnerStmt type: %d",
910 (int) stmt->objectType);
911 return InvalidObjectAddress; /* keep compiler happy */
912 }
913 }
914
915 /*
916 * Generic function to change the ownership of a given object, for simple
917 * cases (won't work for tables, nor other cases where we need to do more than
918 * change the ownership column of a single catalog entry).
919 *
920 * rel: catalog relation containing object (RowExclusiveLock'd by caller)
921 * objectId: OID of object to change the ownership of
922 * new_ownerId: OID of new object owner
923 */
924 void
AlterObjectOwner_internal(Relation rel,Oid objectId,Oid new_ownerId)925 AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId)
926 {
927 Oid classId = RelationGetRelid(rel);
928 AttrNumber Anum_oid = get_object_attnum_oid(classId);
929 AttrNumber Anum_owner = get_object_attnum_owner(classId);
930 AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
931 AttrNumber Anum_acl = get_object_attnum_acl(classId);
932 AttrNumber Anum_name = get_object_attnum_name(classId);
933 HeapTuple oldtup;
934 Datum datum;
935 bool isnull;
936 Oid old_ownerId;
937 Oid namespaceId = InvalidOid;
938
939 oldtup = get_catalog_object_by_oid(rel, Anum_oid, objectId);
940 if (oldtup == NULL)
941 elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
942 objectId, RelationGetRelationName(rel));
943
944 datum = heap_getattr(oldtup, Anum_owner,
945 RelationGetDescr(rel), &isnull);
946 Assert(!isnull);
947 old_ownerId = DatumGetObjectId(datum);
948
949 if (Anum_namespace != InvalidAttrNumber)
950 {
951 datum = heap_getattr(oldtup, Anum_namespace,
952 RelationGetDescr(rel), &isnull);
953 Assert(!isnull);
954 namespaceId = DatumGetObjectId(datum);
955 }
956
957 if (old_ownerId != new_ownerId)
958 {
959 AttrNumber nattrs;
960 HeapTuple newtup;
961 Datum *values;
962 bool *nulls;
963 bool *replaces;
964
965 /* Superusers can bypass permission checks */
966 if (!superuser())
967 {
968 /* must be owner */
969 if (!has_privs_of_role(GetUserId(), old_ownerId))
970 {
971 char *objname;
972 char namebuf[NAMEDATALEN];
973
974 if (Anum_name != InvalidAttrNumber)
975 {
976 datum = heap_getattr(oldtup, Anum_name,
977 RelationGetDescr(rel), &isnull);
978 Assert(!isnull);
979 objname = NameStr(*DatumGetName(datum));
980 }
981 else
982 {
983 snprintf(namebuf, sizeof(namebuf), "%u", objectId);
984 objname = namebuf;
985 }
986 aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objectId),
987 objname);
988 }
989 /* Must be able to become new owner */
990 check_is_member_of_role(GetUserId(), new_ownerId);
991
992 /* New owner must have CREATE privilege on namespace */
993 if (OidIsValid(namespaceId))
994 {
995 AclResult aclresult;
996
997 aclresult = pg_namespace_aclcheck(namespaceId, new_ownerId,
998 ACL_CREATE);
999 if (aclresult != ACLCHECK_OK)
1000 aclcheck_error(aclresult, OBJECT_SCHEMA,
1001 get_namespace_name(namespaceId));
1002 }
1003 }
1004
1005 /* Build a modified tuple */
1006 nattrs = RelationGetNumberOfAttributes(rel);
1007 values = palloc0(nattrs * sizeof(Datum));
1008 nulls = palloc0(nattrs * sizeof(bool));
1009 replaces = palloc0(nattrs * sizeof(bool));
1010 values[Anum_owner - 1] = ObjectIdGetDatum(new_ownerId);
1011 replaces[Anum_owner - 1] = true;
1012
1013 /*
1014 * Determine the modified ACL for the new owner. This is only
1015 * necessary when the ACL is non-null.
1016 */
1017 if (Anum_acl != InvalidAttrNumber)
1018 {
1019 datum = heap_getattr(oldtup,
1020 Anum_acl, RelationGetDescr(rel), &isnull);
1021 if (!isnull)
1022 {
1023 Acl *newAcl;
1024
1025 newAcl = aclnewowner(DatumGetAclP(datum),
1026 old_ownerId, new_ownerId);
1027 values[Anum_acl - 1] = PointerGetDatum(newAcl);
1028 replaces[Anum_acl - 1] = true;
1029 }
1030 }
1031
1032 newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
1033 values, nulls, replaces);
1034
1035 /* Perform actual update */
1036 CatalogTupleUpdate(rel, &newtup->t_self, newtup);
1037
1038 /* Update owner dependency reference */
1039 if (classId == LargeObjectMetadataRelationId)
1040 classId = LargeObjectRelationId;
1041 changeDependencyOnOwner(classId, objectId, new_ownerId);
1042
1043 /* Release memory */
1044 pfree(values);
1045 pfree(nulls);
1046 pfree(replaces);
1047 }
1048
1049 InvokeObjectPostAlterHook(classId, objectId, 0);
1050 }
1051