Compare commits

...

34 Commits

Author SHA1 Message Date
Andrew Kane
89dc41ce2b Fixed crash with debugging disabled [skip ci] 2026-06-10 14:31:54 -07:00
Andrew Kane
06ab41c094 Removed extra whitespace [skip ci] 2026-06-10 13:49:39 -07:00
Andrew Kane
8a8bc90d6d Added support for amgetbatch for IVFFlat [skip ci] 2026-06-10 13:47:36 -07:00
Andrew Kane
91717c81b3 Improved batch size flexibility [skip ci] 2026-06-10 12:10:20 -07:00
Andrew Kane
98a7f94608 Added comment [skip ci] 2026-06-10 11:59:38 -07:00
Andrew Kane
7db3d67b2d Re-added check for iterative scans [skip ci] 2026-06-10 11:58:19 -07:00
Andrew Kane
1dd39f66de Minimized diff [skip ci] 2026-06-10 11:56:52 -07:00
Andrew Kane
cd431c51ca Removed unneeded check [skip ci] 2026-06-10 11:55:31 -07:00
Andrew Kane
73890f2f70 Fixed comment [skip ci] 2026-06-10 11:46:15 -07:00
Andrew Kane
18980387ec Added support for amgetbatch [skip ci] 2026-06-10 11:31:57 -07:00
Andrew Kane
12368bd79c Updated readme [skip ci] 2026-05-30 11:58:10 -07:00
Andrew Kane
88a0085459 Improved casting [skip ci] 2026-05-26 14:09:19 -07:00
Andrew Kane
ea23884efd Ran latest pgindent [skip ci] 2026-05-26 13:39:55 -07:00
Andrew Kane
3351f3d43e Simplified comment [skip ci] 2026-05-26 13:39:20 -07:00
Andrew Kane
d238409bec Merge pull request #979 from xingtanzjr/fix_ivf_create_concurrently
Fix CREATE INDEX CONCURRENTLY crash on ivfflat with assertion-enabled PostgreSQL
2026-04-27 03:28:52 -07:00
Zhang Jinrui
529f37175b Fix CREATE INDEX CONCURRENTLY crash on ivfflat with assertion-enabled PostgreSQL by passing anyvisible=false in ivfflat SampleRows 2026-04-27 10:15:32 +00:00
Andrew Kane
bce3946392 Added link to PgHero [skip ci] 2026-04-27 02:11:17 -07:00
Andrew Kane
13cb253d30 Added link to PgDog [skip ci] 2026-04-27 00:09:20 -07:00
Andrew Kane
41b3cdc011 Updated readme [skip ci] 2026-04-26 14:47:17 -07:00
Andrew Kane
ec02a96239 Updated readme [skip ci] 2026-04-26 14:45:12 -07:00
Andrew Kane
610d95b8d2 Moved scaling section [skip ci] 2026-04-26 13:12:28 -07:00
Andrew Kane
609d01f4c6 Added more scaling advice to readme [skip ci] 2026-04-26 12:56:07 -07:00
Andrew Kane
a7551a61ca Updated style [skip ci] 2026-04-14 16:21:32 -07:00
lychen0613
5c9a97af3b optimize: defer sample normalization when building ivfflat 2026-04-14 16:14:17 -07:00
Andrew Kane
8af675cd25 Added comment [skip ci] 2026-04-14 16:10:45 -07:00
Andrew Kane
97e0ed4464 Fixed compilation errors with Postgres 19 2026-04-14 15:46:10 -07:00
Andrew Kane
533ad160e0 Fixed CI 2026-04-14 13:51:04 -07:00
Andrew Kane
dfbd724a1f Changed rowstoskip to double to match reservoir_get_next_S 2026-04-14 13:38:20 -07:00
lychen0613
5f270c9663 Add benchmark for ivfflat sample rows 2026-04-14 21:56:10 +08:00
lychen0613
352ec5be29 fix: correctly use reservoir_get_next_S when building ivfflat 2026-04-14 17:57:40 +08:00
Andrew Kane
17916cad00 Improved includes [skip ci] 2026-02-25 17:35:23 -08:00
Andrew Kane
284f2f0fec Improved casting for HNSW scan memory limit [skip ci] 2026-02-25 12:59:17 -08:00
Andrew Kane
edd49863ba Improved limit for HNSW scan memory 2026-02-25 12:52:29 -08:00
Andrew Kane
411291189d Updated changelog [skip ci] 2026-02-25 11:28:43 -08:00
14 changed files with 205 additions and 86 deletions

View File

@@ -1,6 +1,6 @@
## 0.8.2 (2026-02-25)
- Fixed buffer overflow with parallel HNSW index build
- Fixed buffer overflow with parallel HNSW index build - [more info](https://github.com/pgvector/pgvector/issues/959)
- Improved `install` target on Windows
- Fixed `Index Searches` in `EXPLAIN` output for Postgres 18

View File

@@ -11,6 +11,8 @@ Store your vectors with the rest of your data. Supports:
Plus [ACID](https://en.wikipedia.org/wiki/ACID) compliance, point-in-time recovery, JOINs, and all of the other [great features](https://www.postgresql.org/about/) of Postgres
Have a lot of vectors? Use [quantization](#scaling) to scale
[![Build Status](https://github.com/pgvector/pgvector/actions/workflows/build.yml/badge.svg)](https://github.com/pgvector/pgvector/actions)
## Installation
@@ -314,6 +316,8 @@ For a large number of workers, you may need to increase `max_parallel_workers` (
The [index options](#index-options) also have a significant impact on build time (use the defaults unless seeing low recall)
Use [binary quantization](#binary-quantization) for faster build times at scale
### Indexing Progress
Check [indexing progress](https://www.postgresql.org/docs/current/progress-reporting.html#CREATE-INDEX-PROGRESS-REPORTING)
@@ -443,13 +447,7 @@ Exact indexes work well for conditions that match a low percentage of rows. Othe
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
```
With approximate indexes, filtering is applied *after* the index is scanned. If a condition matches 10% of rows, with HNSW and the default `hnsw.ef_search` of 40, only 4 rows will match on average. For more rows, increase `hnsw.ef_search`.
```sql
SET hnsw.ef_search = 200;
```
Starting with 0.8.0, you can enable [iterative index scans](#iterative-index-scans), which will automatically scan more of the index when needed.
With approximate indexes, filtering is applied *after* the index is scanned. If a condition matches 10% of rows, with HNSW and the default `hnsw.ef_search` of 40, only 4 rows will match on average. For more rows, enable [iterative index scans](#iterative-index-scans), which will automatically scan more of the index when needed.
```sql
SET hnsw.iterative_scan = strict_order;
@@ -673,6 +671,10 @@ SHOW shared_buffers;
Be sure to restart Postgres for changes to take effect.
### Storing
Use the `halfvec` type instead of `vector` for a smaller working set.
### Loading
Use `COPY` for bulk loading data ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/loading/example.py)).
@@ -687,6 +689,8 @@ Add any indexes *after* loading the initial data for best performance.
See index build time for [HNSW](#index-build-time) and [IVFFlat](#index-build-time-1).
Use [binary quantization](#binary-quantization) for smaller indexes and faster build times at scale.
In production environments, create indexes concurrently to avoid blocking writes.
```sql
@@ -717,6 +721,8 @@ SELECT * FROM items ORDER BY embedding <#> '[3,1,2]' LIMIT 5;
#### Approximate Search
Use [binary quantization](#binary-quantization) with re-ranking to keep indexes in-memory at scale.
To speed up queries with an IVFFlat index, increase the number of inverted lists (at the expense of recall).
```sql
@@ -732,21 +738,20 @@ REINDEX INDEX CONCURRENTLY index_name;
VACUUM table_name;
```
## Scaling
For a smaller working set:
1. Use the `halfvec` type instead of `vector` for tables
2. Use [binary quantization](#binary-quantization) for indexes (with re-ranking for search)
Scale vertically by increasing memory, CPU, and storage on a single instance. Use existing tools to [tune parameters](#tuning) and [monitor performance](#monitoring).
Scale horizontally with [replicas](https://www.postgresql.org/docs/current/hot-standby.html), or use [Citus](https://github.com/citusdata/citus), [PgDog](https://github.com/pgdogdev/pgdog), or another approach for sharding ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/citus/example.py)).
## Monitoring
Monitor performance with [pg_stat_statements](https://www.postgresql.org/docs/current/pgstatstatements.html) (be sure to add it to `shared_preload_libraries`).
```sql
CREATE EXTENSION pg_stat_statements;
```
Get the most time-consuming queries with:
```sql
SELECT query, calls, ROUND((total_plan_time + total_exec_time) / calls) AS avg_time_ms,
ROUND((total_plan_time + total_exec_time) / 60000) AS total_time_min
FROM pg_stat_statements ORDER BY total_plan_time + total_exec_time DESC LIMIT 20;
```
Use existing tools like [pg_stat_statements](https://www.postgresql.org/docs/current/pgstatstatements.html) or [PgHero](https://github.com/ankane/pghero) to monitor performance.
Monitor recall by comparing results from approximate search with exact search.
@@ -757,14 +762,6 @@ SELECT ...
COMMIT;
```
## Scaling
Scale pgvector the same way you scale Postgres.
Scale vertically by increasing memory, CPU, and storage on a single instance. Use existing tools to [tune parameters](#tuning) and [monitor performance](#monitoring).
Scale horizontally with [replicas](https://www.postgresql.org/docs/current/hot-standby.html), or use [Citus](https://github.com/citusdata/citus) or another approach for sharding ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/citus/example.py)).
## Languages
Use pgvector from any language with a Postgres client. You can even generate and store vectors in one language and query them in another.
@@ -878,6 +875,8 @@ No, but like other index types, youll likely see better performance if they d
SELECT pg_size_pretty(pg_relation_size('index_name'));
```
Use [half-precision indexing](#half-precision-indexing) or [binary quantization](#binary-quantization) for smaller indexes.
## Troubleshooting
#### Why isnt a query using an index?

View File

@@ -169,7 +169,7 @@ BitJaccardDistanceAvx512Popcount(uint32 bytes, unsigned char *ax, unsigned char
#endif
TARGET_XSAVE static bool
SupportsAvx512Popcount()
SupportsAvx512Popcount(void)
{
unsigned int exx[4] = {0, 0, 0, 0};

View File

@@ -13,6 +13,7 @@
#include "hnsw.h"
#include "miscadmin.h"
#include "nodes/pg_list.h"
#include "storage/lwlock.h"
#include "utils/float.h"
#include "utils/guc.h"
#include "utils/relcache.h"
@@ -257,6 +258,11 @@ hnswvalidate(Oid opclassoid)
return true;
}
static void
hnswunguardbatch(IndexScanDesc scan, IndexScanBatch batch)
{
}
/*
* Define index handler
*
@@ -278,6 +284,7 @@ hnswhandler(PG_FUNCTION_ARGS)
.amconsistentequality = false,
.amconsistentordering = false,
.amcanbackward = false,
.amcanmarkpos = false,
.amcanunique = false,
.amcanmulticol = false,
.amoptionalkey = true,
@@ -310,11 +317,14 @@ hnswhandler(PG_FUNCTION_ARGS)
.amadjustmembers = NULL,
.ambeginscan = hnswbeginscan,
.amrescan = hnswrescan,
.amgettuple = hnswgettuple,
.amgettuple = NULL,
.amgetbatch = hnswgetbatch,
.amunguardbatch = hnswunguardbatch,
.amkillitemsbatch = NULL,
.amgettransform = NULL,
.amgetbitmap = NULL,
.amendscan = hnswendscan,
.ammarkpos = NULL,
.amrestrpos = NULL,
.amposreset = NULL,
.amestimateparallelscan = NULL,
.aminitparallelscan = NULL,
.amparallelrescan = NULL,

View File

@@ -10,6 +10,10 @@
#include "lib/pairingheap.h"
#include "nodes/execnodes.h"
#include "port.h" /* for random() */
#include "storage/bufpage.h"
#include "storage/condition_variable.h"
#include "storage/lwlock.h"
#include "storage/s_lock.h"
#include "utils/relptr.h"
#include "utils/sampling.h"
#include "vector.h"
@@ -461,7 +465,7 @@ IndexBulkDeleteResult *hnswbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResu
IndexBulkDeleteResult *hnswvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats);
IndexScanDesc hnswbeginscan(Relation index, int nkeys, int norderbys);
void hnswrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys);
bool hnswgettuple(IndexScanDesc scan, ScanDirection dir);
IndexScanBatch hnswgetbatch(IndexScanDesc scan, IndexScanBatch priorbatch, ScanDirection dir);
void hnswendscan(IndexScanDesc scan);
static inline HnswNeighborArray *

View File

@@ -36,6 +36,8 @@
*/
#include "postgres.h"
#include <limits.h>
#include "access/genam.h"
#include "access/parallel.h"
#include "access/relscan.h"
@@ -52,6 +54,7 @@
#include "nodes/execnodes.h"
#include "optimizer/optimizer.h"
#include "storage/bufmgr.h"
#include "storage/condition_variable.h"
#include "tcop/tcopprot.h"
#include "utils/datum.h"
#include "utils/memutils.h"
@@ -800,7 +803,11 @@ HnswParallelScanAndInsert(Relation heapRel, Relation indexRel, HnswShared * hnsw
buildstate.hnswarea = hnswarea;
InitAllocator(&buildstate.allocator, &HnswSharedMemoryAlloc, &buildstate);
scan = table_beginscan_parallel(heapRel,
ParallelTableScanFromHnswShared(hnswshared));
ParallelTableScanFromHnswShared(hnswshared)
#if PG_VERSION_NUM >= 190000
,SO_NONE
#endif
);
reltuples = table_index_build_scan(heapRel, indexRel, indexInfo,
true, progress, BuildCallback,
(void *) &buildstate, scan);

View File

@@ -6,6 +6,7 @@
#include "nodes/execnodes.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/lwlock.h"
#include "utils/datum.h"
#include "utils/memutils.h"
#include "utils/rel.h"

View File

@@ -1,6 +1,9 @@
#include "postgres.h"
#include <limits.h>
#include "access/genam.h"
#include "access/indexbatch.h"
#include "access/relscan.h"
#include "hnsw.h"
#include "lib/pairingheap.h"
@@ -133,6 +136,11 @@ hnswbeginscan(Relation index, int nkeys, int norderbys)
double maxMemory;
scan = RelationGetIndexScan(index, nkeys, norderbys);
scan->maxitemsbatch = hnsw_ef_search * HNSW_HEAPTIDS;
/* unused but must be > 0 */
scan->batch_index_opaque_static = MAXALIGN(1);
scan->batch_index_opaque_dyn = 0;
scan->batch_tuples_workspace = 0;
so = (HnswScanOpaque) palloc(sizeof(HnswScanOpaqueData));
so->typeInfo = HnswGetTypeInfo(index);
@@ -151,7 +159,7 @@ hnswbeginscan(Relation index, int nkeys, int norderbys)
/* Calculate max memory */
/* Add 256 extra bytes to fill last block when close */
maxMemory = (double) work_mem * hnsw_scan_mem_multiplier * 1024.0 + 256;
so->maxMemory = Min(maxMemory, (double) SIZE_MAX);
so->maxMemory = Min(maxMemory, (double) (SIZE_MAX / 2));
scan->opaque = so;
@@ -182,12 +190,13 @@ hnswrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int no
}
/*
* Fetch the next tuple in the given scan
* Fetch the next batch in the given scan
*/
bool
hnswgettuple(IndexScanDesc scan, ScanDirection dir)
IndexScanBatch
hnswgetbatch(IndexScanDesc scan, IndexScanBatch priorbatch, ScanDirection dir)
{
HnswScanOpaque so = (HnswScanOpaque) scan->opaque;
IndexScanBatch batch = indexam_util_alloc_batch(scan);
MemoryContext oldCtx = MemoryContextSwitchTo(so->tmpCtx);
/*
@@ -243,6 +252,7 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir)
HnswSearchCandidate *sc;
HnswElement element;
ItemPointer heaptid;
int nitems = 0;
if (list_length(so->w) == 0)
{
@@ -288,6 +298,8 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir)
break;
}
while (list_length(so->w) != 0)
{
sc = llast(so->w);
element = HnswPtrAccess(base, sc->element);
@@ -316,16 +328,34 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir)
so->previousDistance = sc->distance;
}
batch->items[nitems].tableTid = *heaptid;
batch->items[nitems].indexOffset = -1;
batch->items[nitems].tupleOffset = 0;
nitems++;
/* Keep batch size flexible */
if (nitems == scan->maxitemsbatch)
break;
}
/* Needed for strict iterative scans */
if (nitems == 0)
continue;
MemoryContextSwitchTo(oldCtx);
scan->xs_heaptid = *heaptid;
scan->xs_recheck = false;
scan->xs_recheckorderby = false;
return true;
batch->firstItem = 0;
batch->lastItem = nitems - 1;
batch->dir = ForwardScanDirection;
return batch;
}
MemoryContextSwitchTo(oldCtx);
return false;
indexam_util_release_batch(scan, batch);
return NULL;
}
/*

View File

@@ -22,6 +22,7 @@
#include "nodes/execnodes.h"
#include "optimizer/optimizer.h"
#include "storage/bufmgr.h"
#include "storage/condition_variable.h"
#include "tcop/tcopprot.h"
#include "utils/memutils.h"
#include "utils/rel.h"
@@ -62,15 +63,13 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
/*
* Normalize with KMEANS_NORM_PROC since spherical distance function
* expects unit vectors
* Check with KMEANS_NORM_PROC that the value can be normalized since
* spherical distance function expects unit vectors
*/
if (buildstate->kmeansnormprocinfo != NULL)
{
if (!IvfflatCheckNorm(buildstate->kmeansnormprocinfo, buildstate->collation, value))
return;
value = IvfflatNormValue(buildstate->typeInfo, buildstate->collation, value);
}
if (samples->length < targsamples)
@@ -81,7 +80,7 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
else
{
if (buildstate->rowstoskip < 0)
buildstate->rowstoskip = reservoir_get_next_S(&buildstate->rstate, samples->length, targsamples);
buildstate->rowstoskip = reservoir_get_next_S(&buildstate->rstate, buildstate->samplerows, targsamples);
if (buildstate->rowstoskip <= 0)
{
@@ -97,6 +96,9 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
buildstate->rowstoskip -= 1;
}
/* Increment after reservoir_get_next_S */
buildstate->samplerows += 1;
}
/*
@@ -133,6 +135,7 @@ SampleRows(IvfflatBuildState * buildstate)
int targsamples = buildstate->samples->maxlen;
BlockNumber totalblocks = RelationGetNumberOfBlocks(buildstate->heap);
buildstate->samplerows = 0;
buildstate->rowstoskip = -1;
BlockSampler_Init(&buildstate->bs, totalblocks, targsamples, RandomInt());
@@ -142,8 +145,24 @@ SampleRows(IvfflatBuildState * buildstate)
{
BlockNumber targblock = BlockSampler_Next(&buildstate->bs);
/* Set anyvisible to false like table_index_build_scan */
table_index_build_range_scan(buildstate->heap, buildstate->index, buildstate->indexInfo,
false, true, false, targblock, 1, SampleCallback, (void *) buildstate, NULL);
false, false, false, targblock, 1, SampleCallback, (void *) buildstate, NULL);
}
/* Normalize if needed */
if (buildstate->kmeansnormprocinfo != NULL)
{
VectorArray samples = buildstate->samples;
for (int i = 0; i < samples->length; i++)
{
Datum value = PointerGetDatum(VectorArrayGet(samples, i));
Datum normValue = IvfflatNormValue(buildstate->typeInfo, buildstate->collation, value);
VectorArraySet(samples, i, DatumGetPointer(normValue));
pfree(DatumGetPointer(normValue));
}
}
}
@@ -374,6 +393,9 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 1, "list", INT4OID, -1, 0);
TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 2, "tid", TIDOID, -1, 0);
TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 3, "vector", TupleDescAttr(buildstate->tupdesc, 0)->atttypid, -1, 0);
#if PG_VERSION_NUM >= 190000
TupleDescFinalize(buildstate->sortdesc);
#endif
buildstate->slot = MakeSingleTupleTableSlot(buildstate->sortdesc, &TTSOpsVirtual);
@@ -435,7 +457,7 @@ ComputeCenters(IvfflatBuildState * buildstate)
buildstate->samples = VectorArrayInit(numSamples, buildstate->dimensions, buildstate->centers->itemsize);
if (buildstate->heap != NULL)
{
SampleRows(buildstate);
IvfflatBench("sample rows", SampleRows(buildstate));
if (buildstate->samples->length < buildstate->lists)
{
@@ -650,7 +672,11 @@ IvfflatParallelScanAndSort(IvfflatSpool * ivfspool, IvfflatShared * ivfshared, S
ivfspool->sortstate = InitBuildSortState(buildstate.sortdesc, sortmem, coordinate);
buildstate.sortstate = ivfspool->sortstate;
scan = table_beginscan_parallel(ivfspool->heap,
ParallelTableScanFromIvfflatShared(ivfshared));
ParallelTableScanFromIvfflatShared(ivfshared)
#if PG_VERSION_NUM >= 190000
,SO_NONE
#endif
);
reltuples = table_index_build_scan(ivfspool->heap, ivfspool->index, indexInfo,
true, progress, BuildCallback,
(void *) &buildstate, scan);

View File

@@ -175,6 +175,11 @@ ivfflatvalidate(Oid opclassoid)
return true;
}
static void
ivfflatunguardbatch(IndexScanDesc scan, IndexScanBatch batch)
{
}
/*
* Define index handler
*
@@ -196,6 +201,7 @@ ivfflathandler(PG_FUNCTION_ARGS)
.amconsistentequality = false,
.amconsistentordering = false,
.amcanbackward = false,
.amcanmarkpos = false,
.amcanunique = false,
.amcanmulticol = false,
.amoptionalkey = true,
@@ -228,11 +234,14 @@ ivfflathandler(PG_FUNCTION_ARGS)
.amadjustmembers = NULL,
.ambeginscan = ivfflatbeginscan,
.amrescan = ivfflatrescan,
.amgettuple = ivfflatgettuple,
.amgettuple = NULL,
.amgetbatch = ivfflatgetbatch,
.amunguardbatch = ivfflatunguardbatch,
.amkillitemsbatch = NULL,
.amgettransform = NULL,
.amgetbitmap = NULL,
.amendscan = ivfflatendscan,
.ammarkpos = NULL,
.amrestrpos = NULL,
.amposreset = NULL,
.amestimateparallelscan = NULL,
.aminitparallelscan = NULL,
.amparallelrescan = NULL,

View File

@@ -9,6 +9,7 @@
#include "lib/pairingheap.h"
#include "nodes/execnodes.h"
#include "port.h" /* for random() */
#include "storage/condition_variable.h"
#include "utils/sampling.h"
#include "utils/tuplesort.h"
#include "vector.h"
@@ -213,7 +214,8 @@ typedef struct IvfflatBuildState
/* Sampling */
BlockSamplerData bs;
ReservoirStateData rstate;
int rowstoskip;
double samplerows;
double rowstoskip;
/* Sorting */
Tuplesortstate *sortstate;
@@ -342,7 +344,7 @@ IndexBulkDeleteResult *ivfflatbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteR
IndexBulkDeleteResult *ivfflatvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats);
IndexScanDesc ivfflatbeginscan(Relation index, int nkeys, int norderbys);
void ivfflatrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys);
bool ivfflatgettuple(IndexScanDesc scan, ScanDirection dir);
IndexScanBatch ivfflatgetbatch(IndexScanDesc scan, IndexScanBatch priorbatch, ScanDirection dir);
void ivfflatendscan(IndexScanDesc scan);
#endif

View File

@@ -3,6 +3,7 @@
#include <float.h>
#include "access/genam.h"
#include "access/indexbatch.h"
#include "access/itup.h"
#include "access/relscan.h"
#include "access/tupdesc.h"
@@ -261,6 +262,11 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
MemoryContext oldCtx;
scan = RelationGetIndexScan(index, nkeys, norderbys);
scan->maxitemsbatch = 1000;
/* unused but must be > 0 */
scan->batch_index_opaque_static = MAXALIGN(1);
scan->batch_index_opaque_dyn = 0;
scan->batch_tuples_workspace = 0;
/* Get lists and dimensions from metapage */
IvfflatGetMetaPageInfo(index, &lists, &dimensions);
@@ -298,6 +304,9 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
so->tupdesc = CreateTemplateTupleDesc(2);
TupleDescInitEntry(so->tupdesc, (AttrNumber) 1, "distance", FLOAT8OID, -1, 0);
TupleDescInitEntry(so->tupdesc, (AttrNumber) 2, "heaptid", TIDOID, -1, 0);
#if PG_VERSION_NUM >= 190000
TupleDescFinalize(so->tupdesc);
#endif
/* Prep sort */
so->sortstate = InitScanSortState(so->tupdesc);
@@ -345,14 +354,16 @@ ivfflatrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int
}
/*
* Fetch the next tuple in the given scan
* Fetch the next batch in the given scan
*/
bool
ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
IndexScanBatch
ivfflatgetbatch(IndexScanDesc scan, IndexScanBatch priorbatch, ScanDirection dir)
{
IvfflatScanOpaque so = (IvfflatScanOpaque) scan->opaque;
IndexScanBatch batch = indexam_util_alloc_batch(scan);
ItemPointer heaptid;
bool isnull;
int nitems = 0;
/*
* Index can be used to scan backward, but Postgres doesn't support
@@ -390,17 +401,37 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
while (!tuplesort_gettupleslot(so->sortstate, true, false, so->mslot, NULL))
{
if (so->listIndex == so->maxProbes)
return false;
{
indexam_util_release_batch(scan, batch);
return NULL;
}
IvfflatBench("GetScanItems", GetScanItems(scan, so->value));
}
for (;;)
{
heaptid = (ItemPointer) DatumGetPointer(slot_getattr(so->mslot, 2, &isnull));
scan->xs_heaptid = *heaptid;
batch->items[nitems].tableTid = *heaptid;
batch->items[nitems].indexOffset = -1;
batch->items[nitems].tupleOffset = 0;
nitems++;
if (nitems == scan->maxitemsbatch)
break;
if (!tuplesort_gettupleslot(so->sortstate, true, false, so->mslot, NULL))
break;
}
scan->xs_recheck = false;
scan->xs_recheckorderby = false;
return true;
batch->firstItem = 0;
batch->lastItem = nitems - 1;
batch->dir = ForwardScanDirection;
return batch;
}
/*

View File

@@ -182,10 +182,10 @@ sparsevec_isspace(char ch)
static int
CompareIndices(const void *a, const void *b)
{
if (((SparseInputElement *) a)->index < ((SparseInputElement *) b)->index)
if (((const SparseInputElement *) a)->index < ((const SparseInputElement *) b)->index)
return -1;
if (((SparseInputElement *) a)->index > ((SparseInputElement *) b)->index)
if (((const SparseInputElement *) a)->index > ((const SparseInputElement *) b)->index)
return 1;
return 0;

View File

@@ -40,7 +40,7 @@
#endif
#if PG_VERSION_NUM >= 180000
PG_MODULE_MAGIC_EXT(.name = "vector",.version = "0.8.2");
PG_MODULE_MAGIC_EXT(.name = "vector", .version = "0.8.2");
#else
PG_MODULE_MAGIC;
#endif