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