diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 16802b8..f91f717 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -143,7 +143,7 @@ TidListEval(TidScanState *tidstate) */ if (tidstate->ss.ss_currentScanDesc == NULL) tidstate->ss.ss_currentScanDesc = - table_beginscan(tidstate->ss.ss_currentRelation, + table_beginscan_tid(tidstate->ss.ss_currentRelation, tidstate->ss.ps.state->es_snapshot, 0, NULL); scan = tidstate->ss.ss_currentScanDesc; diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h index 696451f..de2de9a 100644 --- a/src/include/access/tableam.h +++ b/src/include/access/tableam.h @@ -830,6 +830,21 @@ table_beginscan_sampling(Relation rel, Snapshot snapshot, } /* + * table_beginscan_tid is an alternative entry point for setting up a + * TableScanDesc for a Tid scan. Although that scan technology is + * really quite unlike a standard seqscan, there is just enough commonality to + * make it worth using the same data structure. + */ +static inline TableScanDesc +table_beginscan_tid(Relation rel, Snapshot snapshot, + int nkeys, struct ScanKeyData *key) +{ + uint32 flags = SO_ALLOW_STRAT | SO_ALLOW_SYNC | SO_ALLOW_PAGEMODE; + + return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, NULL, flags); +} + +/* * table_beginscan_analyze is an alternative entry point for setting up a * TableScanDesc for an ANALYZE scan. As with bitmap scans, it's worth using * the same data structure although the behavior is rather different. diff --git a/src/test/isolation/expected/tid-scan.out b/src/test/isolation/expected/tid-scan.out new file mode 100644 index 0000000..6801da5 --- /dev/null +++ b/src/test/isolation/expected/tid-scan.out @@ -0,0 +1,30 @@ +Parsed test spec with 2 sessions + +starting permutation: select1 select2 insert1 insert2 c1 c2 +step select1: SELECT * FROM t WHERE ctid = '(0,1)'; +a + +1 +step select2: SELECT * FROM t WHERE ctid = '(1,1)'; +a + +227 +step insert1: INSERT INTO t SELECT 501; +step insert2: INSERT INTO t SELECT 501; +step c1: COMMIT; +step c2: COMMIT; + +starting permutation: select1 select2 update1 update2 c1 c2 +step select1: SELECT * FROM t WHERE ctid = '(0,1)'; +a + +1 +step select2: SELECT * FROM t WHERE ctid = '(1,1)'; +a + +227 +step update1: UPDATE t SET a = 501 WHERE ctid = '(1,1)'; +step update2: UPDATE t SET a = 501 WHERE ctid = '(0,1)'; +step c1: COMMIT; +step c2: COMMIT; +ERROR: could not serialize access due to read/write dependencies among transactions diff --git a/src/test/isolation/specs/tid-scan.spec b/src/test/isolation/specs/tid-scan.spec new file mode 100644 index 0000000..f50fb8b --- /dev/null +++ b/src/test/isolation/specs/tid-scan.spec @@ -0,0 +1,42 @@ +# TID Scan test +# +# This test checks for unnecessary predicate locks on the entire +# table during a Tid scan with serializable isolation. + +setup +{ + CREATE TABLE t (a int); + INSERT INTO t SELECT generate_series(1,500); +} + +teardown +{ + DROP TABLE t; +} + +session "s1" +setup +{ + BEGIN ISOLATION LEVEL SERIALIZABLE; +} +step "select1" { SELECT * FROM t WHERE ctid = '(0,1)'; } +step "insert1" { INSERT INTO t SELECT 501; } +step "update1" { UPDATE t SET a = 501 WHERE ctid = '(1,1)'; } +step "c1" { COMMIT; } + +session "s2" +setup +{ + BEGIN ISOLATION LEVEL SERIALIZABLE; +} +step "select2" { SELECT * FROM t WHERE ctid = '(1,1)'; } +step "insert2" { INSERT INTO t SELECT 501; } +step "update2" { UPDATE t SET a = 501 WHERE ctid = '(0,1)'; } +step "c2" { COMMIT; } + +# This case, both session will be commit. +permutation "select1" "select2" "insert1" "insert2" "c1" "c2" + +# This case, session s2 will be failed because of +# serializable isolation violation. +permutation "select1" "select2" "update1" "update2" "c1" "c2"