Age Owner Branch data TLA 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
4990 alvherre@alvh.no-ip. 76 :CBC 16 : report_name_conflict(Oid classId, const char *name)
77 : : {
78 : : char *msgfmt;
79 : :
80 [ + + + + : 16 : switch (classId)
- - - ]
81 : : {
82 : 4 : case EventTriggerRelationId:
83 : 4 : msgfmt = gettext_noop("event trigger \"%s\" already exists");
84 : 4 : break;
85 : 4 : case ForeignDataWrapperRelationId:
86 : 4 : msgfmt = gettext_noop("foreign-data wrapper \"%s\" already exists");
87 : 4 : break;
88 : 4 : case ForeignServerRelationId:
89 : 4 : msgfmt = gettext_noop("server \"%s\" already exists");
90 : 4 : break;
91 : 4 : case LanguageRelationId:
92 : 4 : msgfmt = gettext_noop("language \"%s\" already exists");
93 : 4 : break;
3488 peter_e@gmx.net 94 :UBC 0 : case PublicationRelationId:
95 : 0 : msgfmt = gettext_noop("publication \"%s\" already exists");
96 : 0 : break;
97 : 0 : case SubscriptionRelationId:
98 : 0 : msgfmt = gettext_noop("subscription \"%s\" already exists");
99 : 0 : break;
4990 alvherre@alvh.no-ip. 100 : 0 : default:
1412 peter@eisentraut.org 101 [ # # ]: 0 : elog(ERROR, "unsupported object class: %u", classId);
102 : : break;
103 : : }
104 : :
4990 alvherre@alvh.no-ip. 105 [ + - ]:CBC 16 : ereport(ERROR,
106 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
107 : : errmsg(msgfmt, name)));
108 : : }
109 : :
110 : : static void
111 : 48 : report_namespace_conflict(Oid classId, const char *name, Oid nspOid)
112 : : {
113 : : char *msgfmt;
114 : :
115 [ - + ]: 48 : Assert(OidIsValid(nspOid));
116 : :
117 [ + + + + : 48 : switch (classId)
+ + - ]
118 : : {
119 : 8 : case ConversionRelationId:
120 : 8 : msgfmt = gettext_noop("conversion \"%s\" already exists in schema \"%s\"");
121 : 8 : break;
3467 122 : 8 : case StatisticExtRelationId:
3416 tgl@sss.pgh.pa.us 123 : 8 : msgfmt = gettext_noop("statistics object \"%s\" already exists in schema \"%s\"");
3467 alvherre@alvh.no-ip. 124 : 8 : break;
4990 125 : 8 : case TSParserRelationId:
126 : 8 : msgfmt = gettext_noop("text search parser \"%s\" already exists in schema \"%s\"");
127 : 8 : break;
128 : 8 : case TSDictionaryRelationId:
129 : 8 : msgfmt = gettext_noop("text search dictionary \"%s\" already exists in schema \"%s\"");
130 : 8 : break;
131 : 8 : case TSTemplateRelationId:
132 : 8 : msgfmt = gettext_noop("text search template \"%s\" already exists in schema \"%s\"");
133 : 8 : break;
134 : 8 : case TSConfigRelationId:
135 : 8 : msgfmt = gettext_noop("text search configuration \"%s\" already exists in schema \"%s\"");
136 : 8 : break;
4990 alvherre@alvh.no-ip. 137 :UBC 0 : default:
1412 peter@eisentraut.org 138 [ # # ]: 0 : elog(ERROR, "unsupported object class: %u", classId);
139 : : break;
140 : : }
141 : :
4990 alvherre@alvh.no-ip. 142 [ + - ]:CBC 48 : ereport(ERROR,
143 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
144 : : errmsg(msgfmt, name, get_namespace_name(nspOid))));
145 : : }
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 : 281 : AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
160 : : {
161 : 281 : Oid classId = RelationGetRelid(rel);
214 michael@paquier.xyz 162 : 281 : SysCacheIdentifier oidCacheId = get_object_catcache_oid(classId);
163 : 281 : SysCacheIdentifier nameCacheId = get_object_catcache_name(classId);
4990 alvherre@alvh.no-ip. 164 : 281 : AttrNumber Anum_name = get_object_attnum_name(classId);
165 : 281 : AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
166 : 281 : AttrNumber Anum_owner = get_object_attnum_owner(classId);
167 : : HeapTuple oldtup;
168 : : HeapTuple newtup;
169 : : Datum datum;
170 : : bool isnull;
171 : : Oid namespaceId;
172 : : Oid ownerId;
173 : : char *old_name;
174 : : AclResult aclresult;
175 : : Datum *values;
176 : : bool *nulls;
177 : : bool *replaces;
178 : : NameData nameattrdata;
179 : :
180 : 281 : oldtup = SearchSysCache1(oidCacheId, ObjectIdGetDatum(objectId));
181 [ - + ]: 281 : if (!HeapTupleIsValid(oldtup))
4990 alvherre@alvh.no-ip. 182 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
183 : : objectId, RelationGetRelationName(rel));
184 : :
4990 alvherre@alvh.no-ip. 185 :CBC 281 : datum = heap_getattr(oldtup, Anum_name,
186 : : RelationGetDescr(rel), &isnull);
187 [ - + ]: 281 : Assert(!isnull);
188 : 281 : old_name = NameStr(*(DatumGetName(datum)));
189 : :
190 : : /* Get OID of namespace */
191 [ + + ]: 281 : if (Anum_namespace > 0)
192 : : {
193 : 180 : datum = heap_getattr(oldtup, Anum_namespace,
194 : : RelationGetDescr(rel), &isnull);
195 [ - + ]: 180 : Assert(!isnull);
196 : 180 : namespaceId = DatumGetObjectId(datum);
197 : : }
198 : : else
199 : 101 : namespaceId = InvalidOid;
200 : :
201 : : /* Permission checks ... superusers can always do it */
202 [ + + ]: 281 : if (!superuser())
203 : : {
204 : : /* Fail if object does not have an explicit owner */
205 [ - + ]: 164 : if (Anum_owner <= 0)
4990 alvherre@alvh.no-ip. 206 [ # # ]:UBC 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 */
4990 alvherre@alvh.no-ip. 212 :CBC 164 : datum = heap_getattr(oldtup, Anum_owner,
213 : : RelationGetDescr(rel), &isnull);
214 [ - + ]: 164 : Assert(!isnull);
215 : 164 : ownerId = DatumGetObjectId(datum);
216 : :
408 peter@eisentraut.org 217 [ + + ]: 164 : if (!has_privs_of_role(GetUserId(), ownerId))
2511 tgl@sss.pgh.pa.us 218 : 48 : aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objectId),
219 : : old_name);
220 : :
221 : : /* User must have CREATE privilege on the namespace */
4990 alvherre@alvh.no-ip. 222 [ + + ]: 116 : if (OidIsValid(namespaceId))
223 : : {
1407 peter@eisentraut.org 224 : 96 : aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(),
225 : : ACL_CREATE);
4990 alvherre@alvh.no-ip. 226 [ - + ]: 96 : if (aclresult != ACLCHECK_OK)
3214 peter_e@gmx.net 227 :UBC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
4990 alvherre@alvh.no-ip. 228 : 0 : get_namespace_name(namespaceId));
229 : : }
230 : :
1270 rhaas@postgresql.org 231 [ + + ]:CBC 116 : if (classId == SubscriptionRelationId)
232 : : {
233 : : Form_pg_subscription form;
234 : :
235 : : /* must have CREATE privilege on database */
236 : 12 : aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId,
237 : : GetUserId(), ACL_CREATE);
238 [ + + ]: 12 : if (aclresult != ACLCHECK_OK)
239 : 4 : aclcheck_error(aclresult, OBJECT_DATABASE,
240 : 4 : get_database_name(MyDatabaseId));
241 : :
242 : : /*
243 : : * Don't allow non-superuser modification of a subscription with
244 : : * password_required=false.
245 : : */
246 : 8 : form = (Form_pg_subscription) GETSTRUCT(oldtup);
247 [ - + - - ]: 8 : if (!form->subpasswordrequired && !superuser())
1270 rhaas@postgresql.org 248 [ # # ]:UBC 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 : : }
253 : : }
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 : : */
4990 alvherre@alvh.no-ip. 260 [ + + ]:CBC 229 : if (classId == ProcedureRelationId)
261 : : {
262 : 48 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(oldtup);
263 : :
264 : 48 : IsThereFunctionInNamespace(new_name, proc->pronargs,
265 : : &proc->proargtypes, proc->pronamespace);
266 : : }
267 [ + + ]: 181 : else if (classId == CollationRelationId)
268 : : {
269 : 8 : Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(oldtup);
270 : :
271 : 8 : IsThereCollationInNamespace(new_name, coll->collnamespace);
272 : : }
273 [ + + ]: 173 : else if (classId == OperatorClassRelationId)
274 : : {
275 : 12 : Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(oldtup);
276 : :
277 : 12 : IsThereOpClassInNamespace(new_name, opc->opcmethod,
278 : : opc->opcnamespace);
279 : : }
280 [ + + ]: 161 : else if (classId == OperatorFamilyRelationId)
281 : : {
282 : 12 : Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(oldtup);
283 : :
284 : 12 : IsThereOpFamilyInNamespace(new_name, opf->opfmethod,
285 : : opf->opfnamespace);
286 : : }
3488 peter_e@gmx.net 287 [ + + ]: 149 : else if (classId == SubscriptionRelationId)
288 : : {
1158 michael@paquier.xyz 289 [ - + ]: 17 : if (SearchSysCacheExists2(SUBSCRIPTIONNAME,
290 : : ObjectIdGetDatum(MyDatabaseId),
291 : : CStringGetDatum(new_name)))
3488 peter_e@gmx.net 292 :UBC 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 */
1353 tgl@sss.pgh.pa.us 301 :CBC 17 : LogicalRepWorkersWakeupAtCommit(objectId);
302 : : }
4990 alvherre@alvh.no-ip. 303 [ + - ]: 132 : else if (nameCacheId >= 0)
304 : : {
305 [ + + ]: 132 : if (OidIsValid(namespaceId))
306 : : {
307 [ + + ]: 64 : if (SearchSysCacheExists2(nameCacheId,
308 : : CStringGetDatum(new_name),
309 : : ObjectIdGetDatum(namespaceId)))
310 : 24 : report_namespace_conflict(classId, new_name, namespaceId);
311 : : }
312 : : else
313 : : {
314 [ + + ]: 68 : if (SearchSysCacheExists1(nameCacheId,
315 : : CStringGetDatum(new_name)))
316 : 16 : report_name_conflict(classId, new_name);
317 : : }
318 : : }
319 : :
320 : : /* Build modified tuple */
34 michael@paquier.xyz 321 :GNC 169 : values = palloc0_array(Datum, RelationGetNumberOfAttributes(rel));
322 : 169 : nulls = palloc0_array(bool, RelationGetNumberOfAttributes(rel));
323 : 169 : replaces = palloc0_array(bool, RelationGetNumberOfAttributes(rel));
4848 noah@leadboat.com 324 :CBC 169 : namestrcpy(&nameattrdata, new_name);
325 : 169 : values[Anum_name - 1] = NameGetDatum(&nameattrdata);
4990 alvherre@alvh.no-ip. 326 : 169 : replaces[Anum_name - 1] = true;
327 : 169 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
328 : : values, nulls, replaces);
329 : :
330 : : /* Perform actual update */
3519 331 : 169 : CatalogTupleUpdate(rel, &oldtup->t_self, newtup);
332 : :
4935 rhaas@postgresql.org 333 [ - + ]: 169 : InvokeObjectPostAlterHook(classId, objectId, 0);
334 : :
335 : : /* Do post catalog-update tasks */
556 akapila@postgresql.o 336 [ + + ]: 169 : if (classId == PublicationRelationId)
337 : : {
338 : 20 : 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 : 20 : InvalidatePubRelSyncCache(pub->oid, pub->puballtables);
349 : : }
350 : :
351 : : /* Release memory */
4990 alvherre@alvh.no-ip. 352 : 169 : pfree(values);
353 : 169 : pfree(nulls);
354 : 169 : pfree(replaces);
355 : 169 : heap_freetuple(newtup);
356 : :
357 : 169 : ReleaseSysCache(oldtup);
358 : 169 : }
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 : 1017 : ExecRenameStmt(RenameStmt *stmt)
368 : : {
369 [ + + + + : 1017 : switch (stmt->renameType)
+ + + + +
+ + + - ]
370 : : {
4289 371 : 60 : case OBJECT_TABCONSTRAINT:
372 : : case OBJECT_DOMCONSTRAINT:
4990 373 : 60 : return RenameConstraint(stmt);
374 : :
375 : 9 : case OBJECT_DATABASE:
376 : 9 : return RenameDatabase(stmt->subname, stmt->newname);
377 : :
7754 tgl@sss.pgh.pa.us 378 : 20 : case OBJECT_ROLE:
5019 rhaas@postgresql.org 379 : 20 : return RenameRole(stmt->subname, stmt->newname);
380 : :
8486 peter_e@gmx.net 381 : 13 : case OBJECT_SCHEMA:
5019 rhaas@postgresql.org 382 : 13 : return RenameSchema(stmt->subname, stmt->newname);
383 : :
8122 tgl@sss.pgh.pa.us 384 : 9 : case OBJECT_TABLESPACE:
5019 rhaas@postgresql.org 385 : 9 : return RenameTableSpace(stmt->subname, stmt->newname);
386 : :
8486 peter_e@gmx.net 387 : 303 : case OBJECT_TABLE:
388 : : case OBJECT_SEQUENCE:
389 : : case OBJECT_VIEW:
390 : : case OBJECT_MATVIEW:
391 : : case OBJECT_INDEX:
392 : : case OBJECT_FOREIGN_TABLE:
5019 rhaas@postgresql.org 393 : 303 : return RenameRelation(stmt);
394 : :
8486 peter_e@gmx.net 395 : 213 : case OBJECT_COLUMN:
396 : : case OBJECT_ATTRIBUTE:
5019 rhaas@postgresql.org 397 : 213 : return renameatt(stmt);
398 : :
4972 tgl@sss.pgh.pa.us 399 : 30 : case OBJECT_RULE:
400 : 30 : return RenameRewriteRule(stmt->relation, stmt->subname,
401 : 30 : stmt->newname);
402 : :
8486 peter_e@gmx.net 403 : 38 : case OBJECT_TRIGGER:
5019 rhaas@postgresql.org 404 : 38 : return renametrig(stmt);
405 : :
4384 sfrost@snowman.net 406 : 12 : case OBJECT_POLICY:
407 : 12 : return rename_policy(stmt);
408 : :
4990 alvherre@alvh.no-ip. 409 : 21 : case OBJECT_DOMAIN:
410 : : case OBJECT_TYPE:
411 : 21 : return RenameType(stmt);
412 : :
413 : 289 : 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 : : Relation catalog;
435 : :
436 : 289 : address = get_object_address(stmt->renameType,
437 : : stmt->object,
438 : : NULL,
439 : : AccessExclusiveLock, false);
440 : :
2799 andres@anarazel.de 441 : 281 : catalog = table_open(address.classId, RowExclusiveLock);
4990 alvherre@alvh.no-ip. 442 : 281 : AlterObjectRename_internal(catalog,
443 : : address.objectId,
444 : 281 : stmt->newname);
2799 andres@anarazel.de 445 : 169 : table_close(catalog, RowExclusiveLock);
446 : :
4219 alvherre@alvh.no-ip. 447 : 169 : return address;
448 : : }
449 : :
8486 peter_e@gmx.net 450 :UBC 0 : default:
8463 tgl@sss.pgh.pa.us 451 [ # # ]: 0 : elog(ERROR, "unrecognized rename stmt type: %d",
452 : : (int) stmt->renameType);
453 : : return InvalidObjectAddress; /* keep compiler happy */
454 : : }
455 : : }
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
3820 alvherre@alvh.no-ip. 465 :CBC 23 : ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddress)
466 : : {
467 : : ObjectAddress address;
468 : : ObjectAddress refAddr;
469 : : Relation rel;
470 : :
471 : : address =
3599 peter_e@gmx.net 472 : 23 : 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 : : */
2414 alvherre@alvh.no-ip. 483 : 23 : check_object_ownership(GetUserId(),
484 : : 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 : : */
3820 490 [ + + ]: 23 : if (rel)
2799 andres@anarazel.de 491 : 17 : table_close(rel, NoLock);
492 : :
3599 peter_e@gmx.net 493 : 23 : refAddr = get_object_address(OBJECT_EXTENSION, (Node *) stmt->extname,
494 : : NULL, AccessExclusiveLock, false);
3820 alvherre@alvh.no-ip. 495 [ + - ]: 23 : if (refAddress)
496 : 23 : *refAddress = refAddr;
497 : :
2344 498 [ + + ]: 23 : if (stmt->remove)
499 : : {
500 : 4 : deleteDependencyRecordsForSpecific(address.classId, address.objectId,
501 : : DEPENDENCY_AUTO_EXTENSION,
502 : : refAddr.classId, refAddr.objectId);
503 : : }
504 : : else
505 : : {
506 : : List *currexts;
507 : :
508 : : /* Avoid duplicates */
509 : 19 : currexts = getAutoExtensionsOfObject(address.classId,
510 : : address.objectId);
511 [ + + ]: 19 : if (!list_member_oid(currexts, refAddr.objectId))
512 : 18 : recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
513 : : }
514 : :
3820 515 : 23 : return address;
516 : : }
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
4219 528 : 271 : ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
529 : : ObjectAddress *oldSchemaAddr)
530 : : {
531 : : ObjectAddress address;
532 : : Oid oldNspOid;
533 : :
7720 tgl@sss.pgh.pa.us 534 [ + + + + : 271 : switch (stmt->objectType)
- ]
535 : : {
5703 536 : 6 : case OBJECT_EXTENSION:
1837 peter@eisentraut.org 537 [ + - ]: 6 : address = AlterExtensionNamespace(strVal(stmt->object), stmt->newschema,
538 : : oldSchemaAddr ? &oldNspOid : NULL);
4219 alvherre@alvh.no-ip. 539 : 4 : break;
540 : :
4996 541 : 76 : case OBJECT_FOREIGN_TABLE:
542 : : case OBJECT_SEQUENCE:
543 : : case OBJECT_TABLE:
544 : : case OBJECT_VIEW:
545 : : case OBJECT_MATVIEW:
4219 546 [ + - ]: 76 : address = AlterTableNamespace(stmt,
547 : : oldSchemaAddr ? &oldNspOid : NULL);
548 : 63 : break;
549 : :
5106 550 : 12 : case OBJECT_DOMAIN:
551 : : case OBJECT_TYPE:
3599 peter_e@gmx.net 552 [ + - ]: 12 : address = AlterTypeNamespace(castNode(List, stmt->object), stmt->newschema,
553 : : stmt->objectType,
554 : : oldSchemaAddr ? &oldNspOid : NULL);
4219 alvherre@alvh.no-ip. 555 : 12 : break;
556 : :
557 : : /* generic code path */
4996 558 : 177 : 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 : : Relation catalog;
574 : : Oid classId;
575 : : Oid nspOid;
576 : :
5106 577 : 177 : address = get_object_address(stmt->objectType,
578 : : stmt->object,
579 : : NULL,
580 : : AccessExclusiveLock,
581 : : false);
582 : 173 : classId = address.classId;
2799 andres@anarazel.de 583 : 173 : catalog = table_open(classId, RowExclusiveLock);
5106 alvherre@alvh.no-ip. 584 : 173 : nspOid = LookupCreationNamespace(stmt->newschema);
585 : :
4219 586 : 173 : oldNspOid = AlterObjectNamespace_internal(catalog, address.objectId,
587 : : nspOid);
2799 andres@anarazel.de 588 : 96 : table_close(catalog, RowExclusiveLock);
589 : : }
7720 tgl@sss.pgh.pa.us 590 : 96 : break;
591 : :
7720 tgl@sss.pgh.pa.us 592 :UBC 0 : default:
593 [ # # ]: 0 : elog(ERROR, "unrecognized AlterObjectSchemaStmt type: %d",
594 : : (int) stmt->objectType);
595 : : return InvalidObjectAddress; /* keep compiler happy */
596 : : }
597 : :
4219 alvherre@alvh.no-ip. 598 [ + - ]:CBC 175 : if (oldSchemaAddr)
599 : 175 : ObjectAddressSet(*oldSchemaAddr, NamespaceRelationId, oldNspOid);
600 : :
601 : 175 : return address;
602 : : }
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
5072 619 : 21 : AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
620 : : ObjectAddresses *objsMoved)
621 : : {
5703 tgl@sss.pgh.pa.us 622 : 21 : Oid oldNspOid = InvalidOid;
623 : :
908 peter@eisentraut.org 624 [ + + + + ]: 21 : switch (classId)
625 : : {
626 : 1 : case RelationRelationId:
627 : : {
628 : : Relation rel;
629 : :
5642 bruce@momjian.us 630 : 1 : rel = relation_open(objid, AccessExclusiveLock);
631 : 1 : oldNspOid = RelationGetNamespace(rel);
632 : :
5072 alvherre@alvh.no-ip. 633 : 1 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
634 : :
5642 bruce@momjian.us 635 : 1 : relation_close(rel, NoLock);
636 : 1 : break;
637 : : }
638 : :
908 peter@eisentraut.org 639 : 8 : case TypeRelationId:
864 tgl@sss.pgh.pa.us 640 : 8 : oldNspOid = AlterTypeNamespace_oid(objid, nspOid, true, objsMoved);
5703 641 : 8 : break;
642 : :
908 peter@eisentraut.org 643 : 11 : 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 : : Relation catalog;
656 : :
2799 andres@anarazel.de 657 : 11 : catalog = table_open(classId, RowExclusiveLock);
658 : :
5106 alvherre@alvh.no-ip. 659 : 11 : oldNspOid = AlterObjectNamespace_internal(catalog, objid,
660 : : nspOid);
661 : :
2799 andres@anarazel.de 662 : 11 : table_close(catalog, RowExclusiveLock);
663 : : }
5703 tgl@sss.pgh.pa.us 664 : 11 : break;
665 : :
908 peter@eisentraut.org 666 : 1 : default:
667 : : /* ignore object types that don't have schema-qualified names */
668 [ - + ]: 1 : Assert(get_object_attnum_namespace(classId) == InvalidAttrNumber);
669 : : }
670 : :
5703 tgl@sss.pgh.pa.us 671 : 21 : return oldNspOid;
672 : : }
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
5106 alvherre@alvh.no-ip. 686 : 184 : AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
687 : : {
5703 tgl@sss.pgh.pa.us 688 : 184 : Oid classId = RelationGetRelid(rel);
214 michael@paquier.xyz 689 : 184 : SysCacheIdentifier oidCacheId = get_object_catcache_oid(classId);
690 : 184 : SysCacheIdentifier nameCacheId = get_object_catcache_name(classId);
5106 alvherre@alvh.no-ip. 691 : 184 : AttrNumber Anum_name = get_object_attnum_name(classId);
692 : 184 : AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
693 : 184 : AttrNumber Anum_owner = get_object_attnum_owner(classId);
694 : : Oid oldNspOid;
695 : : Datum name,
696 : : namespace;
697 : : bool isnull;
698 : : HeapTuple tup,
699 : : newtup;
700 : : Datum *values;
701 : : bool *nulls;
702 : : bool *replaces;
703 : :
5703 tgl@sss.pgh.pa.us 704 : 184 : tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid));
5777 rhaas@postgresql.org 705 [ - + ]: 184 : if (!HeapTupleIsValid(tup)) /* should not happen */
5703 tgl@sss.pgh.pa.us 706 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
707 : : objid, RelationGetRelationName(rel));
708 : :
5703 tgl@sss.pgh.pa.us 709 :CBC 184 : name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
710 [ - + ]: 184 : Assert(!isnull);
5106 alvherre@alvh.no-ip. 711 : 184 : namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel),
712 : : &isnull);
5703 tgl@sss.pgh.pa.us 713 [ - + ]: 184 : Assert(!isnull);
5777 rhaas@postgresql.org 714 : 184 : 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 : : */
3958 720 [ + + ]: 184 : if (oldNspOid == nspOid)
721 : : {
722 [ - + ]: 4 : InvokeObjectPostAlterHook(classId, objid, 0);
723 : 4 : return oldNspOid;
724 : : }
725 : :
726 : : /* Check basic namespace related issues */
727 : 180 : CheckSetNamespace(oldNspOid, nspOid);
728 : :
729 : : /* Permission checks ... superusers can always do it */
5777 730 [ + + ]: 180 : if (!superuser())
731 : : {
732 : : Datum owner;
733 : : Oid ownerId;
734 : : AclResult aclresult;
735 : :
736 : : /* Fail if object does not have an explicit owner */
5703 tgl@sss.pgh.pa.us 737 [ - + ]: 104 : if (Anum_owner <= 0)
5777 rhaas@postgresql.org 738 [ # # ]:UBC 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 */
5703 tgl@sss.pgh.pa.us 744 :CBC 104 : owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull);
745 [ - + ]: 104 : Assert(!isnull);
5777 rhaas@postgresql.org 746 : 104 : ownerId = DatumGetObjectId(owner);
747 : :
748 [ + + ]: 104 : if (!has_privs_of_role(GetUserId(), ownerId))
2511 tgl@sss.pgh.pa.us 749 : 36 : aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objid),
5777 rhaas@postgresql.org 750 : 36 : NameStr(*(DatumGetName(name))));
751 : :
752 : : /* User must have CREATE privilege on new namespace */
1407 peter@eisentraut.org 753 : 68 : aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
5777 rhaas@postgresql.org 754 [ - + ]: 68 : if (aclresult != ACLCHECK_OK)
3214 peter_e@gmx.net 755 :UBC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
5703 tgl@sss.pgh.pa.us 756 : 0 : get_namespace_name(nspOid));
757 : : }
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 : : */
4996 alvherre@alvh.no-ip. 764 [ + + ]:CBC 144 : if (classId == ProcedureRelationId)
765 : : {
4990 766 : 40 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tup);
767 : :
4996 768 : 40 : IsThereFunctionInNamespace(NameStr(proc->proname), proc->pronargs,
769 : : &proc->proargtypes, nspOid);
770 : : }
771 [ + + ]: 104 : else if (classId == CollationRelationId)
772 : : {
4990 773 : 4 : Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(tup);
774 : :
775 : 4 : IsThereCollationInNamespace(NameStr(coll->collname), nspOid);
776 : : }
777 [ + + ]: 100 : else if (classId == OperatorClassRelationId)
778 : : {
779 : 12 : Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(tup);
780 : :
781 : 12 : IsThereOpClassInNamespace(NameStr(opc->opcname),
782 : : opc->opcmethod, nspOid);
783 : : }
784 [ + + ]: 88 : else if (classId == OperatorFamilyRelationId)
785 : : {
786 : 12 : Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(tup);
787 : :
788 : 12 : IsThereOpFamilyInNamespace(NameStr(opf->opfname),
789 : : opf->opfmethod, nspOid);
790 : : }
4996 791 [ + + + + ]: 144 : else if (nameCacheId >= 0 &&
792 : 68 : SearchSysCacheExists2(nameCacheId, name,
793 : : ObjectIdGetDatum(nspOid)))
794 : 24 : report_namespace_conflict(classId,
795 : 24 : NameStr(*(DatumGetName(name))),
796 : : nspOid);
797 : :
798 : : /* Build modified tuple */
34 michael@paquier.xyz 799 :GNC 104 : values = palloc0_array(Datum, RelationGetNumberOfAttributes(rel));
800 : 104 : nulls = palloc0_array(bool, RelationGetNumberOfAttributes(rel));
801 : 104 : replaces = palloc0_array(bool, RelationGetNumberOfAttributes(rel));
5703 tgl@sss.pgh.pa.us 802 :CBC 104 : values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid);
5777 rhaas@postgresql.org 803 : 104 : replaces[Anum_namespace - 1] = true;
5703 tgl@sss.pgh.pa.us 804 : 104 : newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
805 : : values, nulls, replaces);
806 : :
807 : : /* Perform actual update */
3519 alvherre@alvh.no-ip. 808 : 104 : CatalogTupleUpdate(rel, &tup->t_self, newtup);
809 : :
810 : : /* Release memory */
5777 rhaas@postgresql.org 811 : 104 : pfree(values);
812 : 104 : pfree(nulls);
813 : 104 : pfree(replaces);
814 : :
815 : : /* update dependency to point to the new schema */
1168 michael@paquier.xyz 816 [ - + ]: 104 : if (changeDependencyFor(classId, objid,
817 : : NamespaceRelationId, oldNspOid, nspOid) != 1)
1168 michael@paquier.xyz 818 [ # # ]:UBC 0 : elog(ERROR, "could not change schema dependency for object %u",
819 : : objid);
820 : :
4935 rhaas@postgresql.org 821 [ - + ]:CBC 103 : InvokeObjectPostAlterHook(classId, objid, 0);
822 : :
5703 tgl@sss.pgh.pa.us 823 : 103 : return oldNspOid;
824 : : }
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
8122 831 : 973 : ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
832 : : {
4213 alvherre@alvh.no-ip. 833 : 973 : Oid newowner = get_rolespec_oid(stmt->newowner, false);
834 : :
8122 tgl@sss.pgh.pa.us 835 [ + + + + : 966 : switch (stmt->objectType)
+ + + + +
- ]
836 : : {
837 : 50 : case OBJECT_DATABASE:
1837 peter@eisentraut.org 838 : 50 : return AlterDatabaseOwner(strVal(stmt->object), newowner);
839 : :
8122 tgl@sss.pgh.pa.us 840 : 37 : case OBJECT_SCHEMA:
1837 peter@eisentraut.org 841 : 37 : return AlterSchemaOwner(strVal(stmt->object), newowner);
842 : :
8122 tgl@sss.pgh.pa.us 843 : 71 : case OBJECT_TYPE:
844 : : case OBJECT_DOMAIN: /* same as TYPE */
3599 peter_e@gmx.net 845 : 71 : return AlterTypeOwner(castNode(List, stmt->object), newowner, stmt->objectType);
846 : : break;
847 : :
6484 848 : 13 : case OBJECT_FDW:
1837 peter@eisentraut.org 849 : 13 : return AlterForeignDataWrapperOwner(strVal(stmt->object),
850 : : newowner);
851 : :
6484 peter_e@gmx.net 852 : 45 : case OBJECT_FOREIGN_SERVER:
1837 peter@eisentraut.org 853 : 45 : return AlterForeignServerOwner(strVal(stmt->object),
854 : : newowner);
855 : :
5177 rhaas@postgresql.org 856 : 9 : case OBJECT_EVENT_TRIGGER:
1837 peter@eisentraut.org 857 : 9 : return AlterEventTriggerOwner(strVal(stmt->object),
858 : : newowner);
859 : :
3531 peter_e@gmx.net 860 : 26 : case OBJECT_PUBLICATION:
1837 peter@eisentraut.org 861 : 26 : return AlterPublicationOwner(strVal(stmt->object),
862 : : newowner);
863 : :
3531 peter_e@gmx.net 864 : 27 : case OBJECT_SUBSCRIPTION:
1837 peter@eisentraut.org 865 : 27 : return AlterSubscriptionOwner(strVal(stmt->object),
866 : : newowner);
867 : :
868 : : /* Generic cases */
5100 alvherre@alvh.no-ip. 869 : 688 : 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 : :
13 peter@eisentraut.org 887 : 688 : address = get_object_address(stmt->objectType,
888 : : stmt->object,
889 : : NULL,
890 : : AccessExclusiveLock,
891 : : false);
892 : :
1010 tgl@sss.pgh.pa.us 893 : 683 : AlterObjectOwner_internal(address.classId, address.objectId,
894 : : newowner);
895 : :
4219 alvherre@alvh.no-ip. 896 : 567 : return address;
897 : : }
898 : : break;
899 : :
8122 tgl@sss.pgh.pa.us 900 :UBC 0 : default:
901 [ # # ]: 0 : elog(ERROR, "unrecognized AlterOwnerStmt type: %d",
902 : : (int) stmt->objectType);
903 : : return InvalidObjectAddress; /* keep compiler happy */
904 : : }
905 : : }
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
1010 tgl@sss.pgh.pa.us 920 :CBC 699 : AlterObjectOwner_internal(Oid classId, Oid objectId, Oid new_ownerId)
921 : : {
922 : : /* For large objects, the catalog to modify is pg_largeobject_metadata */
923 [ + + ]: 699 : Oid catalogId = (classId == LargeObjectRelationId) ? LargeObjectMetadataRelationId : classId;
924 : 699 : AttrNumber Anum_oid = get_object_attnum_oid(catalogId);
925 : 699 : AttrNumber Anum_owner = get_object_attnum_owner(catalogId);
926 : 699 : AttrNumber Anum_namespace = get_object_attnum_namespace(catalogId);
927 : 699 : AttrNumber Anum_acl = get_object_attnum_acl(catalogId);
928 : 699 : AttrNumber Anum_name = get_object_attnum_name(catalogId);
929 : : Relation rel;
930 : : HeapTuple oldtup;
931 : : Datum datum;
932 : : bool isnull;
933 : : Oid old_ownerId;
5100 alvherre@alvh.no-ip. 934 : 699 : Oid namespaceId = InvalidOid;
935 : :
1010 tgl@sss.pgh.pa.us 936 : 699 : rel = table_open(catalogId, RowExclusiveLock);
937 : :
938 : : /* Search tuple and lock it. */
939 : : oldtup =
631 noah@leadboat.com 940 : 699 : get_catalog_object_by_oid_extended(rel, Anum_oid, objectId, true);
5100 alvherre@alvh.no-ip. 941 [ - + ]: 699 : if (oldtup == NULL)
5100 alvherre@alvh.no-ip. 942 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
943 : : objectId, RelationGetRelationName(rel));
944 : :
5100 alvherre@alvh.no-ip. 945 :CBC 699 : datum = heap_getattr(oldtup, Anum_owner,
946 : : RelationGetDescr(rel), &isnull);
947 [ - + ]: 699 : Assert(!isnull);
948 : 699 : old_ownerId = DatumGetObjectId(datum);
949 : :
950 [ + + ]: 699 : if (Anum_namespace != InvalidAttrNumber)
951 : : {
952 : 594 : datum = heap_getattr(oldtup, Anum_namespace,
953 : : RelationGetDescr(rel), &isnull);
954 [ - + ]: 594 : Assert(!isnull);
955 : 594 : namespaceId = DatumGetObjectId(datum);
956 : : }
957 : :
958 [ + + ]: 699 : if (old_ownerId != new_ownerId)
959 : : {
960 : : AttrNumber nattrs;
961 : : HeapTuple newtup;
962 : : Datum *values;
963 : : bool *nulls;
964 : : bool *replaces;
965 : :
966 : : /* Superusers can bypass permission checks */
967 [ + + ]: 261 : if (!superuser())
968 : : {
969 : : /* must be owner */
970 [ + + ]: 156 : if (!has_privs_of_role(GetUserId(), old_ownerId))
971 : : {
972 : : char *objname;
973 : : char namebuf[NAMEDATALEN];
974 : :
975 [ + - ]: 40 : if (Anum_name != InvalidAttrNumber)
976 : : {
977 : 40 : datum = heap_getattr(oldtup, Anum_name,
978 : : RelationGetDescr(rel), &isnull);
979 [ - + ]: 40 : Assert(!isnull);
980 : 40 : objname = NameStr(*DatumGetName(datum));
981 : : }
982 : : else
983 : : {
2861 andres@anarazel.de 984 :UBC 0 : snprintf(namebuf, sizeof(namebuf), "%u", objectId);
5100 alvherre@alvh.no-ip. 985 : 0 : objname = namebuf;
986 : : }
1010 tgl@sss.pgh.pa.us 987 :CBC 40 : aclcheck_error(ACLCHECK_NOT_OWNER,
988 : : get_object_type(catalogId, objectId),
989 : : objname);
990 : : }
991 : : /* Must be able to become new owner */
1402 rhaas@postgresql.org 992 : 116 : check_can_set_role(GetUserId(), new_ownerId);
993 : :
994 : : /* New owner must have CREATE privilege on namespace */
5100 alvherre@alvh.no-ip. 995 [ + + ]: 40 : if (OidIsValid(namespaceId))
996 : : {
997 : : AclResult aclresult;
998 : :
1407 peter@eisentraut.org 999 : 36 : aclresult = object_aclcheck(NamespaceRelationId, namespaceId, new_ownerId,
1000 : : ACL_CREATE);
5100 alvherre@alvh.no-ip. 1001 [ - + ]: 36 : if (aclresult != ACLCHECK_OK)
3214 peter_e@gmx.net 1002 :UBC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
5100 alvherre@alvh.no-ip. 1003 : 0 : get_namespace_name(namespaceId));
1004 : : }
1005 : : }
1006 : :
1007 : : /* Build a modified tuple */
5100 alvherre@alvh.no-ip. 1008 :CBC 145 : nattrs = RelationGetNumberOfAttributes(rel);
34 michael@paquier.xyz 1009 :GNC 145 : values = palloc0_array(Datum, nattrs);
1010 : 145 : nulls = palloc0_array(bool, nattrs);
1011 : 145 : replaces = palloc0_array(bool, nattrs);
5100 alvherre@alvh.no-ip. 1012 :CBC 145 : values[Anum_owner - 1] = ObjectIdGetDatum(new_ownerId);
1013 : 145 : 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 [ + + ]: 145 : if (Anum_acl != InvalidAttrNumber)
1020 : : {
1021 : 76 : datum = heap_getattr(oldtup,
1022 : : Anum_acl, RelationGetDescr(rel), &isnull);
1023 [ + + ]: 76 : if (!isnull)
1024 : : {
1025 : : Acl *newAcl;
1026 : :
1027 : 8 : newAcl = aclnewowner(DatumGetAclP(datum),
1028 : : old_ownerId, new_ownerId);
1029 : 8 : values[Anum_acl - 1] = PointerGetDatum(newAcl);
1030 : 8 : replaces[Anum_acl - 1] = true;
1031 : : }
1032 : : }
1033 : :
1034 : 145 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
1035 : : values, nulls, replaces);
1036 : :
1037 : : /* Perform actual update */
3519 1038 : 145 : CatalogTupleUpdate(rel, &newtup->t_self, newtup);
1039 : :
631 noah@leadboat.com 1040 : 145 : UnlockTuple(rel, &oldtup->t_self, InplaceUpdateTupleLock);
1041 : :
1042 : : /* Update owner dependency reference */
2861 andres@anarazel.de 1043 : 145 : changeDependencyOnOwner(classId, objectId, new_ownerId);
1044 : :
1045 : : /* Release memory */
5100 alvherre@alvh.no-ip. 1046 : 145 : pfree(values);
1047 : 145 : pfree(nulls);
1048 : 145 : pfree(replaces);
1049 : : }
1050 : : else
631 noah@leadboat.com 1051 : 438 : UnlockTuple(rel, &oldtup->t_self, InplaceUpdateTupleLock);
1052 : :
1053 : : /* Note the post-alter hook gets classId not catalogId */
4935 rhaas@postgresql.org 1054 [ - + ]: 583 : InvokeObjectPostAlterHook(classId, objectId, 0);
1055 : :
1010 tgl@sss.pgh.pa.us 1056 : 583 : table_close(rel, RowExclusiveLock);
5100 alvherre@alvh.no-ip. 1057 : 583 : }
|