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