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

Generated by: LCOV version 1.11