Index-only-scans, indexam API changes

Поиск
Список
Период
Сортировка
От Heikki Linnakangas
Тема Index-only-scans, indexam API changes
Дата
Msg-id 4A5ADFE6.6060507@enterprisedb.com
обсуждение исходный текст
Ответы Re: Index-only-scans, indexam API changes  (Greg Stark <gsstark@mit.edu>)
Re: Index-only-scans, indexam API changes  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
At the moment, amgettuple only returns pointers to heap tuples. There is
no way to return data from the index tuples. That needs to be changed to
support index-only scans.

I propose that we split index_getnext into two steps: fetching the next
match from the index (index_next()), and fetching the corresponding heap
tuple (index_fetch()). Patch attached. There is still a shorthand
index_getnext() that is compatible with the old function, but it now
just calls index_next()+index_fetch().

The new index_fetch() function can only fetch one match from a HOT
chain. That greatly simplifies the code in indexam.c. The only caller
needing to fetch more than one tuple from a HOT chain (= using
SnapshotAny) is CLUSTER, so I moved the HOT-chain following logic into
cluster.c, with small changes to heap_hot_search_buffer().

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com
*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
***************
*** 1490,1497 **** heap_fetch(Relation relation,
   * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
   * of a HOT chain), and buffer is the buffer holding this tuple.  We search
   * for the first chain member satisfying the given snapshot.  If one is
!  * found, we update *tid to reference that tuple's offset number, and
!  * return TRUE.  If no match, return FALSE without modifying *tid.
   *
   * If all_dead is not NULL, we check non-visible tuples to see if they are
   * globally dead; *all_dead is set TRUE if all members of the HOT chain
--- 1490,1503 ----
   * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
   * of a HOT chain), and buffer is the buffer holding this tuple.  We search
   * for the first chain member satisfying the given snapshot.  If one is
!  * found, we update *tid and *heapTuple to reference that tuple, and
!  * return TRUE.  If no match, return FALSE without modifying *tid (*heapTuple
!  * is undefined in that case).
!  *
!  * To fetch the next matching tuple in the chain, call again with 'firstCall'
!  * set to FALSE and *tid still pointing to the tuple returned by previous call.
!  * (normally only one tuple in a chain can be visible at a time, but that does
!  * not hold for special snapshots like SnapshotAny)
   *
   * If all_dead is not NULL, we check non-visible tuples to see if they are
   * globally dead; *all_dead is set TRUE if all members of the HOT chain
***************
*** 1503,1529 **** heap_fetch(Relation relation,
   */
  bool
  heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
!                        bool *all_dead)
  {
      Page        dp = (Page) BufferGetPage(buffer);
      TransactionId prev_xmax = InvalidTransactionId;
      OffsetNumber offnum;
      bool        at_chain_start;

!     if (all_dead)
          *all_dead = true;

      Assert(TransactionIdIsValid(RecentGlobalXmin));

      Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
      offnum = ItemPointerGetOffsetNumber(tid);
!     at_chain_start = true;

      /* Scan through possible multiple members of HOT-chain */
      for (;;)
      {
          ItemId        lp;
-         HeapTupleData heapTuple;

          /* check for bogus TID */
          if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
--- 1509,1537 ----
   */
  bool
  heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
!                        HeapTuple heapTuple, bool *all_dead, bool firstCall)
  {
      Page        dp = (Page) BufferGetPage(buffer);
      TransactionId prev_xmax = InvalidTransactionId;
      OffsetNumber offnum;
      bool        at_chain_start;
+     bool        skip;

!     if (all_dead && firstCall)
          *all_dead = true;

      Assert(TransactionIdIsValid(RecentGlobalXmin));

      Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
      offnum = ItemPointerGetOffsetNumber(tid);
!
!     at_chain_start = firstCall;
!     skip = !firstCall;

      /* Scan through possible multiple members of HOT-chain */
      for (;;)
      {
          ItemId        lp;

          /* check for bogus TID */
          if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
***************
*** 1546,1558 **** heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
              break;
          }

!         heapTuple.t_data = (HeapTupleHeader) PageGetItem(dp, lp);
!         heapTuple.t_len = ItemIdGetLength(lp);

          /*
           * Shouldn't see a HEAP_ONLY tuple at chain start.
           */
!         if (at_chain_start && HeapTupleIsHeapOnly(&heapTuple))
              break;

          /*
--- 1554,1566 ----
              break;
          }

!         heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
!         heapTuple->t_len = ItemIdGetLength(lp);

          /*
           * Shouldn't see a HEAP_ONLY tuple at chain start.
           */
!         if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
              break;

          /*
***************
*** 1561,1577 **** heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
           */
          if (TransactionIdIsValid(prev_xmax) &&
              !TransactionIdEquals(prev_xmax,
!                                  HeapTupleHeaderGetXmin(heapTuple.t_data)))
              break;

          /* If it's visible per the snapshot, we must return it */
!         if (HeapTupleSatisfiesVisibility(&heapTuple, snapshot, buffer))
          {
              ItemPointerSetOffsetNumber(tid, offnum);
              if (all_dead)
                  *all_dead = false;
              return true;
          }

          /*
           * If we can't see it, maybe no one else can either.  At caller
--- 1569,1586 ----
           */
          if (TransactionIdIsValid(prev_xmax) &&
              !TransactionIdEquals(prev_xmax,
!                                  HeapTupleHeaderGetXmin(heapTuple->t_data)))
              break;

          /* If it's visible per the snapshot, we must return it */
!         if (!skip && HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer))
          {
              ItemPointerSetOffsetNumber(tid, offnum);
              if (all_dead)
                  *all_dead = false;
              return true;
          }
+         skip = false;

          /*
           * If we can't see it, maybe no one else can either.  At caller
***************
*** 1579,1585 **** heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
           * transactions.
           */
          if (all_dead && *all_dead &&
!             HeapTupleSatisfiesVacuum(heapTuple.t_data, RecentGlobalXmin,
                                       buffer) != HEAPTUPLE_DEAD)
              *all_dead = false;

--- 1588,1594 ----
           * transactions.
           */
          if (all_dead && *all_dead &&
!             HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
                                       buffer) != HEAPTUPLE_DEAD)
              *all_dead = false;

***************
*** 1587,1599 **** heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
           * Check to see if HOT chain continues past this tuple; if so fetch
           * the next offnum and loop around.
           */
!         if (HeapTupleIsHotUpdated(&heapTuple))
          {
!             Assert(ItemPointerGetBlockNumber(&heapTuple.t_data->t_ctid) ==
                     ItemPointerGetBlockNumber(tid));
!             offnum = ItemPointerGetOffsetNumber(&heapTuple.t_data->t_ctid);
              at_chain_start = false;
!             prev_xmax = HeapTupleHeaderGetXmax(heapTuple.t_data);
          }
          else
              break;                /* end of chain */
--- 1596,1608 ----
           * Check to see if HOT chain continues past this tuple; if so fetch
           * the next offnum and loop around.
           */
!         if (HeapTupleIsHotUpdated(heapTuple))
          {
!             Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
                     ItemPointerGetBlockNumber(tid));
!             offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
              at_chain_start = false;
!             prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
          }
          else
              break;                /* end of chain */
***************
*** 1615,1624 **** heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
  {
      bool        result;
      Buffer        buffer;

      buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
      LockBuffer(buffer, BUFFER_LOCK_SHARE);
!     result = heap_hot_search_buffer(tid, buffer, snapshot, all_dead);
      LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
      ReleaseBuffer(buffer);
      return result;
--- 1624,1635 ----
  {
      bool        result;
      Buffer        buffer;
+     HeapTupleData heapTuple;

      buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
      LockBuffer(buffer, BUFFER_LOCK_SHARE);
!     result = heap_hot_search_buffer(tid, buffer, snapshot, &heapTuple,
!                                     all_dead, true);
      LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
      ReleaseBuffer(buffer);
      return result;
*** a/src/backend/access/index/genam.c
--- b/src/backend/access/index/genam.c
***************
*** 97,105 **** RelationGetIndexScan(Relation indexRelation,
      ItemPointerSetInvalid(&scan->xs_ctup.t_self);
      scan->xs_ctup.t_data = NULL;
      scan->xs_cbuf = InvalidBuffer;
-     scan->xs_hot_dead = false;
-     scan->xs_next_hot = InvalidOffsetNumber;
-     scan->xs_prev_xmax = InvalidTransactionId;

      /*
       * Let the AM fill in the key and any opaque data it wants.
--- 97,102 ----
*** a/src/backend/access/index/indexam.c
--- b/src/backend/access/index/indexam.c
***************
*** 20,26 ****
   *        index_insert    - insert an index tuple into a relation
   *        index_markpos    - mark a scan position
   *        index_restrpos    - restore a scan position
!  *        index_getnext    - get the next tuple from a scan
   *        index_getbitmap - get all tuples from a scan
   *        index_bulk_delete    - bulk deletion of index tuples
   *        index_vacuum_cleanup    - post-deletion cleanup of an index
--- 20,27 ----
   *        index_insert    - insert an index tuple into a relation
   *        index_markpos    - mark a scan position
   *        index_restrpos    - restore a scan position
!  *        index_next        - advance index scan to next match
!  *        index_fetch        - fetch heap tuple for current index scan position
   *        index_getbitmap - get all tuples from a scan
   *        index_bulk_delete    - bulk deletion of index tuples
   *        index_vacuum_cleanup    - post-deletion cleanup of an index
***************
*** 310,317 **** index_rescan(IndexScanDesc scan, ScanKey key)
          scan->xs_cbuf = InvalidBuffer;
      }

-     scan->xs_next_hot = InvalidOffsetNumber;
-
      scan->kill_prior_tuple = false;        /* for safety */

      FunctionCall2(procedure,
--- 311,316 ----
***************
*** 389,420 **** index_restrpos(IndexScanDesc scan)
      SCAN_CHECKS;
      GET_SCAN_PROCEDURE(amrestrpos);

-     scan->xs_next_hot = InvalidOffsetNumber;
-
      scan->kill_prior_tuple = false;        /* for safety */

      FunctionCall1(procedure, PointerGetDatum(scan));
  }

  /* ----------------
!  *        index_getnext - get the next heap tuple from a scan
   *
!  * The result is the next heap tuple satisfying the scan keys and the
!  * snapshot, or NULL if no more matching tuples exist.    On success,
!  * the buffer containing the heap tuple is pinned (the pin will be dropped
!  * at the next index_getnext or index_endscan).
   *
   * Note: caller must check scan->xs_recheck, and perform rechecking of the
   * scan keys if required.  We do not do that here because we don't have
   * enough information to do it efficiently in the general case.
   * ----------------
   */
! HeapTuple
! index_getnext(IndexScanDesc scan, ScanDirection direction)
  {
-     HeapTuple    heapTuple = &scan->xs_ctup;
-     ItemPointer tid = &heapTuple->t_self;
      FmgrInfo   *procedure;

      SCAN_CHECKS;
      GET_SCAN_PROCEDURE(amgettuple);
--- 388,415 ----
      SCAN_CHECKS;
      GET_SCAN_PROCEDURE(amrestrpos);

      scan->kill_prior_tuple = false;        /* for safety */

      FunctionCall1(procedure, PointerGetDatum(scan));
  }

  /* ----------------
!  *        index_next - get the next index tuple from a scan
   *
!  * Advances to the next index tuple satisfying the scan keys, returning TRUE
!  * if there was one, FALSE otherwise. The heap TID pointer of the next match
!  * is stored in scan->xs_ctup.self.
   *
   * Note: caller must check scan->xs_recheck, and perform rechecking of the
   * scan keys if required.  We do not do that here because we don't have
   * enough information to do it efficiently in the general case.
   * ----------------
   */
! bool
! index_next(IndexScanDesc scan, ScanDirection direction)
  {
      FmgrInfo   *procedure;
+     bool         found;

      SCAN_CHECKS;
      GET_SCAN_PROCEDURE(amgettuple);
***************
*** 422,641 **** index_getnext(IndexScanDesc scan, ScanDirection direction)
      Assert(TransactionIdIsValid(RecentGlobalXmin));

      /*
!      * We always reset xs_hot_dead; if we are here then either we are just
!      * starting the scan, or we previously returned a visible tuple, and in
!      * either case it's inappropriate to kill the prior index entry.
       */
!     scan->xs_hot_dead = false;

!     for (;;)
!     {
!         OffsetNumber offnum;
!         bool        at_chain_start;
!         Page        dp;

!         if (scan->xs_next_hot != InvalidOffsetNumber)
!         {
!             /*
!              * We are resuming scan of a HOT chain after having returned an
!              * earlier member.    Must still hold pin on current heap page.
!              */
!             Assert(BufferIsValid(scan->xs_cbuf));
!             Assert(ItemPointerGetBlockNumber(tid) ==
!                    BufferGetBlockNumber(scan->xs_cbuf));
!             Assert(TransactionIdIsValid(scan->xs_prev_xmax));
!             offnum = scan->xs_next_hot;
!             at_chain_start = false;
!             scan->xs_next_hot = InvalidOffsetNumber;
!         }
!         else
          {
!             bool        found;
!             Buffer        prev_buf;
!
!             /*
!              * If we scanned a whole HOT chain and found only dead tuples,
!              * tell index AM to kill its entry for that TID.
!              */
!             scan->kill_prior_tuple = scan->xs_hot_dead;
!
!             /*
!              * The AM's gettuple proc finds the next index entry matching the
!              * scan keys, and puts the TID in xs_ctup.t_self (ie, *tid). It
!              * should also set scan->xs_recheck, though we pay no attention to
!              * that here.
!              */
!             found = DatumGetBool(FunctionCall2(procedure,
!                                                PointerGetDatum(scan),
!                                                Int32GetDatum(direction)));
!
!             /* Reset kill flag immediately for safety */
!             scan->kill_prior_tuple = false;
!
!             /* If we're out of index entries, break out of outer loop */
!             if (!found)
!                 break;
!
!             pgstat_count_index_tuples(scan->indexRelation, 1);
!
!             /* Switch to correct buffer if we don't have it already */
!             prev_buf = scan->xs_cbuf;
!             scan->xs_cbuf = ReleaseAndReadBuffer(scan->xs_cbuf,
!                                                  scan->heapRelation,
!                                              ItemPointerGetBlockNumber(tid));
!
!             /*
!              * Prune page, but only if we weren't already on this page
!              */
!             if (prev_buf != scan->xs_cbuf)
!                 heap_page_prune_opt(scan->heapRelation, scan->xs_cbuf,
!                                     RecentGlobalXmin);
!
!             /* Prepare to scan HOT chain starting at index-referenced offnum */
!             offnum = ItemPointerGetOffsetNumber(tid);
!             at_chain_start = true;
!
!             /* We don't know what the first tuple's xmin should be */
!             scan->xs_prev_xmax = InvalidTransactionId;
!
!             /* Initialize flag to detect if all entries are dead */
!             scan->xs_hot_dead = true;
          }

!         /* Obtain share-lock on the buffer so we can examine visibility */
!         LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);

!         dp = (Page) BufferGetPage(scan->xs_cbuf);

-         /* Scan through possible multiple members of HOT-chain */
-         for (;;)
-         {
-             ItemId        lp;
-             ItemPointer ctid;
-
-             /* check for bogus TID */
-             if (offnum < FirstOffsetNumber ||
-                 offnum > PageGetMaxOffsetNumber(dp))
-                 break;
-
-             lp = PageGetItemId(dp, offnum);
-
-             /* check for unused, dead, or redirected items */
-             if (!ItemIdIsNormal(lp))
-             {
-                 /* We should only see a redirect at start of chain */
-                 if (ItemIdIsRedirected(lp) && at_chain_start)
-                 {
-                     /* Follow the redirect */
-                     offnum = ItemIdGetRedirect(lp);
-                     at_chain_start = false;
-                     continue;
-                 }
-                 /* else must be end of chain */
-                 break;
-             }
-
-             /*
-              * We must initialize all of *heapTuple (ie, scan->xs_ctup) since
-              * it is returned to the executor on success.
-              */
-             heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
-             heapTuple->t_len = ItemIdGetLength(lp);
-             ItemPointerSetOffsetNumber(tid, offnum);
-             heapTuple->t_tableOid = RelationGetRelid(scan->heapRelation);
-             ctid = &heapTuple->t_data->t_ctid;
-
-             /*
-              * Shouldn't see a HEAP_ONLY tuple at chain start.  (This test
-              * should be unnecessary, since the chain root can't be removed
-              * while we have pin on the index entry, but let's make it
-              * anyway.)
-              */
-             if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
-                 break;
-
-             /*
-              * The xmin should match the previous xmax value, else chain is
-              * broken.    (Note: this test is not optional because it protects
-              * us against the case where the prior chain member's xmax aborted
-              * since we looked at it.)
-              */
-             if (TransactionIdIsValid(scan->xs_prev_xmax) &&
-                 !TransactionIdEquals(scan->xs_prev_xmax,
-                                   HeapTupleHeaderGetXmin(heapTuple->t_data)))
-                 break;
-
-             /* If it's visible per the snapshot, we must return it */
-             if (HeapTupleSatisfiesVisibility(heapTuple, scan->xs_snapshot,
-                                              scan->xs_cbuf))
-             {
-                 /*
-                  * If the snapshot is MVCC, we know that it could accept at
-                  * most one member of the HOT chain, so we can skip examining
-                  * any more members.  Otherwise, check for continuation of the
-                  * HOT-chain, and set state for next time.
-                  */
-                 if (IsMVCCSnapshot(scan->xs_snapshot))
-                     scan->xs_next_hot = InvalidOffsetNumber;
-                 else if (HeapTupleIsHotUpdated(heapTuple))
-                 {
-                     Assert(ItemPointerGetBlockNumber(ctid) ==
-                            ItemPointerGetBlockNumber(tid));
-                     scan->xs_next_hot = ItemPointerGetOffsetNumber(ctid);
-                     scan->xs_prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
-                 }
-                 else
-                     scan->xs_next_hot = InvalidOffsetNumber;
-
-                 LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
-
-                 pgstat_count_heap_fetch(scan->indexRelation);
-
-                 return heapTuple;
-             }
-
-             /*
-              * If we can't see it, maybe no one else can either.  Check to see
-              * if the tuple is dead to all transactions.  If we find that all
-              * the tuples in the HOT chain are dead, we'll signal the index AM
-              * to not return that TID on future indexscans.
-              */
-             if (scan->xs_hot_dead &&
-                 HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
-                                          scan->xs_cbuf) != HEAPTUPLE_DEAD)
-                 scan->xs_hot_dead = false;
-
-             /*
-              * Check to see if HOT chain continues past this tuple; if so
-              * fetch the next offnum (we don't bother storing it into
-              * xs_next_hot, but must store xs_prev_xmax), and loop around.
-              */
-             if (HeapTupleIsHotUpdated(heapTuple))
-             {
-                 Assert(ItemPointerGetBlockNumber(ctid) ==
-                        ItemPointerGetBlockNumber(tid));
-                 offnum = ItemPointerGetOffsetNumber(ctid);
-                 at_chain_start = false;
-                 scan->xs_prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
-             }
-             else
-                 break;            /* end of chain */
-         }                        /* loop over a single HOT chain */
-
-         LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
-
-         /* Loop around to ask index AM for another TID */
-         scan->xs_next_hot = InvalidOffsetNumber;
-     }

!     /* Release any held pin on a heap page */
!     if (BufferIsValid(scan->xs_cbuf))
      {
!         ReleaseBuffer(scan->xs_cbuf);
!         scan->xs_cbuf = InvalidBuffer;
      }

!     return NULL;                /* failure exit */
  }

  /* ----------------
--- 417,541 ----
      Assert(TransactionIdIsValid(RecentGlobalXmin));

      /*
!      * The AM's gettuple proc finds the next index entry matching the scan
!      * keys, and puts the TID in xs_ctup.t_self. It should also set
!      * scan->xs_recheck, though we pay no attention to that here.
       */
!     found = DatumGetBool(FunctionCall2(procedure,
!                                        PointerGetDatum(scan),
!                                        Int32GetDatum(direction)));

!     /* Reset kill flag immediately for safety */
!     scan->kill_prior_tuple = false;

!     /* If we're out of index entries, release any held pin on a heap page */
!     if (!found)
!     {
!         if (BufferIsValid(scan->xs_cbuf))
          {
!             ReleaseBuffer(scan->xs_cbuf);
!             scan->xs_cbuf = InvalidBuffer;
          }
+         return false;
+     }

!     pgstat_count_index_tuples(scan->indexRelation, 1);

!     return true;
! }


! /* ----------------
!  *        index_fetch - fetch the heap tuple the current index pointer points to
!  *
!  * The result is the heap tuple the current index tuple points to if it
!  * matches the snapshot, or NULL if it doesn't. On success, the buffer
!  * containing the heap tuple is pinned (the pin will be dropped at the next
!  * index_fetch or index_endscan).
!  * ----------------
!  */
! HeapTuple
! index_fetch(IndexScanDesc scan)
! {
!     ItemPointer tid = &scan->xs_ctup.t_self;
!     bool        found;
!     Buffer        prev_buf;
!
!     /*
!      * We only fetch the first matching tuple in a chain, so we can't support
!      * SnapshotAny. All other snapshots only consider one tuple in a HOT chain
!      * as visible at any given moment.
!      */
!     Assert(scan->xs_snapshot != SnapshotAny);
!
!     /* Switch to correct buffer if we don't have it already */
!     prev_buf = scan->xs_cbuf;
!     scan->xs_cbuf = ReleaseAndReadBuffer(scan->xs_cbuf,
!                                          scan->heapRelation,
!                                          ItemPointerGetBlockNumber(tid));
!
!     /*
!      * Prune page, but only if we weren't already on this page
!      */
!     if (prev_buf != scan->xs_cbuf)
!         heap_page_prune_opt(scan->heapRelation, scan->xs_cbuf,
!                             RecentGlobalXmin);
!
!     /* Obtain share-lock on the buffer so we can examine visibility */
!     LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
!
!     found = heap_hot_search_buffer(tid, scan->xs_cbuf,
!                                    scan->xs_snapshot, &scan->xs_ctup,
!                                    &scan->kill_prior_tuple, true);
!     LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
!
!     if (found)
      {
!         Assert(!scan->kill_prior_tuple);
!
!         /*
!          * We must initialize all of *heapTuple (ie, scan->xs_ctup) since
!          * it is returned to the executor on success.
!          */
!         scan->xs_ctup.t_tableOid = RelationGetRelid(scan->heapRelation);
!
!         pgstat_count_heap_fetch(scan->indexRelation);
!
!         return &scan->xs_ctup;
      }
+     else
+         return NULL;
+ }

! /* ----------------
!  *        index_getnext - get the next heap tuple from a scan
!  *
!  * This is a shorthand for index_next() + index_fetch().
!  *
!  * The result is the next heap tuple satisfying the scan keys and the
!  * snapshot, or NULL if no more matching tuples exist.    On success,
!  * the buffer containing the heap tuple is pinned (the pin will be dropped
!  * at the next index_getnext or index_endscan).
!  *
!  * Note: caller must check scan->xs_recheck, and perform rechecking of the
!  * scan keys if required.  We do not do that here because we don't have
!  * enough information to do it efficiently in the general case.
!  * ----------------
!  */
! HeapTuple
! index_getnext(IndexScanDesc scan, ScanDirection direction)
! {
!     for(;;)
!     {
!         HeapTuple tup;
!
!         if (!index_next(scan, direction))
!             return NULL;
!
!         tup = index_fetch(scan);
!         if (tup != NULL)
!             return tup;
!     }
  }

  /* ----------------
*** a/src/backend/commands/cluster.c
--- b/src/backend/commands/cluster.c
***************
*** 772,777 **** copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
--- 772,778 ----
      TransactionId OldestXmin;
      TransactionId FreezeXid;
      RewriteState rwstate;
+     ItemPointerData tid;

      /*
       * Open the relations we need.
***************
*** 829,847 **** copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
      scan = index_beginscan(OldHeap, OldIndex,
                             SnapshotAny, 0, (ScanKey) NULL);

!     while ((tuple = index_getnext(scan, ForwardScanDirection)) != NULL)
      {
          HeapTuple    copiedTuple;
          bool        isdead;
          int            i;

          CHECK_FOR_INTERRUPTS();

!         /* Since we used no scan keys, should never need to recheck */
!         if (scan->xs_recheck)
!             elog(ERROR, "CLUSTER does not support lossy index conditions");

!         LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);

          switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin,
                                           scan->xs_cbuf))
--- 830,885 ----
      scan = index_beginscan(OldHeap, OldIndex,
                             SnapshotAny, 0, (ScanKey) NULL);

!     tuple = NULL;
!     for(;;)
      {
          HeapTuple    copiedTuple;
          bool        isdead;
          int            i;
+         bool        found;

          CHECK_FOR_INTERRUPTS();

!         /* Advance to next index tuple if we're done with current HOT chain */
!         if (tuple == NULL)
!         {
!             if (!index_next(scan, ForwardScanDirection))
!                 break;
!
!             /* Since we used no scan keys, should never need to recheck */
!             if (scan->xs_recheck)
!                 elog(ERROR, "CLUSTER does not support lossy index conditions");
!
!             tid = scan->xs_ctup.t_self;

!             scan->xs_cbuf = ReleaseAndReadBuffer(scan->xs_cbuf, OldHeap,
!                                                  ItemPointerGetBlockNumber(&tid));
!             /* Obtain share-lock on the buffer so we can examine visibility */
!             LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
!             found = heap_hot_search_buffer(&tid, scan->xs_cbuf,
!                                            scan->xs_snapshot, &scan->xs_ctup,
!                                            &scan->kill_prior_tuple, true);
!             if (!found)
!             {
!                 LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
!                 break;
!             }
!         }
!         else
!         {
!             /* Fetch next tuple from current HOT chain */
!             LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
!             found = heap_hot_search_buffer(&tid, scan->xs_cbuf,
!                                            scan->xs_snapshot, &scan->xs_ctup,
!                                            &scan->kill_prior_tuple, false);
!             if (!found)
!             {
!                 LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
!                 tuple = NULL;
!                 continue;
!             }
!         }
!         tuple = &scan->xs_ctup;

          switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin,
                                           scan->xs_cbuf))
*** a/src/backend/executor/nodeBitmapHeapscan.c
--- b/src/backend/executor/nodeBitmapHeapscan.c
***************
*** 382,390 **** bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
          {
              OffsetNumber offnum = tbmres->offsets[curslot];
              ItemPointerData tid;

              ItemPointerSet(&tid, page, offnum);
!             if (heap_hot_search_buffer(&tid, buffer, snapshot, NULL))
                  scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
          }
      }
--- 382,392 ----
          {
              OffsetNumber offnum = tbmres->offsets[curslot];
              ItemPointerData tid;
+             HeapTupleData heapTuple;

              ItemPointerSet(&tid, page, offnum);
!             if (heap_hot_search_buffer(&tid, buffer, snapshot, &heapTuple,
!                                        NULL, true))
                  scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
          }
      }
*** a/src/include/access/genam.h
--- b/src/include/access/genam.h
***************
*** 117,122 **** extern void index_endscan(IndexScanDesc scan);
--- 117,125 ----
  extern void index_markpos(IndexScanDesc scan);
  extern void index_restrpos(IndexScanDesc scan);
  extern HeapTuple index_getnext(IndexScanDesc scan, ScanDirection direction);
+ extern bool index_next(IndexScanDesc scan, ScanDirection direction);
+ extern HeapTuple index_fetch(IndexScanDesc scan);
+
  extern int64 index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap);

  extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info,
*** a/src/include/access/heapam.h
--- b/src/include/access/heapam.h
***************
*** 83,89 **** extern bool heap_fetch(Relation relation, Snapshot snapshot,
             HeapTuple tuple, Buffer *userbuf, bool keep_buf,
             Relation stats_relation);
  extern bool heap_hot_search_buffer(ItemPointer tid, Buffer buffer,
!                        Snapshot snapshot, bool *all_dead);
  extern bool heap_hot_search(ItemPointer tid, Relation relation,
                  Snapshot snapshot, bool *all_dead);

--- 83,90 ----
             HeapTuple tuple, Buffer *userbuf, bool keep_buf,
             Relation stats_relation);
  extern bool heap_hot_search_buffer(ItemPointer tid, Buffer buffer,
!                        Snapshot snapshot, HeapTuple tuple,
!                        bool *all_dead, bool firstCall);
  extern bool heap_hot_search(ItemPointer tid, Relation relation,
                  Snapshot snapshot, bool *all_dead);

*** a/src/include/access/relscan.h
--- b/src/include/access/relscan.h
***************
*** 77,87 **** typedef struct IndexScanDescData
      Buffer        xs_cbuf;        /* current heap buffer in scan, if any */
      /* NB: if xs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
      bool        xs_recheck;        /* T means scan keys must be rechecked */
-
-     /* state data for traversing HOT chains in index_getnext */
-     bool        xs_hot_dead;    /* T if all members of HOT chain are dead */
-     OffsetNumber xs_next_hot;    /* next member of HOT chain, if any */
-     TransactionId xs_prev_xmax; /* previous HOT chain member's XMAX, if any */
  } IndexScanDescData;

  /* Struct for heap-or-index scans of system tables */
--- 77,82 ----

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Heikki Linnakangas
Дата:
Сообщение: Index-only scans
Следующее
От: Greg Stark
Дата:
Сообщение: Re: Index-only-scans, indexam API changes