1 /*-------------------------------------------------------------------------
2 *
3 * alter.c
4 * Drivers for generic alter commands
5 *
6 * Portions Copyright (c) 1996-2016, 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/sysattr.h"
19 #include "catalog/dependency.h"
20 #include "catalog/indexing.h"
21 #include "catalog/namespace.h"
22 #include "catalog/objectaccess.h"
23 #include "catalog/pg_collation.h"
24 #include "catalog/pg_conversion.h"
25 #include "catalog/pg_event_trigger.h"
26 #include "catalog/pg_foreign_data_wrapper.h"
27 #include "catalog/pg_foreign_server.h"
28 #include "catalog/pg_language.h"
29 #include "catalog/pg_largeobject.h"
30 #include "catalog/pg_largeobject_metadata.h"
31 #include "catalog/pg_namespace.h"
32 #include "catalog/pg_opclass.h"
33 #include "catalog/pg_opfamily.h"
34 #include "catalog/pg_proc.h"
35 #include "catalog/pg_ts_config.h"
36 #include "catalog/pg_ts_dict.h"
37 #include "catalog/pg_ts_parser.h"
38 #include "catalog/pg_ts_template.h"
39 #include "commands/alter.h"
40 #include "commands/collationcmds.h"
41 #include "commands/conversioncmds.h"
42 #include "commands/dbcommands.h"
43 #include "commands/defrem.h"
44 #include "commands/event_trigger.h"
45 #include "commands/extension.h"
46 #include "commands/policy.h"
47 #include "commands/proclang.h"
48 #include "commands/schemacmds.h"
49 #include "commands/tablecmds.h"
50 #include "commands/tablespace.h"
51 #include "commands/trigger.h"
52 #include "commands/typecmds.h"
53 #include "commands/user.h"
54 #include "parser/parse_func.h"
55 #include "miscadmin.h"
56 #include "rewrite/rewriteDefine.h"
57 #include "tcop/utility.h"
58 #include "utils/builtins.h"
59 #include "utils/fmgroids.h"
60 #include "utils/lsyscache.h"
61 #include "utils/rel.h"
62 #include "utils/syscache.h"
63 #include "utils/tqual.h"
64
65
66 static Oid AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid);
67
68 /*
69 * Raise an error to the effect that an object of the given name is already
70 * present in the given namespace.
71 */
72 static void
report_name_conflict(Oid classId,const char * name)73 report_name_conflict(Oid classId, const char *name)
74 {
75 char *msgfmt;
76
77 switch (classId)
78 {
79 case EventTriggerRelationId:
80 msgfmt = gettext_noop("event trigger \"%s\" already exists");
81 break;
82 case ForeignDataWrapperRelationId:
83 msgfmt = gettext_noop("foreign-data wrapper \"%s\" already exists");
84 break;
85 case ForeignServerRelationId:
86 msgfmt = gettext_noop("server \"%s\" already exists");
87 break;
88 case LanguageRelationId:
89 msgfmt = gettext_noop("language \"%s\" already exists");
90 break;
91 default:
92 elog(ERROR, "unsupported object class %u", classId);
93 break;
94 }
95
96 ereport(ERROR,
97 (errcode(ERRCODE_DUPLICATE_OBJECT),
98 errmsg(msgfmt, name)));
99 }
100
101 static void
report_namespace_conflict(Oid classId,const char * name,Oid nspOid)102 report_namespace_conflict(Oid classId, const char *name, Oid nspOid)
103 {
104 char *msgfmt;
105
106 Assert(OidIsValid(nspOid));
107
108 switch (classId)
109 {
110 case ConversionRelationId:
111 Assert(OidIsValid(nspOid));
112 msgfmt = gettext_noop("conversion \"%s\" already exists in schema \"%s\"");
113 break;
114 case TSParserRelationId:
115 Assert(OidIsValid(nspOid));
116 msgfmt = gettext_noop("text search parser \"%s\" already exists in schema \"%s\"");
117 break;
118 case TSDictionaryRelationId:
119 Assert(OidIsValid(nspOid));
120 msgfmt = gettext_noop("text search dictionary \"%s\" already exists in schema \"%s\"");
121 break;
122 case TSTemplateRelationId:
123 Assert(OidIsValid(nspOid));
124 msgfmt = gettext_noop("text search template \"%s\" already exists in schema \"%s\"");
125 break;
126 case TSConfigRelationId:
127 Assert(OidIsValid(nspOid));
128 msgfmt = gettext_noop("text search configuration \"%s\" already exists in schema \"%s\"");
129 break;
130 default:
131 elog(ERROR, "unsupported object class %u", classId);
132 break;
133 }
134
135 ereport(ERROR,
136 (errcode(ERRCODE_DUPLICATE_OBJECT),
137 errmsg(msgfmt, name, get_namespace_name(nspOid))));
138 }
139
140 /*
141 * AlterObjectRename_internal
142 *
143 * Generic function to rename the given object, for simple cases (won't
144 * work for tables, nor other cases where we need to do more than change
145 * the name column of a single catalog entry).
146 *
147 * rel: catalog relation containing object (RowExclusiveLock'd by caller)
148 * objectId: OID of object to be renamed
149 * new_name: CString representation of new name
150 */
151 static void
AlterObjectRename_internal(Relation rel,Oid objectId,const char * new_name)152 AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
153 {
154 Oid classId = RelationGetRelid(rel);
155 int oidCacheId = get_object_catcache_oid(classId);
156 int nameCacheId = get_object_catcache_name(classId);
157 AttrNumber Anum_name = get_object_attnum_name(classId);
158 AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
159 AttrNumber Anum_owner = get_object_attnum_owner(classId);
160 AclObjectKind acl_kind = get_object_aclkind(classId);
161 HeapTuple oldtup;
162 HeapTuple newtup;
163 Datum datum;
164 bool isnull;
165 Oid namespaceId;
166 Oid ownerId;
167 char *old_name;
168 AclResult aclresult;
169 Datum *values;
170 bool *nulls;
171 bool *replaces;
172 NameData nameattrdata;
173
174 oldtup = SearchSysCache1(oidCacheId, ObjectIdGetDatum(objectId));
175 if (!HeapTupleIsValid(oldtup))
176 elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
177 objectId, RelationGetRelationName(rel));
178
179 datum = heap_getattr(oldtup, Anum_name,
180 RelationGetDescr(rel), &isnull);
181 Assert(!isnull);
182 old_name = NameStr(*(DatumGetName(datum)));
183
184 /* Get OID of namespace */
185 if (Anum_namespace > 0)
186 {
187 datum = heap_getattr(oldtup, Anum_namespace,
188 RelationGetDescr(rel), &isnull);
189 Assert(!isnull);
190 namespaceId = DatumGetObjectId(datum);
191 }
192 else
193 namespaceId = InvalidOid;
194
195 /* Permission checks ... superusers can always do it */
196 if (!superuser())
197 {
198 /* Fail if object does not have an explicit owner */
199 if (Anum_owner <= 0)
200 ereport(ERROR,
201 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
202 (errmsg("must be superuser to rename %s",
203 getObjectDescriptionOids(classId, objectId)))));
204
205 /* Otherwise, must be owner of the existing object */
206 datum = heap_getattr(oldtup, Anum_owner,
207 RelationGetDescr(rel), &isnull);
208 Assert(!isnull);
209 ownerId = DatumGetObjectId(datum);
210
211 if (!has_privs_of_role(GetUserId(), DatumGetObjectId(ownerId)))
212 aclcheck_error(ACLCHECK_NOT_OWNER, acl_kind, old_name);
213
214 /* User must have CREATE privilege on the namespace */
215 if (OidIsValid(namespaceId))
216 {
217 aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
218 ACL_CREATE);
219 if (aclresult != ACLCHECK_OK)
220 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
221 get_namespace_name(namespaceId));
222 }
223 }
224
225 /*
226 * Check for duplicate name (more friendly than unique-index failure).
227 * Since this is just a friendliness check, we can just skip it in cases
228 * where there isn't suitable support.
229 */
230 if (classId == ProcedureRelationId)
231 {
232 Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(oldtup);
233
234 IsThereFunctionInNamespace(new_name, proc->pronargs,
235 &proc->proargtypes, proc->pronamespace);
236 }
237 else if (classId == CollationRelationId)
238 {
239 Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(oldtup);
240
241 IsThereCollationInNamespace(new_name, coll->collnamespace);
242 }
243 else if (classId == OperatorClassRelationId)
244 {
245 Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(oldtup);
246
247 IsThereOpClassInNamespace(new_name, opc->opcmethod,
248 opc->opcnamespace);
249 }
250 else if (classId == OperatorFamilyRelationId)
251 {
252 Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(oldtup);
253
254 IsThereOpFamilyInNamespace(new_name, opf->opfmethod,
255 opf->opfnamespace);
256 }
257 else if (nameCacheId >= 0)
258 {
259 if (OidIsValid(namespaceId))
260 {
261 if (SearchSysCacheExists2(nameCacheId,
262 CStringGetDatum(new_name),
263 ObjectIdGetDatum(namespaceId)))
264 report_namespace_conflict(classId, new_name, namespaceId);
265 }
266 else
267 {
268 if (SearchSysCacheExists1(nameCacheId,
269 CStringGetDatum(new_name)))
270 report_name_conflict(classId, new_name);
271 }
272 }
273
274 /* Build modified tuple */
275 values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
276 nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
277 replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
278 namestrcpy(&nameattrdata, new_name);
279 values[Anum_name - 1] = NameGetDatum(&nameattrdata);
280 replaces[Anum_name - 1] = true;
281 newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
282 values, nulls, replaces);
283
284 /* Perform actual update */
285 simple_heap_update(rel, &oldtup->t_self, newtup);
286 CatalogUpdateIndexes(rel, newtup);
287
288 InvokeObjectPostAlterHook(classId, objectId, 0);
289
290 /* Release memory */
291 pfree(values);
292 pfree(nulls);
293 pfree(replaces);
294 heap_freetuple(newtup);
295
296 ReleaseSysCache(oldtup);
297 }
298
299 /*
300 * Executes an ALTER OBJECT / RENAME TO statement. Based on the object
301 * type, the function appropriate to that type is executed.
302 *
303 * Return value is the address of the renamed object.
304 */
305 ObjectAddress
ExecRenameStmt(RenameStmt * stmt)306 ExecRenameStmt(RenameStmt *stmt)
307 {
308 switch (stmt->renameType)
309 {
310 case OBJECT_TABCONSTRAINT:
311 case OBJECT_DOMCONSTRAINT:
312 return RenameConstraint(stmt);
313
314 case OBJECT_DATABASE:
315 return RenameDatabase(stmt->subname, stmt->newname);
316
317 case OBJECT_ROLE:
318 return RenameRole(stmt->subname, stmt->newname);
319
320 case OBJECT_SCHEMA:
321 return RenameSchema(stmt->subname, stmt->newname);
322
323 case OBJECT_TABLESPACE:
324 return RenameTableSpace(stmt->subname, stmt->newname);
325
326 case OBJECT_TABLE:
327 case OBJECT_SEQUENCE:
328 case OBJECT_VIEW:
329 case OBJECT_MATVIEW:
330 case OBJECT_INDEX:
331 case OBJECT_FOREIGN_TABLE:
332 return RenameRelation(stmt);
333
334 case OBJECT_COLUMN:
335 case OBJECT_ATTRIBUTE:
336 return renameatt(stmt);
337
338 case OBJECT_RULE:
339 return RenameRewriteRule(stmt->relation, stmt->subname,
340 stmt->newname);
341
342 case OBJECT_TRIGGER:
343 return renametrig(stmt);
344
345 case OBJECT_POLICY:
346 return rename_policy(stmt);
347
348 case OBJECT_DOMAIN:
349 case OBJECT_TYPE:
350 return RenameType(stmt);
351
352 case OBJECT_AGGREGATE:
353 case OBJECT_COLLATION:
354 case OBJECT_CONVERSION:
355 case OBJECT_EVENT_TRIGGER:
356 case OBJECT_FDW:
357 case OBJECT_FOREIGN_SERVER:
358 case OBJECT_FUNCTION:
359 case OBJECT_OPCLASS:
360 case OBJECT_OPFAMILY:
361 case OBJECT_LANGUAGE:
362 case OBJECT_TSCONFIGURATION:
363 case OBJECT_TSDICTIONARY:
364 case OBJECT_TSPARSER:
365 case OBJECT_TSTEMPLATE:
366 {
367 ObjectAddress address;
368 Relation catalog;
369 Relation relation;
370
371 address = get_object_address(stmt->renameType,
372 stmt->object, stmt->objarg,
373 &relation,
374 AccessExclusiveLock, false);
375 Assert(relation == NULL);
376
377 catalog = heap_open(address.classId, RowExclusiveLock);
378 AlterObjectRename_internal(catalog,
379 address.objectId,
380 stmt->newname);
381 heap_close(catalog, RowExclusiveLock);
382
383 return address;
384 }
385
386 default:
387 elog(ERROR, "unrecognized rename stmt type: %d",
388 (int) stmt->renameType);
389 return InvalidObjectAddress; /* keep compiler happy */
390 }
391 }
392
393 /*
394 * Executes an ALTER OBJECT / DEPENDS ON [EXTENSION] statement.
395 *
396 * Return value is the address of the altered object. refAddress is an output
397 * argument which, if not null, receives the address of the object that the
398 * altered object now depends on.
399 */
400 ObjectAddress
ExecAlterObjectDependsStmt(AlterObjectDependsStmt * stmt,ObjectAddress * refAddress)401 ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddress)
402 {
403 ObjectAddress address;
404 ObjectAddress refAddr;
405 Relation rel;
406 List *currexts;
407
408 address =
409 get_object_address_rv(stmt->objectType, stmt->relation, stmt->objname,
410 stmt->objargs, &rel, AccessExclusiveLock, false);
411
412 /*
413 * Verify that the user is entitled to run the command.
414 *
415 * We don't check any privileges on the extension, because that's not
416 * needed. The object owner is stipulating, by running this command, that
417 * the extension owner can drop the object whenever they feel like it,
418 * which is not considered a problem.
419 */
420 check_object_ownership(GetUserId(),
421 stmt->objectType, address,
422 stmt->objname, stmt->objargs,
423 rel);
424
425 /*
426 * If a relation was involved, it would have been opened and locked. We
427 * don't need the relation here, but we'll retain the lock until commit.
428 */
429 if (rel)
430 heap_close(rel, NoLock);
431
432 refAddr = get_object_address(OBJECT_EXTENSION, list_make1(stmt->extname),
433 NULL, &rel, AccessExclusiveLock, false);
434 Assert(rel == NULL);
435 if (refAddress)
436 *refAddress = refAddr;
437
438 /* Avoid duplicates */
439 currexts = getAutoExtensionsOfObject(address.classId,
440 address.objectId);
441 if (!list_member_oid(currexts, refAddr.objectId))
442 recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
443
444 return address;
445 }
446
447 /*
448 * Executes an ALTER OBJECT / SET SCHEMA statement. Based on the object
449 * type, the function appropriate to that type is executed.
450 *
451 * Return value is that of the altered object.
452 *
453 * oldSchemaAddr is an output argument which, if not NULL, is set to the object
454 * address of the original schema.
455 */
456 ObjectAddress
ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt * stmt,ObjectAddress * oldSchemaAddr)457 ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
458 ObjectAddress *oldSchemaAddr)
459 {
460 ObjectAddress address;
461 Oid oldNspOid;
462
463 switch (stmt->objectType)
464 {
465 case OBJECT_EXTENSION:
466 address = AlterExtensionNamespace(stmt->object, stmt->newschema,
467 oldSchemaAddr ? &oldNspOid : NULL);
468 break;
469
470 case OBJECT_FOREIGN_TABLE:
471 case OBJECT_SEQUENCE:
472 case OBJECT_TABLE:
473 case OBJECT_VIEW:
474 case OBJECT_MATVIEW:
475 address = AlterTableNamespace(stmt,
476 oldSchemaAddr ? &oldNspOid : NULL);
477 break;
478
479 case OBJECT_DOMAIN:
480 case OBJECT_TYPE:
481 address = AlterTypeNamespace(stmt->object, stmt->newschema,
482 stmt->objectType,
483 oldSchemaAddr ? &oldNspOid : NULL);
484 break;
485
486 /* generic code path */
487 case OBJECT_AGGREGATE:
488 case OBJECT_COLLATION:
489 case OBJECT_CONVERSION:
490 case OBJECT_FUNCTION:
491 case OBJECT_OPERATOR:
492 case OBJECT_OPCLASS:
493 case OBJECT_OPFAMILY:
494 case OBJECT_TSCONFIGURATION:
495 case OBJECT_TSDICTIONARY:
496 case OBJECT_TSPARSER:
497 case OBJECT_TSTEMPLATE:
498 {
499 Relation catalog;
500 Relation relation;
501 Oid classId;
502 Oid nspOid;
503
504 address = get_object_address(stmt->objectType,
505 stmt->object,
506 stmt->objarg,
507 &relation,
508 AccessExclusiveLock,
509 false);
510 Assert(relation == NULL);
511 classId = address.classId;
512 catalog = heap_open(classId, RowExclusiveLock);
513 nspOid = LookupCreationNamespace(stmt->newschema);
514
515 oldNspOid = AlterObjectNamespace_internal(catalog, address.objectId,
516 nspOid);
517 heap_close(catalog, RowExclusiveLock);
518 }
519 break;
520
521 default:
522 elog(ERROR, "unrecognized AlterObjectSchemaStmt type: %d",
523 (int) stmt->objectType);
524 return InvalidObjectAddress; /* keep compiler happy */
525 }
526
527 if (oldSchemaAddr)
528 ObjectAddressSet(*oldSchemaAddr, NamespaceRelationId, oldNspOid);
529
530 return address;
531 }
532
533 /*
534 * Change an object's namespace given its classOid and object Oid.
535 *
536 * Objects that don't have a namespace should be ignored.
537 *
538 * This function is currently used only by ALTER EXTENSION SET SCHEMA,
539 * so it only needs to cover object types that can be members of an
540 * extension, and it doesn't have to deal with certain special cases
541 * such as not wanting to process array types --- those should never
542 * be direct members of an extension anyway.
543 *
544 * Returns the OID of the object's previous namespace, or InvalidOid if
545 * object doesn't have a schema.
546 */
547 Oid
AlterObjectNamespace_oid(Oid classId,Oid objid,Oid nspOid,ObjectAddresses * objsMoved)548 AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
549 ObjectAddresses *objsMoved)
550 {
551 Oid oldNspOid = InvalidOid;
552 ObjectAddress dep;
553
554 dep.classId = classId;
555 dep.objectId = objid;
556 dep.objectSubId = 0;
557
558 switch (getObjectClass(&dep))
559 {
560 case OCLASS_CLASS:
561 {
562 Relation rel;
563
564 rel = relation_open(objid, AccessExclusiveLock);
565 oldNspOid = RelationGetNamespace(rel);
566
567 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
568
569 relation_close(rel, NoLock);
570 break;
571 }
572
573 case OCLASS_TYPE:
574 oldNspOid = AlterTypeNamespace_oid(objid, nspOid, objsMoved);
575 break;
576
577 case OCLASS_COLLATION:
578 case OCLASS_CONVERSION:
579 case OCLASS_OPERATOR:
580 case OCLASS_OPCLASS:
581 case OCLASS_OPFAMILY:
582 case OCLASS_PROC:
583 case OCLASS_TSPARSER:
584 case OCLASS_TSDICT:
585 case OCLASS_TSTEMPLATE:
586 case OCLASS_TSCONFIG:
587 {
588 Relation catalog;
589
590 catalog = heap_open(classId, RowExclusiveLock);
591
592 oldNspOid = AlterObjectNamespace_internal(catalog, objid,
593 nspOid);
594
595 heap_close(catalog, RowExclusiveLock);
596 }
597 break;
598
599 default:
600 break;
601 }
602
603 return oldNspOid;
604 }
605
606 /*
607 * Generic function to change the namespace of a given object, for simple
608 * cases (won't work for tables, nor other cases where we need to do more
609 * than change the namespace column of a single catalog entry).
610 *
611 * rel: catalog relation containing object (RowExclusiveLock'd by caller)
612 * objid: OID of object to change the namespace of
613 * nspOid: OID of new namespace
614 *
615 * Returns the OID of the object's previous namespace.
616 */
617 static Oid
AlterObjectNamespace_internal(Relation rel,Oid objid,Oid nspOid)618 AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
619 {
620 Oid classId = RelationGetRelid(rel);
621 int oidCacheId = get_object_catcache_oid(classId);
622 int nameCacheId = get_object_catcache_name(classId);
623 AttrNumber Anum_name = get_object_attnum_name(classId);
624 AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
625 AttrNumber Anum_owner = get_object_attnum_owner(classId);
626 AclObjectKind acl_kind = get_object_aclkind(classId);
627 Oid oldNspOid;
628 Datum name,
629 namespace;
630 bool isnull;
631 HeapTuple tup,
632 newtup;
633 Datum *values;
634 bool *nulls;
635 bool *replaces;
636
637 tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid));
638 if (!HeapTupleIsValid(tup)) /* should not happen */
639 elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
640 objid, RelationGetRelationName(rel));
641
642 name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
643 Assert(!isnull);
644 namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel),
645 &isnull);
646 Assert(!isnull);
647 oldNspOid = DatumGetObjectId(namespace);
648
649 /*
650 * If the object is already in the correct namespace, we don't need to do
651 * anything except fire the object access hook.
652 */
653 if (oldNspOid == nspOid)
654 {
655 InvokeObjectPostAlterHook(classId, objid, 0);
656 return oldNspOid;
657 }
658
659 /* Check basic namespace related issues */
660 CheckSetNamespace(oldNspOid, nspOid);
661
662 /* Permission checks ... superusers can always do it */
663 if (!superuser())
664 {
665 Datum owner;
666 Oid ownerId;
667 AclResult aclresult;
668
669 /* Fail if object does not have an explicit owner */
670 if (Anum_owner <= 0)
671 ereport(ERROR,
672 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
673 (errmsg("must be superuser to set schema of %s",
674 getObjectDescriptionOids(classId, objid)))));
675
676 /* Otherwise, must be owner of the existing object */
677 owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull);
678 Assert(!isnull);
679 ownerId = DatumGetObjectId(owner);
680
681 if (!has_privs_of_role(GetUserId(), ownerId))
682 aclcheck_error(ACLCHECK_NOT_OWNER, acl_kind,
683 NameStr(*(DatumGetName(name))));
684
685 /* User must have CREATE privilege on new namespace */
686 aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE);
687 if (aclresult != ACLCHECK_OK)
688 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
689 get_namespace_name(nspOid));
690 }
691
692 /*
693 * Check for duplicate name (more friendly than unique-index failure).
694 * Since this is just a friendliness check, we can just skip it in cases
695 * where there isn't suitable support.
696 */
697 if (classId == ProcedureRelationId)
698 {
699 Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tup);
700
701 IsThereFunctionInNamespace(NameStr(proc->proname), proc->pronargs,
702 &proc->proargtypes, nspOid);
703 }
704 else if (classId == CollationRelationId)
705 {
706 Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(tup);
707
708 IsThereCollationInNamespace(NameStr(coll->collname), nspOid);
709 }
710 else if (classId == OperatorClassRelationId)
711 {
712 Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(tup);
713
714 IsThereOpClassInNamespace(NameStr(opc->opcname),
715 opc->opcmethod, nspOid);
716 }
717 else if (classId == OperatorFamilyRelationId)
718 {
719 Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(tup);
720
721 IsThereOpFamilyInNamespace(NameStr(opf->opfname),
722 opf->opfmethod, nspOid);
723 }
724 else if (nameCacheId >= 0 &&
725 SearchSysCacheExists2(nameCacheId, name,
726 ObjectIdGetDatum(nspOid)))
727 report_namespace_conflict(classId,
728 NameStr(*(DatumGetName(name))),
729 nspOid);
730
731 /* Build modified tuple */
732 values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
733 nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
734 replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
735 values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid);
736 replaces[Anum_namespace - 1] = true;
737 newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
738 values, nulls, replaces);
739
740 /* Perform actual update */
741 simple_heap_update(rel, &tup->t_self, newtup);
742 CatalogUpdateIndexes(rel, newtup);
743
744 /* Release memory */
745 pfree(values);
746 pfree(nulls);
747 pfree(replaces);
748
749 /* update dependencies to point to the new schema */
750 changeDependencyFor(classId, objid,
751 NamespaceRelationId, oldNspOid, nspOid);
752
753 InvokeObjectPostAlterHook(classId, objid, 0);
754
755 return oldNspOid;
756 }
757
758 /*
759 * Executes an ALTER OBJECT / OWNER TO statement. Based on the object
760 * type, the function appropriate to that type is executed.
761 */
762 ObjectAddress
ExecAlterOwnerStmt(AlterOwnerStmt * stmt)763 ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
764 {
765 Oid newowner = get_rolespec_oid(stmt->newowner, false);
766
767 switch (stmt->objectType)
768 {
769 case OBJECT_DATABASE:
770 return AlterDatabaseOwner(strVal(linitial(stmt->object)), newowner);
771
772 case OBJECT_SCHEMA:
773 return AlterSchemaOwner(strVal(linitial(stmt->object)), newowner);
774
775 case OBJECT_TYPE:
776 case OBJECT_DOMAIN: /* same as TYPE */
777 return AlterTypeOwner(stmt->object, newowner, stmt->objectType);
778 break;
779
780 case OBJECT_FDW:
781 return AlterForeignDataWrapperOwner(strVal(linitial(stmt->object)),
782 newowner);
783
784 case OBJECT_FOREIGN_SERVER:
785 return AlterForeignServerOwner(strVal(linitial(stmt->object)),
786 newowner);
787
788 case OBJECT_EVENT_TRIGGER:
789 return AlterEventTriggerOwner(strVal(linitial(stmt->object)),
790 newowner);
791
792 /* Generic cases */
793 case OBJECT_AGGREGATE:
794 case OBJECT_COLLATION:
795 case OBJECT_CONVERSION:
796 case OBJECT_FUNCTION:
797 case OBJECT_LANGUAGE:
798 case OBJECT_LARGEOBJECT:
799 case OBJECT_OPERATOR:
800 case OBJECT_OPCLASS:
801 case OBJECT_OPFAMILY:
802 case OBJECT_TABLESPACE:
803 case OBJECT_TSDICTIONARY:
804 case OBJECT_TSCONFIGURATION:
805 {
806 Relation catalog;
807 Relation relation;
808 Oid classId;
809 ObjectAddress address;
810
811 address = get_object_address(stmt->objectType,
812 stmt->object,
813 stmt->objarg,
814 &relation,
815 AccessExclusiveLock,
816 false);
817 Assert(relation == NULL);
818 classId = address.classId;
819
820 /*
821 * XXX - get_object_address returns Oid of pg_largeobject
822 * catalog for OBJECT_LARGEOBJECT because of historical
823 * reasons. Fix up it here.
824 */
825 if (classId == LargeObjectRelationId)
826 classId = LargeObjectMetadataRelationId;
827
828 catalog = heap_open(classId, RowExclusiveLock);
829
830 AlterObjectOwner_internal(catalog, address.objectId, newowner);
831 heap_close(catalog, RowExclusiveLock);
832
833 return address;
834 }
835 break;
836
837 default:
838 elog(ERROR, "unrecognized AlterOwnerStmt type: %d",
839 (int) stmt->objectType);
840 return InvalidObjectAddress; /* keep compiler happy */
841 }
842 }
843
844 /*
845 * Generic function to change the ownership of a given object, for simple
846 * cases (won't work for tables, nor other cases where we need to do more than
847 * change the ownership column of a single catalog entry).
848 *
849 * rel: catalog relation containing object (RowExclusiveLock'd by caller)
850 * objectId: OID of object to change the ownership of
851 * new_ownerId: OID of new object owner
852 */
853 void
AlterObjectOwner_internal(Relation rel,Oid objectId,Oid new_ownerId)854 AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId)
855 {
856 Oid classId = RelationGetRelid(rel);
857 AttrNumber Anum_owner = get_object_attnum_owner(classId);
858 AttrNumber Anum_namespace = get_object_attnum_namespace(classId);
859 AttrNumber Anum_acl = get_object_attnum_acl(classId);
860 AttrNumber Anum_name = get_object_attnum_name(classId);
861 HeapTuple oldtup;
862 Datum datum;
863 bool isnull;
864 Oid old_ownerId;
865 Oid namespaceId = InvalidOid;
866
867 oldtup = get_catalog_object_by_oid(rel, objectId);
868 if (oldtup == NULL)
869 elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
870 objectId, RelationGetRelationName(rel));
871
872 datum = heap_getattr(oldtup, Anum_owner,
873 RelationGetDescr(rel), &isnull);
874 Assert(!isnull);
875 old_ownerId = DatumGetObjectId(datum);
876
877 if (Anum_namespace != InvalidAttrNumber)
878 {
879 datum = heap_getattr(oldtup, Anum_namespace,
880 RelationGetDescr(rel), &isnull);
881 Assert(!isnull);
882 namespaceId = DatumGetObjectId(datum);
883 }
884
885 if (old_ownerId != new_ownerId)
886 {
887 AttrNumber nattrs;
888 HeapTuple newtup;
889 Datum *values;
890 bool *nulls;
891 bool *replaces;
892
893 /* Superusers can bypass permission checks */
894 if (!superuser())
895 {
896 AclObjectKind aclkind = get_object_aclkind(classId);
897
898 /* must be owner */
899 if (!has_privs_of_role(GetUserId(), old_ownerId))
900 {
901 char *objname;
902 char namebuf[NAMEDATALEN];
903
904 if (Anum_name != InvalidAttrNumber)
905 {
906 datum = heap_getattr(oldtup, Anum_name,
907 RelationGetDescr(rel), &isnull);
908 Assert(!isnull);
909 objname = NameStr(*DatumGetName(datum));
910 }
911 else
912 {
913 snprintf(namebuf, sizeof(namebuf), "%u",
914 HeapTupleGetOid(oldtup));
915 objname = namebuf;
916 }
917 aclcheck_error(ACLCHECK_NOT_OWNER, aclkind, objname);
918 }
919 /* Must be able to become new owner */
920 check_is_member_of_role(GetUserId(), new_ownerId);
921
922 /* New owner must have CREATE privilege on namespace */
923 if (OidIsValid(namespaceId))
924 {
925 AclResult aclresult;
926
927 aclresult = pg_namespace_aclcheck(namespaceId, new_ownerId,
928 ACL_CREATE);
929 if (aclresult != ACLCHECK_OK)
930 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
931 get_namespace_name(namespaceId));
932 }
933 }
934
935 /* Build a modified tuple */
936 nattrs = RelationGetNumberOfAttributes(rel);
937 values = palloc0(nattrs * sizeof(Datum));
938 nulls = palloc0(nattrs * sizeof(bool));
939 replaces = palloc0(nattrs * sizeof(bool));
940 values[Anum_owner - 1] = ObjectIdGetDatum(new_ownerId);
941 replaces[Anum_owner - 1] = true;
942
943 /*
944 * Determine the modified ACL for the new owner. This is only
945 * necessary when the ACL is non-null.
946 */
947 if (Anum_acl != InvalidAttrNumber)
948 {
949 datum = heap_getattr(oldtup,
950 Anum_acl, RelationGetDescr(rel), &isnull);
951 if (!isnull)
952 {
953 Acl *newAcl;
954
955 newAcl = aclnewowner(DatumGetAclP(datum),
956 old_ownerId, new_ownerId);
957 values[Anum_acl - 1] = PointerGetDatum(newAcl);
958 replaces[Anum_acl - 1] = true;
959 }
960 }
961
962 newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
963 values, nulls, replaces);
964
965 /* Perform actual update */
966 simple_heap_update(rel, &newtup->t_self, newtup);
967 CatalogUpdateIndexes(rel, newtup);
968
969 /* Update owner dependency reference */
970 if (classId == LargeObjectMetadataRelationId)
971 classId = LargeObjectRelationId;
972 changeDependencyOnOwner(classId, HeapTupleGetOid(newtup), new_ownerId);
973
974 /* Release memory */
975 pfree(values);
976 pfree(nulls);
977 pfree(replaces);
978 }
979
980 InvokeObjectPostAlterHook(classId, objectId, 0);
981 }
982