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