LCOV - differential code coverage report
Current view: top level - src/backend/access/index - genam.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GIC CBC EUB ECB
Current: Differential Code Coverage HEAD vs 15 Lines: 83.1 % 201 167 3 6 11 14 76 91 20 70
Current Date: 2023-04-08 15:15:32 Functions: 91.7 % 12 11 1 8 3 1 8
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * genam.c
       4                 :  *    general index access method routines
       5                 :  *
       6                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       7                 :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :  *
       9                 :  *
      10                 :  * IDENTIFICATION
      11                 :  *    src/backend/access/index/genam.c
      12                 :  *
      13                 :  * NOTES
      14                 :  *    many of the old access method routines have been turned into
      15                 :  *    macros and moved to genam.h -cim 4/30/91
      16                 :  *
      17                 :  *-------------------------------------------------------------------------
      18                 :  */
      19                 : 
      20                 : #include "postgres.h"
      21                 : 
      22                 : #include "access/genam.h"
      23                 : #include "access/heapam.h"
      24                 : #include "access/relscan.h"
      25                 : #include "access/tableam.h"
      26                 : #include "access/transam.h"
      27                 : #include "catalog/index.h"
      28                 : #include "lib/stringinfo.h"
      29                 : #include "miscadmin.h"
      30                 : #include "storage/bufmgr.h"
      31                 : #include "storage/procarray.h"
      32                 : #include "utils/acl.h"
      33                 : #include "utils/builtins.h"
      34                 : #include "utils/lsyscache.h"
      35                 : #include "utils/rel.h"
      36                 : #include "utils/rls.h"
      37                 : #include "utils/ruleutils.h"
      38                 : #include "utils/snapmgr.h"
      39                 : #include "utils/syscache.h"
      40                 : 
      41                 : 
      42                 : /* ----------------------------------------------------------------
      43                 :  *      general access method routines
      44                 :  *
      45                 :  *      All indexed access methods use an identical scan structure.
      46                 :  *      We don't know how the various AMs do locking, however, so we don't
      47                 :  *      do anything about that here.
      48                 :  *
      49                 :  *      The intent is that an AM implementor will define a beginscan routine
      50                 :  *      that calls RelationGetIndexScan, to fill in the scan, and then does
      51                 :  *      whatever kind of locking he wants.
      52                 :  *
      53                 :  *      At the end of a scan, the AM's endscan routine undoes the locking,
      54                 :  *      but does *not* call IndexScanEnd --- the higher-level index_endscan
      55                 :  *      routine does that.  (We can't do it in the AM because index_endscan
      56                 :  *      still needs to touch the IndexScanDesc after calling the AM.)
      57                 :  *
      58                 :  *      Because of this, the AM does not have a choice whether to call
      59                 :  *      RelationGetIndexScan or not; its beginscan routine must return an
      60                 :  *      object made by RelationGetIndexScan.  This is kinda ugly but not
      61                 :  *      worth cleaning up now.
      62                 :  * ----------------------------------------------------------------
      63                 :  */
      64                 : 
      65                 : /* ----------------
      66                 :  *  RelationGetIndexScan -- Create and fill an IndexScanDesc.
      67                 :  *
      68                 :  *      This routine creates an index scan structure and sets up initial
      69                 :  *      contents for it.
      70                 :  *
      71                 :  *      Parameters:
      72                 :  *              indexRelation -- index relation for scan.
      73                 :  *              nkeys -- count of scan keys (index qual conditions).
      74                 :  *              norderbys -- count of index order-by operators.
      75                 :  *
      76                 :  *      Returns:
      77                 :  *              An initialized IndexScanDesc.
      78                 :  * ----------------
      79                 :  */
      80                 : IndexScanDesc
      81 CBC     8905358 : RelationGetIndexScan(Relation indexRelation, int nkeys, int norderbys)
      82                 : {
      83                 :     IndexScanDesc scan;
      84                 : 
      85         8905358 :     scan = (IndexScanDesc) palloc(sizeof(IndexScanDescData));
      86                 : 
      87         8905358 :     scan->heapRelation = NULL;   /* may be set later */
      88         8905358 :     scan->xs_heapfetch = NULL;
      89         8905358 :     scan->indexRelation = indexRelation;
      90         8905358 :     scan->xs_snapshot = InvalidSnapshot; /* caller must initialize this */
      91         8905358 :     scan->numberOfKeys = nkeys;
      92         8905358 :     scan->numberOfOrderBys = norderbys;
      93                 : 
      94                 :     /*
      95                 :      * We allocate key workspace here, but it won't get filled until amrescan.
      96                 :      */
      97         8905358 :     if (nkeys > 0)
      98         8900408 :         scan->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * nkeys);
      99                 :     else
     100            4950 :         scan->keyData = NULL;
     101         8905358 :     if (norderbys > 0)
     102              96 :         scan->orderByData = (ScanKey) palloc(sizeof(ScanKeyData) * norderbys);
     103                 :     else
     104         8905262 :         scan->orderByData = NULL;
     105                 : 
     106         8905358 :     scan->xs_want_itup = false; /* may be set later */
     107                 : 
     108                 :     /*
     109                 :      * During recovery we ignore killed tuples and don't bother to kill them
     110                 :      * either. We do this because the xmin on the primary node could easily be
     111                 :      * later than the xmin on the standby node, so that what the primary
     112                 :      * thinks is killed is supposed to be visible on standby. So for correct
     113                 :      * MVCC for queries during recovery we must ignore these hints and check
     114                 :      * all tuples. Do *not* set ignore_killed_tuples to true when running in a
     115                 :      * transaction that was started during recovery. xactStartedInRecovery
     116                 :      * should not be altered by index AMs.
     117                 :      */
     118         8905358 :     scan->kill_prior_tuple = false;
     119         8905358 :     scan->xactStartedInRecovery = TransactionStartedDuringRecovery();
     120         8905358 :     scan->ignore_killed_tuples = !scan->xactStartedInRecovery;
     121                 : 
     122         8905358 :     scan->opaque = NULL;
     123                 : 
     124         8905358 :     scan->xs_itup = NULL;
     125         8905358 :     scan->xs_itupdesc = NULL;
     126         8905358 :     scan->xs_hitup = NULL;
     127         8905358 :     scan->xs_hitupdesc = NULL;
     128                 : 
     129         8905358 :     return scan;
     130                 : }
     131                 : 
     132                 : /* ----------------
     133                 :  *  IndexScanEnd -- End an index scan.
     134                 :  *
     135                 :  *      This routine just releases the storage acquired by
     136                 :  *      RelationGetIndexScan().  Any AM-level resources are
     137                 :  *      assumed to already have been released by the AM's
     138                 :  *      endscan routine.
     139                 :  *
     140                 :  *  Returns:
     141                 :  *      None.
     142                 :  * ----------------
     143                 :  */
     144                 : void
     145         8904646 : IndexScanEnd(IndexScanDesc scan)
     146                 : {
     147         8904646 :     if (scan->keyData != NULL)
     148         8899758 :         pfree(scan->keyData);
     149         8904646 :     if (scan->orderByData != NULL)
     150              93 :         pfree(scan->orderByData);
     151                 : 
     152         8904646 :     pfree(scan);
     153         8904646 : }
     154                 : 
     155                 : /*
     156                 :  * BuildIndexValueDescription
     157                 :  *
     158                 :  * Construct a string describing the contents of an index entry, in the
     159                 :  * form "(key_name, ...)=(key_value, ...)".  This is currently used
     160                 :  * for building unique-constraint and exclusion-constraint error messages,
     161                 :  * so only key columns of the index are checked and printed.
     162                 :  *
     163                 :  * Note that if the user does not have permissions to view all of the
     164                 :  * columns involved then a NULL is returned.  Returning a partial key seems
     165                 :  * unlikely to be useful and we have no way to know which of the columns the
     166                 :  * user provided (unlike in ExecBuildSlotValueDescription).
     167                 :  *
     168                 :  * The passed-in values/nulls arrays are the "raw" input to the index AM,
     169                 :  * e.g. results of FormIndexDatum --- this is not necessarily what is stored
     170                 :  * in the index, but it's what the user perceives to be stored.
     171                 :  *
     172                 :  * Note: if you change anything here, check whether
     173                 :  * ExecBuildSlotPartitionKeyDescription() in execMain.c needs a similar
     174                 :  * change.
     175                 :  */
     176                 : char *
     177             562 : BuildIndexValueDescription(Relation indexRelation,
     178                 :                            Datum *values, bool *isnull)
     179                 : {
     180                 :     StringInfoData buf;
     181                 :     Form_pg_index idxrec;
     182                 :     int         indnkeyatts;
     183                 :     int         i;
     184                 :     int         keyno;
     185             562 :     Oid         indexrelid = RelationGetRelid(indexRelation);
     186                 :     Oid         indrelid;
     187                 :     AclResult   aclresult;
     188                 : 
     189             562 :     indnkeyatts = IndexRelationGetNumberOfKeyAttributes(indexRelation);
     190                 : 
     191                 :     /*
     192                 :      * Check permissions- if the user does not have access to view all of the
     193                 :      * key columns then return NULL to avoid leaking data.
     194                 :      *
     195                 :      * First check if RLS is enabled for the relation.  If so, return NULL to
     196                 :      * avoid leaking data.
     197                 :      *
     198                 :      * Next we need to check table-level SELECT access and then, if there is
     199                 :      * no access there, check column-level permissions.
     200                 :      */
     201             562 :     idxrec = indexRelation->rd_index;
     202             562 :     indrelid = idxrec->indrelid;
     203             562 :     Assert(indexrelid == idxrec->indexrelid);
     204                 : 
     205                 :     /* RLS check- if RLS is enabled then we don't return anything. */
     206             562 :     if (check_enable_rls(indrelid, InvalidOid, true) == RLS_ENABLED)
     207               6 :         return NULL;
     208                 : 
     209                 :     /* Table-level SELECT is enough, if the user has it */
     210             556 :     aclresult = pg_class_aclcheck(indrelid, GetUserId(), ACL_SELECT);
     211             556 :     if (aclresult != ACLCHECK_OK)
     212                 :     {
     213                 :         /*
     214                 :          * No table-level access, so step through the columns in the index and
     215                 :          * make sure the user has SELECT rights on all of them.
     216                 :          */
     217              12 :         for (keyno = 0; keyno < indnkeyatts; keyno++)
     218                 :         {
     219              12 :             AttrNumber  attnum = idxrec->indkey.values[keyno];
     220                 : 
     221                 :             /*
     222                 :              * Note that if attnum == InvalidAttrNumber, then this is an index
     223                 :              * based on an expression and we return no detail rather than try
     224                 :              * to figure out what column(s) the expression includes and if the
     225                 :              * user has SELECT rights on them.
     226                 :              */
     227              24 :             if (attnum == InvalidAttrNumber ||
     228              12 :                 pg_attribute_aclcheck(indrelid, attnum, GetUserId(),
     229                 :                                       ACL_SELECT) != ACLCHECK_OK)
     230                 :             {
     231                 :                 /* No access, so clean up and return */
     232               6 :                 return NULL;
     233                 :             }
     234                 :         }
     235                 :     }
     236                 : 
     237             550 :     initStringInfo(&buf);
     238             550 :     appendStringInfo(&buf, "(%s)=(",
     239                 :                      pg_get_indexdef_columns(indexrelid, true));
     240                 : 
     241            1167 :     for (i = 0; i < indnkeyatts; i++)
     242                 :     {
     243                 :         char       *val;
     244                 : 
     245             617 :         if (isnull[i])
     246               9 :             val = "null";
     247                 :         else
     248                 :         {
     249                 :             Oid         foutoid;
     250                 :             bool        typisvarlena;
     251                 : 
     252                 :             /*
     253                 :              * The provided data is not necessarily of the type stored in the
     254                 :              * index; rather it is of the index opclass's input type. So look
     255                 :              * at rd_opcintype not the index tupdesc.
     256                 :              *
     257                 :              * Note: this is a bit shaky for opclasses that have pseudotype
     258                 :              * input types such as ANYARRAY or RECORD.  Currently, the
     259                 :              * typoutput functions associated with the pseudotypes will work
     260                 :              * okay, but we might have to try harder in future.
     261                 :              */
     262             608 :             getTypeOutputInfo(indexRelation->rd_opcintype[i],
     263                 :                               &foutoid, &typisvarlena);
     264             608 :             val = OidOutputFunctionCall(foutoid, values[i]);
     265                 :         }
     266                 : 
     267             617 :         if (i > 0)
     268              67 :             appendStringInfoString(&buf, ", ");
     269             617 :         appendStringInfoString(&buf, val);
     270                 :     }
     271                 : 
     272             550 :     appendStringInfoChar(&buf, ')');
     273                 : 
     274             550 :     return buf.data;
     275                 : }
     276                 : 
     277                 : /*
     278                 :  * Get the snapshotConflictHorizon from the table entries pointed to by the
     279                 :  * index tuples being deleted using an AM-generic approach.
     280                 :  *
     281                 :  * This is a table_index_delete_tuples() shim used by index AMs that only need
     282                 :  * to consult the tableam to get a snapshotConflictHorizon value, and only
     283                 :  * expect to delete index tuples that are already known deletable (typically
     284                 :  * due to having LP_DEAD bits set).  When a snapshotConflictHorizon value
     285                 :  * isn't needed in index AM's deletion WAL record, it is safe for it to skip
     286                 :  * calling here entirely.
     287                 :  *
     288                 :  * We assume that caller index AM uses the standard IndexTuple representation,
     289                 :  * with table TIDs stored in the t_tid field.  We also expect (and assert)
     290                 :  * that the line pointers on page for 'itemnos' offsets are already marked
     291                 :  * LP_DEAD.
     292                 :  */
     293                 : TransactionId
     294 UIC           0 : index_compute_xid_horizon_for_tuples(Relation irel,
     295 EUB             :                                      Relation hrel,
     296                 :                                      Buffer ibuf,
     297                 :                                      OffsetNumber *itemnos,
     298                 :                                      int nitems)
     299                 : {
     300                 :     TM_IndexDeleteOp delstate;
     301 UNC           0 :     TransactionId snapshotConflictHorizon = InvalidTransactionId;
     302 UBC           0 :     Page        ipage = BufferGetPage(ibuf);
     303 EUB             :     IndexTuple  itup;
     304                 : 
     305 UIC           0 :     Assert(nitems > 0);
     306 EUB             : 
     307 UIC           0 :     delstate.irel = irel;
     308 UBC           0 :     delstate.iblknum = BufferGetBlockNumber(ibuf);
     309               0 :     delstate.bottomup = false;
     310               0 :     delstate.bottomupfreespace = 0;
     311               0 :     delstate.ndeltids = 0;
     312               0 :     delstate.deltids = palloc(nitems * sizeof(TM_IndexDelete));
     313               0 :     delstate.status = palloc(nitems * sizeof(TM_IndexStatus));
     314 EUB             : 
     315                 :     /* identify what the index tuples about to be deleted point to */
     316 UIC           0 :     for (int i = 0; i < nitems; i++)
     317 EUB             :     {
     318 UIC           0 :         OffsetNumber offnum = itemnos[i];
     319 EUB             :         ItemId      iitemid;
     320                 : 
     321 UIC           0 :         iitemid = PageGetItemId(ipage, offnum);
     322 UBC           0 :         itup = (IndexTuple) PageGetItem(ipage, iitemid);
     323 EUB             : 
     324 UIC           0 :         Assert(ItemIdIsDead(iitemid));
     325 EUB             : 
     326 UIC           0 :         ItemPointerCopy(&itup->t_tid, &delstate.deltids[i].tid);
     327 UBC           0 :         delstate.deltids[i].id = delstate.ndeltids;
     328               0 :         delstate.status[i].idxoffnum = offnum;
     329               0 :         delstate.status[i].knowndeletable = true;   /* LP_DEAD-marked */
     330               0 :         delstate.status[i].promising = false;   /* unused */
     331               0 :         delstate.status[i].freespace = 0;   /* unused */
     332 EUB             : 
     333 UIC           0 :         delstate.ndeltids++;
     334 EUB             :     }
     335                 : 
     336                 :     /* determine the actual xid horizon */
     337 UNC           0 :     snapshotConflictHorizon = table_index_delete_tuples(hrel, &delstate);
     338 EUB             : 
     339                 :     /* assert tableam agrees that all items are deletable */
     340 UIC           0 :     Assert(delstate.ndeltids == nitems);
     341 EUB             : 
     342 UIC           0 :     pfree(delstate.deltids);
     343 UBC           0 :     pfree(delstate.status);
     344 EUB             : 
     345 UNC           0 :     return snapshotConflictHorizon;
     346 EUB             : }
     347                 : 
     348                 : 
     349                 : /* ----------------------------------------------------------------
     350                 :  *      heap-or-index-scan access to system catalogs
     351                 :  *
     352                 :  *      These functions support system catalog accesses that normally use
     353                 :  *      an index but need to be capable of being switched to heap scans
     354                 :  *      if the system indexes are unavailable.
     355                 :  *
     356                 :  *      The specified scan keys must be compatible with the named index.
     357                 :  *      Generally this means that they must constrain either all columns
     358                 :  *      of the index, or the first K columns of an N-column index.
     359                 :  *
     360                 :  *      These routines could work with non-system tables, actually,
     361                 :  *      but they're only useful when there is a known index to use with
     362                 :  *      the given scan keys; so in practice they're only good for
     363                 :  *      predetermined types of scans of system catalogs.
     364                 :  * ----------------------------------------------------------------
     365                 :  */
     366                 : 
     367                 : /*
     368                 :  * systable_beginscan --- set up for heap-or-index scan
     369                 :  *
     370                 :  *  rel: catalog to scan, already opened and suitably locked
     371                 :  *  indexId: OID of index to conditionally use
     372                 :  *  indexOK: if false, forces a heap scan (see notes below)
     373                 :  *  snapshot: time qual to use (NULL for a recent catalog snapshot)
     374                 :  *  nkeys, key: scan keys
     375                 :  *
     376                 :  * The attribute numbers in the scan key should be set for the heap case.
     377                 :  * If we choose to index, we reset them to 1..n to reference the index
     378                 :  * columns.  Note this means there must be one scankey qualification per
     379                 :  * index column!  This is checked by the Asserts in the normal, index-using
     380                 :  * case, but won't be checked if the heapscan path is taken.
     381                 :  *
     382                 :  * The routine checks the normal cases for whether an indexscan is safe,
     383                 :  * but caller can make additional checks and pass indexOK=false if needed.
     384                 :  * In standard case indexOK can simply be constant TRUE.
     385                 :  */
     386                 : SysScanDesc
     387 GIC     9189314 : systable_beginscan(Relation heapRelation,
     388 ECB             :                    Oid indexId,
     389                 :                    bool indexOK,
     390                 :                    Snapshot snapshot,
     391                 :                    int nkeys, ScanKey key)
     392                 : {
     393                 :     SysScanDesc sysscan;
     394                 :     Relation    irel;
     395                 : 
     396 GIC     9189314 :     if (indexOK &&
     397 CBC     8947477 :         !IgnoreSystemIndexes &&
     398         8667487 :         !ReindexIsProcessingIndex(indexId))
     399         8663012 :         irel = index_open(indexId, AccessShareLock);
     400 ECB             :     else
     401 GIC      526302 :         irel = NULL;
     402 ECB             : 
     403 GIC     9189310 :     sysscan = (SysScanDesc) palloc(sizeof(SysScanDescData));
     404 ECB             : 
     405 GIC     9189310 :     sysscan->heap_rel = heapRelation;
     406 CBC     9189310 :     sysscan->irel = irel;
     407         9189310 :     sysscan->slot = table_slot_create(heapRelation, NULL);
     408 ECB             : 
     409 GIC     9189310 :     if (snapshot == NULL)
     410 ECB             :     {
     411 GIC     8073730 :         Oid         relid = RelationGetRelid(heapRelation);
     412 ECB             : 
     413 GIC     8073730 :         snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
     414 CBC     8073730 :         sysscan->snapshot = snapshot;
     415 ECB             :     }
     416                 :     else
     417                 :     {
     418                 :         /* Caller is responsible for any snapshot. */
     419 GIC     1115580 :         sysscan->snapshot = NULL;
     420 ECB             :     }
     421                 : 
     422 GIC     9189310 :     if (irel)
     423 ECB             :     {
     424                 :         int         i;
     425                 : 
     426                 :         /* Change attribute numbers to be index column numbers. */
     427 GIC    24125029 :         for (i = 0; i < nkeys; i++)
     428 ECB             :         {
     429                 :             int         j;
     430                 : 
     431 GIC    24247050 :             for (j = 0; j < IndexRelationGetNumberOfAttributes(irel); j++)
     432 ECB             :             {
     433 GIC    24247050 :                 if (key[i].sk_attno == irel->rd_index->indkey.values[j])
     434 ECB             :                 {
     435 GIC    15462021 :                     key[i].sk_attno = j + 1;
     436 CBC    15462021 :                     break;
     437 ECB             :                 }
     438                 :             }
     439 GIC    15462021 :             if (j == IndexRelationGetNumberOfAttributes(irel))
     440 LBC           0 :                 elog(ERROR, "column is not in index");
     441 EUB             :         }
     442                 : 
     443 GIC     8663008 :         sysscan->iscan = index_beginscan(heapRelation, irel,
     444 ECB             :                                          snapshot, nkeys, 0);
     445 GIC     8663008 :         index_rescan(sysscan->iscan, key, nkeys, NULL, 0);
     446 CBC     8663008 :         sysscan->scan = NULL;
     447 ECB             :     }
     448                 :     else
     449                 :     {
     450                 :         /*
     451                 :          * We disallow synchronized scans when forced to use a heapscan on a
     452                 :          * catalog.  In most cases the desired rows are near the front, so
     453                 :          * that the unpredictable start point of a syncscan is a serious
     454                 :          * disadvantage; and there are no compensating advantages, because
     455                 :          * it's unlikely that such scans will occur in parallel.
     456                 :          */
     457 GIC      526302 :         sysscan->scan = table_beginscan_strat(heapRelation, snapshot,
     458 ECB             :                                               nkeys, key,
     459                 :                                               true, false);
     460 GIC      526302 :         sysscan->iscan = NULL;
     461 ECB             :     }
     462                 : 
     463                 :     /*
     464                 :      * If CheckXidAlive is set then set a flag to indicate that system table
     465                 :      * scan is in-progress.  See detailed comments in xact.c where these
     466                 :      * variables are declared.
     467                 :      */
     468 GIC     9189310 :     if (TransactionIdIsValid(CheckXidAlive))
     469 CBC         672 :         bsysscan = true;
     470 ECB             : 
     471 GIC     9189310 :     return sysscan;
     472 ECB             : }
     473                 : 
     474                 : /*
     475                 :  * HandleConcurrentAbort - Handle concurrent abort of the CheckXidAlive.
     476                 :  *
     477                 :  * Error out, if CheckXidAlive is aborted. We can't directly use
     478                 :  * TransactionIdDidAbort as after crash such transaction might not have been
     479                 :  * marked as aborted.  See detailed comments in xact.c where the variable
     480                 :  * is declared.
     481                 :  */
     482                 : static inline void
     483 GIC    19811170 : HandleConcurrentAbort()
     484 ECB             : {
     485 GIC    19811170 :     if (TransactionIdIsValid(CheckXidAlive) &&
     486 CBC         980 :         !TransactionIdIsInProgress(CheckXidAlive) &&
     487               7 :         !TransactionIdDidCommit(CheckXidAlive))
     488               7 :         ereport(ERROR,
     489 ECB             :                 (errcode(ERRCODE_TRANSACTION_ROLLBACK),
     490                 :                  errmsg("transaction aborted during system catalog scan")));
     491 GIC    19811163 : }
     492 ECB             : 
     493                 : /*
     494                 :  * systable_getnext --- get next tuple in a heap-or-index scan
     495                 :  *
     496                 :  * Returns NULL if no more tuples available.
     497                 :  *
     498                 :  * Note that returned tuple is a reference to data in a disk buffer;
     499                 :  * it must not be modified, and should be presumed inaccessible after
     500                 :  * next getnext() or endscan() call.
     501                 :  *
     502                 :  * XXX: It'd probably make sense to offer a slot based interface, at least
     503                 :  * optionally.
     504                 :  */
     505                 : HeapTuple
     506 GIC    19486879 : systable_getnext(SysScanDesc sysscan)
     507 ECB             : {
     508 GIC    19486879 :     HeapTuple   htup = NULL;
     509 ECB             : 
     510 GIC    19486879 :     if (sysscan->irel)
     511 ECB             :     {
     512 GIC    17708275 :         if (index_getnext_slot(sysscan->iscan, ForwardScanDirection, sysscan->slot))
     513 ECB             :         {
     514                 :             bool        shouldFree;
     515                 : 
     516 GIC    12749633 :             htup = ExecFetchSlotHeapTuple(sysscan->slot, false, &shouldFree);
     517 CBC    12749633 :             Assert(!shouldFree);
     518 ECB             : 
     519                 :             /*
     520                 :              * We currently don't need to support lossy index operators for
     521                 :              * any system catalog scan.  It could be done here, using the scan
     522                 :              * keys to drive the operator calls, if we arranged to save the
     523                 :              * heap attnums during systable_beginscan(); this is practical
     524                 :              * because we still wouldn't need to support indexes on
     525                 :              * expressions.
     526                 :              */
     527 GIC    12749633 :             if (sysscan->iscan->xs_recheck)
     528 LBC           0 :                 elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
     529 EUB             :         }
     530                 :     }
     531                 :     else
     532                 :     {
     533 GIC     1778604 :         if (table_scan_getnextslot(sysscan->scan, ForwardScanDirection, sysscan->slot))
     534 ECB             :         {
     535                 :             bool        shouldFree;
     536                 : 
     537 GIC     1597300 :             htup = ExecFetchSlotHeapTuple(sysscan->slot, false, &shouldFree);
     538 CBC     1597300 :             Assert(!shouldFree);
     539 ECB             :         }
     540                 :     }
     541                 : 
     542                 :     /*
     543                 :      * Handle the concurrent abort while fetching the catalog tuple during
     544                 :      * logical streaming of a transaction.
     545                 :      */
     546 GIC    19486878 :     HandleConcurrentAbort();
     547 ECB             : 
     548 GIC    19486871 :     return htup;
     549 ECB             : }
     550                 : 
     551                 : /*
     552                 :  * systable_recheck_tuple --- recheck visibility of most-recently-fetched tuple
     553                 :  *
     554                 :  * In particular, determine if this tuple would be visible to a catalog scan
     555                 :  * that started now.  We don't handle the case of a non-MVCC scan snapshot,
     556                 :  * because no caller needs that yet.
     557                 :  *
     558                 :  * This is useful to test whether an object was deleted while we waited to
     559                 :  * acquire lock on it.
     560                 :  *
     561                 :  * Note: we don't actually *need* the tuple to be passed in, but it's a
     562                 :  * good crosscheck that the caller is interested in the right tuple.
     563                 :  */
     564                 : bool
     565 GIC       89536 : systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
     566 ECB             : {
     567                 :     Snapshot    freshsnap;
     568                 :     bool        result;
     569                 : 
     570 GIC       89536 :     Assert(tup == ExecFetchSlotHeapTuple(sysscan->slot, false, NULL));
     571 ECB             : 
     572                 :     /*
     573                 :      * Trust that table_tuple_satisfies_snapshot() and its subsidiaries
     574                 :      * (commonly LockBuffer() and HeapTupleSatisfiesMVCC()) do not themselves
     575                 :      * acquire snapshots, so we need not register the snapshot.  Those
     576                 :      * facilities are too low-level to have any business scanning tables.
     577                 :      */
     578 GIC       89536 :     freshsnap = GetCatalogSnapshot(RelationGetRelid(sysscan->heap_rel));
     579 ECB             : 
     580 GIC       89536 :     result = table_tuple_satisfies_snapshot(sysscan->heap_rel,
     581 CBC       89536 :                                             sysscan->slot,
     582 ECB             :                                             freshsnap);
     583                 : 
     584                 :     /*
     585                 :      * Handle the concurrent abort while fetching the catalog tuple during
     586                 :      * logical streaming of a transaction.
     587                 :      */
     588 GIC       89536 :     HandleConcurrentAbort();
     589 ECB             : 
     590 GIC       89536 :     return result;
     591 ECB             : }
     592                 : 
     593                 : /*
     594                 :  * systable_endscan --- close scan, release resources
     595                 :  *
     596                 :  * Note that it's still up to the caller to close the heap relation.
     597                 :  */
     598                 : void
     599 GIC     9189046 : systable_endscan(SysScanDesc sysscan)
     600 ECB             : {
     601 GIC     9189046 :     if (sysscan->slot)
     602 ECB             :     {
     603 GIC     9189046 :         ExecDropSingleTupleTableSlot(sysscan->slot);
     604 CBC     9189046 :         sysscan->slot = NULL;
     605 ECB             :     }
     606                 : 
     607 GIC     9189046 :     if (sysscan->irel)
     608 ECB             :     {
     609 GIC     8662751 :         index_endscan(sysscan->iscan);
     610 CBC     8662751 :         index_close(sysscan->irel, AccessShareLock);
     611 ECB             :     }
     612                 :     else
     613 GIC      526295 :         table_endscan(sysscan->scan);
     614 ECB             : 
     615 GIC     9189046 :     if (sysscan->snapshot)
     616 CBC     8073466 :         UnregisterSnapshot(sysscan->snapshot);
     617 ECB             : 
     618                 :     /*
     619                 :      * Reset the bsysscan flag at the end of the systable scan.  See detailed
     620                 :      * comments in xact.c where these variables are declared.
     621                 :      */
     622 GIC     9189046 :     if (TransactionIdIsValid(CheckXidAlive))
     623 CBC         665 :         bsysscan = false;
     624 ECB             : 
     625 GIC     9189046 :     pfree(sysscan);
     626 CBC     9189046 : }
     627 ECB             : 
     628                 : 
     629                 : /*
     630                 :  * systable_beginscan_ordered --- set up for ordered catalog scan
     631                 :  *
     632                 :  * These routines have essentially the same API as systable_beginscan etc,
     633                 :  * except that they guarantee to return multiple matching tuples in
     634                 :  * index order.  Also, for largely historical reasons, the index to use
     635                 :  * is opened and locked by the caller, not here.
     636                 :  *
     637                 :  * Currently we do not support non-index-based scans here.  (In principle
     638                 :  * we could do a heapscan and sort, but the uses are in places that
     639                 :  * probably don't need to still work with corrupted catalog indexes.)
     640                 :  * For the moment, therefore, these functions are merely the thinest of
     641                 :  * wrappers around index_beginscan/index_getnext_slot.  The main reason for
     642                 :  * their existence is to centralize possible future support of lossy operators
     643                 :  * in catalog scans.
     644                 :  */
     645                 : SysScanDesc
     646 GIC       55407 : systable_beginscan_ordered(Relation heapRelation,
     647 ECB             :                            Relation indexRelation,
     648                 :                            Snapshot snapshot,
     649                 :                            int nkeys, ScanKey key)
     650                 : {
     651                 :     SysScanDesc sysscan;
     652                 :     int         i;
     653                 : 
     654                 :     /* REINDEX can probably be a hard error here ... */
     655 GIC       55407 :     if (ReindexIsProcessingIndex(RelationGetRelid(indexRelation)))
     656 LBC           0 :         elog(ERROR, "cannot do ordered scan on index \"%s\", because it is being reindexed",
     657 EUB             :              RelationGetRelationName(indexRelation));
     658                 :     /* ... but we only throw a warning about violating IgnoreSystemIndexes */
     659 GIC       55407 :     if (IgnoreSystemIndexes)
     660 LBC           0 :         elog(WARNING, "using index \"%s\" despite IgnoreSystemIndexes",
     661 EUB             :              RelationGetRelationName(indexRelation));
     662                 : 
     663 GIC       55407 :     sysscan = (SysScanDesc) palloc(sizeof(SysScanDescData));
     664 ECB             : 
     665 GIC       55407 :     sysscan->heap_rel = heapRelation;
     666 CBC       55407 :     sysscan->irel = indexRelation;
     667           55407 :     sysscan->slot = table_slot_create(heapRelation, NULL);
     668 ECB             : 
     669 GIC       55407 :     if (snapshot == NULL)
     670 ECB             :     {
     671 GIC        3799 :         Oid         relid = RelationGetRelid(heapRelation);
     672 ECB             : 
     673 GIC        3799 :         snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
     674 CBC        3799 :         sysscan->snapshot = snapshot;
     675 ECB             :     }
     676                 :     else
     677                 :     {
     678                 :         /* Caller is responsible for any snapshot. */
     679 GIC       51608 :         sysscan->snapshot = NULL;
     680 ECB             :     }
     681                 : 
     682                 :     /* Change attribute numbers to be index column numbers. */
     683 GIC      109569 :     for (i = 0; i < nkeys; i++)
     684 ECB             :     {
     685                 :         int         j;
     686                 : 
     687 GIC       55786 :         for (j = 0; j < IndexRelationGetNumberOfAttributes(indexRelation); j++)
     688 ECB             :         {
     689 GIC       55786 :             if (key[i].sk_attno == indexRelation->rd_index->indkey.values[j])
     690 ECB             :             {
     691 GIC       54162 :                 key[i].sk_attno = j + 1;
     692 CBC       54162 :                 break;
     693 ECB             :             }
     694                 :         }
     695 GIC       54162 :         if (j == IndexRelationGetNumberOfAttributes(indexRelation))
     696 LBC           0 :             elog(ERROR, "column is not in index");
     697 EUB             :     }
     698                 : 
     699 GIC       55407 :     sysscan->iscan = index_beginscan(heapRelation, indexRelation,
     700 ECB             :                                      snapshot, nkeys, 0);
     701 GIC       55407 :     index_rescan(sysscan->iscan, key, nkeys, NULL, 0);
     702 CBC       55407 :     sysscan->scan = NULL;
     703 ECB             : 
     704 GIC       55407 :     return sysscan;
     705 ECB             : }
     706                 : 
     707                 : /*
     708                 :  * systable_getnext_ordered --- get next tuple in an ordered catalog scan
     709                 :  */
     710                 : HeapTuple
     711 GIC      234759 : systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction)
     712 ECB             : {
     713 GIC      234759 :     HeapTuple   htup = NULL;
     714 ECB             : 
     715 GIC      234759 :     Assert(sysscan->irel);
     716 CBC      234759 :     if (index_getnext_slot(sysscan->iscan, direction, sysscan->slot))
     717          179970 :         htup = ExecFetchSlotHeapTuple(sysscan->slot, false, NULL);
     718 ECB             : 
     719                 :     /* See notes in systable_getnext */
     720 GIC      234756 :     if (htup && sysscan->iscan->xs_recheck)
     721 LBC           0 :         elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
     722 EUB             : 
     723                 :     /*
     724                 :      * Handle the concurrent abort while fetching the catalog tuple during
     725                 :      * logical streaming of a transaction.
     726                 :      */
     727 GIC      234756 :     HandleConcurrentAbort();
     728 ECB             : 
     729 GIC      234756 :     return htup;
     730 ECB             : }
     731                 : 
     732                 : /*
     733                 :  * systable_endscan_ordered --- close scan, release resources
     734                 :  */
     735                 : void
     736 GIC       55395 : systable_endscan_ordered(SysScanDesc sysscan)
     737 ECB             : {
     738 GIC       55395 :     if (sysscan->slot)
     739 ECB             :     {
     740 GIC       55395 :         ExecDropSingleTupleTableSlot(sysscan->slot);
     741 CBC       55395 :         sysscan->slot = NULL;
     742 ECB             :     }
     743                 : 
     744 GIC       55395 :     Assert(sysscan->irel);
     745 CBC       55395 :     index_endscan(sysscan->iscan);
     746           55395 :     if (sysscan->snapshot)
     747            3790 :         UnregisterSnapshot(sysscan->snapshot);
     748           55395 :     pfree(sysscan);
     749           55395 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a