TLA 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 GIC 12 : report_name_conflict(Oid classId, const char *name)
80 : {
81 ECB : char *msgfmt;
82 :
83 GIC 12 : switch (classId)
84 : {
85 CBC 3 : case EventTriggerRelationId:
86 GIC 3 : msgfmt = gettext_noop("event trigger \"%s\" already exists");
87 CBC 3 : break;
88 3 : case ForeignDataWrapperRelationId:
89 3 : msgfmt = gettext_noop("foreign-data wrapper \"%s\" already exists");
90 3 : break;
91 3 : case ForeignServerRelationId:
92 3 : msgfmt = gettext_noop("server \"%s\" already exists");
93 3 : break;
94 3 : case LanguageRelationId:
95 3 : msgfmt = gettext_noop("language \"%s\" already exists");
96 3 : break;
97 LBC 0 : case PublicationRelationId:
98 0 : msgfmt = gettext_noop("publication \"%s\" already exists");
99 UBC 0 : break;
100 0 : case SubscriptionRelationId:
101 0 : msgfmt = gettext_noop("subscription \"%s\" already exists");
102 0 : break;
103 0 : default:
104 UNC 0 : elog(ERROR, "unsupported object class: %u", classId);
105 EUB : break;
106 : }
107 :
108 GIC 12 : ereport(ERROR,
109 : (errcode(ERRCODE_DUPLICATE_OBJECT),
110 ECB : errmsg(msgfmt, name)));
111 : }
112 :
113 : static void
114 GIC 36 : report_namespace_conflict(Oid classId, const char *name, Oid nspOid)
115 : {
116 ECB : char *msgfmt;
117 :
118 GIC 36 : Assert(OidIsValid(nspOid));
119 :
120 CBC 36 : switch (classId)
121 : {
122 6 : case ConversionRelationId:
123 GIC 6 : Assert(OidIsValid(nspOid));
124 CBC 6 : msgfmt = gettext_noop("conversion \"%s\" already exists in schema \"%s\"");
125 6 : break;
126 6 : case StatisticExtRelationId:
127 6 : Assert(OidIsValid(nspOid));
128 6 : msgfmt = gettext_noop("statistics object \"%s\" already exists in schema \"%s\"");
129 6 : break;
130 6 : case TSParserRelationId:
131 6 : Assert(OidIsValid(nspOid));
132 6 : msgfmt = gettext_noop("text search parser \"%s\" already exists in schema \"%s\"");
133 6 : break;
134 6 : case TSDictionaryRelationId:
135 6 : Assert(OidIsValid(nspOid));
136 6 : msgfmt = gettext_noop("text search dictionary \"%s\" already exists in schema \"%s\"");
137 6 : break;
138 6 : case TSTemplateRelationId:
139 6 : Assert(OidIsValid(nspOid));
140 6 : msgfmt = gettext_noop("text search template \"%s\" already exists in schema \"%s\"");
141 6 : break;
142 6 : case TSConfigRelationId:
143 6 : Assert(OidIsValid(nspOid));
144 6 : msgfmt = gettext_noop("text search configuration \"%s\" already exists in schema \"%s\"");
145 6 : break;
146 LBC 0 : default:
147 UNC 0 : elog(ERROR, "unsupported object class: %u", classId);
148 EUB : break;
149 : }
150 :
151 GIC 36 : ereport(ERROR,
152 : (errcode(ERRCODE_DUPLICATE_OBJECT),
153 ECB : 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 GIC 208 : AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
169 : {
170 CBC 208 : Oid classId = RelationGetRelid(rel);
171 GIC 208 : int oidCacheId = get_object_catcache_oid(classId);
172 CBC 208 : int nameCacheId = get_object_catcache_name(classId);
173 208 : AttrNumber Anum_name = get_object_attnum_name(classId);
174 208 : AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
175 208 : AttrNumber Anum_owner = get_object_attnum_owner(classId);
176 ECB : 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 GIC 208 : oldtup = SearchSysCache1(oidCacheId, ObjectIdGetDatum(objectId));
190 208 : if (!HeapTupleIsValid(oldtup))
191 LBC 0 : elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
192 ECB : objectId, RelationGetRelationName(rel));
193 EUB :
194 GIC 208 : datum = heap_getattr(oldtup, Anum_name,
195 : RelationGetDescr(rel), &isnull);
196 CBC 208 : Assert(!isnull);
197 GIC 208 : old_name = NameStr(*(DatumGetName(datum)));
198 ECB :
199 : /* Get OID of namespace */
200 GIC 208 : if (Anum_namespace > 0)
201 : {
202 CBC 141 : datum = heap_getattr(oldtup, Anum_namespace,
203 : RelationGetDescr(rel), &isnull);
204 141 : Assert(!isnull);
205 GIC 141 : namespaceId = DatumGetObjectId(datum);
206 ECB : }
207 : else
208 GIC 67 : namespaceId = InvalidOid;
209 :
210 ECB : /* Permission checks ... superusers can always do it */
211 GIC 208 : if (!superuser())
212 : {
213 ECB : /* Fail if object does not have an explicit owner */
214 GIC 123 : if (Anum_owner <= 0)
215 UIC 0 : ereport(ERROR,
216 ECB : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
217 EUB : errmsg("must be superuser to rename %s",
218 : getObjectDescriptionOids(classId, objectId))));
219 :
220 : /* Otherwise, must be owner of the existing object */
221 GIC 123 : datum = heap_getattr(oldtup, Anum_owner,
222 : RelationGetDescr(rel), &isnull);
223 CBC 123 : Assert(!isnull);
224 GIC 123 : ownerId = DatumGetObjectId(datum);
225 ECB :
226 CBC 123 : if (!has_privs_of_role(GetUserId(), DatumGetObjectId(ownerId)))
227 GIC 36 : aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objectId),
228 ECB : old_name);
229 :
230 : /* User must have CREATE privilege on the namespace */
231 GIC 87 : if (OidIsValid(namespaceId))
232 : {
233 GNC 72 : aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(),
234 : ACL_CREATE);
235 CBC 72 : if (aclresult != ACLCHECK_OK)
236 UIC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
237 LBC 0 : get_namespace_name(namespaceId));
238 EUB : }
239 :
240 GNC 87 : if (classId == SubscriptionRelationId)
241 : {
242 : Form_pg_subscription form;
243 :
244 : /* must have CREATE privilege on database */
245 9 : aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId,
246 : GetUserId(), ACL_CREATE);
247 9 : if (aclresult != ACLCHECK_OK)
248 3 : aclcheck_error(aclresult, OBJECT_DATABASE,
249 3 : get_database_name(MyDatabaseId));
250 :
251 : /*
252 : * Don't allow non-superuser modification of a subscription with
253 : * password_required=false.
254 : */
255 6 : form = (Form_pg_subscription) GETSTRUCT(oldtup);
256 6 : if (!form->subpasswordrequired && !superuser())
257 UNC 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 EUB : }
263 :
264 : /*
265 ECB : * 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 GIC 169 : if (classId == ProcedureRelationId)
270 ECB : {
271 GIC 36 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(oldtup);
272 ECB :
273 CBC 36 : IsThereFunctionInNamespace(new_name, proc->pronargs,
274 ECB : &proc->proargtypes, proc->pronamespace);
275 : }
276 GIC 133 : else if (classId == CollationRelationId)
277 : {
278 12 : Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(oldtup);
279 :
280 CBC 12 : IsThereCollationInNamespace(new_name, coll->collnamespace);
281 ECB : }
282 GBC 121 : else if (classId == OperatorClassRelationId)
283 : {
284 GIC 9 : Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(oldtup);
285 :
286 9 : IsThereOpClassInNamespace(new_name, opc->opcmethod,
287 : opc->opcnamespace);
288 : }
289 112 : else if (classId == OperatorFamilyRelationId)
290 : {
291 9 : Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(oldtup);
292 :
293 9 : IsThereOpFamilyInNamespace(new_name, opf->opfmethod,
294 ECB : opf->opfnamespace);
295 : }
296 CBC 103 : else if (classId == SubscriptionRelationId)
297 : {
298 13 : if (SearchSysCacheExists2(SUBSCRIPTIONNAME, MyDatabaseId,
299 : CStringGetDatum(new_name)))
300 UIC 0 : report_name_conflict(classId, new_name);
301 ECB :
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 GNC 13 : LogicalRepWorkersWakeupAtCommit(objectId);
310 ECB : }
311 GIC 90 : else if (nameCacheId >= 0)
312 ECB : {
313 GIC 90 : if (OidIsValid(namespaceId))
314 ECB : {
315 GIC 48 : if (SearchSysCacheExists2(nameCacheId,
316 : CStringGetDatum(new_name),
317 ECB : ObjectIdGetDatum(namespaceId)))
318 GIC 18 : report_namespace_conflict(classId, new_name, namespaceId);
319 ECB : }
320 : else
321 : {
322 GIC 42 : if (SearchSysCacheExists1(nameCacheId,
323 : CStringGetDatum(new_name)))
324 CBC 12 : report_name_conflict(classId, new_name);
325 : }
326 ECB : }
327 :
328 EUB : /* Build modified tuple */
329 GIC 121 : values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
330 121 : nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
331 121 : replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
332 121 : namestrcpy(&nameattrdata, new_name);
333 121 : values[Anum_name - 1] = NameGetDatum(&nameattrdata);
334 121 : replaces[Anum_name - 1] = true;
335 121 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
336 : values, nulls, replaces);
337 ECB :
338 : /* Perform actual update */
339 CBC 121 : CatalogTupleUpdate(rel, &oldtup->t_self, newtup);
340 :
341 121 : InvokeObjectPostAlterHook(classId, objectId, 0);
342 :
343 ECB : /* Release memory */
344 GIC 121 : pfree(values);
345 121 : pfree(nulls);
346 CBC 121 : pfree(replaces);
347 GIC 121 : heap_freetuple(newtup);
348 :
349 121 : ReleaseSysCache(oldtup);
350 CBC 121 : }
351 :
352 ECB : /*
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 CBC 750 : ExecRenameStmt(RenameStmt *stmt)
360 ECB : {
361 CBC 750 : switch (stmt->renameType)
362 ECB : {
363 CBC 39 : case OBJECT_TABCONSTRAINT:
364 : case OBJECT_DOMCONSTRAINT:
365 GIC 39 : return RenameConstraint(stmt);
366 :
367 LBC 0 : case OBJECT_DATABASE:
368 UIC 0 : return RenameDatabase(stmt->subname, stmt->newname);
369 ECB :
370 GIC 15 : case OBJECT_ROLE:
371 15 : return RenameRole(stmt->subname, stmt->newname);
372 ECB :
373 CBC 10 : case OBJECT_SCHEMA:
374 10 : return RenameSchema(stmt->subname, stmt->newname);
375 ECB :
376 GIC 3 : case OBJECT_TABLESPACE:
377 CBC 3 : return RenameTableSpace(stmt->subname, stmt->newname);
378 ECB :
379 GIC 255 : 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 255 : return RenameRelation(stmt);
386 :
387 CBC 149 : case OBJECT_COLUMN:
388 : case OBJECT_ATTRIBUTE:
389 149 : return renameatt(stmt);
390 :
391 17 : case OBJECT_RULE:
392 GIC 17 : return RenameRewriteRule(stmt->relation, stmt->subname,
393 CBC 17 : stmt->newname);
394 :
395 GBC 20 : case OBJECT_TRIGGER:
396 20 : return renametrig(stmt);
397 :
398 CBC 9 : case OBJECT_POLICY:
399 9 : return rename_policy(stmt);
400 :
401 16 : case OBJECT_DOMAIN:
402 ECB : case OBJECT_TYPE:
403 GIC 16 : return RenameType(stmt);
404 ECB :
405 CBC 217 : case OBJECT_AGGREGATE:
406 : case OBJECT_COLLATION:
407 ECB : 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 CBC 217 : address = get_object_address(stmt->renameType,
430 : stmt->object,
431 ECB : &relation,
432 : AccessExclusiveLock, false);
433 CBC 208 : Assert(relation == NULL);
434 :
435 GIC 208 : catalog = table_open(address.classId, RowExclusiveLock);
436 208 : AlterObjectRename_internal(catalog,
437 : address.objectId,
438 208 : stmt->newname);
439 121 : table_close(catalog, RowExclusiveLock);
440 :
441 121 : return address;
442 : }
443 :
444 UIC 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 ECB : */
458 : ObjectAddress
459 GIC 23 : ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddress)
460 : {
461 ECB : ObjectAddress address;
462 : ObjectAddress refAddr;
463 : Relation rel;
464 :
465 : address =
466 CBC 23 : get_object_address_rv(stmt->objectType, stmt->relation, (List *) stmt->object,
467 ECB : &rel, AccessExclusiveLock, false);
468 :
469 : /*
470 : * Verify that the user is entitled to run the command.
471 : *
472 EUB : * 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 GIC 23 : 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 23 : if (rel)
485 17 : table_close(rel, NoLock);
486 :
487 CBC 23 : refAddr = get_object_address(OBJECT_EXTENSION, (Node *) stmt->extname,
488 : &rel, AccessExclusiveLock, false);
489 GIC 23 : Assert(rel == NULL);
490 23 : if (refAddress)
491 23 : *refAddress = refAddr;
492 :
493 23 : if (stmt->remove)
494 ECB : {
495 GIC 4 : 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 19 : currexts = getAutoExtensionsOfObject(address.classId,
505 ECB : address.objectId);
506 GIC 19 : if (!list_member_oid(currexts, refAddr.objectId))
507 18 : recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
508 : }
509 :
510 23 : return address;
511 : }
512 ECB :
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 CBC 194 : ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
524 : ObjectAddress *oldSchemaAddr)
525 : {
526 : ObjectAddress address;
527 : Oid oldNspOid;
528 :
529 GIC 194 : switch (stmt->objectType)
530 : {
531 3 : case OBJECT_EXTENSION:
532 CBC 3 : address = AlterExtensionNamespace(strVal(stmt->object), stmt->newschema,
533 : oldSchemaAddr ? &oldNspOid : NULL);
534 2 : break;
535 ECB :
536 GIC 49 : case OBJECT_FOREIGN_TABLE:
537 : case OBJECT_SEQUENCE:
538 ECB : case OBJECT_TABLE:
539 : case OBJECT_VIEW:
540 : case OBJECT_MATVIEW:
541 GIC 49 : address = AlterTableNamespace(stmt,
542 : oldSchemaAddr ? &oldNspOid : NULL);
543 48 : break;
544 :
545 9 : case OBJECT_DOMAIN:
546 : case OBJECT_TYPE:
547 9 : address = AlterTypeNamespace(castNode(List, stmt->object), stmt->newschema,
548 : stmt->objectType,
549 : oldSchemaAddr ? &oldNspOid : NULL);
550 9 : break;
551 ECB :
552 : /* generic code path */
553 GIC 133 : case OBJECT_AGGREGATE:
554 : case OBJECT_COLLATION:
555 : case OBJECT_CONVERSION:
556 : case OBJECT_FUNCTION:
557 ECB : 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 CBC 133 : address = get_object_address(stmt->objectType,
574 : stmt->object,
575 ECB : &relation,
576 : AccessExclusiveLock,
577 : false);
578 CBC 130 : Assert(relation == NULL);
579 GIC 130 : classId = address.classId;
580 130 : catalog = table_open(classId, RowExclusiveLock);
581 CBC 130 : nspOid = LookupCreationNamespace(stmt->newschema);
582 :
583 GIC 130 : oldNspOid = AlterObjectNamespace_internal(catalog, address.objectId,
584 : nspOid);
585 73 : table_close(catalog, RowExclusiveLock);
586 : }
587 73 : break;
588 :
589 UIC 0 : default:
590 0 : elog(ERROR, "unrecognized AlterObjectSchemaStmt type: %d",
591 : (int) stmt->objectType);
592 : return InvalidObjectAddress; /* keep compiler happy */
593 : }
594 :
595 GIC 132 : if (oldSchemaAddr)
596 132 : ObjectAddressSet(*oldSchemaAddr, NamespaceRelationId, oldNspOid);
597 :
598 132 : return address;
599 : }
600 :
601 ECB : /*
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 GBC 3 : AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
618 EUB : ObjectAddresses *objsMoved)
619 : {
620 GIC 3 : Oid oldNspOid = InvalidOid;
621 : ObjectAddress dep;
622 :
623 CBC 3 : dep.classId = classId;
624 3 : dep.objectId = objid;
625 GIC 3 : dep.objectSubId = 0;
626 ECB :
627 GIC 3 : switch (getObjectClass(&dep))
628 : {
629 UIC 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 ECB :
646 GIC 3 : case OCLASS_PROC:
647 : case OCLASS_COLLATION:
648 ECB : 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 EUB : {
658 : Relation catalog;
659 :
660 GIC 3 : catalog = table_open(classId, RowExclusiveLock);
661 EUB :
662 GBC 3 : oldNspOid = AlterObjectNamespace_internal(catalog, objid,
663 : nspOid);
664 EUB :
665 GIC 3 : table_close(catalog, RowExclusiveLock);
666 EUB : }
667 GBC 3 : break;
668 :
669 UIC 0 : case OCLASS_CAST:
670 EUB : case OCLASS_CONSTRAINT:
671 : case OCLASS_DEFAULT:
672 : case OCLASS_LANGUAGE:
673 : case OCLASS_LARGEOBJECT:
674 ECB : 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 UBC 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 GIC 3 : 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 133 : AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
722 : {
723 133 : Oid classId = RelationGetRelid(rel);
724 133 : int oidCacheId = get_object_catcache_oid(classId);
725 133 : int nameCacheId = get_object_catcache_name(classId);
726 133 : AttrNumber Anum_name = get_object_attnum_name(classId);
727 GBC 133 : AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
728 GIC 133 : 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 ECB : Datum *values;
736 : bool *nulls;
737 : bool *replaces;
738 :
739 GIC 133 : tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid));
740 133 : if (!HeapTupleIsValid(tup)) /* should not happen */
741 UIC 0 : elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
742 : objid, RelationGetRelationName(rel));
743 :
744 GIC 133 : name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
745 133 : Assert(!isnull);
746 133 : namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel),
747 : &isnull);
748 133 : Assert(!isnull);
749 133 : oldNspOid = DatumGetObjectId(namespace);
750 ECB :
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 CBC 133 : if (oldNspOid == nspOid)
756 ECB : {
757 CBC 3 : InvokeObjectPostAlterHook(classId, objid, 0);
758 GIC 3 : return oldNspOid;
759 : }
760 :
761 : /* Check basic namespace related issues */
762 130 : CheckSetNamespace(oldNspOid, nspOid);
763 :
764 : /* Permission checks ... superusers can always do it */
765 130 : if (!superuser())
766 : {
767 : Datum owner;
768 ECB : Oid ownerId;
769 : AclResult aclresult;
770 EUB :
771 : /* Fail if object does not have an explicit owner */
772 GIC 78 : if (Anum_owner <= 0)
773 LBC 0 : ereport(ERROR,
774 ECB : (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 GIC 78 : owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull);
780 78 : Assert(!isnull);
781 78 : ownerId = DatumGetObjectId(owner);
782 :
783 78 : if (!has_privs_of_role(GetUserId(), ownerId))
784 CBC 27 : aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objid),
785 GIC 27 : NameStr(*(DatumGetName(name))));
786 ECB :
787 : /* User must have CREATE privilege on new namespace */
788 GNC 51 : aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
789 GIC 51 : if (aclresult != ACLCHECK_OK)
790 UIC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
791 LBC 0 : get_namespace_name(nspOid));
792 : }
793 :
794 ECB : /*
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 GIC 103 : if (classId == ProcedureRelationId)
800 : {
801 CBC 22 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tup);
802 EUB :
803 GIC 22 : IsThereFunctionInNamespace(NameStr(proc->proname), proc->pronargs,
804 : &proc->proargtypes, nspOid);
805 : }
806 81 : else if (classId == CollationRelationId)
807 : {
808 CBC 6 : Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(tup);
809 ECB :
810 CBC 6 : IsThereCollationInNamespace(NameStr(coll->collname), nspOid);
811 : }
812 75 : else if (classId == OperatorClassRelationId)
813 ECB : {
814 CBC 9 : Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(tup);
815 :
816 GIC 9 : IsThereOpClassInNamespace(NameStr(opc->opcname),
817 ECB : opc->opcmethod, nspOid);
818 : }
819 GBC 66 : else if (classId == OperatorFamilyRelationId)
820 EUB : {
821 GIC 9 : Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(tup);
822 :
823 9 : IsThereOpFamilyInNamespace(NameStr(opf->opfname),
824 : opf->opfmethod, nspOid);
825 : }
826 108 : else if (nameCacheId >= 0 &&
827 51 : SearchSysCacheExists2(nameCacheId, name,
828 ECB : ObjectIdGetDatum(nspOid)))
829 GIC 18 : report_namespace_conflict(classId,
830 CBC 18 : NameStr(*(DatumGetName(name))),
831 : nspOid);
832 ECB :
833 : /* Build modified tuple */
834 GIC 73 : values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
835 CBC 73 : nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
836 GIC 73 : replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
837 CBC 73 : values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid);
838 GIC 73 : replaces[Anum_namespace - 1] = true;
839 CBC 73 : newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
840 : values, nulls, replaces);
841 ECB :
842 : /* Perform actual update */
843 CBC 73 : CatalogTupleUpdate(rel, &tup->t_self, newtup);
844 :
845 ECB : /* Release memory */
846 GIC 73 : pfree(values);
847 73 : pfree(nulls);
848 CBC 73 : pfree(replaces);
849 :
850 ECB : /* update dependencies to point to the new schema */
851 GIC 73 : changeDependencyFor(classId, objid,
852 ECB : NamespaceRelationId, oldNspOid, nspOid);
853 :
854 GIC 73 : InvokeObjectPostAlterHook(classId, objid, 0);
855 ECB :
856 CBC 73 : return oldNspOid;
857 : }
858 ECB :
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 CBC 1014 : ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
865 ECB : {
866 CBC 1014 : Oid newowner = get_rolespec_oid(stmt->newowner, false);
867 ECB :
868 CBC 1005 : switch (stmt->objectType)
869 : {
870 GIC 18 : case OBJECT_DATABASE:
871 18 : return AlterDatabaseOwner(strVal(stmt->object), newowner);
872 ECB :
873 GIC 26 : case OBJECT_SCHEMA:
874 26 : return AlterSchemaOwner(strVal(stmt->object), newowner);
875 ECB :
876 CBC 48 : case OBJECT_TYPE:
877 ECB : case OBJECT_DOMAIN: /* same as TYPE */
878 GIC 48 : return AlterTypeOwner(castNode(List, stmt->object), newowner, stmt->objectType);
879 : break;
880 ECB :
881 GIC 10 : case OBJECT_FDW:
882 10 : return AlterForeignDataWrapperOwner(strVal(stmt->object),
883 ECB : newowner);
884 :
885 CBC 34 : case OBJECT_FOREIGN_SERVER:
886 GIC 34 : return AlterForeignServerOwner(strVal(stmt->object),
887 : newowner);
888 :
889 6 : case OBJECT_EVENT_TRIGGER:
890 6 : return AlterEventTriggerOwner(strVal(stmt->object),
891 : newowner);
892 :
893 CBC 12 : case OBJECT_PUBLICATION:
894 GIC 12 : return AlterPublicationOwner(strVal(stmt->object),
895 ECB : newowner);
896 :
897 CBC 6 : case OBJECT_SUBSCRIPTION:
898 GIC 6 : return AlterSubscriptionOwner(strVal(stmt->object),
899 ECB : newowner);
900 :
901 : /* Generic cases */
902 CBC 845 : case OBJECT_AGGREGATE:
903 ECB : 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 CBC 845 : address = get_object_address(stmt->objectType,
924 : stmt->object,
925 : &relation,
926 ECB : AccessExclusiveLock,
927 : false);
928 GIC 841 : Assert(relation == NULL);
929 841 : classId = address.classId;
930 :
931 ECB : /*
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 GIC 841 : if (classId == LargeObjectRelationId)
937 6 : classId = LargeObjectMetadataRelationId;
938 :
939 841 : catalog = table_open(classId, RowExclusiveLock);
940 :
941 841 : AlterObjectOwner_internal(catalog, address.objectId, newowner);
942 754 : table_close(catalog, RowExclusiveLock);
943 :
944 754 : return address;
945 : }
946 : break;
947 :
948 UIC 0 : default:
949 0 : elog(ERROR, "unrecognized AlterOwnerStmt type: %d",
950 : (int) stmt->objectType);
951 : return InvalidObjectAddress; /* keep compiler happy */
952 ECB : }
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 CBC 844 : AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId)
966 ECB : {
967 GIC 844 : Oid classId = RelationGetRelid(rel);
968 CBC 844 : AttrNumber Anum_oid = get_object_attnum_oid(classId);
969 GIC 844 : AttrNumber Anum_owner = get_object_attnum_owner(classId);
970 CBC 844 : AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
971 844 : AttrNumber Anum_acl = get_object_attnum_acl(classId);
972 GIC 844 : AttrNumber Anum_name = get_object_attnum_name(classId);
973 ECB : HeapTuple oldtup;
974 : Datum datum;
975 : bool isnull;
976 : Oid old_ownerId;
977 GBC 844 : Oid namespaceId = InvalidOid;
978 EUB :
979 GIC 844 : oldtup = get_catalog_object_by_oid(rel, Anum_oid, objectId);
980 844 : if (oldtup == NULL)
981 UIC 0 : elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
982 : objectId, RelationGetRelationName(rel));
983 :
984 GIC 844 : datum = heap_getattr(oldtup, Anum_owner,
985 : RelationGetDescr(rel), &isnull);
986 844 : Assert(!isnull);
987 844 : old_ownerId = DatumGetObjectId(datum);
988 :
989 844 : if (Anum_namespace != InvalidAttrNumber)
990 : {
991 509 : datum = heap_getattr(oldtup, Anum_namespace,
992 : RelationGetDescr(rel), &isnull);
993 509 : Assert(!isnull);
994 CBC 509 : namespaceId = DatumGetObjectId(datum);
995 : }
996 ECB :
997 CBC 844 : if (old_ownerId != new_ownerId)
998 ECB : {
999 : AttrNumber nattrs;
1000 : HeapTuple newtup;
1001 : Datum *values;
1002 : bool *nulls;
1003 : bool *replaces;
1004 :
1005 : /* Superusers can bypass permission checks */
1006 CBC 179 : if (!superuser())
1007 : {
1008 ECB : /* must be owner */
1009 CBC 117 : if (!has_privs_of_role(GetUserId(), old_ownerId))
1010 EUB : {
1011 : char *objname;
1012 : char namebuf[NAMEDATALEN];
1013 ECB :
1014 GIC 30 : if (Anum_name != InvalidAttrNumber)
1015 ECB : {
1016 CBC 30 : datum = heap_getattr(oldtup, Anum_name,
1017 : RelationGetDescr(rel), &isnull);
1018 30 : Assert(!isnull);
1019 GIC 30 : objname = NameStr(*DatumGetName(datum));
1020 ECB : }
1021 : else
1022 : {
1023 LBC 0 : snprintf(namebuf, sizeof(namebuf), "%u", objectId);
1024 UIC 0 : objname = namebuf;
1025 : }
1026 CBC 30 : aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objectId),
1027 : objname);
1028 : }
1029 : /* Must be able to become new owner */
1030 GNC 87 : check_can_set_role(GetUserId(), new_ownerId);
1031 :
1032 : /* New owner must have CREATE privilege on namespace */
1033 GIC 30 : if (OidIsValid(namespaceId))
1034 : {
1035 ECB : AclResult aclresult;
1036 :
1037 GNC 27 : aclresult = object_aclcheck(NamespaceRelationId, namespaceId, new_ownerId,
1038 ECB : ACL_CREATE);
1039 GIC 27 : if (aclresult != ACLCHECK_OK)
1040 UIC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
1041 0 : get_namespace_name(namespaceId));
1042 : }
1043 ECB : }
1044 :
1045 : /* Build a modified tuple */
1046 GIC 92 : nattrs = RelationGetNumberOfAttributes(rel);
1047 CBC 92 : values = palloc0(nattrs * sizeof(Datum));
1048 92 : nulls = palloc0(nattrs * sizeof(bool));
1049 GIC 92 : replaces = palloc0(nattrs * sizeof(bool));
1050 92 : values[Anum_owner - 1] = ObjectIdGetDatum(new_ownerId);
1051 92 : replaces[Anum_owner - 1] = true;
1052 EUB :
1053 : /*
1054 : * Determine the modified ACL for the new owner. This is only
1055 ECB : * necessary when the ACL is non-null.
1056 : */
1057 GIC 92 : if (Anum_acl != InvalidAttrNumber)
1058 : {
1059 CBC 41 : datum = heap_getattr(oldtup,
1060 : Anum_acl, RelationGetDescr(rel), &isnull);
1061 GIC 41 : if (!isnull)
1062 ECB : {
1063 : Acl *newAcl;
1064 :
1065 GIC 1 : newAcl = aclnewowner(DatumGetAclP(datum),
1066 ECB : old_ownerId, new_ownerId);
1067 GIC 1 : values[Anum_acl - 1] = PointerGetDatum(newAcl);
1068 CBC 1 : replaces[Anum_acl - 1] = true;
1069 EUB : }
1070 : }
1071 :
1072 GIC 92 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
1073 : values, nulls, replaces);
1074 :
1075 ECB : /* Perform actual update */
1076 CBC 92 : CatalogTupleUpdate(rel, &newtup->t_self, newtup);
1077 ECB :
1078 : /* Update owner dependency reference */
1079 CBC 92 : if (classId == LargeObjectMetadataRelationId)
1080 3 : classId = LargeObjectRelationId;
1081 GIC 92 : changeDependencyOnOwner(classId, objectId, new_ownerId);
1082 :
1083 : /* Release memory */
1084 92 : pfree(values);
1085 92 : pfree(nulls);
1086 CBC 92 : pfree(replaces);
1087 : }
1088 ECB :
1089 GIC 757 : InvokeObjectPostAlterHook(classId, objectId, 0);
1090 CBC 757 : }
|