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