LCOV - code coverage report
Current view: top level - contrib/amcheck - verify_common.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 84.2 % 38 32
Test Date: 2026-03-01 06:14:43 Functions: 100.0 % 3 3
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * verify_common.c
       4              :  *      Utility functions common to all access methods.
       5              :  *
       6              :  * Copyright (c) 2016-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  * IDENTIFICATION
       9              :  *    contrib/amcheck/verify_common.c
      10              :  *
      11              :  *-------------------------------------------------------------------------
      12              :  */
      13              : #include "postgres.h"
      14              : 
      15              : #include "access/genam.h"
      16              : #include "access/table.h"
      17              : #include "access/tableam.h"
      18              : #include "verify_common.h"
      19              : #include "catalog/index.h"
      20              : #include "catalog/pg_am.h"
      21              : #include "commands/defrem.h"
      22              : #include "commands/tablecmds.h"
      23              : #include "utils/guc.h"
      24              : #include "utils/syscache.h"
      25              : 
      26              : static bool amcheck_index_mainfork_expected(Relation rel);
      27              : static bool index_checkable(Relation rel, Oid am_id);
      28              : 
      29              : 
      30              : /*
      31              :  * Check if index relation should have a file for its main relation fork.
      32              :  * Verification uses this to skip unlogged indexes when in hot standby mode,
      33              :  * where there is simply nothing to verify.
      34              :  *
      35              :  * NB: Caller should call index_checkable() before calling here.
      36              :  */
      37              : static bool
      38         4033 : amcheck_index_mainfork_expected(Relation rel)
      39              : {
      40         4033 :     if (rel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED ||
      41            0 :         !RecoveryInProgress())
      42         4033 :         return true;
      43              : 
      44            0 :     ereport(NOTICE,
      45              :             (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
      46              :              errmsg("cannot verify unlogged index \"%s\" during recovery, skipping",
      47              :                     RelationGetRelationName(rel))));
      48              : 
      49            0 :     return false;
      50              : }
      51              : 
      52              : /*
      53              : * Amcheck main workhorse.
      54              : * Given index relation OID, lock relation.
      55              : * Next, take a number of standard actions:
      56              : * 1) Make sure the index can be checked
      57              : * 2) change the context of the user,
      58              : * 3) keep track of GUCs modified via index functions
      59              : * 4) execute callback function to verify integrity.
      60              : */
      61              : void
      62         4039 : amcheck_lock_relation_and_check(Oid indrelid,
      63              :                                 Oid am_id,
      64              :                                 IndexDoCheckCallback check,
      65              :                                 LOCKMODE lockmode,
      66              :                                 void *state)
      67              : {
      68              :     Oid         heapid;
      69              :     Relation    indrel;
      70              :     Relation    heaprel;
      71              :     Oid         save_userid;
      72              :     int         save_sec_context;
      73              :     int         save_nestlevel;
      74              : 
      75              :     /*
      76              :      * We must lock table before index to avoid deadlocks.  However, if the
      77              :      * passed indrelid isn't an index then IndexGetRelation() will fail.
      78              :      * Rather than emitting a not-very-helpful error message, postpone
      79              :      * complaining, expecting that the is-it-an-index test below will fail.
      80              :      *
      81              :      * In hot standby mode this will raise an error when parentcheck is true.
      82              :      */
      83         4039 :     heapid = IndexGetRelation(indrelid, true);
      84         4039 :     if (OidIsValid(heapid))
      85              :     {
      86         4035 :         heaprel = table_open(heapid, lockmode);
      87              : 
      88              :         /*
      89              :          * Switch to the table owner's userid, so that any index functions are
      90              :          * run as that user.  Also lock down security-restricted operations
      91              :          * and arrange to make GUC variable changes local to this command.
      92              :          */
      93         4035 :         GetUserIdAndSecContext(&save_userid, &save_sec_context);
      94         4035 :         SetUserIdAndSecContext(heaprel->rd_rel->relowner,
      95              :                                save_sec_context | SECURITY_RESTRICTED_OPERATION);
      96         4035 :         save_nestlevel = NewGUCNestLevel();
      97              :     }
      98              :     else
      99              :     {
     100            4 :         heaprel = NULL;
     101              :         /* Set these just to suppress "uninitialized variable" warnings */
     102            4 :         save_userid = InvalidOid;
     103            4 :         save_sec_context = -1;
     104            4 :         save_nestlevel = -1;
     105              :     }
     106              : 
     107              :     /*
     108              :      * Open the target index relations separately (like relation_openrv(), but
     109              :      * with heap relation locked first to prevent deadlocking).  In hot
     110              :      * standby mode this will raise an error when parentcheck is true.
     111              :      *
     112              :      * There is no need for the usual indcheckxmin usability horizon test
     113              :      * here, even in the heapallindexed case, because index undergoing
     114              :      * verification only needs to have entries for a new transaction snapshot.
     115              :      * (If this is a parentcheck verification, there is no question about
     116              :      * committed or recently dead heap tuples lacking index entries due to
     117              :      * concurrent activity.)
     118              :      */
     119         4039 :     indrel = index_open(indrelid, lockmode);
     120              : 
     121              :     /*
     122              :      * Since we did the IndexGetRelation call above without any lock, it's
     123              :      * barely possible that a race against an index drop/recreation could have
     124              :      * netted us the wrong table.
     125              :      */
     126         4035 :     if (heaprel == NULL || heapid != IndexGetRelation(indrelid, false))
     127            0 :         ereport(ERROR,
     128              :                 (errcode(ERRCODE_UNDEFINED_TABLE),
     129              :                  errmsg("could not open parent table of index \"%s\"",
     130              :                         RelationGetRelationName(indrel))));
     131              : 
     132              :     /* Check that relation suitable for checking */
     133         4035 :     if (index_checkable(indrel, am_id))
     134         4033 :         check(indrel, heaprel, state, lockmode == ShareLock);
     135              : 
     136              :     /* Roll back any GUC changes executed by index functions */
     137         3990 :     AtEOXact_GUC(false, save_nestlevel);
     138              : 
     139              :     /* Restore userid and security context */
     140         3990 :     SetUserIdAndSecContext(save_userid, save_sec_context);
     141              : 
     142              :     /*
     143              :      * Release locks early. That's ok here because nothing in the called
     144              :      * routines will trigger shared cache invalidations to be sent, so we can
     145              :      * relax the usual pattern of only releasing locks after commit.
     146              :      */
     147         3990 :     index_close(indrel, lockmode);
     148         3990 :     if (heaprel)
     149         3990 :         table_close(heaprel, lockmode);
     150         3990 : }
     151              : 
     152              : /*
     153              :  * Basic checks about the suitability of a relation for checking as an index.
     154              :  *
     155              :  *
     156              :  * NB: Intentionally not checking permissions, the function is normally not
     157              :  * callable by non-superusers. If granted, it's useful to be able to check a
     158              :  * whole cluster.
     159              :  */
     160              : static bool
     161         4035 : index_checkable(Relation rel, Oid am_id)
     162              : {
     163         4035 :     if (rel->rd_rel->relkind != RELKIND_INDEX)
     164            1 :         ereport(ERROR,
     165              :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     166              :                  errmsg("expected index as targets for verification"),
     167              :                  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
     168              : 
     169         4034 :     if (rel->rd_rel->relam != am_id)
     170            1 :         ereport(ERROR,
     171              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     172              :                  errmsg("expected \"%s\" index as targets for verification", get_am_name(am_id)),
     173              :                  errdetail("Relation \"%s\" is a %s index.",
     174              :                            RelationGetRelationName(rel), get_am_name(rel->rd_rel->relam))));
     175              : 
     176         4033 :     if (RELATION_IS_OTHER_TEMP(rel))
     177            0 :         ereport(ERROR,
     178              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     179              :                  errmsg("cannot access temporary tables of other sessions"),
     180              :                  errdetail("Index \"%s\" is associated with temporary relation.",
     181              :                            RelationGetRelationName(rel))));
     182              : 
     183         4033 :     if (!rel->rd_index->indisvalid)
     184            0 :         ereport(ERROR,
     185              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     186              :                  errmsg("cannot check index \"%s\"",
     187              :                         RelationGetRelationName(rel)),
     188              :                  errdetail("Index is not valid.")));
     189              : 
     190         4033 :     return amcheck_index_mainfork_expected(rel);
     191              : }
        

Generated by: LCOV version 2.0-1