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

Generated by: LCOV version 1.14