tableam: Add helper for indexes to check if a corresponding table tuples exist.
authorAndres Freund <[email protected]>
Mon, 25 Mar 2019 23:52:55 +0000 (16:52 -0700)
committerAndres Freund <[email protected]>
Mon, 25 Mar 2019 23:52:55 +0000 (16:52 -0700)
This is, likely exclusively, useful to verify that conflicts detected
in a unique index are with live tuples, rather than dead ones.

Author: Andres Freund
Discussion: https://postgr.es/m/20180703070645[email protected]

src/backend/access/nbtree/nbtinsert.c
src/backend/access/nbtree/nbtsort.c
src/backend/access/table/tableam.c
src/include/access/tableam.h

index 2c98405aac889108e62e4b4d6c1f48659953bdf6..96b7593fc1c22b8d69db10a60f6d74d7bd6c26d2 100644 (file)
@@ -15,9 +15,9 @@
 
 #include "postgres.h"
 
-#include "access/heapam.h"
 #include "access/nbtree.h"
 #include "access/nbtxlog.h"
+#include "access/tableam.h"
 #include "access/transam.h"
 #include "access/xloginsert.h"
 #include "miscadmin.h"
@@ -431,12 +431,14 @@ _bt_check_unique(Relation rel, BTInsertState insertstate, Relation heapRel,
                                }
 
                                /*
-                                * We check the whole HOT-chain to see if there is any tuple
-                                * that satisfies SnapshotDirty.  This is necessary because we
-                                * have just a single index entry for the entire chain.
+                                * Check if there's any table tuples for this index entry
+                                * satisfying SnapshotDirty. This is necessary because for AMs
+                                * with optimizations like heap's HOT, we have just a single
+                                * index entry for the entire chain.
                                 */
-                               else if (heap_hot_search(&htid, heapRel, &SnapshotDirty,
-                                                                                &all_dead))
+                               else if (table_index_fetch_tuple_check(heapRel, &htid,
+                                                                                                          &SnapshotDirty,
+                                                                                                          &all_dead))
                                {
                                        TransactionId xwait;
 
@@ -489,7 +491,8 @@ _bt_check_unique(Relation rel, BTInsertState insertstate, Relation heapRel,
                                         * entry.
                                         */
                                        htid = itup->t_tid;
-                                       if (heap_hot_search(&htid, heapRel, SnapshotSelf, NULL))
+                                       if (table_index_fetch_tuple_check(heapRel, &htid,
+                                                                                                         SnapshotSelf, NULL))
                                        {
                                                /* Normal case --- it's still live */
                                        }
index 2762a2d54857c44e344f971831b3e4539fdf3cbe..46e0831834e5872f036117b55a35290dff04dd7f 100644 (file)
 
 #include "postgres.h"
 
-#include "access/heapam.h"
 #include "access/nbtree.h"
 #include "access/parallel.h"
 #include "access/relscan.h"
+#include "access/table.h"
 #include "access/tableam.h"
 #include "access/xact.h"
 #include "access/xlog.h"
index b1e3198291868c890647c8b1a95fb17c80db7e3c..8ad4c6294324a0df2e9ae0781b039965f286c3f4 100644 (file)
@@ -176,6 +176,40 @@ table_beginscan_parallel(Relation relation, ParallelTableScanDesc parallel_scan)
 }
 
 
+/* ----------------------------------------------------------------------------
+ * Index scan related functions.
+ * ----------------------------------------------------------------------------
+ */
+
+/*
+ * To perform that check simply start an index scan, create the necessary
+ * slot, do the heap lookup, and shut everything down again. This could be
+ * optimized, but is unlikely to matter from a performance POV. If there
+ * frequently are live index pointers also matching a unique index key, the
+ * CPU overhead of this routine is unlikely to matter.
+ */
+bool
+table_index_fetch_tuple_check(Relation rel,
+                                                         ItemPointer tid,
+                                                         Snapshot snapshot,
+                                                         bool *all_dead)
+{
+       IndexFetchTableData *scan;
+       TupleTableSlot *slot;
+       bool            call_again = false;
+       bool            found;
+
+       slot = table_slot_create(rel, NULL);
+       scan = table_index_fetch_begin(rel);
+       found = table_index_fetch_tuple(scan, tid, snapshot, slot, &call_again,
+                                                                       all_dead);
+       table_index_fetch_end(scan);
+       ExecDropSingleTupleTableSlot(slot);
+
+       return found;
+}
+
+
 /* ----------------------------------------------------------------------------
  * Functions to make modifications a bit simpler.
  * ----------------------------------------------------------------------------
index 51c370e6ca81f6d07a5552cb9a745d8fa1d5e753..29371f4c479bd9a7117968ec0c98bb3f66a9b3cc 100644 (file)
@@ -256,9 +256,10 @@ typedef struct TableAmRoutine
         * needs be set to true by index_fetch_tuple, signalling to the caller
         * that index_fetch_tuple should be called again for the same tid.
         *
-        * *all_dead should be set to true by index_fetch_tuple iff it is
-        * guaranteed that no backend needs to see that tuple. Index AMs can use
-        * that do avoid returning that tid in future searches.
+        * *all_dead, if all_dead is not NULL, should be set to true if by
+        * index_fetch_tuple iff it is guaranteed that no backend needs to see
+        * that tuple. Index AMs can use that do avoid returning that tid in
+        * future searches.
         */
        bool            (*index_fetch_tuple) (struct IndexFetchTableData *scan,
                                                                          ItemPointer tid,
@@ -594,9 +595,10 @@ table_index_fetch_end(struct IndexFetchTableData *scan)
  * will be set to true, signalling that table_index_fetch_tuple() should be called
  * again for the same tid.
  *
- * *all_dead will be set to true by table_index_fetch_tuple() iff it is guaranteed
- * that no backend needs to see that tuple. Index AMs can use that do avoid
- * returning that tid in future searches.
+ * *all_dead, if all_dead is not NULL, will be set to true by
+ * table_index_fetch_tuple() iff it is guaranteed that no backend needs to see
+ * that tuple. Index AMs can use that do avoid returning that tid in future
+ * searches.
  *
  * The difference between this function and table_fetch_row_version is that
  * this function returns the currently visible version of a row if the AM
@@ -618,6 +620,17 @@ table_index_fetch_tuple(struct IndexFetchTableData *scan,
                                                                                                        all_dead);
 }
 
+/*
+ * This is a convenience wrapper around table_index_fetch_tuple() which
+ * returns whether there are table tuple items corresponding to an index
+ * entry.  This likely is only useful to verify if there's a conflict in a
+ * unique index.
+ */
+extern bool table_index_fetch_tuple_check(Relation rel,
+                                                         ItemPointer tid,
+                                                         Snapshot snapshot,
+                                                         bool *all_dead);
+
 
 /* ------------------------------------------------------------------------
  * Functions for non-modifying operations on individual tuples