Skip to content

Commit a1f9de1

Browse files
petergeogheganCommitfest Bot
authored andcommitted
Add amgetbatch interface for index scan prefetching.
Allows the index AM to provide items (TIDs and tuples) in batches, which is then used to implement prefetching of heap tuples in index scans (including index-only scans). This is similar to prefetching already done in bitmap scans, and can result in significant speedups. The index AM may implement an optional "amgetbatch" callback, returning a batch of items. The indexam.c code then handles this transparently through the existing "getnext" interface.
1 parent 882792c commit a1f9de1

File tree

34 files changed

+2106
-859
lines changed

34 files changed

+2106
-859
lines changed

contrib/bloom/blutils.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,7 @@ blhandler(PG_FUNCTION_ARGS)
148148
amroutine->amgettuple = NULL;
149149
amroutine->amgetbitmap = blgetbitmap;
150150
amroutine->amendscan = blendscan;
151-
amroutine->ammarkpos = NULL;
152-
amroutine->amrestrpos = NULL;
151+
amroutine->amposreset = NULL;
153152
amroutine->amestimateparallelscan = NULL;
154153
amroutine->aminitparallelscan = NULL;
155154
amroutine->amparallelrescan = NULL;

doc/src/sgml/indexam.sgml

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,7 @@ typedef struct IndexAmRoutine
163163
amgettuple_function amgettuple; /* can be NULL */
164164
amgetbitmap_function amgetbitmap; /* can be NULL */
165165
amendscan_function amendscan;
166-
ammarkpos_function ammarkpos; /* can be NULL */
167-
amrestrpos_function amrestrpos; /* can be NULL */
166+
amposreset_function amposreset; /* can be NULL */
168167

169168
/* interface functions to support parallel index scans */
170169
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
@@ -789,32 +788,25 @@ amendscan (IndexScanDesc scan);
789788
<para>
790789
<programlisting>
791790
void
792-
ammarkpos (IndexScanDesc scan);
791+
amposreset (IndexScanDesc scan);
793792
</programlisting>
794-
Mark current scan position. The access method need only support one
795-
remembered scan position per scan.
796-
</para>
797-
798-
<para>
799-
The <function>ammarkpos</function> function need only be provided if the access
800-
method supports ordered scans. If it doesn't,
801-
the <structfield>ammarkpos</structfield> field in its <structname>IndexAmRoutine</structname>
802-
struct may be set to NULL.
803-
</para>
804-
805-
<para>
806-
<programlisting>
807-
void
808-
amrestrpos (IndexScanDesc scan);
809-
</programlisting>
810-
Restore the scan to the most recently marked position.
811-
</para>
812-
813-
<para>
814-
The <function>amrestrpos</function> function need only be provided if the access
815-
method supports ordered scans. If it doesn't,
816-
the <structfield>amrestrpos</structfield> field in its <structname>IndexAmRoutine</structname>
817-
struct may be set to NULL.
793+
Notify index AM that core code will change the scan's position to an item
794+
returned as part of an earlier batch. The index AM must therefore
795+
invalidate any state that independently tracks the scan's progress
796+
(e.g., array keys used with a ScalarArrayOpExpr qual). Called by the core
797+
system when it is about to restore a mark.
798+
</para>
799+
800+
<para>
801+
The <function>amposreset</function> function need only be provided if the access
802+
method supports ordered scans through the <function>amgetbatch</function>
803+
interface. If it doesn't, the <structfield>amposreset</structfield> field
804+
in its <structname>IndexAmRoutine</structname> struct should be set to
805+
NULL. Index AMs that don't have any private state that might need to be
806+
invalidated might still find it useful to provide an empty
807+
<structfield>amposreset</structfield> function; if <function>amposreset</function>
808+
is set to NULL, the core system will assume that it is unsafe to restore a
809+
marked position.
818810
</para>
819811

820812
<para>

src/backend/access/brin/brin.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,7 @@ brinhandler(PG_FUNCTION_ARGS)
296296
amroutine->amgettuple = NULL;
297297
amroutine->amgetbitmap = bringetbitmap;
298298
amroutine->amendscan = brinendscan;
299-
amroutine->ammarkpos = NULL;
300-
amroutine->amrestrpos = NULL;
299+
amroutine->amposreset = NULL;
301300
amroutine->amestimateparallelscan = NULL;
302301
amroutine->aminitparallelscan = NULL;
303302
amroutine->amparallelrescan = NULL;

src/backend/access/gin/ginutil.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,7 @@ ginhandler(PG_FUNCTION_ARGS)
8484
amroutine->amgettuple = NULL;
8585
amroutine->amgetbitmap = gingetbitmap;
8686
amroutine->amendscan = ginendscan;
87-
amroutine->ammarkpos = NULL;
88-
amroutine->amrestrpos = NULL;
87+
amroutine->amposreset = NULL;
8988
amroutine->amestimateparallelscan = NULL;
9089
amroutine->aminitparallelscan = NULL;
9190
amroutine->amparallelrescan = NULL;

src/backend/access/gist/gist.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,7 @@ gisthandler(PG_FUNCTION_ARGS)
105105
amroutine->amgettuple = gistgettuple;
106106
amroutine->amgetbitmap = gistgetbitmap;
107107
amroutine->amendscan = gistendscan;
108-
amroutine->ammarkpos = NULL;
109-
amroutine->amrestrpos = NULL;
108+
amroutine->amposreset = NULL;
110109
amroutine->amestimateparallelscan = NULL;
111110
amroutine->aminitparallelscan = NULL;
112111
amroutine->amparallelrescan = NULL;

src/backend/access/hash/hash.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,7 @@ hashhandler(PG_FUNCTION_ARGS)
104104
amroutine->amgettuple = hashgettuple;
105105
amroutine->amgetbitmap = hashgetbitmap;
106106
amroutine->amendscan = hashendscan;
107-
amroutine->ammarkpos = NULL;
108-
amroutine->amrestrpos = NULL;
107+
amroutine->amposreset = NULL;
109108
amroutine->amestimateparallelscan = NULL;
110109
amroutine->aminitparallelscan = NULL;
111110
amroutine->amparallelrescan = NULL;

src/backend/access/heap/heapam_handler.c

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ heapam_index_fetch_begin(Relation rel)
8484
IndexFetchHeapData *hscan = palloc0(sizeof(IndexFetchHeapData));
8585

8686
hscan->xs_base.rel = rel;
87+
hscan->xs_base.rs = NULL;
8788
hscan->xs_cbuf = InvalidBuffer;
89+
hscan->xs_blk = InvalidBlockNumber;
8890

8991
return &hscan->xs_base;
9092
}
@@ -94,10 +96,14 @@ heapam_index_fetch_reset(IndexFetchTableData *scan)
9496
{
9597
IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan;
9698

99+
if (scan->rs)
100+
read_stream_reset(scan->rs);
101+
97102
if (BufferIsValid(hscan->xs_cbuf))
98103
{
99104
ReleaseBuffer(hscan->xs_cbuf);
100105
hscan->xs_cbuf = InvalidBuffer;
106+
hscan->xs_blk = InvalidBlockNumber;
101107
}
102108
}
103109

@@ -108,6 +114,9 @@ heapam_index_fetch_end(IndexFetchTableData *scan)
108114

109115
heapam_index_fetch_reset(scan);
110116

117+
if (scan->rs)
118+
read_stream_end(scan->rs);
119+
111120
pfree(hscan);
112121
}
113122

@@ -124,23 +133,37 @@ heapam_index_fetch_tuple(struct IndexFetchTableData *scan,
124133

125134
Assert(TTS_IS_BUFFERTUPLE(slot));
126135

127-
/* We can skip the buffer-switching logic if we're in mid-HOT chain. */
128-
if (!*call_again)
136+
/*
137+
* Switch to correct buffer if we don't have it already (we can skip this
138+
* if we're in mid-HOT chain)
139+
*/
140+
if (!*call_again && hscan->xs_blk != ItemPointerGetBlockNumber(tid))
129141
{
130-
/* Switch to correct buffer if we don't have it already */
131-
Buffer prev_buf = hscan->xs_cbuf;
142+
/* Remember this buffer's block number for next time */
143+
hscan->xs_blk = ItemPointerGetBlockNumber(tid);
132144

133-
hscan->xs_cbuf = ReleaseAndReadBuffer(hscan->xs_cbuf,
134-
hscan->xs_base.rel,
135-
ItemPointerGetBlockNumber(tid));
145+
if (BufferIsValid(hscan->xs_cbuf))
146+
ReleaseBuffer(hscan->xs_cbuf);
136147

137148
/*
138-
* Prune page, but only if we weren't already on this page
149+
* When using a read stream, the stream will already know which block
150+
* number comes next (though an assertion will verify a match below)
139151
*/
140-
if (prev_buf != hscan->xs_cbuf)
141-
heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf);
152+
if (scan->rs)
153+
hscan->xs_cbuf = read_stream_next_buffer(scan->rs, NULL);
154+
else
155+
hscan->xs_cbuf = ReadBuffer(hscan->xs_base.rel, hscan->xs_blk);
156+
157+
/*
158+
* Prune page when it is pinned for the first time
159+
*/
160+
heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf);
142161
}
143162

163+
/* Assert that the TID's block number's buffer is now pinned */
164+
Assert(BufferIsValid(hscan->xs_cbuf));
165+
Assert(BufferGetBlockNumber(hscan->xs_cbuf) == hscan->xs_blk);
166+
144167
/* Obtain share-lock on the buffer so we can examine visibility */
145168
LockBuffer(hscan->xs_cbuf, BUFFER_LOCK_SHARE);
146169
got_heap_tuple = heap_hot_search_buffer(tid,

src/backend/access/index/genam.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ RelationGetIndexScan(Relation indexRelation, int nkeys, int norderbys)
8989
scan->xs_snapshot = InvalidSnapshot; /* caller must initialize this */
9090
scan->numberOfKeys = nkeys;
9191
scan->numberOfOrderBys = norderbys;
92+
scan->batchState = NULL; /* used by amgetbatch index AMs */
9293

9394
/*
9495
* We allocate key workspace here, but it won't get filled until amrescan.

0 commit comments

Comments
 (0)