LCOV - code coverage report
Current view: top level - src/backend/commands - comment.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 96.2 % 130 125
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 6 6
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 85.0 % 80 68

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * comment.c
       4                 :             :  *
       5                 :             :  * PostgreSQL object comments utility code.
       6                 :             :  *
       7                 :             :  * Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8                 :             :  *
       9                 :             :  * IDENTIFICATION
      10                 :             :  *    src/backend/commands/comment.c
      11                 :             :  *
      12                 :             :  *-------------------------------------------------------------------------
      13                 :             :  */
      14                 :             : 
      15                 :             : #include "postgres.h"
      16                 :             : 
      17                 :             : #include "access/genam.h"
      18                 :             : #include "access/htup_details.h"
      19                 :             : #include "access/relation.h"
      20                 :             : #include "access/table.h"
      21                 :             : #include "catalog/indexing.h"
      22                 :             : #include "catalog/objectaddress.h"
      23                 :             : #include "catalog/pg_database.h"
      24                 :             : #include "catalog/pg_description.h"
      25                 :             : #include "catalog/pg_shdescription.h"
      26                 :             : #include "commands/comment.h"
      27                 :             : #include "miscadmin.h"
      28                 :             : #include "utils/builtins.h"
      29                 :             : #include "utils/fmgroids.h"
      30                 :             : #include "utils/rel.h"
      31                 :             : 
      32                 :             : 
      33                 :             : /*
      34                 :             :  * CommentObject --
      35                 :             :  *
      36                 :             :  * This routine is used to add the associated comment into
      37                 :             :  * pg_description for the object specified by the given SQL command.
      38                 :             :  */
      39                 :             : ObjectAddress
      40                 :        4508 : CommentObject(CommentStmt *stmt)
      41                 :             : {
      42                 :             :     Relation    relation;
      43                 :        4508 :     ObjectAddress address = InvalidObjectAddress;
      44                 :             :     bool        missing_ok;
      45                 :             : 
      46                 :             :     /*
      47                 :             :      * When loading a dump, we may see a COMMENT ON DATABASE for the old name
      48                 :             :      * of the database.  Erroring out would prevent pg_restore from completing
      49                 :             :      * (which is really pg_restore's fault, but for now we will work around
      50                 :             :      * the problem here).  Consensus is that the best fix is to treat wrong
      51                 :             :      * database name as a WARNING not an ERROR; hence, the following special
      52                 :             :      * case.
      53                 :             :      */
      54         [ +  + ]:        4508 :     if (stmt->objtype == OBJECT_DATABASE)
      55                 :             :     {
      56                 :         138 :         char       *database = strVal(stmt->object);
      57                 :             : 
      58         [ -  + ]:         138 :         if (!OidIsValid(get_database_oid(database, true)))
      59                 :             :         {
      60         [ #  # ]:           0 :             ereport(WARNING,
      61                 :             :                     (errcode(ERRCODE_UNDEFINED_DATABASE),
      62                 :             :                      errmsg("database \"%s\" does not exist", database)));
      63                 :           0 :             return address;
      64                 :             :         }
      65                 :             :     }
      66                 :             : 
      67                 :             :     /*
      68                 :             :      * During binary upgrade, allow nonexistent large objects so that we don't
      69                 :             :      * have to create them during schema restoration.  pg_upgrade will
      70                 :             :      * transfer the contents of pg_largeobject_metadata via COPY or by
      71                 :             :      * copying/linking its files from the old cluster later on.
      72                 :             :      */
      73   [ +  +  +  + ]:        4508 :     missing_ok = IsBinaryUpgrade && stmt->objtype == OBJECT_LARGEOBJECT;
      74                 :             : 
      75                 :             :     /*
      76                 :             :      * Translate the parser representation that identifies this object into an
      77                 :             :      * ObjectAddress.  get_object_address() will throw an error if the object
      78                 :             :      * does not exist, and will also acquire a lock on the target to guard
      79                 :             :      * against concurrent DROP operations.
      80                 :             :      */
      81                 :        4508 :     address = get_object_address(stmt->objtype, stmt->object,
      82                 :             :                                  &relation, ShareUpdateExclusiveLock,
      83                 :             :                                  missing_ok);
      84                 :             : 
      85                 :             :     /* Require ownership of the target object. */
      86                 :        4415 :     check_object_ownership(GetUserId(), stmt->objtype, address,
      87                 :             :                            stmt->object, relation);
      88                 :             : 
      89                 :             :     /* Perform other integrity checks as needed. */
      90         [ +  + ]:        4399 :     switch (stmt->objtype)
      91                 :             :     {
      92                 :         138 :         case OBJECT_COLUMN:
      93                 :             : 
      94                 :             :             /*
      95                 :             :              * Allow comments only on columns of tables, views, materialized
      96                 :             :              * views, composite types, and foreign tables (which are the only
      97                 :             :              * relkinds for which pg_dump will dump per-column comments).  In
      98                 :             :              * particular we wish to disallow comments on index columns,
      99                 :             :              * because the naming of an index's columns may change across PG
     100                 :             :              * versions, so dumping per-column comments could create reload
     101                 :             :              * failures.
     102                 :             :              */
     103         [ +  + ]:         138 :             if (relation->rd_rel->relkind != RELKIND_RELATION &&
     104         [ +  - ]:          37 :                 relation->rd_rel->relkind != RELKIND_VIEW &&
     105         [ +  - ]:          37 :                 relation->rd_rel->relkind != RELKIND_MATVIEW &&
     106         [ +  + ]:          37 :                 relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
     107         [ +  + ]:          28 :                 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
     108         [ -  + ]:          12 :                 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
     109         [ #  # ]:           0 :                 ereport(ERROR,
     110                 :             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     111                 :             :                          errmsg("cannot set comment on relation \"%s\"",
     112                 :             :                                 RelationGetRelationName(relation)),
     113                 :             :                          errdetail_relkind_not_supported(relation->rd_rel->relkind)));
     114                 :         138 :             break;
     115                 :        4261 :         default:
     116                 :        4261 :             break;
     117                 :             :     }
     118                 :             : 
     119                 :             :     /*
     120                 :             :      * Databases, tablespaces, and roles are cluster-wide objects, so any
     121                 :             :      * comments on those objects are recorded in the shared pg_shdescription
     122                 :             :      * catalog.  Comments on all other objects are recorded in pg_description.
     123                 :             :      */
     124   [ +  +  +  - ]:        4399 :     if (stmt->objtype == OBJECT_DATABASE || stmt->objtype == OBJECT_TABLESPACE
     125         [ +  + ]:        4261 :         || stmt->objtype == OBJECT_ROLE)
     126                 :         155 :         CreateSharedComments(address.objectId, address.classId, stmt->comment);
     127                 :             :     else
     128                 :        4244 :         CreateComments(address.objectId, address.classId, address.objectSubId,
     129                 :        4244 :                        stmt->comment);
     130                 :             : 
     131                 :             :     /*
     132                 :             :      * If get_object_address() opened the relation for us, we close it to keep
     133                 :             :      * the reference count correct - but we retain any locks acquired by
     134                 :             :      * get_object_address() until commit time, to guard against concurrent
     135                 :             :      * activity.
     136                 :             :      */
     137         [ +  + ]:        4399 :     if (relation != NULL)
     138                 :         425 :         relation_close(relation, NoLock);
     139                 :             : 
     140                 :        4399 :     return address;
     141                 :             : }
     142                 :             : 
     143                 :             : /*
     144                 :             :  * CreateComments --
     145                 :             :  *
     146                 :             :  * Create a comment for the specified object descriptor.  Inserts a new
     147                 :             :  * pg_description tuple, or replaces an existing one with the same key.
     148                 :             :  *
     149                 :             :  * If the comment given is null or an empty string, instead delete any
     150                 :             :  * existing comment for the specified key.
     151                 :             :  */
     152                 :             : void
     153                 :       51565 : CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
     154                 :             : {
     155                 :             :     Relation    description;
     156                 :             :     ScanKeyData skey[3];
     157                 :             :     SysScanDesc sd;
     158                 :             :     HeapTuple   oldtuple;
     159                 :       51565 :     HeapTuple   newtuple = NULL;
     160                 :             :     Datum       values[Natts_pg_description];
     161                 :             :     bool        nulls[Natts_pg_description];
     162                 :             :     bool        replaces[Natts_pg_description];
     163                 :             :     int         i;
     164                 :             : 
     165                 :             :     /* Reduce empty-string to NULL case */
     166   [ +  +  +  + ]:       51565 :     if (comment != NULL && strlen(comment) == 0)
     167                 :           4 :         comment = NULL;
     168                 :             : 
     169                 :             :     /* Prepare to form or update a tuple, if necessary */
     170         [ +  + ]:       51565 :     if (comment != NULL)
     171                 :             :     {
     172         [ +  + ]:      257480 :         for (i = 0; i < Natts_pg_description; i++)
     173                 :             :         {
     174                 :      205984 :             nulls[i] = false;
     175                 :      205984 :             replaces[i] = true;
     176                 :             :         }
     177                 :       51496 :         values[Anum_pg_description_objoid - 1] = ObjectIdGetDatum(oid);
     178                 :       51496 :         values[Anum_pg_description_classoid - 1] = ObjectIdGetDatum(classoid);
     179                 :       51496 :         values[Anum_pg_description_objsubid - 1] = Int32GetDatum(subid);
     180                 :       51496 :         values[Anum_pg_description_description - 1] = CStringGetTextDatum(comment);
     181                 :             :     }
     182                 :             : 
     183                 :             :     /* Use the index to search for a matching old tuple */
     184                 :             : 
     185                 :       51565 :     ScanKeyInit(&skey[0],
     186                 :             :                 Anum_pg_description_objoid,
     187                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     188                 :             :                 ObjectIdGetDatum(oid));
     189                 :       51565 :     ScanKeyInit(&skey[1],
     190                 :             :                 Anum_pg_description_classoid,
     191                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     192                 :             :                 ObjectIdGetDatum(classoid));
     193                 :       51565 :     ScanKeyInit(&skey[2],
     194                 :             :                 Anum_pg_description_objsubid,
     195                 :             :                 BTEqualStrategyNumber, F_INT4EQ,
     196                 :             :                 Int32GetDatum(subid));
     197                 :             : 
     198                 :       51565 :     description = table_open(DescriptionRelationId, RowExclusiveLock);
     199                 :             : 
     200                 :       51565 :     sd = systable_beginscan(description, DescriptionObjIndexId, true,
     201                 :             :                             NULL, 3, skey);
     202                 :             : 
     203         [ +  + ]:       51565 :     while ((oldtuple = systable_getnext(sd)) != NULL)
     204                 :             :     {
     205                 :             :         /* Found the old tuple, so delete or update it */
     206                 :             : 
     207         [ +  + ]:         110 :         if (comment == NULL)
     208                 :          69 :             CatalogTupleDelete(description, &oldtuple->t_self);
     209                 :             :         else
     210                 :             :         {
     211                 :          41 :             newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(description), values,
     212                 :             :                                          nulls, replaces);
     213                 :          41 :             CatalogTupleUpdate(description, &oldtuple->t_self, newtuple);
     214                 :             :         }
     215                 :             : 
     216                 :         110 :         break;                  /* Assume there can be only one match */
     217                 :             :     }
     218                 :             : 
     219                 :       51565 :     systable_endscan(sd);
     220                 :             : 
     221                 :             :     /* If we didn't find an old tuple, insert a new one */
     222                 :             : 
     223   [ +  +  +  + ]:       51565 :     if (newtuple == NULL && comment != NULL)
     224                 :             :     {
     225                 :       51455 :         newtuple = heap_form_tuple(RelationGetDescr(description),
     226                 :             :                                    values, nulls);
     227                 :       51455 :         CatalogTupleInsert(description, newtuple);
     228                 :             :     }
     229                 :             : 
     230         [ +  + ]:       51565 :     if (newtuple != NULL)
     231                 :       51496 :         heap_freetuple(newtuple);
     232                 :             : 
     233                 :             :     /* Done */
     234                 :             : 
     235                 :       51565 :     table_close(description, NoLock);
     236                 :       51565 : }
     237                 :             : 
     238                 :             : /*
     239                 :             :  * CreateSharedComments --
     240                 :             :  *
     241                 :             :  * Create a comment for the specified shared object descriptor.  Inserts a
     242                 :             :  * new pg_shdescription tuple, or replaces an existing one with the same key.
     243                 :             :  *
     244                 :             :  * If the comment given is null or an empty string, instead delete any
     245                 :             :  * existing comment for the specified key.
     246                 :             :  */
     247                 :             : void
     248                 :         155 : CreateSharedComments(Oid oid, Oid classoid, const char *comment)
     249                 :             : {
     250                 :             :     Relation    shdescription;
     251                 :             :     ScanKeyData skey[2];
     252                 :             :     SysScanDesc sd;
     253                 :             :     HeapTuple   oldtuple;
     254                 :         155 :     HeapTuple   newtuple = NULL;
     255                 :             :     Datum       values[Natts_pg_shdescription];
     256                 :             :     bool        nulls[Natts_pg_shdescription];
     257                 :             :     bool        replaces[Natts_pg_shdescription];
     258                 :             :     int         i;
     259                 :             : 
     260                 :             :     /* Reduce empty-string to NULL case */
     261   [ +  +  +  + ]:         155 :     if (comment != NULL && strlen(comment) == 0)
     262                 :           4 :         comment = NULL;
     263                 :             : 
     264                 :             :     /* Prepare to form or update a tuple, if necessary */
     265         [ +  + ]:         155 :     if (comment != NULL)
     266                 :             :     {
     267         [ +  + ]:         572 :         for (i = 0; i < Natts_pg_shdescription; i++)
     268                 :             :         {
     269                 :         429 :             nulls[i] = false;
     270                 :         429 :             replaces[i] = true;
     271                 :             :         }
     272                 :         143 :         values[Anum_pg_shdescription_objoid - 1] = ObjectIdGetDatum(oid);
     273                 :         143 :         values[Anum_pg_shdescription_classoid - 1] = ObjectIdGetDatum(classoid);
     274                 :         143 :         values[Anum_pg_shdescription_description - 1] = CStringGetTextDatum(comment);
     275                 :             :     }
     276                 :             : 
     277                 :             :     /* Use the index to search for a matching old tuple */
     278                 :             : 
     279                 :         155 :     ScanKeyInit(&skey[0],
     280                 :             :                 Anum_pg_shdescription_objoid,
     281                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     282                 :             :                 ObjectIdGetDatum(oid));
     283                 :         155 :     ScanKeyInit(&skey[1],
     284                 :             :                 Anum_pg_shdescription_classoid,
     285                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     286                 :             :                 ObjectIdGetDatum(classoid));
     287                 :             : 
     288                 :         155 :     shdescription = table_open(SharedDescriptionRelationId, RowExclusiveLock);
     289                 :             : 
     290                 :         155 :     sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
     291                 :             :                             NULL, 2, skey);
     292                 :             : 
     293         [ +  + ]:         155 :     while ((oldtuple = systable_getnext(sd)) != NULL)
     294                 :             :     {
     295                 :             :         /* Found the old tuple, so delete or update it */
     296                 :             : 
     297         [ +  - ]:          12 :         if (comment == NULL)
     298                 :          12 :             CatalogTupleDelete(shdescription, &oldtuple->t_self);
     299                 :             :         else
     300                 :             :         {
     301                 :           0 :             newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(shdescription),
     302                 :             :                                          values, nulls, replaces);
     303                 :           0 :             CatalogTupleUpdate(shdescription, &oldtuple->t_self, newtuple);
     304                 :             :         }
     305                 :             : 
     306                 :          12 :         break;                  /* Assume there can be only one match */
     307                 :             :     }
     308                 :             : 
     309                 :         155 :     systable_endscan(sd);
     310                 :             : 
     311                 :             :     /* If we didn't find an old tuple, insert a new one */
     312                 :             : 
     313   [ +  -  +  + ]:         155 :     if (newtuple == NULL && comment != NULL)
     314                 :             :     {
     315                 :         143 :         newtuple = heap_form_tuple(RelationGetDescr(shdescription),
     316                 :             :                                    values, nulls);
     317                 :         143 :         CatalogTupleInsert(shdescription, newtuple);
     318                 :             :     }
     319                 :             : 
     320         [ +  + ]:         155 :     if (newtuple != NULL)
     321                 :         143 :         heap_freetuple(newtuple);
     322                 :             : 
     323                 :             :     /* Done */
     324                 :             : 
     325                 :         155 :     table_close(shdescription, NoLock);
     326                 :         155 : }
     327                 :             : 
     328                 :             : /*
     329                 :             :  * DeleteComments -- remove comments for an object
     330                 :             :  *
     331                 :             :  * If subid is nonzero then only comments matching it will be removed.
     332                 :             :  * If subid is zero, all comments matching the oid/classoid will be removed
     333                 :             :  * (this corresponds to deleting a whole object).
     334                 :             :  */
     335                 :             : void
     336                 :      152504 : DeleteComments(Oid oid, Oid classoid, int32 subid)
     337                 :             : {
     338                 :             :     Relation    description;
     339                 :             :     ScanKeyData skey[3];
     340                 :             :     int         nkeys;
     341                 :             :     SysScanDesc sd;
     342                 :             :     HeapTuple   oldtuple;
     343                 :             : 
     344                 :             :     /* Use the index to search for all matching old tuples */
     345                 :             : 
     346                 :      152504 :     ScanKeyInit(&skey[0],
     347                 :             :                 Anum_pg_description_objoid,
     348                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     349                 :             :                 ObjectIdGetDatum(oid));
     350                 :      152504 :     ScanKeyInit(&skey[1],
     351                 :             :                 Anum_pg_description_classoid,
     352                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     353                 :             :                 ObjectIdGetDatum(classoid));
     354                 :             : 
     355         [ +  + ]:      152504 :     if (subid != 0)
     356                 :             :     {
     357                 :        1422 :         ScanKeyInit(&skey[2],
     358                 :             :                     Anum_pg_description_objsubid,
     359                 :             :                     BTEqualStrategyNumber, F_INT4EQ,
     360                 :             :                     Int32GetDatum(subid));
     361                 :        1422 :         nkeys = 3;
     362                 :             :     }
     363                 :             :     else
     364                 :      151082 :         nkeys = 2;
     365                 :             : 
     366                 :      152504 :     description = table_open(DescriptionRelationId, RowExclusiveLock);
     367                 :             : 
     368                 :      152504 :     sd = systable_beginscan(description, DescriptionObjIndexId, true,
     369                 :             :                             NULL, nkeys, skey);
     370                 :             : 
     371         [ +  + ]:      153016 :     while ((oldtuple = systable_getnext(sd)) != NULL)
     372                 :         512 :         CatalogTupleDelete(description, &oldtuple->t_self);
     373                 :             : 
     374                 :             :     /* Done */
     375                 :             : 
     376                 :      152504 :     systable_endscan(sd);
     377                 :      152504 :     table_close(description, RowExclusiveLock);
     378                 :      152504 : }
     379                 :             : 
     380                 :             : /*
     381                 :             :  * DeleteSharedComments -- remove comments for a shared object
     382                 :             :  */
     383                 :             : void
     384                 :        1084 : DeleteSharedComments(Oid oid, Oid classoid)
     385                 :             : {
     386                 :             :     Relation    shdescription;
     387                 :             :     ScanKeyData skey[2];
     388                 :             :     SysScanDesc sd;
     389                 :             :     HeapTuple   oldtuple;
     390                 :             : 
     391                 :             :     /* Use the index to search for all matching old tuples */
     392                 :             : 
     393                 :        1084 :     ScanKeyInit(&skey[0],
     394                 :             :                 Anum_pg_shdescription_objoid,
     395                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     396                 :             :                 ObjectIdGetDatum(oid));
     397                 :        1084 :     ScanKeyInit(&skey[1],
     398                 :             :                 Anum_pg_shdescription_classoid,
     399                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     400                 :             :                 ObjectIdGetDatum(classoid));
     401                 :             : 
     402                 :        1084 :     shdescription = table_open(SharedDescriptionRelationId, RowExclusiveLock);
     403                 :             : 
     404                 :        1084 :     sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
     405                 :             :                             NULL, 2, skey);
     406                 :             : 
     407         [ +  + ]:        1105 :     while ((oldtuple = systable_getnext(sd)) != NULL)
     408                 :          21 :         CatalogTupleDelete(shdescription, &oldtuple->t_self);
     409                 :             : 
     410                 :             :     /* Done */
     411                 :             : 
     412                 :        1084 :     systable_endscan(sd);
     413                 :        1084 :     table_close(shdescription, RowExclusiveLock);
     414                 :        1084 : }
     415                 :             : 
     416                 :             : /*
     417                 :             :  * GetComment -- get the comment for an object, or null if not found.
     418                 :             :  */
     419                 :             : char *
     420                 :        1129 : GetComment(Oid oid, Oid classoid, int32 subid)
     421                 :             : {
     422                 :             :     Relation    description;
     423                 :             :     ScanKeyData skey[3];
     424                 :             :     SysScanDesc sd;
     425                 :             :     TupleDesc   tupdesc;
     426                 :             :     HeapTuple   tuple;
     427                 :             :     char       *comment;
     428                 :             : 
     429                 :             :     /* Use the index to search for a matching old tuple */
     430                 :             : 
     431                 :        1129 :     ScanKeyInit(&skey[0],
     432                 :             :                 Anum_pg_description_objoid,
     433                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     434                 :             :                 ObjectIdGetDatum(oid));
     435                 :        1129 :     ScanKeyInit(&skey[1],
     436                 :             :                 Anum_pg_description_classoid,
     437                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
     438                 :             :                 ObjectIdGetDatum(classoid));
     439                 :        1129 :     ScanKeyInit(&skey[2],
     440                 :             :                 Anum_pg_description_objsubid,
     441                 :             :                 BTEqualStrategyNumber, F_INT4EQ,
     442                 :             :                 Int32GetDatum(subid));
     443                 :             : 
     444                 :        1129 :     description = table_open(DescriptionRelationId, AccessShareLock);
     445                 :        1129 :     tupdesc = RelationGetDescr(description);
     446                 :             : 
     447                 :        1129 :     sd = systable_beginscan(description, DescriptionObjIndexId, true,
     448                 :             :                             NULL, 3, skey);
     449                 :             : 
     450                 :        1129 :     comment = NULL;
     451         [ +  + ]:        1129 :     while ((tuple = systable_getnext(sd)) != NULL)
     452                 :             :     {
     453                 :             :         Datum       value;
     454                 :             :         bool        isnull;
     455                 :             : 
     456                 :             :         /* Found the tuple, get description field */
     457                 :         232 :         value = heap_getattr(tuple, Anum_pg_description_description, tupdesc, &isnull);
     458         [ +  - ]:         232 :         if (!isnull)
     459                 :         232 :             comment = TextDatumGetCString(value);
     460                 :         232 :         break;                  /* Assume there can be only one match */
     461                 :             :     }
     462                 :             : 
     463                 :        1129 :     systable_endscan(sd);
     464                 :             : 
     465                 :             :     /* Done */
     466                 :        1129 :     table_close(description, AccessShareLock);
     467                 :             : 
     468                 :        1129 :     return comment;
     469                 :             : }
        

Generated by: LCOV version 2.0-1