#include "executor/nodeIndexonlyscan.h"
#include "executor/nodeIndexscan.h"
#include "storage/bufmgr.h"
+#include "storage/predicate.h"
#include "utils/memutils.h"
#include "utils/rel.h"
ExprContext *econtext;
ScanDirection direction;
IndexScanDesc scandesc;
- HeapTuple tuple;
TupleTableSlot *slot;
ItemPointer tid;
*/
while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
{
+ HeapTuple tuple = NULL;
+
/*
* We can skip the heap fetch if the TID references a heap page on
* which all tuples are known visible to everybody. In any case,
}
}
+ /*
+ * Predicate locks for index-only scans must be acquired at the page
+ * level when the heap is not accessed, since tuple-level predicate
+ * locks need the tuple's xmin value. If we had to visit the tuple
+ * anyway, then we already have the tuple-level lock and can skip the
+ * page lock.
+ */
+ if (tuple == NULL)
+ PredicateLockPage(scandesc->heapRelation,
+ ItemPointerGetBlockNumber(tid),
+ estate->es_snapshot);
+
return slot;
}
--- /dev/null
+Parsed test spec with 2 sessions
+
+starting permutation: rxwy1 c1 rywx2 c2
+step rxwy1: DELETE FROM taby WHERE id = (SELECT min(id) FROM tabx);
+step c1: COMMIT;
+step rywx2: DELETE FROM tabx WHERE id = (SELECT min(id) FROM taby);
+step c2: COMMIT;
+
+starting permutation: rxwy1 rywx2 c1 c2
+step rxwy1: DELETE FROM taby WHERE id = (SELECT min(id) FROM tabx);
+step rywx2: DELETE FROM tabx WHERE id = (SELECT min(id) FROM taby);
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR: could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rxwy1 rywx2 c2 c1
+step rxwy1: DELETE FROM taby WHERE id = (SELECT min(id) FROM tabx);
+step rywx2: DELETE FROM tabx WHERE id = (SELECT min(id) FROM taby);
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR: could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rywx2 rxwy1 c1 c2
+step rywx2: DELETE FROM tabx WHERE id = (SELECT min(id) FROM taby);
+step rxwy1: DELETE FROM taby WHERE id = (SELECT min(id) FROM tabx);
+step c1: COMMIT;
+step c2: COMMIT;
+ERROR: could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rywx2 rxwy1 c2 c1
+step rywx2: DELETE FROM tabx WHERE id = (SELECT min(id) FROM taby);
+step rxwy1: DELETE FROM taby WHERE id = (SELECT min(id) FROM tabx);
+step c2: COMMIT;
+step c1: COMMIT;
+ERROR: could not serialize access due to read/write dependencies among transactions
+
+starting permutation: rywx2 c2 rxwy1 c1
+step rywx2: DELETE FROM tabx WHERE id = (SELECT min(id) FROM taby);
+step c2: COMMIT;
+step rxwy1: DELETE FROM taby WHERE id = (SELECT min(id) FROM tabx);
+step c1: COMMIT;
test: partial-index
test: two-ids
test: multiple-row-versions
+test: index-only-scan
test: fk-contention
test: fk-deadlock
test: fk-deadlock2
--- /dev/null
+# index-only scan test
+#
+# This test tries to expose problems with the interaction between index-only
+# scans and SSI.
+#
+# Any overlap between the transactions must cause a serialization failure.
+
+setup
+{
+ CREATE TABLE tabx (id int NOT NULL);
+ INSERT INTO tabx SELECT generate_series(1,10000);
+ ALTER TABLE tabx ADD PRIMARY KEY (id);
+ CREATE TABLE taby (id int NOT NULL);
+ INSERT INTO taby SELECT generate_series(1,10000);
+ ALTER TABLE taby ADD PRIMARY KEY (id);
+}
+setup { VACUUM FREEZE ANALYZE tabx; }
+setup { VACUUM FREEZE ANALYZE taby; }
+
+teardown
+{
+ DROP TABLE tabx;
+ DROP TABLE taby;
+}
+
+session "s1"
+setup
+{
+ BEGIN ISOLATION LEVEL SERIALIZABLE;
+ SET LOCAL seq_page_cost = 0.1;
+ SET LOCAL random_page_cost = 0.1;
+ SET LOCAL cpu_tuple_cost = 0.03;
+}
+step "rxwy1" { DELETE FROM taby WHERE id = (SELECT min(id) FROM tabx); }
+step "c1" { COMMIT; }
+
+session "s2"
+setup
+{
+ BEGIN ISOLATION LEVEL SERIALIZABLE;
+ SET LOCAL seq_page_cost = 0.1;
+ SET LOCAL random_page_cost = 0.1;
+ SET LOCAL cpu_tuple_cost = 0.03;
+}
+step "rywx2" { DELETE FROM tabx WHERE id = (SELECT min(id) FROM taby); }
+step "c2" { COMMIT; }