mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 03:57:34 +08:00
Compare commits
13 Commits
v0.8.2
...
hnsw-read-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90aaf2102b | ||
|
|
a7551a61ca | ||
|
|
5c9a97af3b | ||
|
|
8af675cd25 | ||
|
|
97e0ed4464 | ||
|
|
533ad160e0 | ||
|
|
dfbd724a1f | ||
|
|
5f270c9663 | ||
|
|
352ec5be29 | ||
|
|
17916cad00 | ||
|
|
284f2f0fec | ||
|
|
edd49863ba | ||
|
|
411291189d |
@@ -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
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
15
src/hnsw.h
15
src/hnsw.h
@@ -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"
|
||||
@@ -368,6 +372,13 @@ typedef union
|
||||
ItemPointerData indextid;
|
||||
} HnswUnvisited;
|
||||
|
||||
typedef struct HnswReadStreamData
|
||||
{
|
||||
HnswUnvisited *unvisited;
|
||||
int unvisitedLength;
|
||||
int visited;
|
||||
} HnswReadStreamData;
|
||||
|
||||
typedef struct HnswScanOpaqueData
|
||||
{
|
||||
const HnswTypeInfo *typeInfo;
|
||||
@@ -423,13 +434,13 @@ bool HnswCheckNorm(HnswSupport * support, Datum value);
|
||||
Buffer HnswNewBuffer(Relation index, ForkNumber forkNum);
|
||||
void HnswInitPage(Buffer buf, Page page);
|
||||
void HnswInit(void);
|
||||
List *HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation index, HnswSupport * support, int m, bool inserting, HnswElement skipElement, visited_hash * v, pairingheap **discarded, bool initVisited, int64 *tuples);
|
||||
List *HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation index, HnswSupport * support, int m, bool inserting, HnswElement skipElement, visited_hash * v, pairingheap **discarded, bool initVisited, int64 *tuples, bool maintenance);
|
||||
HnswElement HnswGetEntryPoint(Relation index);
|
||||
void HnswGetMetaPageInfo(Relation index, int *m, HnswElement * entryPoint);
|
||||
void *HnswAlloc(HnswAllocator * allocator, Size size);
|
||||
HnswElement HnswInitElement(char *base, ItemPointer tid, int m, double ml, int maxLevel, HnswAllocator * alloc);
|
||||
HnswElement HnswInitElementFromBlock(BlockNumber blkno, OffsetNumber offno);
|
||||
void HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, HnswSupport * support, int m, int efConstruction, bool existing);
|
||||
void HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, HnswSupport * support, int m, int efConstruction, bool existing, bool maintenance);
|
||||
HnswSearchCandidate *HnswEntryCandidate(char *base, HnswElement entryPoint, HnswQuery * q, Relation index, HnswSupport * support, bool loadVec);
|
||||
void HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum, bool building);
|
||||
void HnswSetNeighborTuple(char *base, HnswNeighborTuple ntup, HnswElement e, int m);
|
||||
|
||||
@@ -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"
|
||||
@@ -467,7 +470,7 @@ InsertTupleInMemory(HnswBuildState * buildstate, HnswElement element)
|
||||
}
|
||||
|
||||
/* Find neighbors for element */
|
||||
HnswFindElementNeighbors(base, element, entryPoint, NULL, support, m, efConstruction, false);
|
||||
HnswFindElementNeighbors(base, element, entryPoint, NULL, support, m, efConstruction, false, true);
|
||||
|
||||
/* Update graph in memory */
|
||||
UpdateGraphInMemory(support, element, m, entryPoint, buildstate);
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
@@ -730,7 +731,7 @@ HnswInsertTupleOnDisk(Relation index, HnswSupport * support, Datum value, ItemPo
|
||||
}
|
||||
|
||||
/* Find neighbors for element */
|
||||
HnswFindElementNeighbors(base, element, entryPoint, index, support, m, efConstruction, false);
|
||||
HnswFindElementNeighbors(base, element, entryPoint, index, support, m, efConstruction, false, building);
|
||||
|
||||
/* Update graph on disk */
|
||||
UpdateGraphOnDisk(index, support, element, m, entryPoint, building);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "access/relscan.h"
|
||||
#include "hnsw.h"
|
||||
@@ -46,11 +48,11 @@ GetScanItems(IndexScanDesc scan, Datum value)
|
||||
|
||||
for (int lc = entryPoint->level; lc >= 1; lc--)
|
||||
{
|
||||
w = HnswSearchLayer(base, q, ep, 1, lc, index, support, m, false, NULL, NULL, NULL, true, NULL);
|
||||
w = HnswSearchLayer(base, q, ep, 1, lc, index, support, m, false, NULL, NULL, NULL, true, NULL, false);
|
||||
ep = w;
|
||||
}
|
||||
|
||||
return HnswSearchLayer(base, q, ep, hnsw_ef_search, 0, index, support, m, false, NULL, &so->v, hnsw_iterative_scan != HNSW_ITERATIVE_SCAN_OFF ? &so->discarded : NULL, true, &so->tuples);
|
||||
return HnswSearchLayer(base, q, ep, hnsw_ef_search, 0, index, support, m, false, NULL, &so->v, hnsw_iterative_scan != HNSW_ITERATIVE_SCAN_OFF ? &so->discarded : NULL, true, &so->tuples, false);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -81,7 +83,7 @@ ResumeScanItems(IndexScanDesc scan)
|
||||
ep = lappend(ep, sc);
|
||||
}
|
||||
|
||||
return HnswSearchLayer(base, &so->q, ep, batch_size, 0, index, &so->support, so->m, false, NULL, &so->v, &so->discarded, false, &so->tuples);
|
||||
return HnswSearchLayer(base, &so->q, ep, batch_size, 0, index, &so->support, so->m, false, NULL, &so->v, &so->discarded, false, &so->tuples, false);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -151,7 +153,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;
|
||||
|
||||
|
||||
104
src/hnswutils.c
104
src/hnswutils.c
@@ -21,6 +21,10 @@
|
||||
#include "varatt.h"
|
||||
#endif
|
||||
|
||||
#if PG_VERSION_NUM >= 190000
|
||||
#include "storage/read_stream.h"
|
||||
#endif
|
||||
|
||||
#if PG_VERSION_NUM < 170000
|
||||
static inline uint64
|
||||
murmurhash64(uint64 data)
|
||||
@@ -531,14 +535,12 @@ HnswGetDistance(Datum a, Datum b, HnswSupport * support)
|
||||
* Load an element and optionally get its distance from q
|
||||
*/
|
||||
static void
|
||||
HnswLoadElementImpl(BlockNumber blkno, OffsetNumber offno, double *distance, HnswQuery * q, Relation index, HnswSupport * support, bool loadVec, double *maxDistance, HnswElement * element)
|
||||
HnswLoadElementImpl(Buffer buf, OffsetNumber offno, double *distance, HnswQuery * q, Relation index, HnswSupport * support, bool loadVec, double *maxDistance, HnswElement * element)
|
||||
{
|
||||
Buffer buf;
|
||||
Page page;
|
||||
HnswElementTuple etup;
|
||||
|
||||
/* Read vector */
|
||||
buf = ReadBuffer(index, blkno);
|
||||
LockBuffer(buf, BUFFER_LOCK_SHARE);
|
||||
page = BufferGetPage(buf);
|
||||
|
||||
@@ -559,7 +561,7 @@ HnswLoadElementImpl(BlockNumber blkno, OffsetNumber offno, double *distance, Hns
|
||||
if (distance == NULL || maxDistance == NULL || *distance < *maxDistance)
|
||||
{
|
||||
if (*element == NULL)
|
||||
*element = HnswInitElementFromBlock(blkno, offno);
|
||||
*element = HnswInitElementFromBlock(BufferGetBlockNumber(buf), offno);
|
||||
|
||||
HnswLoadElementFromTuple(*element, etup, true, loadVec);
|
||||
}
|
||||
@@ -573,7 +575,9 @@ HnswLoadElementImpl(BlockNumber blkno, OffsetNumber offno, double *distance, Hns
|
||||
void
|
||||
HnswLoadElement(HnswElement element, double *distance, HnswQuery * q, Relation index, HnswSupport * support, bool loadVec, double *maxDistance)
|
||||
{
|
||||
HnswLoadElementImpl(element->blkno, element->offno, distance, q, index, support, loadVec, maxDistance, &element);
|
||||
Buffer buf = ReadBuffer(index, element->blkno);
|
||||
|
||||
HnswLoadElementImpl(buf, element->offno, distance, q, index, support, loadVec, maxDistance, &element);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -813,11 +817,31 @@ HnswLoadUnvisitedFromDisk(HnswElement element, HnswUnvisited * unvisited, int *u
|
||||
}
|
||||
}
|
||||
|
||||
#if PG_VERSION_NUM >= 190000
|
||||
/*
|
||||
* Get next block number for read stream
|
||||
*/
|
||||
static BlockNumber
|
||||
HnswReadStreamNextBlock(ReadStream *stream, void *callback_private_data, void *per_buffer_data)
|
||||
{
|
||||
HnswReadStreamData *streamData = callback_private_data;
|
||||
OffsetNumber *offno = per_buffer_data;
|
||||
HnswUnvisited *uv;
|
||||
|
||||
if (streamData->visited == streamData->unvisitedLength)
|
||||
return InvalidBlockNumber;
|
||||
|
||||
uv = &streamData->unvisited[streamData->visited++];
|
||||
*offno = ItemPointerGetOffsetNumber(&uv->indextid);
|
||||
return ItemPointerGetBlockNumber(&uv->indextid);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Algorithm 2 from paper
|
||||
*/
|
||||
List *
|
||||
HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation index, HnswSupport * support, int m, bool inserting, HnswElement skipElement, visited_hash * v, pairingheap **discarded, bool initVisited, int64 *tuples)
|
||||
HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation index, HnswSupport * support, int m, bool inserting, HnswElement skipElement, visited_hash * v, pairingheap **discarded, bool initVisited, int64 *tuples, bool maintenance)
|
||||
{
|
||||
List *w = NIL;
|
||||
pairingheap *C = pairingheap_allocate(CompareNearestCandidates, NULL);
|
||||
@@ -832,6 +856,21 @@ HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation in
|
||||
int unvisitedLength;
|
||||
bool inMemory = index == NULL;
|
||||
|
||||
#if PG_VERSION_NUM >= 190000
|
||||
HnswReadStreamData streamData;
|
||||
ReadStream *stream = NULL;
|
||||
|
||||
if (!inMemory)
|
||||
{
|
||||
int flags = READ_STREAM_USE_BATCHING;
|
||||
|
||||
if (maintenance)
|
||||
flags |= READ_STREAM_MAINTENANCE;
|
||||
|
||||
stream = read_stream_begin_relation(flags, NULL, index, MAIN_FORKNUM, HnswReadStreamNextBlock, &streamData, sizeof(OffsetNumber));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (v == NULL)
|
||||
{
|
||||
v = &vh;
|
||||
@@ -894,13 +933,23 @@ HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation in
|
||||
if (inMemory)
|
||||
HnswLoadUnvisitedFromMemory(base, cElement, unvisited, &unvisitedLength, v, lc, localNeighborhood, neighborhoodSize);
|
||||
else
|
||||
{
|
||||
HnswLoadUnvisitedFromDisk(cElement, unvisited, &unvisitedLength, v, index, m, lm, lc);
|
||||
|
||||
#if PG_VERSION_NUM >= 190000
|
||||
read_stream_resume(stream);
|
||||
|
||||
streamData.unvisited = unvisited;
|
||||
streamData.unvisitedLength = unvisitedLength;
|
||||
streamData.visited = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* OK to count elements instead of tuples */
|
||||
if (tuples != NULL)
|
||||
(*tuples) += unvisitedLength;
|
||||
|
||||
for (int i = 0; i < unvisitedLength; i++)
|
||||
for (int i = 0;; i++)
|
||||
{
|
||||
HnswElement eElement;
|
||||
HnswSearchCandidate *e;
|
||||
@@ -911,18 +960,40 @@ HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation in
|
||||
|
||||
if (inMemory)
|
||||
{
|
||||
if (i == unvisitedLength)
|
||||
break;
|
||||
|
||||
eElement = unvisited[i].element;
|
||||
eDistance = GetElementDistance(base, eElement, q, support);
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemPointer indextid = &unvisited[i].indextid;
|
||||
BlockNumber blkno = ItemPointerGetBlockNumber(indextid);
|
||||
OffsetNumber offno = ItemPointerGetOffsetNumber(indextid);
|
||||
Buffer buf;
|
||||
OffsetNumber offno;
|
||||
|
||||
#if PG_VERSION_NUM >= 190000
|
||||
void *offnoPtr;
|
||||
|
||||
buf = read_stream_next_buffer(stream, &offnoPtr);
|
||||
|
||||
if (!BufferIsValid(buf))
|
||||
break;
|
||||
|
||||
offno = *((OffsetNumber *) offnoPtr);
|
||||
#else
|
||||
ItemPointer indextid;
|
||||
|
||||
if (i == unvisitedLength)
|
||||
break;
|
||||
|
||||
indextid = &unvisited[i].indextid;
|
||||
buf = ReadBuffer(index, ItemPointerGetBlockNumber(indextid));
|
||||
offno = ItemPointerGetOffsetNumber(indextid);
|
||||
#endif
|
||||
|
||||
/* Avoid any allocations if not adding */
|
||||
eElement = NULL;
|
||||
HnswLoadElementImpl(blkno, offno, &eDistance, q, index, support, inserting, alwaysAdd || discarded != NULL ? NULL : &f->distance, &eElement);
|
||||
HnswLoadElementImpl(buf, offno, &eDistance, q, index, support, inserting, alwaysAdd || discarded != NULL ? NULL : &f->distance, &eElement);
|
||||
|
||||
if (eElement == NULL)
|
||||
continue;
|
||||
@@ -978,6 +1049,11 @@ HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation in
|
||||
w = lappend(w, sc);
|
||||
}
|
||||
|
||||
#if PG_VERSION_NUM >= 190000
|
||||
if (!inMemory)
|
||||
read_stream_end(stream);
|
||||
#endif
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
@@ -1273,7 +1349,7 @@ PrecomputeHash(char *base, HnswElement element)
|
||||
* Algorithm 1 from paper
|
||||
*/
|
||||
void
|
||||
HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, HnswSupport * support, int m, int efConstruction, bool existing)
|
||||
HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, HnswSupport * support, int m, int efConstruction, bool existing, bool maintenance)
|
||||
{
|
||||
List *ep;
|
||||
List *w;
|
||||
@@ -1300,7 +1376,7 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
|
||||
/* 1st phase: greedy search to insert level */
|
||||
for (int lc = entryLevel; lc >= level + 1; lc--)
|
||||
{
|
||||
w = HnswSearchLayer(base, &q, ep, 1, lc, index, support, m, true, skipElement, NULL, NULL, true, NULL);
|
||||
w = HnswSearchLayer(base, &q, ep, 1, lc, index, support, m, true, skipElement, NULL, NULL, true, NULL, maintenance);
|
||||
ep = w;
|
||||
}
|
||||
|
||||
@@ -1319,7 +1395,7 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
|
||||
List *lw = NIL;
|
||||
ListCell *lc2;
|
||||
|
||||
w = HnswSearchLayer(base, &q, ep, efConstruction, lc, index, support, m, true, skipElement, NULL, NULL, true, NULL);
|
||||
w = HnswSearchLayer(base, &q, ep, efConstruction, lc, index, support, m, true, skipElement, NULL, NULL, true, NULL, maintenance);
|
||||
|
||||
/* Convert search candidates to candidates */
|
||||
foreach(lc2, w)
|
||||
|
||||
@@ -218,7 +218,7 @@ RepairGraphElement(HnswVacuumState * vacuumstate, HnswElement element, HnswEleme
|
||||
element->heaptidsLength = 0;
|
||||
|
||||
/* Find neighbors for element, skipping itself */
|
||||
HnswFindElementNeighbors(base, element, entryPoint, index, support, m, efConstruction, true);
|
||||
HnswFindElementNeighbors(base, element, entryPoint, index, support, m, efConstruction, true, true);
|
||||
|
||||
/* Zero memory for each element */
|
||||
MemSet(ntup, 0, HNSW_TUPLE_ALLOC_SIZE);
|
||||
|
||||
@@ -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());
|
||||
@@ -145,6 +148,21 @@ SampleRows(IvfflatBuildState * buildstate)
|
||||
table_index_build_range_scan(buildstate->heap, buildstate->index, buildstate->indexInfo,
|
||||
false, true, 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 +392,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 +456,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 +671,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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -298,6 +298,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);
|
||||
|
||||
Reference in New Issue
Block a user