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
4852 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;
3350 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;
4852 alvherre@alvh.no-ip. 100 : 0 : default:
1274 peter@eisentraut.org 101 [ # # ]: 0 : elog(ERROR, "unsupported object class: %u", classId);
102 : : break;
103 : : }
104 : :
4852 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;
3329 122 : 8 : case StatisticExtRelationId:
3278 tgl@sss.pgh.pa.us 123 : 8 : msgfmt = gettext_noop("statistics object \"%s\" already exists in schema \"%s\"");
3329 alvherre@alvh.no-ip. 124 : 8 : break;
4852 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;
4852 alvherre@alvh.no-ip. 137 :UBC 0 : default:
1274 peter@eisentraut.org 138 [ # # ]: 0 : elog(ERROR, "unsupported object class: %u", classId);
139 : : break;
140 : : }
141 : :
4852 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);
76 michael@paquier.xyz 162 :GNC 281 : SysCacheIdentifier oidCacheId = get_object_catcache_oid(classId);
163 : 281 : SysCacheIdentifier nameCacheId = get_object_catcache_name(classId);
4852 alvherre@alvh.no-ip. 164 :CBC 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))
4852 alvherre@alvh.no-ip. 182 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
183 : : objectId, RelationGetRelationName(rel));
184 : :
4852 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)
4852 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 */
4852 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 : :
270 peter@eisentraut.org 217 [ + + ]:GNC 164 : if (!has_privs_of_role(GetUserId(), ownerId))
2373 tgl@sss.pgh.pa.us 218 :CBC 48 : aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objectId),
219 : : old_name);
220 : :
221 : : /* User must have CREATE privilege on the namespace */
4852 alvherre@alvh.no-ip. 222 [ + + ]: 116 : if (OidIsValid(namespaceId))
223 : : {
1269 peter@eisentraut.org 224 : 96 : aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(),
225 : : ACL_CREATE);
4852 alvherre@alvh.no-ip. 226 [ - + ]: 96 : if (aclresult != ACLCHECK_OK)
3076 peter_e@gmx.net 227 :UBC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
4852 alvherre@alvh.no-ip. 228 : 0 : get_namespace_name(namespaceId));
229 : : }
230 : :
1132 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())
1132 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 : : */
4852 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 : : }
3350 peter_e@gmx.net 287 [ + + ]: 149 : else if (classId == SubscriptionRelationId)
288 : : {
1020 michael@paquier.xyz 289 [ - + ]: 17 : if (SearchSysCacheExists2(SUBSCRIPTIONNAME,
290 : : ObjectIdGetDatum(MyDatabaseId),
291 : : CStringGetDatum(new_name)))
3350 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 */
1215 tgl@sss.pgh.pa.us 301 :CBC 17 : LogicalRepWorkersWakeupAtCommit(objectId);
302 : : }
4852 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 */
321 : 169 : values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
322 : 169 : nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
323 : 169 : replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
4710 noah@leadboat.com 324 : 169 : namestrcpy(&nameattrdata, new_name);
325 : 169 : values[Anum_name - 1] = NameGetDatum(&nameattrdata);
4852 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 */
3381 331 : 169 : CatalogTupleUpdate(rel, &oldtup->t_self, newtup);
332 : :
4797 rhaas@postgresql.org 333 [ - + ]: 169 : InvokeObjectPostAlterHook(classId, objectId, 0);
334 : :
335 : : /* Do post catalog-update tasks */
418 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 */
4852 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 : 1012 : ExecRenameStmt(RenameStmt *stmt)
368 : : {
369 [ + + + + : 1012 : switch (stmt->renameType)
+ + + + +
+ + + - ]
370 : : {
4151 371 : 56 : case OBJECT_TABCONSTRAINT:
372 : : case OBJECT_DOMCONSTRAINT:
4852 373 : 56 : return RenameConstraint(stmt);
374 : :
375 : 9 : case OBJECT_DATABASE:
376 : 9 : return RenameDatabase(stmt->subname, stmt->newname);
377 : :
7616 tgl@sss.pgh.pa.us 378 : 20 : case OBJECT_ROLE:
4881 rhaas@postgresql.org 379 : 20 : return RenameRole(stmt->subname, stmt->newname);
380 : :
8348 peter_e@gmx.net 381 : 13 : case OBJECT_SCHEMA:
4881 rhaas@postgresql.org 382 : 13 : return RenameSchema(stmt->subname, stmt->newname);
383 : :
7984 tgl@sss.pgh.pa.us 384 : 7 : case OBJECT_TABLESPACE:
4881 rhaas@postgresql.org 385 : 7 : return RenameTableSpace(stmt->subname, stmt->newname);
386 : :
8348 peter_e@gmx.net 387 : 327 : 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 : : case OBJECT_PROPGRAPH:
4881 rhaas@postgresql.org 394 : 327 : return RenameRelation(stmt);
395 : :
8348 peter_e@gmx.net 396 : 210 : case OBJECT_COLUMN:
397 : : case OBJECT_ATTRIBUTE:
4881 rhaas@postgresql.org 398 : 210 : return renameatt(stmt);
399 : :
4834 tgl@sss.pgh.pa.us 400 : 22 : case OBJECT_RULE:
401 : 22 : return RenameRewriteRule(stmt->relation, stmt->subname,
402 : 22 : stmt->newname);
403 : :
8348 peter_e@gmx.net 404 : 26 : case OBJECT_TRIGGER:
4881 rhaas@postgresql.org 405 : 26 : return renametrig(stmt);
406 : :
4246 sfrost@snowman.net 407 : 12 : case OBJECT_POLICY:
408 : 12 : return rename_policy(stmt);
409 : :
4852 alvherre@alvh.no-ip. 410 : 21 : case OBJECT_DOMAIN:
411 : : case OBJECT_TYPE:
412 : 21 : return RenameType(stmt);
413 : :
414 : 289 : case OBJECT_AGGREGATE:
415 : : case OBJECT_COLLATION:
416 : : case OBJECT_CONVERSION:
417 : : case OBJECT_EVENT_TRIGGER:
418 : : case OBJECT_FDW:
419 : : case OBJECT_FOREIGN_SERVER:
420 : : case OBJECT_FUNCTION:
421 : : case OBJECT_OPCLASS:
422 : : case OBJECT_OPFAMILY:
423 : : case OBJECT_LANGUAGE:
424 : : case OBJECT_PROCEDURE:
425 : : case OBJECT_ROUTINE:
426 : : case OBJECT_STATISTIC_EXT:
427 : : case OBJECT_TSCONFIGURATION:
428 : : case OBJECT_TSDICTIONARY:
429 : : case OBJECT_TSPARSER:
430 : : case OBJECT_TSTEMPLATE:
431 : : case OBJECT_PUBLICATION:
432 : : case OBJECT_SUBSCRIPTION:
433 : : {
434 : : ObjectAddress address;
435 : : Relation catalog;
436 : :
437 : 289 : address = get_object_address(stmt->renameType,
438 : : stmt->object,
439 : : NULL,
440 : : AccessExclusiveLock, false);
441 : :
2661 andres@anarazel.de 442 : 281 : catalog = table_open(address.classId, RowExclusiveLock);
4852 alvherre@alvh.no-ip. 443 : 281 : AlterObjectRename_internal(catalog,
444 : : address.objectId,
445 : 281 : stmt->newname);
2661 andres@anarazel.de 446 : 169 : table_close(catalog, RowExclusiveLock);
447 : :
4081 alvherre@alvh.no-ip. 448 : 169 : return address;
449 : : }
450 : :
8348 peter_e@gmx.net 451 :UBC 0 : default:
8325 tgl@sss.pgh.pa.us 452 [ # # ]: 0 : elog(ERROR, "unrecognized rename stmt type: %d",
453 : : (int) stmt->renameType);
454 : : return InvalidObjectAddress; /* keep compiler happy */
455 : : }
456 : : }
457 : :
458 : : /*
459 : : * Executes an ALTER OBJECT / [NO] DEPENDS ON EXTENSION statement.
460 : : *
461 : : * Return value is the address of the altered object. refAddress is an output
462 : : * argument which, if not null, receives the address of the object that the
463 : : * altered object now depends on.
464 : : */
465 : : ObjectAddress
3682 alvherre@alvh.no-ip. 466 :CBC 35 : ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddress)
467 : : {
468 : : ObjectAddress address;
469 : : ObjectAddress refAddr;
470 : : Relation rel;
471 : :
472 : : address =
3461 peter_e@gmx.net 473 : 35 : get_object_address_rv(stmt->objectType, stmt->relation, (List *) stmt->object,
474 : : &rel, AccessExclusiveLock, false);
475 : :
476 : : /*
477 : : * Verify that the user is entitled to run the command.
478 : : *
479 : : * We don't check any privileges on the extension, because that's not
480 : : * needed. The object owner is stipulating, by running this command, that
481 : : * the extension owner can drop the object whenever they feel like it,
482 : : * which is not considered a problem.
483 : : */
2276 alvherre@alvh.no-ip. 484 : 35 : check_object_ownership(GetUserId(),
485 : : stmt->objectType, address, stmt->object, rel);
486 : :
487 : : /*
488 : : * If a relation was involved, it would have been opened and locked. We
489 : : * don't need the relation here, but we'll retain the lock until commit.
490 : : */
3682 491 [ + + ]: 35 : if (rel)
2661 andres@anarazel.de 492 : 29 : table_close(rel, NoLock);
493 : :
3461 peter_e@gmx.net 494 : 35 : refAddr = get_object_address(OBJECT_EXTENSION, (Node *) stmt->extname,
495 : : NULL, AccessExclusiveLock, false);
3682 alvherre@alvh.no-ip. 496 [ + - ]: 35 : if (refAddress)
497 : 35 : *refAddress = refAddr;
498 : :
2206 499 [ + + ]: 35 : if (stmt->remove)
500 : : {
501 : 6 : deleteDependencyRecordsForSpecific(address.classId, address.objectId,
502 : : DEPENDENCY_AUTO_EXTENSION,
503 : : refAddr.classId, refAddr.objectId);
504 : : }
505 : : else
506 : : {
507 : : List *currexts;
508 : :
509 : : /* Avoid duplicates */
510 : 29 : currexts = getAutoExtensionsOfObject(address.classId,
511 : : address.objectId);
512 [ + + ]: 29 : if (!list_member_oid(currexts, refAddr.objectId))
513 : 28 : recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
514 : : }
515 : :
3682 516 : 35 : return address;
517 : : }
518 : :
519 : : /*
520 : : * Executes an ALTER OBJECT / SET SCHEMA statement. Based on the object
521 : : * type, the function appropriate to that type is executed.
522 : : *
523 : : * Return value is that of the altered object.
524 : : *
525 : : * oldSchemaAddr is an output argument which, if not NULL, is set to the object
526 : : * address of the original schema.
527 : : */
528 : : ObjectAddress
4081 529 : 281 : ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
530 : : ObjectAddress *oldSchemaAddr)
531 : : {
532 : : ObjectAddress address;
533 : : Oid oldNspOid;
534 : :
7582 tgl@sss.pgh.pa.us 535 [ + + + + : 281 : switch (stmt->objectType)
- ]
536 : : {
5565 537 : 6 : case OBJECT_EXTENSION:
1699 peter@eisentraut.org 538 [ + - ]: 6 : address = AlterExtensionNamespace(strVal(stmt->object), stmt->newschema,
539 : : oldSchemaAddr ? &oldNspOid : NULL);
4081 alvherre@alvh.no-ip. 540 : 4 : break;
541 : :
4858 542 : 88 : case OBJECT_FOREIGN_TABLE:
543 : : case OBJECT_SEQUENCE:
544 : : case OBJECT_TABLE:
545 : : case OBJECT_VIEW:
546 : : case OBJECT_MATVIEW:
547 : : case OBJECT_PROPGRAPH:
4081 548 [ + - ]: 88 : address = AlterTableNamespace(stmt,
549 : : oldSchemaAddr ? &oldNspOid : NULL);
550 : 75 : break;
551 : :
4968 552 : 12 : case OBJECT_DOMAIN:
553 : : case OBJECT_TYPE:
3461 peter_e@gmx.net 554 [ + - ]: 12 : address = AlterTypeNamespace(castNode(List, stmt->object), stmt->newschema,
555 : : stmt->objectType,
556 : : oldSchemaAddr ? &oldNspOid : NULL);
4081 alvherre@alvh.no-ip. 557 : 12 : break;
558 : :
559 : : /* generic code path */
4858 560 : 175 : case OBJECT_AGGREGATE:
561 : : case OBJECT_COLLATION:
562 : : case OBJECT_CONVERSION:
563 : : case OBJECT_FUNCTION:
564 : : case OBJECT_OPERATOR:
565 : : case OBJECT_OPCLASS:
566 : : case OBJECT_OPFAMILY:
567 : : case OBJECT_PROCEDURE:
568 : : case OBJECT_ROUTINE:
569 : : case OBJECT_STATISTIC_EXT:
570 : : case OBJECT_TSCONFIGURATION:
571 : : case OBJECT_TSDICTIONARY:
572 : : case OBJECT_TSPARSER:
573 : : case OBJECT_TSTEMPLATE:
574 : : {
575 : : Relation catalog;
576 : : Oid classId;
577 : : Oid nspOid;
578 : :
4968 579 : 175 : address = get_object_address(stmt->objectType,
580 : : stmt->object,
581 : : NULL,
582 : : AccessExclusiveLock,
583 : : false);
584 : 171 : classId = address.classId;
2661 andres@anarazel.de 585 : 171 : catalog = table_open(classId, RowExclusiveLock);
4968 alvherre@alvh.no-ip. 586 : 171 : nspOid = LookupCreationNamespace(stmt->newschema);
587 : :
4081 588 : 171 : oldNspOid = AlterObjectNamespace_internal(catalog, address.objectId,
589 : : nspOid);
2661 andres@anarazel.de 590 : 95 : table_close(catalog, RowExclusiveLock);
591 : : }
7582 tgl@sss.pgh.pa.us 592 : 95 : break;
593 : :
7582 tgl@sss.pgh.pa.us 594 :UBC 0 : default:
595 [ # # ]: 0 : elog(ERROR, "unrecognized AlterObjectSchemaStmt type: %d",
596 : : (int) stmt->objectType);
597 : : return InvalidObjectAddress; /* keep compiler happy */
598 : : }
599 : :
4081 alvherre@alvh.no-ip. 600 [ + - ]:CBC 186 : if (oldSchemaAddr)
601 : 186 : ObjectAddressSet(*oldSchemaAddr, NamespaceRelationId, oldNspOid);
602 : :
603 : 186 : return address;
604 : : }
605 : :
606 : : /*
607 : : * Change an object's namespace given its classOid and object Oid.
608 : : *
609 : : * Objects that don't have a namespace should be ignored, as should
610 : : * dependent types such as array types.
611 : : *
612 : : * This function is currently used only by ALTER EXTENSION SET SCHEMA,
613 : : * so it only needs to cover object kinds that can be members of an
614 : : * extension, and it can silently ignore dependent types --- we assume
615 : : * those will be moved when their parent object is moved.
616 : : *
617 : : * Returns the OID of the object's previous namespace, or InvalidOid if
618 : : * object doesn't have a schema or was ignored due to being a dependent type.
619 : : */
620 : : Oid
4934 621 : 21 : AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
622 : : ObjectAddresses *objsMoved)
623 : : {
5565 tgl@sss.pgh.pa.us 624 : 21 : Oid oldNspOid = InvalidOid;
625 : :
770 peter@eisentraut.org 626 [ + + + + ]: 21 : switch (classId)
627 : : {
628 : 1 : case RelationRelationId:
629 : : {
630 : : Relation rel;
631 : :
5504 bruce@momjian.us 632 : 1 : rel = relation_open(objid, AccessExclusiveLock);
633 : 1 : oldNspOid = RelationGetNamespace(rel);
634 : :
4934 alvherre@alvh.no-ip. 635 : 1 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
636 : :
5504 bruce@momjian.us 637 : 1 : relation_close(rel, NoLock);
638 : 1 : break;
639 : : }
640 : :
770 peter@eisentraut.org 641 : 8 : case TypeRelationId:
726 tgl@sss.pgh.pa.us 642 : 8 : oldNspOid = AlterTypeNamespace_oid(objid, nspOid, true, objsMoved);
5565 643 : 8 : break;
644 : :
770 peter@eisentraut.org 645 : 11 : case ProcedureRelationId:
646 : : case CollationRelationId:
647 : : case ConversionRelationId:
648 : : case OperatorRelationId:
649 : : case OperatorClassRelationId:
650 : : case OperatorFamilyRelationId:
651 : : case StatisticExtRelationId:
652 : : case TSParserRelationId:
653 : : case TSDictionaryRelationId:
654 : : case TSTemplateRelationId:
655 : : case TSConfigRelationId:
656 : : {
657 : : Relation catalog;
658 : :
2661 andres@anarazel.de 659 : 11 : catalog = table_open(classId, RowExclusiveLock);
660 : :
4968 alvherre@alvh.no-ip. 661 : 11 : oldNspOid = AlterObjectNamespace_internal(catalog, objid,
662 : : nspOid);
663 : :
2661 andres@anarazel.de 664 : 11 : table_close(catalog, RowExclusiveLock);
665 : : }
5565 tgl@sss.pgh.pa.us 666 : 11 : break;
667 : :
770 peter@eisentraut.org 668 : 1 : default:
669 : : /* ignore object types that don't have schema-qualified names */
670 [ - + ]: 1 : Assert(get_object_attnum_namespace(classId) == InvalidAttrNumber);
671 : : }
672 : :
5565 tgl@sss.pgh.pa.us 673 : 21 : return oldNspOid;
674 : : }
675 : :
676 : : /*
677 : : * Generic function to change the namespace of a given object, for simple
678 : : * cases (won't work for tables, nor other cases where we need to do more
679 : : * than change the namespace column of a single catalog entry).
680 : : *
681 : : * rel: catalog relation containing object (RowExclusiveLock'd by caller)
682 : : * objid: OID of object to change the namespace of
683 : : * nspOid: OID of new namespace
684 : : *
685 : : * Returns the OID of the object's previous namespace.
686 : : */
687 : : static Oid
4968 alvherre@alvh.no-ip. 688 : 182 : AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
689 : : {
5565 tgl@sss.pgh.pa.us 690 : 182 : Oid classId = RelationGetRelid(rel);
76 michael@paquier.xyz 691 :GNC 182 : SysCacheIdentifier oidCacheId = get_object_catcache_oid(classId);
692 : 182 : SysCacheIdentifier nameCacheId = get_object_catcache_name(classId);
4968 alvherre@alvh.no-ip. 693 :CBC 182 : AttrNumber Anum_name = get_object_attnum_name(classId);
694 : 182 : AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
695 : 182 : AttrNumber Anum_owner = get_object_attnum_owner(classId);
696 : : Oid oldNspOid;
697 : : Datum name,
698 : : namespace;
699 : : bool isnull;
700 : : HeapTuple tup,
701 : : newtup;
702 : : Datum *values;
703 : : bool *nulls;
704 : : bool *replaces;
705 : :
5565 tgl@sss.pgh.pa.us 706 : 182 : tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid));
5639 rhaas@postgresql.org 707 [ - + ]: 182 : if (!HeapTupleIsValid(tup)) /* should not happen */
5565 tgl@sss.pgh.pa.us 708 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
709 : : objid, RelationGetRelationName(rel));
710 : :
5565 tgl@sss.pgh.pa.us 711 :CBC 182 : name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
712 [ - + ]: 182 : Assert(!isnull);
4968 alvherre@alvh.no-ip. 713 : 182 : namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel),
714 : : &isnull);
5565 tgl@sss.pgh.pa.us 715 [ - + ]: 182 : Assert(!isnull);
5639 rhaas@postgresql.org 716 : 182 : oldNspOid = DatumGetObjectId(namespace);
717 : :
718 : : /*
719 : : * If the object is already in the correct namespace, we don't need to do
720 : : * anything except fire the object access hook.
721 : : */
3820 722 [ + + ]: 182 : if (oldNspOid == nspOid)
723 : : {
724 [ - + ]: 4 : InvokeObjectPostAlterHook(classId, objid, 0);
725 : 4 : return oldNspOid;
726 : : }
727 : :
728 : : /* Check basic namespace related issues */
729 : 178 : CheckSetNamespace(oldNspOid, nspOid);
730 : :
731 : : /* Permission checks ... superusers can always do it */
5639 732 [ + + ]: 178 : if (!superuser())
733 : : {
734 : : Datum owner;
735 : : Oid ownerId;
736 : : AclResult aclresult;
737 : :
738 : : /* Fail if object does not have an explicit owner */
5565 tgl@sss.pgh.pa.us 739 [ - + ]: 104 : if (Anum_owner <= 0)
5639 rhaas@postgresql.org 740 [ # # ]:UBC 0 : ereport(ERROR,
741 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
742 : : errmsg("must be superuser to set schema of %s",
743 : : getObjectDescriptionOids(classId, objid))));
744 : :
745 : : /* Otherwise, must be owner of the existing object */
5565 tgl@sss.pgh.pa.us 746 :CBC 104 : owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull);
747 [ - + ]: 104 : Assert(!isnull);
5639 rhaas@postgresql.org 748 : 104 : ownerId = DatumGetObjectId(owner);
749 : :
750 [ + + ]: 104 : if (!has_privs_of_role(GetUserId(), ownerId))
2373 tgl@sss.pgh.pa.us 751 : 36 : aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objid),
5639 rhaas@postgresql.org 752 : 36 : NameStr(*(DatumGetName(name))));
753 : :
754 : : /* User must have CREATE privilege on new namespace */
1269 peter@eisentraut.org 755 : 68 : aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
5639 rhaas@postgresql.org 756 [ - + ]: 68 : if (aclresult != ACLCHECK_OK)
3076 peter_e@gmx.net 757 :UBC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
5565 tgl@sss.pgh.pa.us 758 : 0 : get_namespace_name(nspOid));
759 : : }
760 : :
761 : : /*
762 : : * Check for duplicate name (more friendly than unique-index failure).
763 : : * Since this is just a friendliness check, we can just skip it in cases
764 : : * where there isn't suitable support.
765 : : */
4858 alvherre@alvh.no-ip. 766 [ + + ]:CBC 142 : if (classId == ProcedureRelationId)
767 : : {
4852 768 : 38 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tup);
769 : :
4858 770 : 38 : IsThereFunctionInNamespace(NameStr(proc->proname), proc->pronargs,
771 : : &proc->proargtypes, nspOid);
772 : : }
773 [ + + ]: 104 : else if (classId == CollationRelationId)
774 : : {
4852 775 : 4 : Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(tup);
776 : :
777 : 4 : IsThereCollationInNamespace(NameStr(coll->collname), nspOid);
778 : : }
779 [ + + ]: 100 : else if (classId == OperatorClassRelationId)
780 : : {
781 : 12 : Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(tup);
782 : :
783 : 12 : IsThereOpClassInNamespace(NameStr(opc->opcname),
784 : : opc->opcmethod, nspOid);
785 : : }
786 [ + + ]: 88 : else if (classId == OperatorFamilyRelationId)
787 : : {
788 : 12 : Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(tup);
789 : :
790 : 12 : IsThereOpFamilyInNamespace(NameStr(opf->opfname),
791 : : opf->opfmethod, nspOid);
792 : : }
4858 793 [ + + + + ]: 144 : else if (nameCacheId >= 0 &&
794 : 68 : SearchSysCacheExists2(nameCacheId, name,
795 : : ObjectIdGetDatum(nspOid)))
796 : 24 : report_namespace_conflict(classId,
797 : 24 : NameStr(*(DatumGetName(name))),
798 : : nspOid);
799 : :
800 : : /* Build modified tuple */
5565 tgl@sss.pgh.pa.us 801 : 102 : values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
802 : 102 : nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
803 : 102 : replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
804 : 102 : values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid);
5639 rhaas@postgresql.org 805 : 102 : replaces[Anum_namespace - 1] = true;
5565 tgl@sss.pgh.pa.us 806 : 102 : newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
807 : : values, nulls, replaces);
808 : :
809 : : /* Perform actual update */
3381 alvherre@alvh.no-ip. 810 : 102 : CatalogTupleUpdate(rel, &tup->t_self, newtup);
811 : :
812 : : /* Release memory */
5639 rhaas@postgresql.org 813 : 102 : pfree(values);
814 : 102 : pfree(nulls);
815 : 102 : pfree(replaces);
816 : :
817 : : /* update dependency to point to the new schema */
1030 michael@paquier.xyz 818 [ - + ]: 102 : if (changeDependencyFor(classId, objid,
819 : : NamespaceRelationId, oldNspOid, nspOid) != 1)
1030 michael@paquier.xyz 820 [ # # ]:UBC 0 : elog(ERROR, "could not change schema dependency for object %u",
821 : : objid);
822 : :
4797 rhaas@postgresql.org 823 [ - + ]:CBC 102 : InvokeObjectPostAlterHook(classId, objid, 0);
824 : :
5565 tgl@sss.pgh.pa.us 825 : 102 : return oldNspOid;
826 : : }
827 : :
828 : : /*
829 : : * Executes an ALTER OBJECT / OWNER TO statement. Based on the object
830 : : * type, the function appropriate to that type is executed.
831 : : */
832 : : ObjectAddress
7984 833 : 998 : ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
834 : : {
4075 alvherre@alvh.no-ip. 835 : 998 : Oid newowner = get_rolespec_oid(stmt->newowner, false);
836 : :
7984 tgl@sss.pgh.pa.us 837 [ + + + + : 991 : switch (stmt->objectType)
+ + + + +
- ]
838 : : {
839 : 50 : case OBJECT_DATABASE:
1699 peter@eisentraut.org 840 : 50 : return AlterDatabaseOwner(strVal(stmt->object), newowner);
841 : :
7984 tgl@sss.pgh.pa.us 842 : 41 : case OBJECT_SCHEMA:
1699 peter@eisentraut.org 843 : 41 : return AlterSchemaOwner(strVal(stmt->object), newowner);
844 : :
7984 tgl@sss.pgh.pa.us 845 : 73 : case OBJECT_TYPE:
846 : : case OBJECT_DOMAIN: /* same as TYPE */
3461 peter_e@gmx.net 847 : 73 : return AlterTypeOwner(castNode(List, stmt->object), newowner, stmt->objectType);
848 : : break;
849 : :
6346 850 : 13 : case OBJECT_FDW:
1699 peter@eisentraut.org 851 : 13 : return AlterForeignDataWrapperOwner(strVal(stmt->object),
852 : : newowner);
853 : :
6346 peter_e@gmx.net 854 : 45 : case OBJECT_FOREIGN_SERVER:
1699 peter@eisentraut.org 855 : 45 : return AlterForeignServerOwner(strVal(stmt->object),
856 : : newowner);
857 : :
5039 rhaas@postgresql.org 858 : 9 : case OBJECT_EVENT_TRIGGER:
1699 peter@eisentraut.org 859 : 9 : return AlterEventTriggerOwner(strVal(stmt->object),
860 : : newowner);
861 : :
3393 peter_e@gmx.net 862 : 26 : case OBJECT_PUBLICATION:
1699 peter@eisentraut.org 863 : 26 : return AlterPublicationOwner(strVal(stmt->object),
864 : : newowner);
865 : :
3393 peter_e@gmx.net 866 : 11 : case OBJECT_SUBSCRIPTION:
1699 peter@eisentraut.org 867 : 11 : return AlterSubscriptionOwner(strVal(stmt->object),
868 : : newowner);
869 : :
870 : : /* Generic cases */
4962 alvherre@alvh.no-ip. 871 : 723 : case OBJECT_AGGREGATE:
872 : : case OBJECT_COLLATION:
873 : : case OBJECT_CONVERSION:
874 : : case OBJECT_FUNCTION:
875 : : case OBJECT_LANGUAGE:
876 : : case OBJECT_LARGEOBJECT:
877 : : case OBJECT_OPERATOR:
878 : : case OBJECT_OPCLASS:
879 : : case OBJECT_OPFAMILY:
880 : : case OBJECT_PROCEDURE:
881 : : case OBJECT_PROPGRAPH:
882 : : case OBJECT_ROUTINE:
883 : : case OBJECT_STATISTIC_EXT:
884 : : case OBJECT_TABLESPACE:
885 : : case OBJECT_TSDICTIONARY:
886 : : case OBJECT_TSCONFIGURATION:
887 : : {
888 : : ObjectAddress address;
889 : :
50 peter@eisentraut.org 890 [ + + ]:GNC 723 : if (stmt->relation)
891 : : {
892 : : Relation relation;
893 : :
894 : 33 : address = get_object_address_rv(stmt->objectType,
895 : : stmt->relation,
896 : : NIL,
897 : : &relation,
898 : : AccessExclusiveLock,
899 : : false);
900 : 33 : relation_close(relation, NoLock);
901 : : }
902 : : else
903 : : {
904 : 690 : address = get_object_address(stmt->objectType,
905 : : stmt->object,
906 : : NULL,
907 : : AccessExclusiveLock,
908 : : false);
909 : : }
910 : :
872 tgl@sss.pgh.pa.us 911 :CBC 718 : AlterObjectOwner_internal(address.classId, address.objectId,
912 : : newowner);
913 : :
4081 alvherre@alvh.no-ip. 914 : 590 : return address;
915 : : }
916 : : break;
917 : :
7984 tgl@sss.pgh.pa.us 918 :UBC 0 : default:
919 [ # # ]: 0 : elog(ERROR, "unrecognized AlterOwnerStmt type: %d",
920 : : (int) stmt->objectType);
921 : : return InvalidObjectAddress; /* keep compiler happy */
922 : : }
923 : : }
924 : :
925 : : /*
926 : : * Generic function to change the ownership of a given object, for simple
927 : : * cases (won't work for tables, nor other cases where we need to do more than
928 : : * change the ownership column of a single catalog entry).
929 : : *
930 : : * classId: OID of catalog containing object
931 : : * objectId: OID of object to change the ownership of
932 : : * new_ownerId: OID of new object owner
933 : : *
934 : : * This will work on large objects, but we have to beware of the fact that
935 : : * classId isn't the OID of the catalog to modify in that case.
936 : : */
937 : : void
872 tgl@sss.pgh.pa.us 938 :CBC 730 : AlterObjectOwner_internal(Oid classId, Oid objectId, Oid new_ownerId)
939 : : {
940 : : /* For large objects, the catalog to modify is pg_largeobject_metadata */
941 [ + + ]: 730 : Oid catalogId = (classId == LargeObjectRelationId) ? LargeObjectMetadataRelationId : classId;
942 : 730 : AttrNumber Anum_oid = get_object_attnum_oid(catalogId);
943 : 730 : AttrNumber Anum_owner = get_object_attnum_owner(catalogId);
944 : 730 : AttrNumber Anum_namespace = get_object_attnum_namespace(catalogId);
945 : 730 : AttrNumber Anum_acl = get_object_attnum_acl(catalogId);
946 : 730 : AttrNumber Anum_name = get_object_attnum_name(catalogId);
947 : : Relation rel;
948 : : HeapTuple oldtup;
949 : : Datum datum;
950 : : bool isnull;
951 : : Oid old_ownerId;
4962 alvherre@alvh.no-ip. 952 : 730 : Oid namespaceId = InvalidOid;
953 : :
872 tgl@sss.pgh.pa.us 954 : 730 : rel = table_open(catalogId, RowExclusiveLock);
955 : :
956 : : /* Search tuple and lock it. */
957 : : oldtup =
493 noah@leadboat.com 958 : 730 : get_catalog_object_by_oid_extended(rel, Anum_oid, objectId, true);
4962 alvherre@alvh.no-ip. 959 [ - + ]: 730 : if (oldtup == NULL)
4962 alvherre@alvh.no-ip. 960 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
961 : : objectId, RelationGetRelationName(rel));
962 : :
4962 alvherre@alvh.no-ip. 963 :CBC 730 : datum = heap_getattr(oldtup, Anum_owner,
964 : : RelationGetDescr(rel), &isnull);
965 [ - + ]: 730 : Assert(!isnull);
966 : 730 : old_ownerId = DatumGetObjectId(datum);
967 : :
968 [ + + ]: 730 : if (Anum_namespace != InvalidAttrNumber)
969 : : {
970 : 630 : datum = heap_getattr(oldtup, Anum_namespace,
971 : : RelationGetDescr(rel), &isnull);
972 [ - + ]: 630 : Assert(!isnull);
973 : 630 : namespaceId = DatumGetObjectId(datum);
974 : : }
975 : :
976 [ + + ]: 730 : if (old_ownerId != new_ownerId)
977 : : {
978 : : AttrNumber nattrs;
979 : : HeapTuple newtup;
980 : : Datum *values;
981 : : bool *nulls;
982 : : bool *replaces;
983 : :
984 : : /* Superusers can bypass permission checks */
985 [ + + ]: 272 : if (!superuser())
986 : : {
987 : : /* must be owner */
988 [ + + ]: 172 : if (!has_privs_of_role(GetUserId(), old_ownerId))
989 : : {
990 : : char *objname;
991 : : char namebuf[NAMEDATALEN];
992 : :
993 [ + - ]: 44 : if (Anum_name != InvalidAttrNumber)
994 : : {
995 : 44 : datum = heap_getattr(oldtup, Anum_name,
996 : : RelationGetDescr(rel), &isnull);
997 [ - + ]: 44 : Assert(!isnull);
998 : 44 : objname = NameStr(*DatumGetName(datum));
999 : : }
1000 : : else
1001 : : {
2723 andres@anarazel.de 1002 :UBC 0 : snprintf(namebuf, sizeof(namebuf), "%u", objectId);
4962 alvherre@alvh.no-ip. 1003 : 0 : objname = namebuf;
1004 : : }
872 tgl@sss.pgh.pa.us 1005 :CBC 44 : aclcheck_error(ACLCHECK_NOT_OWNER,
1006 : : get_object_type(catalogId, objectId),
1007 : : objname);
1008 : : }
1009 : : /* Must be able to become new owner */
1264 rhaas@postgresql.org 1010 : 128 : check_can_set_role(GetUserId(), new_ownerId);
1011 : :
1012 : : /* New owner must have CREATE privilege on namespace */
4962 alvherre@alvh.no-ip. 1013 [ + + ]: 44 : if (OidIsValid(namespaceId))
1014 : : {
1015 : : AclResult aclresult;
1016 : :
1269 peter@eisentraut.org 1017 : 40 : aclresult = object_aclcheck(NamespaceRelationId, namespaceId, new_ownerId,
1018 : : ACL_CREATE);
4962 alvherre@alvh.no-ip. 1019 [ - + ]: 40 : if (aclresult != ACLCHECK_OK)
3076 peter_e@gmx.net 1020 :UBC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
4962 alvherre@alvh.no-ip. 1021 : 0 : get_namespace_name(namespaceId));
1022 : : }
1023 : : }
1024 : :
1025 : : /* Build a modified tuple */
4962 alvherre@alvh.no-ip. 1026 :CBC 144 : nattrs = RelationGetNumberOfAttributes(rel);
1027 : 144 : values = palloc0(nattrs * sizeof(Datum));
1028 : 144 : nulls = palloc0(nattrs * sizeof(bool));
1029 : 144 : replaces = palloc0(nattrs * sizeof(bool));
1030 : 144 : values[Anum_owner - 1] = ObjectIdGetDatum(new_ownerId);
1031 : 144 : replaces[Anum_owner - 1] = true;
1032 : :
1033 : : /*
1034 : : * Determine the modified ACL for the new owner. This is only
1035 : : * necessary when the ACL is non-null.
1036 : : */
1037 [ + + ]: 144 : if (Anum_acl != InvalidAttrNumber)
1038 : : {
1039 : 79 : datum = heap_getattr(oldtup,
1040 : : Anum_acl, RelationGetDescr(rel), &isnull);
1041 [ + + ]: 79 : if (!isnull)
1042 : : {
1043 : : Acl *newAcl;
1044 : :
1045 : 4 : newAcl = aclnewowner(DatumGetAclP(datum),
1046 : : old_ownerId, new_ownerId);
1047 : 4 : values[Anum_acl - 1] = PointerGetDatum(newAcl);
1048 : 4 : replaces[Anum_acl - 1] = true;
1049 : : }
1050 : : }
1051 : :
1052 : 144 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
1053 : : values, nulls, replaces);
1054 : :
1055 : : /* Perform actual update */
3381 1056 : 144 : CatalogTupleUpdate(rel, &newtup->t_self, newtup);
1057 : :
493 noah@leadboat.com 1058 : 144 : UnlockTuple(rel, &oldtup->t_self, InplaceUpdateTupleLock);
1059 : :
1060 : : /* Update owner dependency reference */
2723 andres@anarazel.de 1061 : 144 : changeDependencyOnOwner(classId, objectId, new_ownerId);
1062 : :
1063 : : /* Release memory */
4962 alvherre@alvh.no-ip. 1064 : 144 : pfree(values);
1065 : 144 : pfree(nulls);
1066 : 144 : pfree(replaces);
1067 : : }
1068 : : else
493 noah@leadboat.com 1069 : 458 : UnlockTuple(rel, &oldtup->t_self, InplaceUpdateTupleLock);
1070 : :
1071 : : /* Note the post-alter hook gets classId not catalogId */
4797 rhaas@postgresql.org 1072 [ - + ]: 602 : InvokeObjectPostAlterHook(classId, objectId, 0);
1073 : :
872 tgl@sss.pgh.pa.us 1074 : 602 : table_close(rel, RowExclusiveLock);
4962 alvherre@alvh.no-ip. 1075 : 602 : }
|