LCOV - code coverage report
Current view: top level - src/backend/commands - alter.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 320 352 90.9 %
Date: 2026-02-07 08:18:04 Functions: 10 10 100.0 %
Legend: Lines: hit not hit

          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
      76          24 : report_name_conflict(Oid classId, const char *name)
      77             : {
      78             :     char       *msgfmt;
      79             : 
      80          24 :     switch (classId)
      81             :     {
      82           6 :         case EventTriggerRelationId:
      83           6 :             msgfmt = gettext_noop("event trigger \"%s\" already exists");
      84           6 :             break;
      85           6 :         case ForeignDataWrapperRelationId:
      86           6 :             msgfmt = gettext_noop("foreign-data wrapper \"%s\" already exists");
      87           6 :             break;
      88           6 :         case ForeignServerRelationId:
      89           6 :             msgfmt = gettext_noop("server \"%s\" already exists");
      90           6 :             break;
      91           6 :         case LanguageRelationId:
      92           6 :             msgfmt = gettext_noop("language \"%s\" already exists");
      93           6 :             break;
      94           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;
     100           0 :         default:
     101           0 :             elog(ERROR, "unsupported object class: %u", classId);
     102             :             break;
     103             :     }
     104             : 
     105          24 :     ereport(ERROR,
     106             :             (errcode(ERRCODE_DUPLICATE_OBJECT),
     107             :              errmsg(msgfmt, name)));
     108             : }
     109             : 
     110             : static void
     111          72 : report_namespace_conflict(Oid classId, const char *name, Oid nspOid)
     112             : {
     113             :     char       *msgfmt;
     114             : 
     115             :     Assert(OidIsValid(nspOid));
     116             : 
     117          72 :     switch (classId)
     118             :     {
     119          12 :         case ConversionRelationId:
     120          12 :             msgfmt = gettext_noop("conversion \"%s\" already exists in schema \"%s\"");
     121          12 :             break;
     122          12 :         case StatisticExtRelationId:
     123          12 :             msgfmt = gettext_noop("statistics object \"%s\" already exists in schema \"%s\"");
     124          12 :             break;
     125          12 :         case TSParserRelationId:
     126          12 :             msgfmt = gettext_noop("text search parser \"%s\" already exists in schema \"%s\"");
     127          12 :             break;
     128          12 :         case TSDictionaryRelationId:
     129          12 :             msgfmt = gettext_noop("text search dictionary \"%s\" already exists in schema \"%s\"");
     130          12 :             break;
     131          12 :         case TSTemplateRelationId:
     132          12 :             msgfmt = gettext_noop("text search template \"%s\" already exists in schema \"%s\"");
     133          12 :             break;
     134          12 :         case TSConfigRelationId:
     135          12 :             msgfmt = gettext_noop("text search configuration \"%s\" already exists in schema \"%s\"");
     136          12 :             break;
     137           0 :         default:
     138           0 :             elog(ERROR, "unsupported object class: %u", classId);
     139             :             break;
     140             :     }
     141             : 
     142          72 :     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         428 : AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
     160             : {
     161         428 :     Oid         classId = RelationGetRelid(rel);
     162         428 :     int         oidCacheId = get_object_catcache_oid(classId);
     163         428 :     int         nameCacheId = get_object_catcache_name(classId);
     164         428 :     AttrNumber  Anum_name = get_object_attnum_name(classId);
     165         428 :     AttrNumber  Anum_namespace = get_object_attnum_namespace(classId);
     166         428 :     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         428 :     oldtup = SearchSysCache1(oidCacheId, ObjectIdGetDatum(objectId));
     181         428 :     if (!HeapTupleIsValid(oldtup))
     182           0 :         elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
     183             :              objectId, RelationGetRelationName(rel));
     184             : 
     185         428 :     datum = heap_getattr(oldtup, Anum_name,
     186             :                          RelationGetDescr(rel), &isnull);
     187             :     Assert(!isnull);
     188         428 :     old_name = NameStr(*(DatumGetName(datum)));
     189             : 
     190             :     /* Get OID of namespace */
     191         428 :     if (Anum_namespace > 0)
     192             :     {
     193         270 :         datum = heap_getattr(oldtup, Anum_namespace,
     194             :                              RelationGetDescr(rel), &isnull);
     195             :         Assert(!isnull);
     196         270 :         namespaceId = DatumGetObjectId(datum);
     197             :     }
     198             :     else
     199         158 :         namespaceId = InvalidOid;
     200             : 
     201             :     /* Permission checks ... superusers can always do it */
     202         428 :     if (!superuser())
     203             :     {
     204             :         /* Fail if object does not have an explicit owner */
     205         246 :         if (Anum_owner <= 0)
     206           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 */
     212         246 :         datum = heap_getattr(oldtup, Anum_owner,
     213             :                              RelationGetDescr(rel), &isnull);
     214             :         Assert(!isnull);
     215         246 :         ownerId = DatumGetObjectId(datum);
     216             : 
     217         246 :         if (!has_privs_of_role(GetUserId(), ownerId))
     218          72 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objectId),
     219             :                            old_name);
     220             : 
     221             :         /* User must have CREATE privilege on the namespace */
     222         174 :         if (OidIsValid(namespaceId))
     223             :         {
     224         144 :             aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(),
     225             :                                         ACL_CREATE);
     226         144 :             if (aclresult != ACLCHECK_OK)
     227           0 :                 aclcheck_error(aclresult, OBJECT_SCHEMA,
     228           0 :                                get_namespace_name(namespaceId));
     229             :         }
     230             : 
     231         174 :         if (classId == SubscriptionRelationId)
     232             :         {
     233             :             Form_pg_subscription form;
     234             : 
     235             :             /* must have CREATE privilege on database */
     236          18 :             aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId,
     237             :                                         GetUserId(), ACL_CREATE);
     238          18 :             if (aclresult != ACLCHECK_OK)
     239           6 :                 aclcheck_error(aclresult, OBJECT_DATABASE,
     240           6 :                                get_database_name(MyDatabaseId));
     241             : 
     242             :             /*
     243             :              * Don't allow non-superuser modification of a subscription with
     244             :              * password_required=false.
     245             :              */
     246          12 :             form = (Form_pg_subscription) GETSTRUCT(oldtup);
     247          12 :             if (!form->subpasswordrequired && !superuser())
     248           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             :      */
     260         350 :     if (classId == ProcedureRelationId)
     261             :     {
     262          72 :         Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(oldtup);
     263             : 
     264          72 :         IsThereFunctionInNamespace(new_name, proc->pronargs,
     265             :                                    &proc->proargtypes, proc->pronamespace);
     266             :     }
     267         278 :     else if (classId == CollationRelationId)
     268             :     {
     269          12 :         Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(oldtup);
     270             : 
     271          12 :         IsThereCollationInNamespace(new_name, coll->collnamespace);
     272             :     }
     273         266 :     else if (classId == OperatorClassRelationId)
     274             :     {
     275          18 :         Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(oldtup);
     276             : 
     277          18 :         IsThereOpClassInNamespace(new_name, opc->opcmethod,
     278             :                                   opc->opcnamespace);
     279             :     }
     280         248 :     else if (classId == OperatorFamilyRelationId)
     281             :     {
     282          18 :         Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(oldtup);
     283             : 
     284          18 :         IsThereOpFamilyInNamespace(new_name, opf->opfmethod,
     285             :                                    opf->opfnamespace);
     286             :     }
     287         230 :     else if (classId == SubscriptionRelationId)
     288             :     {
     289          26 :         if (SearchSysCacheExists2(SUBSCRIPTIONNAME,
     290             :                                   ObjectIdGetDatum(MyDatabaseId),
     291             :                                   CStringGetDatum(new_name)))
     292           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 */
     301          26 :         LogicalRepWorkersWakeupAtCommit(objectId);
     302             :     }
     303         204 :     else if (nameCacheId >= 0)
     304             :     {
     305         204 :         if (OidIsValid(namespaceId))
     306             :         {
     307          96 :             if (SearchSysCacheExists2(nameCacheId,
     308             :                                       CStringGetDatum(new_name),
     309             :                                       ObjectIdGetDatum(namespaceId)))
     310          36 :                 report_namespace_conflict(classId, new_name, namespaceId);
     311             :         }
     312             :         else
     313             :         {
     314         108 :             if (SearchSysCacheExists1(nameCacheId,
     315             :                                       CStringGetDatum(new_name)))
     316          24 :                 report_name_conflict(classId, new_name);
     317             :         }
     318             :     }
     319             : 
     320             :     /* Build modified tuple */
     321         260 :     values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
     322         260 :     nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
     323         260 :     replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
     324         260 :     namestrcpy(&nameattrdata, new_name);
     325         260 :     values[Anum_name - 1] = NameGetDatum(&nameattrdata);
     326         260 :     replaces[Anum_name - 1] = true;
     327         260 :     newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
     328             :                                values, nulls, replaces);
     329             : 
     330             :     /* Perform actual update */
     331         260 :     CatalogTupleUpdate(rel, &oldtup->t_self, newtup);
     332             : 
     333         260 :     InvokeObjectPostAlterHook(classId, objectId, 0);
     334             : 
     335             :     /* Do post catalog-update tasks */
     336         260 :     if (classId == PublicationRelationId)
     337             :     {
     338          36 :         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          36 :         InvalidatePubRelSyncCache(pub->oid, pub->puballtables);
     349             :     }
     350             : 
     351             :     /* Release memory */
     352         260 :     pfree(values);
     353         260 :     pfree(nulls);
     354         260 :     pfree(replaces);
     355         260 :     heap_freetuple(newtup);
     356             : 
     357         260 :     ReleaseSysCache(oldtup);
     358         260 : }
     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        1548 : ExecRenameStmt(RenameStmt *stmt)
     368             : {
     369        1548 :     switch (stmt->renameType)
     370             :     {
     371          84 :         case OBJECT_TABCONSTRAINT:
     372             :         case OBJECT_DOMCONSTRAINT:
     373          84 :             return RenameConstraint(stmt);
     374             : 
     375          14 :         case OBJECT_DATABASE:
     376          14 :             return RenameDatabase(stmt->subname, stmt->newname);
     377             : 
     378          32 :         case OBJECT_ROLE:
     379          32 :             return RenameRole(stmt->subname, stmt->newname);
     380             : 
     381          20 :         case OBJECT_SCHEMA:
     382          20 :             return RenameSchema(stmt->subname, stmt->newname);
     383             : 
     384           6 :         case OBJECT_TABLESPACE:
     385           6 :             return RenameTableSpace(stmt->subname, stmt->newname);
     386             : 
     387         512 :         case OBJECT_TABLE:
     388             :         case OBJECT_SEQUENCE:
     389             :         case OBJECT_VIEW:
     390             :         case OBJECT_MATVIEW:
     391             :         case OBJECT_INDEX:
     392             :         case OBJECT_FOREIGN_TABLE:
     393         512 :             return RenameRelation(stmt);
     394             : 
     395         316 :         case OBJECT_COLUMN:
     396             :         case OBJECT_ATTRIBUTE:
     397         316 :             return renameatt(stmt);
     398             : 
     399          34 :         case OBJECT_RULE:
     400          34 :             return RenameRewriteRule(stmt->relation, stmt->subname,
     401          34 :                                      stmt->newname);
     402             : 
     403          40 :         case OBJECT_TRIGGER:
     404          40 :             return renametrig(stmt);
     405             : 
     406          18 :         case OBJECT_POLICY:
     407          18 :             return rename_policy(stmt);
     408             : 
     409          32 :         case OBJECT_DOMAIN:
     410             :         case OBJECT_TYPE:
     411          32 :             return RenameType(stmt);
     412             : 
     413         440 :         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         440 :                 address = get_object_address(stmt->renameType,
     437             :                                              stmt->object,
     438             :                                              NULL,
     439             :                                              AccessExclusiveLock, false);
     440             : 
     441         428 :                 catalog = table_open(address.classId, RowExclusiveLock);
     442         428 :                 AlterObjectRename_internal(catalog,
     443             :                                            address.objectId,
     444         428 :                                            stmt->newname);
     445         260 :                 table_close(catalog, RowExclusiveLock);
     446             : 
     447         260 :                 return address;
     448             :             }
     449             : 
     450           0 :         default:
     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
     465          46 : ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddress)
     466             : {
     467             :     ObjectAddress address;
     468             :     ObjectAddress refAddr;
     469             :     Relation    rel;
     470             : 
     471             :     address =
     472          46 :         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             :      */
     483          46 :     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             :      */
     490          46 :     if (rel)
     491          34 :         table_close(rel, NoLock);
     492             : 
     493          46 :     refAddr = get_object_address(OBJECT_EXTENSION, (Node *) stmt->extname,
     494             :                                  NULL, AccessExclusiveLock, false);
     495          46 :     if (refAddress)
     496          46 :         *refAddress = refAddr;
     497             : 
     498          46 :     if (stmt->remove)
     499             :     {
     500           8 :         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          38 :         currexts = getAutoExtensionsOfObject(address.classId,
     510             :                                              address.objectId);
     511          38 :         if (!list_member_oid(currexts, refAddr.objectId))
     512          36 :             recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
     513             :     }
     514             : 
     515          46 :     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
     528         398 : ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
     529             :                           ObjectAddress *oldSchemaAddr)
     530             : {
     531             :     ObjectAddress address;
     532             :     Oid         oldNspOid;
     533             : 
     534         398 :     switch (stmt->objectType)
     535             :     {
     536          12 :         case OBJECT_EXTENSION:
     537          12 :             address = AlterExtensionNamespace(strVal(stmt->object), stmt->newschema,
     538             :                                               oldSchemaAddr ? &oldNspOid : NULL);
     539           8 :             break;
     540             : 
     541         104 :         case OBJECT_FOREIGN_TABLE:
     542             :         case OBJECT_SEQUENCE:
     543             :         case OBJECT_TABLE:
     544             :         case OBJECT_VIEW:
     545             :         case OBJECT_MATVIEW:
     546         104 :             address = AlterTableNamespace(stmt,
     547             :                                           oldSchemaAddr ? &oldNspOid : NULL);
     548          96 :             break;
     549             : 
     550          18 :         case OBJECT_DOMAIN:
     551             :         case OBJECT_TYPE:
     552          18 :             address = AlterTypeNamespace(castNode(List, stmt->object), stmt->newschema,
     553             :                                          stmt->objectType,
     554             :                                          oldSchemaAddr ? &oldNspOid : NULL);
     555          18 :             break;
     556             : 
     557             :             /* generic code path */
     558         264 :         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             : 
     577         264 :                 address = get_object_address(stmt->objectType,
     578             :                                              stmt->object,
     579             :                                              NULL,
     580             :                                              AccessExclusiveLock,
     581             :                                              false);
     582         258 :                 classId = address.classId;
     583         258 :                 catalog = table_open(classId, RowExclusiveLock);
     584         258 :                 nspOid = LookupCreationNamespace(stmt->newschema);
     585             : 
     586         258 :                 oldNspOid = AlterObjectNamespace_internal(catalog, address.objectId,
     587             :                                                           nspOid);
     588         144 :                 table_close(catalog, RowExclusiveLock);
     589             :             }
     590         144 :             break;
     591             : 
     592           0 :         default:
     593           0 :             elog(ERROR, "unrecognized AlterObjectSchemaStmt type: %d",
     594             :                  (int) stmt->objectType);
     595             :             return InvalidObjectAddress;    /* keep compiler happy */
     596             :     }
     597             : 
     598         266 :     if (oldSchemaAddr)
     599         266 :         ObjectAddressSet(*oldSchemaAddr, NamespaceRelationId, oldNspOid);
     600             : 
     601         266 :     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
     619          42 : AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
     620             :                          ObjectAddresses *objsMoved)
     621             : {
     622          42 :     Oid         oldNspOid = InvalidOid;
     623             : 
     624          42 :     switch (classId)
     625             :     {
     626           2 :         case RelationRelationId:
     627             :             {
     628             :                 Relation    rel;
     629             : 
     630           2 :                 rel = relation_open(objid, AccessExclusiveLock);
     631           2 :                 oldNspOid = RelationGetNamespace(rel);
     632             : 
     633           2 :                 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
     634             : 
     635           2 :                 relation_close(rel, NoLock);
     636           2 :                 break;
     637             :             }
     638             : 
     639          16 :         case TypeRelationId:
     640          16 :             oldNspOid = AlterTypeNamespace_oid(objid, nspOid, true, objsMoved);
     641          16 :             break;
     642             : 
     643          22 :         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             : 
     657          22 :                 catalog = table_open(classId, RowExclusiveLock);
     658             : 
     659          22 :                 oldNspOid = AlterObjectNamespace_internal(catalog, objid,
     660             :                                                           nspOid);
     661             : 
     662          22 :                 table_close(catalog, RowExclusiveLock);
     663             :             }
     664          22 :             break;
     665             : 
     666          42 :         default:
     667             :             /* ignore object types that don't have schema-qualified names */
     668             :             Assert(get_object_attnum_namespace(classId) == InvalidAttrNumber);
     669             :     }
     670             : 
     671          42 :     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
     686         280 : AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
     687             : {
     688         280 :     Oid         classId = RelationGetRelid(rel);
     689         280 :     int         oidCacheId = get_object_catcache_oid(classId);
     690         280 :     int         nameCacheId = get_object_catcache_name(classId);
     691         280 :     AttrNumber  Anum_name = get_object_attnum_name(classId);
     692         280 :     AttrNumber  Anum_namespace = get_object_attnum_namespace(classId);
     693         280 :     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             : 
     704         280 :     tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid));
     705         280 :     if (!HeapTupleIsValid(tup)) /* should not happen */
     706           0 :         elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
     707             :              objid, RelationGetRelationName(rel));
     708             : 
     709         280 :     name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
     710             :     Assert(!isnull);
     711         280 :     namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel),
     712             :                              &isnull);
     713             :     Assert(!isnull);
     714         280 :     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             :      */
     720         280 :     if (oldNspOid == nspOid)
     721             :     {
     722           6 :         InvokeObjectPostAlterHook(classId, objid, 0);
     723           6 :         return oldNspOid;
     724             :     }
     725             : 
     726             :     /* Check basic namespace related issues */
     727         274 :     CheckSetNamespace(oldNspOid, nspOid);
     728             : 
     729             :     /* Permission checks ... superusers can always do it */
     730         274 :     if (!superuser())
     731             :     {
     732             :         Datum       owner;
     733             :         Oid         ownerId;
     734             :         AclResult   aclresult;
     735             : 
     736             :         /* Fail if object does not have an explicit owner */
     737         156 :         if (Anum_owner <= 0)
     738           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 */
     744         156 :         owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull);
     745             :         Assert(!isnull);
     746         156 :         ownerId = DatumGetObjectId(owner);
     747             : 
     748         156 :         if (!has_privs_of_role(GetUserId(), ownerId))
     749          54 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_object_type(classId, objid),
     750          54 :                            NameStr(*(DatumGetName(name))));
     751             : 
     752             :         /* User must have CREATE privilege on new namespace */
     753         102 :         aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
     754         102 :         if (aclresult != ACLCHECK_OK)
     755           0 :             aclcheck_error(aclresult, OBJECT_SCHEMA,
     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             :      */
     764         220 :     if (classId == ProcedureRelationId)
     765             :     {
     766          64 :         Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(tup);
     767             : 
     768          64 :         IsThereFunctionInNamespace(NameStr(proc->proname), proc->pronargs,
     769             :                                    &proc->proargtypes, nspOid);
     770             :     }
     771         156 :     else if (classId == CollationRelationId)
     772             :     {
     773           6 :         Form_pg_collation coll = (Form_pg_collation) GETSTRUCT(tup);
     774             : 
     775           6 :         IsThereCollationInNamespace(NameStr(coll->collname), nspOid);
     776             :     }
     777         150 :     else if (classId == OperatorClassRelationId)
     778             :     {
     779          18 :         Form_pg_opclass opc = (Form_pg_opclass) GETSTRUCT(tup);
     780             : 
     781          18 :         IsThereOpClassInNamespace(NameStr(opc->opcname),
     782             :                                   opc->opcmethod, nspOid);
     783             :     }
     784         132 :     else if (classId == OperatorFamilyRelationId)
     785             :     {
     786          18 :         Form_pg_opfamily opf = (Form_pg_opfamily) GETSTRUCT(tup);
     787             : 
     788          18 :         IsThereOpFamilyInNamespace(NameStr(opf->opfname),
     789             :                                    opf->opfmethod, nspOid);
     790             :     }
     791         216 :     else if (nameCacheId >= 0 &&
     792         102 :              SearchSysCacheExists2(nameCacheId, name,
     793             :                                    ObjectIdGetDatum(nspOid)))
     794          36 :         report_namespace_conflict(classId,
     795          36 :                                   NameStr(*(DatumGetName(name))),
     796             :                                   nspOid);
     797             : 
     798             :     /* Build modified tuple */
     799         160 :     values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
     800         160 :     nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
     801         160 :     replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
     802         160 :     values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid);
     803         160 :     replaces[Anum_namespace - 1] = true;
     804         160 :     newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
     805             :                                values, nulls, replaces);
     806             : 
     807             :     /* Perform actual update */
     808         160 :     CatalogTupleUpdate(rel, &tup->t_self, newtup);
     809             : 
     810             :     /* Release memory */
     811         160 :     pfree(values);
     812         160 :     pfree(nulls);
     813         160 :     pfree(replaces);
     814             : 
     815             :     /* update dependency to point to the new schema */
     816         160 :     if (changeDependencyFor(classId, objid,
     817             :                             NamespaceRelationId, oldNspOid, nspOid) != 1)
     818           0 :         elog(ERROR, "could not change schema dependency for object %u",
     819             :              objid);
     820             : 
     821         160 :     InvokeObjectPostAlterHook(classId, objid, 0);
     822             : 
     823         160 :     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
     831        1682 : ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
     832             : {
     833        1682 :     Oid         newowner = get_rolespec_oid(stmt->newowner, false);
     834             : 
     835        1670 :     switch (stmt->objectType)
     836             :     {
     837          90 :         case OBJECT_DATABASE:
     838          90 :             return AlterDatabaseOwner(strVal(stmt->object), newowner);
     839             : 
     840          64 :         case OBJECT_SCHEMA:
     841          64 :             return AlterSchemaOwner(strVal(stmt->object), newowner);
     842             : 
     843         132 :         case OBJECT_TYPE:
     844             :         case OBJECT_DOMAIN:     /* same as TYPE */
     845         132 :             return AlterTypeOwner(castNode(List, stmt->object), newowner, stmt->objectType);
     846             :             break;
     847             : 
     848          20 :         case OBJECT_FDW:
     849          20 :             return AlterForeignDataWrapperOwner(strVal(stmt->object),
     850             :                                                 newowner);
     851             : 
     852          68 :         case OBJECT_FOREIGN_SERVER:
     853          68 :             return AlterForeignServerOwner(strVal(stmt->object),
     854             :                                            newowner);
     855             : 
     856          14 :         case OBJECT_EVENT_TRIGGER:
     857          14 :             return AlterEventTriggerOwner(strVal(stmt->object),
     858             :                                           newowner);
     859             : 
     860          36 :         case OBJECT_PUBLICATION:
     861          36 :             return AlterPublicationOwner(strVal(stmt->object),
     862             :                                          newowner);
     863             : 
     864          18 :         case OBJECT_SUBSCRIPTION:
     865          18 :             return AlterSubscriptionOwner(strVal(stmt->object),
     866             :                                           newowner);
     867             : 
     868             :             /* Generic cases */
     869        1228 :         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        1228 :                 address = get_object_address(stmt->objectType,
     888             :                                              stmt->object,
     889             :                                              NULL,
     890             :                                              AccessExclusiveLock,
     891             :                                              false);
     892             : 
     893        1220 :                 AlterObjectOwner_internal(address.classId, address.objectId,
     894             :                                           newowner);
     895             : 
     896        1046 :                 return address;
     897             :             }
     898             :             break;
     899             : 
     900           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
     920        1240 : AlterObjectOwner_internal(Oid classId, Oid objectId, Oid new_ownerId)
     921             : {
     922             :     /* For large objects, the catalog to modify is pg_largeobject_metadata */
     923        1240 :     Oid         catalogId = (classId == LargeObjectRelationId) ? LargeObjectMetadataRelationId : classId;
     924        1240 :     AttrNumber  Anum_oid = get_object_attnum_oid(catalogId);
     925        1240 :     AttrNumber  Anum_owner = get_object_attnum_owner(catalogId);
     926        1240 :     AttrNumber  Anum_namespace = get_object_attnum_namespace(catalogId);
     927        1240 :     AttrNumber  Anum_acl = get_object_attnum_acl(catalogId);
     928        1240 :     AttrNumber  Anum_name = get_object_attnum_name(catalogId);
     929             :     Relation    rel;
     930             :     HeapTuple   oldtup;
     931             :     Datum       datum;
     932             :     bool        isnull;
     933             :     Oid         old_ownerId;
     934        1240 :     Oid         namespaceId = InvalidOid;
     935             : 
     936        1240 :     rel = table_open(catalogId, RowExclusiveLock);
     937             : 
     938             :     /* Search tuple and lock it. */
     939             :     oldtup =
     940        1240 :         get_catalog_object_by_oid_extended(rel, Anum_oid, objectId, true);
     941        1240 :     if (oldtup == NULL)
     942           0 :         elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
     943             :              objectId, RelationGetRelationName(rel));
     944             : 
     945        1240 :     datum = heap_getattr(oldtup, Anum_owner,
     946             :                          RelationGetDescr(rel), &isnull);
     947             :     Assert(!isnull);
     948        1240 :     old_ownerId = DatumGetObjectId(datum);
     949             : 
     950        1240 :     if (Anum_namespace != InvalidAttrNumber)
     951             :     {
     952        1066 :         datum = heap_getattr(oldtup, Anum_namespace,
     953             :                              RelationGetDescr(rel), &isnull);
     954             :         Assert(!isnull);
     955        1066 :         namespaceId = DatumGetObjectId(datum);
     956             :     }
     957             : 
     958        1240 :     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         388 :         if (!superuser())
     968             :         {
     969             :             /* must be owner */
     970         234 :             if (!has_privs_of_role(GetUserId(), old_ownerId))
     971             :             {
     972             :                 char       *objname;
     973             :                 char        namebuf[NAMEDATALEN];
     974             : 
     975          60 :                 if (Anum_name != InvalidAttrNumber)
     976             :                 {
     977          60 :                     datum = heap_getattr(oldtup, Anum_name,
     978             :                                          RelationGetDescr(rel), &isnull);
     979             :                     Assert(!isnull);
     980          60 :                     objname = NameStr(*DatumGetName(datum));
     981             :                 }
     982             :                 else
     983             :                 {
     984           0 :                     snprintf(namebuf, sizeof(namebuf), "%u", objectId);
     985           0 :                     objname = namebuf;
     986             :                 }
     987          60 :                 aclcheck_error(ACLCHECK_NOT_OWNER,
     988             :                                get_object_type(catalogId, objectId),
     989             :                                objname);
     990             :             }
     991             :             /* Must be able to become new owner */
     992         174 :             check_can_set_role(GetUserId(), new_ownerId);
     993             : 
     994             :             /* New owner must have CREATE privilege on namespace */
     995          60 :             if (OidIsValid(namespaceId))
     996             :             {
     997             :                 AclResult   aclresult;
     998             : 
     999          54 :                 aclresult = object_aclcheck(NamespaceRelationId, namespaceId, new_ownerId,
    1000             :                                             ACL_CREATE);
    1001          54 :                 if (aclresult != ACLCHECK_OK)
    1002           0 :                     aclcheck_error(aclresult, OBJECT_SCHEMA,
    1003           0 :                                    get_namespace_name(namespaceId));
    1004             :             }
    1005             :         }
    1006             : 
    1007             :         /* Build a modified tuple */
    1008         214 :         nattrs = RelationGetNumberOfAttributes(rel);
    1009         214 :         values = palloc0(nattrs * sizeof(Datum));
    1010         214 :         nulls = palloc0(nattrs * sizeof(bool));
    1011         214 :         replaces = palloc0(nattrs * sizeof(bool));
    1012         214 :         values[Anum_owner - 1] = ObjectIdGetDatum(new_ownerId);
    1013         214 :         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         214 :         if (Anum_acl != InvalidAttrNumber)
    1020             :         {
    1021         116 :             datum = heap_getattr(oldtup,
    1022             :                                  Anum_acl, RelationGetDescr(rel), &isnull);
    1023         116 :             if (!isnull)
    1024             :             {
    1025             :                 Acl        *newAcl;
    1026             : 
    1027           8 :                 newAcl = aclnewowner(DatumGetAclP(datum),
    1028             :                                      old_ownerId, new_ownerId);
    1029           8 :                 values[Anum_acl - 1] = PointerGetDatum(newAcl);
    1030           8 :                 replaces[Anum_acl - 1] = true;
    1031             :             }
    1032             :         }
    1033             : 
    1034         214 :         newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
    1035             :                                    values, nulls, replaces);
    1036             : 
    1037             :         /* Perform actual update */
    1038         214 :         CatalogTupleUpdate(rel, &newtup->t_self, newtup);
    1039             : 
    1040         214 :         UnlockTuple(rel, &oldtup->t_self, InplaceUpdateTupleLock);
    1041             : 
    1042             :         /* Update owner dependency reference */
    1043         214 :         changeDependencyOnOwner(classId, objectId, new_ownerId);
    1044             : 
    1045             :         /* Release memory */
    1046         214 :         pfree(values);
    1047         214 :         pfree(nulls);
    1048         214 :         pfree(replaces);
    1049             :     }
    1050             :     else
    1051         852 :         UnlockTuple(rel, &oldtup->t_self, InplaceUpdateTupleLock);
    1052             : 
    1053             :     /* Note the post-alter hook gets classId not catalogId */
    1054        1066 :     InvokeObjectPostAlterHook(classId, objectId, 0);
    1055             : 
    1056        1066 :     table_close(rel, RowExclusiveLock);
    1057        1066 : }

Generated by: LCOV version 1.16