LCOV - code coverage report
Current view: top level - contrib/amcheck - verify_common.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 47 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 3 0
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            0 : amcheck_index_mainfork_expected(Relation rel)
      39              : {
      40            0 :         if (rel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED ||
      41            0 :                 !RecoveryInProgress())
      42            0 :                 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            0 : }
      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            0 : amcheck_lock_relation_and_check(Oid indrelid,
      63              :                                                                 Oid am_id,
      64              :                                                                 IndexDoCheckCallback check,
      65              :                                                                 LOCKMODE lockmode,
      66              :                                                                 void *state)
      67              : {
      68            0 :         Oid                     heapid;
      69            0 :         Relation        indrel;
      70            0 :         Relation        heaprel;
      71            0 :         Oid                     save_userid;
      72            0 :         int                     save_sec_context;
      73            0 :         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            0 :         heapid = IndexGetRelation(indrelid, true);
      84            0 :         if (OidIsValid(heapid))
      85              :         {
      86            0 :                 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            0 :                 GetUserIdAndSecContext(&save_userid, &save_sec_context);
      94            0 :                 SetUserIdAndSecContext(heaprel->rd_rel->relowner,
      95            0 :                                                            save_sec_context | SECURITY_RESTRICTED_OPERATION);
      96            0 :                 save_nestlevel = NewGUCNestLevel();
      97            0 :         }
      98              :         else
      99              :         {
     100            0 :                 heaprel = NULL;
     101              :                 /* Set these just to suppress "uninitialized variable" warnings */
     102            0 :                 save_userid = InvalidOid;
     103            0 :                 save_sec_context = -1;
     104            0 :                 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            0 :         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            0 :         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            0 :         if (index_checkable(indrel, am_id))
     134            0 :                 check(indrel, heaprel, state, lockmode == ShareLock);
     135              : 
     136              :         /* Roll back any GUC changes executed by index functions */
     137            0 :         AtEOXact_GUC(false, save_nestlevel);
     138              : 
     139              :         /* Restore userid and security context */
     140            0 :         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            0 :         index_close(indrel, lockmode);
     148            0 :         if (heaprel)
     149            0 :                 table_close(heaprel, lockmode);
     150            0 : }
     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            0 : index_checkable(Relation rel, Oid am_id)
     162              : {
     163            0 :         if (rel->rd_rel->relkind != RELKIND_INDEX)
     164            0 :                 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            0 :         if (rel->rd_rel->relam != am_id)
     170            0 :                 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            0 :         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            0 :         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            0 :         return amcheck_index_mainfork_expected(rel);
     191              : }
        

Generated by: LCOV version 2.3.2-1