LCOV - code coverage report
Current view: top level - src/backend/commands - alter.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 91.0 % 355 323
Test Date: 2026-04-05 23:16:10 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1