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
4801 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;
3299 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;
4801 alvherre@alvh.no-ip. 100 : 0 : default:
1223 peter@eisentraut.org 101 [ # # ]: 0 : elog(ERROR, "unsupported object class: %u", classId);
102 : : break;
103 : : }
104 : :
4801 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 : msgfmt = gettext_noop("conversion \"%s\" already exists in schema \"%s\"");
121 : 6 : break;
3278 122 : 6 : case StatisticExtRelationId:
3227 tgl@sss.pgh.pa.us 123 : 6 : msgfmt = gettext_noop("statistics object \"%s\" already exists in schema \"%s\"");
3278 alvherre@alvh.no-ip. 124 : 6 : break;
4801 125 : 6 : case TSParserRelationId:
126 : 6 : msgfmt = gettext_noop("text search parser \"%s\" already exists in schema \"%s\"");
127 : 6 : break;
128 : 6 : case TSDictionaryRelationId:
129 : 6 : msgfmt = gettext_noop("text search dictionary \"%s\" already exists in schema \"%s\"");
130 : 6 : break;
131 : 6 : case TSTemplateRelationId:
132 : 6 : msgfmt = gettext_noop("text search template \"%s\" already exists in schema \"%s\"");
133 : 6 : break;
134 : 6 : case TSConfigRelationId:
135 : 6 : msgfmt = gettext_noop("text search configuration \"%s\" already exists in schema \"%s\"");
136 : 6 : break;
4801 alvherre@alvh.no-ip. 137 :UBC 0 : default:
1223 peter@eisentraut.org 138 [ # # ]: 0 : elog(ERROR, "unsupported object class: %u", classId);
139 : : break;
140 : : }
141 : :
4801 alvherre@alvh.no-ip. 142 [ + - ]:CBC 36 : 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 : 214 : AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
160 : : {
161 : 214 : Oid classId = RelationGetRelid(rel);
25 michael@paquier.xyz 162 :GNC 214 : SysCacheIdentifier oidCacheId = get_object_catcache_oid(classId);
163 : 214 : SysCacheIdentifier nameCacheId = get_object_catcache_name(classId);
4801 alvherre@alvh.no-ip. 164 :CBC 214 : AttrNumber Anum_name = get_object_attnum_name(classId);
165 : 214 : AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
166 : 214 : 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 : 214 : oldtup = SearchSysCache1(oidCacheId, ObjectIdGetDatum(objectId));
181 [ - + ]: 214 : if (!HeapTupleIsValid(oldtup))
4801 alvherre@alvh.no-ip. 182 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
183 : : objectId, RelationGetRelationName(rel));
184 : :
4801 alvherre@alvh.no-ip. 185 :CBC 214 : datum = heap_getattr(oldtup, Anum_name,
186 : : RelationGetDescr(rel), &isnull);
187 [ - + ]: 214 : Assert(!isnull);
188 : 214 : old_name = NameStr(*(DatumGetName(datum)));
189 : :
190 : : /* Get OID of namespace */
191 [ + + ]: 214 : if (Anum_namespace > 0)
192 : : {
193 : 135 : datum = heap_getattr(oldtup, Anum_namespace,
194 : : RelationGetDescr(rel), &isnull);
195 [ - + ]: 135 : Assert(!isnull);
196 : 135 : namespaceId = DatumGetObjectId(datum);
197 : : }
198 : : else
199 : 79 : namespaceId = InvalidOid;
200 : :
201 : : /* Permission checks ... superusers can always do it */
202 [ + + ]: 214 : if (!superuser())
203 : : {
204 : : /* Fail if object does not have an explicit owner */
205 [ - + ]: 123 : if (Anum_owner <= 0)
4801 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 */
4801 alvherre@alvh.no-ip. 212 :CBC 123 : datum = heap_getattr(oldtup, Anum_owner,
213 : : RelationGetDescr(rel), &isnull);
214 [ - + ]: 123 : Assert(!isnull);
215 : 123 : ownerId = DatumGetObjectId(datum);
216 : :
219 peter@eisentraut.org 217 [ + + ]:GNC 123 : if (!has_privs_of_role(GetUserId(), ownerId))
2322 tgl@sss.pgh.pa.us 218 :CBC 36 : aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objectId),
219 : : old_name);
220 : :
221 : : /* User must have CREATE privilege on the namespace */
4801 alvherre@alvh.no-ip. 222 [ + + ]: 87 : if (OidIsValid(namespaceId))
223 : : {
1218 peter@eisentraut.org 224 : 72 : aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(),
225 : : ACL_CREATE);
4801 alvherre@alvh.no-ip. 226 [ - + ]: 72 : if (aclresult != ACLCHECK_OK)
3025 peter_e@gmx.net 227 :UBC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
4801 alvherre@alvh.no-ip. 228 : 0 : get_namespace_name(namespaceId));
229 : : }
230 : :
1081 rhaas@postgresql.org 231 [ + + ]:CBC 87 : if (classId == SubscriptionRelationId)
232 : : {
233 : : Form_pg_subscription form;
234 : :
235 : : /* must have CREATE privilege on database */
236 : 9 : aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId,
237 : : GetUserId(), ACL_CREATE);
238 [ + + ]: 9 : if (aclresult != ACLCHECK_OK)
239 : 3 : aclcheck_error(aclresult, OBJECT_DATABASE,
240 : 3 : get_database_name(MyDatabaseId));
241 : :
242 : : /*
243 : : * Don't allow non-superuser modification of a subscription with
244 : : * password_required=false.
245 : : */
246 : 6 : form = (Form_pg_subscription) GETSTRUCT(oldtup);
247 [ - + - - ]: 6 : if (!form->subpasswordrequired && !superuser())
1081 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 : : */
4801 alvherre@alvh.no-ip. 260 [ + + ]:CBC 175 : if (classId == ProcedureRelationId)
261 : : {
262 : 36 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(oldtup);
263 : :
264 : 36 : IsThereFunctionInNamespace(new_name, proc->pronargs,
265 : : &proc->proargtypes, proc->pronamespace);
266 : : }
267 [ + + ]: 139 : else if (classId == CollationRelationId)
268 : : {
269 : 6 : Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(oldtup);
270 : :
271 : 6 : IsThereCollationInNamespace(new_name, coll->collnamespace);
272 : : }
273 [ + + ]: 133 : else if (classId == OperatorClassRelationId)
274 : : {
275 : 9 : Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(oldtup);
276 : :
277 : 9 : IsThereOpClassInNamespace(new_name, opc->opcmethod,
278 : : opc->opcnamespace);
279 : : }
280 [ + + ]: 124 : else if (classId == OperatorFamilyRelationId)
281 : : {
282 : 9 : Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(oldtup);
283 : :
284 : 9 : IsThereOpFamilyInNamespace(new_name, opf->opfmethod,
285 : : opf->opfnamespace);
286 : : }
3299 peter_e@gmx.net 287 [ + + ]: 115 : else if (classId == SubscriptionRelationId)
288 : : {
969 michael@paquier.xyz 289 [ - + ]: 13 : if (SearchSysCacheExists2(SUBSCRIPTIONNAME,
290 : : ObjectIdGetDatum(MyDatabaseId),
291 : : CStringGetDatum(new_name)))
3299 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 */
1164 tgl@sss.pgh.pa.us 301 :CBC 13 : LogicalRepWorkersWakeupAtCommit(objectId);
302 : : }
4801 alvherre@alvh.no-ip. 303 [ + - ]: 102 : else if (nameCacheId >= 0)
304 : : {
305 [ + + ]: 102 : if (OidIsValid(namespaceId))
306 : : {
307 [ + + ]: 48 : if (SearchSysCacheExists2(nameCacheId,
308 : : CStringGetDatum(new_name),
309 : : ObjectIdGetDatum(namespaceId)))
310 : 18 : report_namespace_conflict(classId, new_name, namespaceId);
311 : : }
312 : : else
313 : : {
314 [ + + ]: 54 : if (SearchSysCacheExists1(nameCacheId,
315 : : CStringGetDatum(new_name)))
316 : 12 : report_name_conflict(classId, new_name);
317 : : }
318 : : }
319 : :
320 : : /* Build modified tuple */
321 : 130 : values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
322 : 130 : nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
323 : 130 : replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
4659 noah@leadboat.com 324 : 130 : namestrcpy(&nameattrdata, new_name);
325 : 130 : values[Anum_name - 1] = NameGetDatum(&nameattrdata);
4801 alvherre@alvh.no-ip. 326 : 130 : replaces[Anum_name - 1] = true;
327 : 130 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
328 : : values, nulls, replaces);
329 : :
330 : : /* Perform actual update */
3330 331 : 130 : CatalogTupleUpdate(rel, &oldtup->t_self, newtup);
332 : :
4746 rhaas@postgresql.org 333 [ - + ]: 130 : InvokeObjectPostAlterHook(classId, objectId, 0);
334 : :
335 : : /* Do post catalog-update tasks */
367 akapila@postgresql.o 336 [ + + ]: 130 : if (classId == PublicationRelationId)
337 : : {
338 : 18 : 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 : 18 : InvalidatePubRelSyncCache(pub->oid, pub->puballtables);
349 : : }
350 : :
351 : : /* Release memory */
4801 alvherre@alvh.no-ip. 352 : 130 : pfree(values);
353 : 130 : pfree(nulls);
354 : 130 : pfree(replaces);
355 : 130 : heap_freetuple(newtup);
356 : :
357 : 130 : ReleaseSysCache(oldtup);
358 : 130 : }
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 : 779 : ExecRenameStmt(RenameStmt *stmt)
368 : : {
369 [ + + + + : 779 : switch (stmt->renameType)
+ + + + +
+ + + - ]
370 : : {
4100 371 : 42 : case OBJECT_TABCONSTRAINT:
372 : : case OBJECT_DOMCONSTRAINT:
4801 373 : 42 : return RenameConstraint(stmt);
374 : :
375 : 7 : case OBJECT_DATABASE:
376 : 7 : return RenameDatabase(stmt->subname, stmt->newname);
377 : :
7565 tgl@sss.pgh.pa.us 378 : 15 : case OBJECT_ROLE:
4830 rhaas@postgresql.org 379 : 15 : return RenameRole(stmt->subname, stmt->newname);
380 : :
8297 peter_e@gmx.net 381 : 10 : case OBJECT_SCHEMA:
4830 rhaas@postgresql.org 382 : 10 : return RenameSchema(stmt->subname, stmt->newname);
383 : :
7933 tgl@sss.pgh.pa.us 384 : 6 : case OBJECT_TABLESPACE:
4830 rhaas@postgresql.org 385 : 6 : return RenameTableSpace(stmt->subname, stmt->newname);
386 : :
8297 peter_e@gmx.net 387 : 259 : case OBJECT_TABLE:
388 : : case OBJECT_SEQUENCE:
389 : : case OBJECT_VIEW:
390 : : case OBJECT_MATVIEW:
391 : : case OBJECT_INDEX:
392 : : case OBJECT_FOREIGN_TABLE:
4830 rhaas@postgresql.org 393 : 259 : return RenameRelation(stmt);
394 : :
8297 peter_e@gmx.net 395 : 158 : case OBJECT_COLUMN:
396 : : case OBJECT_ATTRIBUTE:
4830 rhaas@postgresql.org 397 : 158 : return renameatt(stmt);
398 : :
4783 tgl@sss.pgh.pa.us 399 : 17 : case OBJECT_RULE:
400 : 17 : return RenameRewriteRule(stmt->relation, stmt->subname,
401 : 17 : stmt->newname);
402 : :
8297 peter_e@gmx.net 403 : 20 : case OBJECT_TRIGGER:
4830 rhaas@postgresql.org 404 : 20 : return renametrig(stmt);
405 : :
4195 sfrost@snowman.net 406 : 9 : case OBJECT_POLICY:
407 : 9 : return rename_policy(stmt);
408 : :
4801 alvherre@alvh.no-ip. 409 : 16 : case OBJECT_DOMAIN:
410 : : case OBJECT_TYPE:
411 : 16 : return RenameType(stmt);
412 : :
413 : 220 : 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 : 220 : address = get_object_address(stmt->renameType,
437 : : stmt->object,
438 : : NULL,
439 : : AccessExclusiveLock, false);
440 : :
2610 andres@anarazel.de 441 : 214 : catalog = table_open(address.classId, RowExclusiveLock);
4801 alvherre@alvh.no-ip. 442 : 214 : AlterObjectRename_internal(catalog,
443 : : address.objectId,
444 : 214 : stmt->newname);
2610 andres@anarazel.de 445 : 130 : table_close(catalog, RowExclusiveLock);
446 : :
4030 alvherre@alvh.no-ip. 447 : 130 : return address;
448 : : }
449 : :
8297 peter_e@gmx.net 450 :UBC 0 : default:
8274 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
3631 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 =
3410 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 : : */
2225 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 : : */
3631 490 [ + + ]: 23 : if (rel)
2610 andres@anarazel.de 491 : 17 : table_close(rel, NoLock);
492 : :
3410 peter_e@gmx.net 493 : 23 : refAddr = get_object_address(OBJECT_EXTENSION, (Node *) stmt->extname,
494 : : NULL, AccessExclusiveLock, false);
3631 alvherre@alvh.no-ip. 495 [ + - ]: 23 : if (refAddress)
496 : 23 : *refAddress = refAddr;
497 : :
2155 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 : :
3631 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
4030 528 : 199 : ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
529 : : ObjectAddress *oldSchemaAddr)
530 : : {
531 : : ObjectAddress address;
532 : : Oid oldNspOid;
533 : :
7531 tgl@sss.pgh.pa.us 534 [ + + + + : 199 : switch (stmt->objectType)
- ]
535 : : {
5514 536 : 6 : case OBJECT_EXTENSION:
1648 peter@eisentraut.org 537 [ + - ]: 6 : address = AlterExtensionNamespace(strVal(stmt->object), stmt->newschema,
538 : : oldSchemaAddr ? &oldNspOid : NULL);
4030 alvherre@alvh.no-ip. 539 : 4 : break;
540 : :
4807 541 : 52 : case OBJECT_FOREIGN_TABLE:
542 : : case OBJECT_SEQUENCE:
543 : : case OBJECT_TABLE:
544 : : case OBJECT_VIEW:
545 : : case OBJECT_MATVIEW:
4030 546 [ + - ]: 52 : address = AlterTableNamespace(stmt,
547 : : oldSchemaAddr ? &oldNspOid : NULL);
548 : 48 : break;
549 : :
4917 550 : 9 : case OBJECT_DOMAIN:
551 : : case OBJECT_TYPE:
3410 peter_e@gmx.net 552 [ + - ]: 9 : address = AlterTypeNamespace(castNode(List, stmt->object), stmt->newschema,
553 : : stmt->objectType,
554 : : oldSchemaAddr ? &oldNspOid : NULL);
4030 alvherre@alvh.no-ip. 555 : 9 : break;
556 : :
557 : : /* generic code path */
4807 558 : 132 : 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 : :
4917 577 : 132 : address = get_object_address(stmt->objectType,
578 : : stmt->object,
579 : : NULL,
580 : : AccessExclusiveLock,
581 : : false);
582 : 129 : classId = address.classId;
2610 andres@anarazel.de 583 : 129 : catalog = table_open(classId, RowExclusiveLock);
4917 alvherre@alvh.no-ip. 584 : 129 : nspOid = LookupCreationNamespace(stmt->newschema);
585 : :
4030 586 : 129 : oldNspOid = AlterObjectNamespace_internal(catalog, address.objectId,
587 : : nspOid);
2610 andres@anarazel.de 588 : 72 : table_close(catalog, RowExclusiveLock);
589 : : }
7531 tgl@sss.pgh.pa.us 590 : 72 : break;
591 : :
7531 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 : :
4030 alvherre@alvh.no-ip. 598 [ + - ]:CBC 133 : if (oldSchemaAddr)
599 : 133 : ObjectAddressSet(*oldSchemaAddr, NamespaceRelationId, oldNspOid);
600 : :
601 : 133 : 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
4883 619 : 21 : AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
620 : : ObjectAddresses *objsMoved)
621 : : {
5514 tgl@sss.pgh.pa.us 622 : 21 : Oid oldNspOid = InvalidOid;
623 : :
719 peter@eisentraut.org 624 [ + + + + ]: 21 : switch (classId)
625 : : {
626 : 1 : case RelationRelationId:
627 : : {
628 : : Relation rel;
629 : :
5453 bruce@momjian.us 630 : 1 : rel = relation_open(objid, AccessExclusiveLock);
631 : 1 : oldNspOid = RelationGetNamespace(rel);
632 : :
4883 alvherre@alvh.no-ip. 633 : 1 : AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
634 : :
5453 bruce@momjian.us 635 : 1 : relation_close(rel, NoLock);
636 : 1 : break;
637 : : }
638 : :
719 peter@eisentraut.org 639 : 8 : case TypeRelationId:
675 tgl@sss.pgh.pa.us 640 : 8 : oldNspOid = AlterTypeNamespace_oid(objid, nspOid, true, objsMoved);
5514 641 : 8 : break;
642 : :
719 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 : :
2610 andres@anarazel.de 657 : 11 : catalog = table_open(classId, RowExclusiveLock);
658 : :
4917 alvherre@alvh.no-ip. 659 : 11 : oldNspOid = AlterObjectNamespace_internal(catalog, objid,
660 : : nspOid);
661 : :
2610 andres@anarazel.de 662 : 11 : table_close(catalog, RowExclusiveLock);
663 : : }
5514 tgl@sss.pgh.pa.us 664 : 11 : break;
665 : :
719 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 : :
5514 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
4917 alvherre@alvh.no-ip. 686 : 140 : AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
687 : : {
5514 tgl@sss.pgh.pa.us 688 : 140 : Oid classId = RelationGetRelid(rel);
25 michael@paquier.xyz 689 :GNC 140 : SysCacheIdentifier oidCacheId = get_object_catcache_oid(classId);
690 : 140 : SysCacheIdentifier nameCacheId = get_object_catcache_name(classId);
4917 alvherre@alvh.no-ip. 691 :CBC 140 : AttrNumber Anum_name = get_object_attnum_name(classId);
692 : 140 : AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
693 : 140 : 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 : :
5514 tgl@sss.pgh.pa.us 704 : 140 : tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid));
5588 rhaas@postgresql.org 705 [ - + ]: 140 : if (!HeapTupleIsValid(tup)) /* should not happen */
5514 tgl@sss.pgh.pa.us 706 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
707 : : objid, RelationGetRelationName(rel));
708 : :
5514 tgl@sss.pgh.pa.us 709 :CBC 140 : name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
710 [ - + ]: 140 : Assert(!isnull);
4917 alvherre@alvh.no-ip. 711 : 140 : namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel),
712 : : &isnull);
5514 tgl@sss.pgh.pa.us 713 [ - + ]: 140 : Assert(!isnull);
5588 rhaas@postgresql.org 714 : 140 : 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 : : */
3769 720 [ + + ]: 140 : if (oldNspOid == nspOid)
721 : : {
722 [ - + ]: 3 : InvokeObjectPostAlterHook(classId, objid, 0);
723 : 3 : return oldNspOid;
724 : : }
725 : :
726 : : /* Check basic namespace related issues */
727 : 137 : CheckSetNamespace(oldNspOid, nspOid);
728 : :
729 : : /* Permission checks ... superusers can always do it */
5588 730 [ + + ]: 137 : if (!superuser())
731 : : {
732 : : Datum owner;
733 : : Oid ownerId;
734 : : AclResult aclresult;
735 : :
736 : : /* Fail if object does not have an explicit owner */
5514 tgl@sss.pgh.pa.us 737 [ - + ]: 78 : if (Anum_owner <= 0)
5588 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 */
5514 tgl@sss.pgh.pa.us 744 :CBC 78 : owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull);
745 [ - + ]: 78 : Assert(!isnull);
5588 rhaas@postgresql.org 746 : 78 : ownerId = DatumGetObjectId(owner);
747 : :
748 [ + + ]: 78 : if (!has_privs_of_role(GetUserId(), ownerId))
2322 tgl@sss.pgh.pa.us 749 : 27 : aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objid),
5588 rhaas@postgresql.org 750 : 27 : NameStr(*(DatumGetName(name))));
751 : :
752 : : /* User must have CREATE privilege on new namespace */
1218 peter@eisentraut.org 753 : 51 : aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
5588 rhaas@postgresql.org 754 [ - + ]: 51 : if (aclresult != ACLCHECK_OK)
3025 peter_e@gmx.net 755 :UBC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
5514 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 : : */
4807 alvherre@alvh.no-ip. 764 [ + + ]:CBC 110 : if (classId == ProcedureRelationId)
765 : : {
4801 766 : 32 : Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tup);
767 : :
4807 768 : 32 : IsThereFunctionInNamespace(NameStr(proc->proname), proc->pronargs,
769 : : &proc->proargtypes, nspOid);
770 : : }
771 [ + + ]: 78 : else if (classId == CollationRelationId)
772 : : {
4801 773 : 3 : Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(tup);
774 : :
775 : 3 : IsThereCollationInNamespace(NameStr(coll->collname), nspOid);
776 : : }
777 [ + + ]: 75 : else if (classId == OperatorClassRelationId)
778 : : {
779 : 9 : Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(tup);
780 : :
781 : 9 : IsThereOpClassInNamespace(NameStr(opc->opcname),
782 : : opc->opcmethod, nspOid);
783 : : }
784 [ + + ]: 66 : else if (classId == OperatorFamilyRelationId)
785 : : {
786 : 9 : Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(tup);
787 : :
788 : 9 : IsThereOpFamilyInNamespace(NameStr(opf->opfname),
789 : : opf->opfmethod, nspOid);
790 : : }
4807 791 [ + + + + ]: 108 : else if (nameCacheId >= 0 &&
792 : 51 : SearchSysCacheExists2(nameCacheId, name,
793 : : ObjectIdGetDatum(nspOid)))
794 : 18 : report_namespace_conflict(classId,
795 : 18 : NameStr(*(DatumGetName(name))),
796 : : nspOid);
797 : :
798 : : /* Build modified tuple */
5514 tgl@sss.pgh.pa.us 799 : 80 : values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
800 : 80 : nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
801 : 80 : replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
802 : 80 : values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid);
5588 rhaas@postgresql.org 803 : 80 : replaces[Anum_namespace - 1] = true;
5514 tgl@sss.pgh.pa.us 804 : 80 : newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
805 : : values, nulls, replaces);
806 : :
807 : : /* Perform actual update */
3330 alvherre@alvh.no-ip. 808 : 80 : CatalogTupleUpdate(rel, &tup->t_self, newtup);
809 : :
810 : : /* Release memory */
5588 rhaas@postgresql.org 811 : 80 : pfree(values);
812 : 80 : pfree(nulls);
813 : 80 : pfree(replaces);
814 : :
815 : : /* update dependency to point to the new schema */
979 michael@paquier.xyz 816 [ - + ]: 80 : if (changeDependencyFor(classId, objid,
817 : : NamespaceRelationId, oldNspOid, nspOid) != 1)
979 michael@paquier.xyz 818 [ # # ]:UBC 0 : elog(ERROR, "could not change schema dependency for object %u",
819 : : objid);
820 : :
4746 rhaas@postgresql.org 821 [ - + ]:CBC 80 : InvokeObjectPostAlterHook(classId, objid, 0);
822 : :
5514 tgl@sss.pgh.pa.us 823 : 80 : 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
7933 831 : 842 : ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
832 : : {
4024 alvherre@alvh.no-ip. 833 : 842 : Oid newowner = get_rolespec_oid(stmt->newowner, false);
834 : :
7933 tgl@sss.pgh.pa.us 835 [ + + + + : 836 : switch (stmt->objectType)
+ + + + +
- ]
836 : : {
837 : 45 : case OBJECT_DATABASE:
1648 peter@eisentraut.org 838 : 45 : return AlterDatabaseOwner(strVal(stmt->object), newowner);
839 : :
7933 tgl@sss.pgh.pa.us 840 : 32 : case OBJECT_SCHEMA:
1648 peter@eisentraut.org 841 : 32 : return AlterSchemaOwner(strVal(stmt->object), newowner);
842 : :
7933 tgl@sss.pgh.pa.us 843 : 66 : case OBJECT_TYPE:
844 : : case OBJECT_DOMAIN: /* same as TYPE */
3410 peter_e@gmx.net 845 : 66 : return AlterTypeOwner(castNode(List, stmt->object), newowner, stmt->objectType);
846 : : break;
847 : :
6295 848 : 10 : case OBJECT_FDW:
1648 peter@eisentraut.org 849 : 10 : return AlterForeignDataWrapperOwner(strVal(stmt->object),
850 : : newowner);
851 : :
6295 peter_e@gmx.net 852 : 34 : case OBJECT_FOREIGN_SERVER:
1648 peter@eisentraut.org 853 : 34 : return AlterForeignServerOwner(strVal(stmt->object),
854 : : newowner);
855 : :
4988 rhaas@postgresql.org 856 : 7 : case OBJECT_EVENT_TRIGGER:
1648 peter@eisentraut.org 857 : 7 : return AlterEventTriggerOwner(strVal(stmt->object),
858 : : newowner);
859 : :
3342 peter_e@gmx.net 860 : 18 : case OBJECT_PUBLICATION:
1648 peter@eisentraut.org 861 : 18 : return AlterPublicationOwner(strVal(stmt->object),
862 : : newowner);
863 : :
3342 peter_e@gmx.net 864 : 9 : case OBJECT_SUBSCRIPTION:
1648 peter@eisentraut.org 865 : 9 : return AlterSubscriptionOwner(strVal(stmt->object),
866 : : newowner);
867 : :
868 : : /* Generic cases */
4911 alvherre@alvh.no-ip. 869 : 615 : case OBJECT_AGGREGATE:
870 : : case OBJECT_COLLATION:
871 : : case OBJECT_CONVERSION:
872 : : case OBJECT_FUNCTION:
873 : : case OBJECT_LANGUAGE:
874 : : case OBJECT_LARGEOBJECT:
875 : : case OBJECT_OPERATOR:
876 : : case OBJECT_OPCLASS:
877 : : case OBJECT_OPFAMILY:
878 : : case OBJECT_PROCEDURE:
879 : : case OBJECT_ROUTINE:
880 : : case OBJECT_STATISTIC_EXT:
881 : : case OBJECT_TABLESPACE:
882 : : case OBJECT_TSDICTIONARY:
883 : : case OBJECT_TSCONFIGURATION:
884 : : {
885 : : ObjectAddress address;
886 : :
887 : 615 : address = get_object_address(stmt->objectType,
888 : : stmt->object,
889 : : NULL,
890 : : AccessExclusiveLock,
891 : : false);
892 : :
821 tgl@sss.pgh.pa.us 893 : 611 : AlterObjectOwner_internal(address.classId, address.objectId,
894 : : newowner);
895 : :
4030 alvherre@alvh.no-ip. 896 : 524 : return address;
897 : : }
898 : : break;
899 : :
7933 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
821 tgl@sss.pgh.pa.us 920 :CBC 621 : AlterObjectOwner_internal(Oid classId, Oid objectId, Oid new_ownerId)
921 : : {
922 : : /* For large objects, the catalog to modify is pg_largeobject_metadata */
923 [ + + ]: 621 : Oid catalogId = (classId == LargeObjectRelationId) ? LargeObjectMetadataRelationId : classId;
924 : 621 : AttrNumber Anum_oid = get_object_attnum_oid(catalogId);
925 : 621 : AttrNumber Anum_owner = get_object_attnum_owner(catalogId);
926 : 621 : AttrNumber Anum_namespace = get_object_attnum_namespace(catalogId);
927 : 621 : AttrNumber Anum_acl = get_object_attnum_acl(catalogId);
928 : 621 : AttrNumber Anum_name = get_object_attnum_name(catalogId);
929 : : Relation rel;
930 : : HeapTuple oldtup;
931 : : Datum datum;
932 : : bool isnull;
933 : : Oid old_ownerId;
4911 alvherre@alvh.no-ip. 934 : 621 : Oid namespaceId = InvalidOid;
935 : :
821 tgl@sss.pgh.pa.us 936 : 621 : rel = table_open(catalogId, RowExclusiveLock);
937 : :
938 : : /* Search tuple and lock it. */
939 : : oldtup =
442 noah@leadboat.com 940 : 621 : get_catalog_object_by_oid_extended(rel, Anum_oid, objectId, true);
4911 alvherre@alvh.no-ip. 941 [ - + ]: 621 : if (oldtup == NULL)
4911 alvherre@alvh.no-ip. 942 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
943 : : objectId, RelationGetRelationName(rel));
944 : :
4911 alvherre@alvh.no-ip. 945 :CBC 621 : datum = heap_getattr(oldtup, Anum_owner,
946 : : RelationGetDescr(rel), &isnull);
947 [ - + ]: 621 : Assert(!isnull);
948 : 621 : old_ownerId = DatumGetObjectId(datum);
949 : :
950 [ + + ]: 621 : if (Anum_namespace != InvalidAttrNumber)
951 : : {
952 : 534 : datum = heap_getattr(oldtup, Anum_namespace,
953 : : RelationGetDescr(rel), &isnull);
954 [ - + ]: 534 : Assert(!isnull);
955 : 534 : namespaceId = DatumGetObjectId(datum);
956 : : }
957 : :
958 [ + + ]: 621 : 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 [ + + ]: 194 : if (!superuser())
968 : : {
969 : : /* must be owner */
970 [ + + ]: 117 : if (!has_privs_of_role(GetUserId(), old_ownerId))
971 : : {
972 : : char *objname;
973 : : char namebuf[NAMEDATALEN];
974 : :
975 [ + - ]: 30 : if (Anum_name != InvalidAttrNumber)
976 : : {
977 : 30 : datum = heap_getattr(oldtup, Anum_name,
978 : : RelationGetDescr(rel), &isnull);
979 [ - + ]: 30 : Assert(!isnull);
980 : 30 : objname = NameStr(*DatumGetName(datum));
981 : : }
982 : : else
983 : : {
2672 andres@anarazel.de 984 :UBC 0 : snprintf(namebuf, sizeof(namebuf), "%u", objectId);
4911 alvherre@alvh.no-ip. 985 : 0 : objname = namebuf;
986 : : }
821 tgl@sss.pgh.pa.us 987 :CBC 30 : aclcheck_error(ACLCHECK_NOT_OWNER,
988 : : get_object_type(catalogId, objectId),
989 : : objname);
990 : : }
991 : : /* Must be able to become new owner */
1213 rhaas@postgresql.org 992 : 87 : check_can_set_role(GetUserId(), new_ownerId);
993 : :
994 : : /* New owner must have CREATE privilege on namespace */
4911 alvherre@alvh.no-ip. 995 [ + + ]: 30 : if (OidIsValid(namespaceId))
996 : : {
997 : : AclResult aclresult;
998 : :
1218 peter@eisentraut.org 999 : 27 : aclresult = object_aclcheck(NamespaceRelationId, namespaceId, new_ownerId,
1000 : : ACL_CREATE);
4911 alvherre@alvh.no-ip. 1001 [ - + ]: 27 : if (aclresult != ACLCHECK_OK)
3025 peter_e@gmx.net 1002 :UBC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
4911 alvherre@alvh.no-ip. 1003 : 0 : get_namespace_name(namespaceId));
1004 : : }
1005 : : }
1006 : :
1007 : : /* Build a modified tuple */
4911 alvherre@alvh.no-ip. 1008 :CBC 107 : nattrs = RelationGetNumberOfAttributes(rel);
1009 : 107 : values = palloc0(nattrs * sizeof(Datum));
1010 : 107 : nulls = palloc0(nattrs * sizeof(bool));
1011 : 107 : replaces = palloc0(nattrs * sizeof(bool));
1012 : 107 : values[Anum_owner - 1] = ObjectIdGetDatum(new_ownerId);
1013 : 107 : 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 [ + + ]: 107 : if (Anum_acl != InvalidAttrNumber)
1020 : : {
1021 : 58 : datum = heap_getattr(oldtup,
1022 : : Anum_acl, RelationGetDescr(rel), &isnull);
1023 [ + + ]: 58 : if (!isnull)
1024 : : {
1025 : : Acl *newAcl;
1026 : :
1027 : 4 : newAcl = aclnewowner(DatumGetAclP(datum),
1028 : : old_ownerId, new_ownerId);
1029 : 4 : values[Anum_acl - 1] = PointerGetDatum(newAcl);
1030 : 4 : replaces[Anum_acl - 1] = true;
1031 : : }
1032 : : }
1033 : :
1034 : 107 : newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
1035 : : values, nulls, replaces);
1036 : :
1037 : : /* Perform actual update */
3330 1038 : 107 : CatalogTupleUpdate(rel, &newtup->t_self, newtup);
1039 : :
442 noah@leadboat.com 1040 : 107 : UnlockTuple(rel, &oldtup->t_self, InplaceUpdateTupleLock);
1041 : :
1042 : : /* Update owner dependency reference */
2672 andres@anarazel.de 1043 : 107 : changeDependencyOnOwner(classId, objectId, new_ownerId);
1044 : :
1045 : : /* Release memory */
4911 alvherre@alvh.no-ip. 1046 : 107 : pfree(values);
1047 : 107 : pfree(nulls);
1048 : 107 : pfree(replaces);
1049 : : }
1050 : : else
442 noah@leadboat.com 1051 : 427 : UnlockTuple(rel, &oldtup->t_self, InplaceUpdateTupleLock);
1052 : :
1053 : : /* Note the post-alter hook gets classId not catalogId */
4746 rhaas@postgresql.org 1054 [ - + ]: 534 : InvokeObjectPostAlterHook(classId, objectId, 0);
1055 : :
821 tgl@sss.pgh.pa.us 1056 : 534 : table_close(rel, RowExclusiveLock);
4911 alvherre@alvh.no-ip. 1057 : 534 : }
|