Compare commits

..

25 Commits

Author SHA1 Message Date
Andrew Kane
6c347b7f3e Removed unused functions 2023-12-19 19:47:03 -05:00
Andrew Kane
3e628986a1 Removed metapage logging [skip ci] 2023-12-19 17:23:35 -05:00
Andrew Kane
858a89575b Removed metapage [skip ci] 2023-12-19 17:20:55 -05:00
Andrew Kane
d81c1c9de0 Improved code [skip ci] 2023-12-19 17:01:11 -05:00
Andrew Kane
4988d04338 Improved code [skip ci] 2023-12-19 17:00:14 -05:00
Andrew Kane
c640def56c Use forkNum [skip ci] 2023-12-19 13:58:58 -05:00
Andrew Kane
87fe23d9ec Added code for Postgres < 11.8 2023-12-19 13:55:56 -05:00
Andrew Kane
041f939bde Fixed warnings 2023-12-19 12:01:09 -05:00
Andrew Kane
5d7bf9509d Reduced WAL generation for HNSW index builds 2023-12-19 11:49:40 -05:00
Andrew Kane
921427ee03 Replace dynahash hash table in HNSW with simplehash for speed - #378
Co-authored-by: Heikki Linnakangas <heikki.linnakangas@iki.fi>
2023-12-17 11:24:13 -05:00
Andrew Kane
a59aa02dd9 Only show message if flushed due to memory [skip ci] 2023-12-04 15:29:23 -08:00
Andrew Kane
2fef497b7e Fixed check 2023-12-04 15:22:12 -08:00
Andrew Kane
0e19a984fb Updated changelog [skip ci] 2023-12-04 15:15:20 -08:00
Andrew Kane
a156f6c7ae Fixed invalid memory alloc request size error with HNSW - fixes #43 2023-12-04 15:00:32 -08:00
Andrew Kane
c653ac524f Simplified code [skip ci] 2023-12-04 12:19:38 -08:00
Andrew Kane
bf0d56e78e Updated comment [skip ci] 2023-12-03 13:09:57 -08:00
Andrew Kane
4d6739a7af Added Lisp to readme [skip ci] 2023-12-03 10:58:54 -08:00
Andrew Kane
ff744214d0 Added Visual Basic to readme [skip ci] 2023-12-03 00:25:07 -08:00
Andrew Kane
7ca9298163 Updated badge [skip ci] 2023-12-01 15:36:49 -08:00
Andrew Kane
ff3bffd9a8 Moved FAQ [skip ci] 2023-11-29 22:09:09 -08:00
Andrew Kane
014753eb9c Added FAQ about memory [skip ci] 2023-11-29 22:02:27 -08:00
Andrew Kane
6763661d3d Added OCaml to readme [skip ci] 2023-11-28 21:42:30 -08:00
Andrew Kane
d287921d15 Added F# to readme [skip ci] 2023-11-28 01:22:12 -08:00
Andrew Kane
5b12ae8225 Added note about ef_construction [skip ci] 2023-11-16 18:53:48 -08:00
Japin Li
4549e8aeb1 Fix coredump about HnswFreeElement() (#357)
The HnswInitElement() allocate an element with not initialize value
filed, which may has garbage that lead HnswFreeElement() free an
invalid pointer.
2023-11-15 16:19:59 -08:00
10 changed files with 313 additions and 154 deletions

View File

@@ -1,6 +1,9 @@
## 0.5.2 (unreleased) ## 0.5.2 (unreleased)
- Improved performance of HNSW
- Added support for on-disk parallel index builds for HNSW - Added support for on-disk parallel index builds for HNSW
- Reduced WAL generation for HNSW index builds
- Fixed `invalid memory alloc request size` error with HNSW index build
## 0.5.1 (2023-10-10) ## 0.5.1 (2023-10-10)

View File

@@ -10,7 +10,7 @@ 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 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
[![Build Status](https://github.com/pgvector/pgvector/workflows/build/badge.svg?branch=master)](https://github.com/pgvector/pgvector/actions) [![Build Status](https://github.com/pgvector/pgvector/actions/workflows/build.yml/badge.svg)](https://github.com/pgvector/pgvector/actions)
## Installation ## Installation
@@ -269,6 +269,8 @@ Specify HNSW parameters
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops) WITH (m = 16, ef_construction = 64); CREATE INDEX ON items USING hnsw (embedding vector_l2_ops) WITH (m = 16, ef_construction = 64);
``` ```
A higher value of `ef_construction` provides better recall at the cost of index build time / insert speed.
### Query Options ### Query Options
Specify the size of the dynamic candidate list for search (40 by default) Specify the size of the dynamic candidate list for search (40 by default)
@@ -377,7 +379,7 @@ Language | Libraries / Examples
--- | --- --- | ---
C | [pgvector-c](https://github.com/pgvector/pgvector-c) C | [pgvector-c](https://github.com/pgvector/pgvector-c)
C++ | [pgvector-cpp](https://github.com/pgvector/pgvector-cpp) C++ | [pgvector-cpp](https://github.com/pgvector/pgvector-cpp)
C# | [pgvector-dotnet](https://github.com/pgvector/pgvector-dotnet) C#, F#, Visual Basic | [pgvector-dotnet](https://github.com/pgvector/pgvector-dotnet)
Crystal | [pgvector-crystal](https://github.com/pgvector/pgvector-crystal) Crystal | [pgvector-crystal](https://github.com/pgvector/pgvector-crystal)
Dart | [pgvector-dart](https://github.com/pgvector/pgvector-dart) Dart | [pgvector-dart](https://github.com/pgvector/pgvector-dart)
Elixir | [pgvector-elixir](https://github.com/pgvector/pgvector-elixir) Elixir | [pgvector-elixir](https://github.com/pgvector/pgvector-elixir)
@@ -386,8 +388,10 @@ Haskell | [pgvector-haskell](https://github.com/pgvector/pgvector-haskell)
Java, Kotlin, Groovy, Scala | [pgvector-java](https://github.com/pgvector/pgvector-java) Java, Kotlin, Groovy, Scala | [pgvector-java](https://github.com/pgvector/pgvector-java)
JavaScript, TypeScript | [pgvector-node](https://github.com/pgvector/pgvector-node) JavaScript, TypeScript | [pgvector-node](https://github.com/pgvector/pgvector-node)
Julia | [pgvector-julia](https://github.com/pgvector/pgvector-julia) Julia | [pgvector-julia](https://github.com/pgvector/pgvector-julia)
Lisp | [pgvector-lisp](https://github.com/pgvector/pgvector-lisp)
Lua | [pgvector-lua](https://github.com/pgvector/pgvector-lua) Lua | [pgvector-lua](https://github.com/pgvector/pgvector-lua)
Nim | [pgvector-nim](https://github.com/pgvector/pgvector-nim) Nim | [pgvector-nim](https://github.com/pgvector/pgvector-nim)
OCaml | [pgvector-ocaml](https://github.com/pgvector/pgvector-ocaml)
Perl | [pgvector-perl](https://github.com/pgvector/pgvector-perl) Perl | [pgvector-perl](https://github.com/pgvector/pgvector-perl)
PHP | [pgvector-php](https://github.com/pgvector/pgvector-php) PHP | [pgvector-php](https://github.com/pgvector/pgvector-php)
Python | [pgvector-python](https://github.com/pgvector/pgvector-python) Python | [pgvector-python](https://github.com/pgvector/pgvector-python)
@@ -460,6 +464,14 @@ and query with:
SELECT * FROM items ORDER BY embedding::vector(3) <-> '[3,1,2]' LIMIT 5; SELECT * FROM items ORDER BY embedding::vector(3) <-> '[3,1,2]' LIMIT 5;
``` ```
#### Do indexes need to fit into memory?
No, but like other index types, youll likely see better performance if they do. You can get the size of an index with:
```sql
SELECT pg_size_pretty(pg_relation_size('index_name'));
```
## Troubleshooting ## Troubleshooting
#### Why isnt a query using an index? #### Why isnt a query using an index?
@@ -678,16 +690,6 @@ SELECT extversion FROM pg_extension WHERE extname = 'vector';
## Upgrade Notes ## Upgrade Notes
### 0.6.0
If upgrading with Postgres < 13, remove this line from `sql/vector--0.5.1--0.6.0.sql`:
```sql
ALTER TYPE vector SET (STORAGE = external);
```
Then run `make install` and `ALTER EXTENSION vector UPDATE;`.
### 0.4.0 ### 0.4.0
If upgrading with Postgres < 13, remove this line from `sql/vector--0.3.2--0.4.0.sql`: If upgrading with Postgres < 13, remove this line from `sql/vector--0.3.2--0.4.0.sql`:

View File

@@ -1,5 +0,0 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION vector UPDATE TO '0.6.0'" to load this file. \quit
-- remove this single line for Postgres < 13
ALTER TYPE vector SET (STORAGE = external);

View File

@@ -26,7 +26,7 @@ CREATE TYPE vector (
TYPMOD_IN = vector_typmod_in, TYPMOD_IN = vector_typmod_in,
RECEIVE = vector_recv, RECEIVE = vector_recv,
SEND = vector_send, SEND = vector_send,
STORAGE = external STORAGE = extended
); );
-- functions -- functions

View File

@@ -104,6 +104,7 @@ typedef struct HnswElementData
List *heaptids; List *heaptids;
uint8 level; uint8 level;
uint8 deleted; uint8 deleted;
uint32 hash;
HnswNeighborArray *neighbors; HnswNeighborArray *neighbors;
BlockNumber blkno; BlockNumber blkno;
OffsetNumber offno; OffsetNumber offno;
@@ -303,7 +304,7 @@ typedef struct HnswVacuumState
Oid collation; Oid collation;
/* Variables */ /* Variables */
HTAB *deleted; struct tidhash_hash *deleted;
BufferAccessStrategy bas; BufferAccessStrategy bas;
HnswNeighborTuple ntup; HnswNeighborTuple ntup;
HnswElementData highestPoint; HnswElementData highestPoint;
@@ -317,10 +318,8 @@ int HnswGetM(Relation index);
int HnswGetEfConstruction(Relation index); int HnswGetEfConstruction(Relation index);
FmgrInfo *HnswOptionalProcInfo(Relation index, uint16 procnum); FmgrInfo *HnswOptionalProcInfo(Relation index, uint16 procnum);
bool HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result); bool HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result);
void HnswCommitBuffer(Buffer buf, GenericXLogState *state);
Buffer HnswNewBuffer(Relation index, ForkNumber forkNum); Buffer HnswNewBuffer(Relation index, ForkNumber forkNum);
void HnswInitPage(Buffer buf, Page page); void HnswInitPage(Buffer buf, Page page);
void HnswInitRegisterPage(Relation index, Buffer *buf, Page *page, GenericXLogState **state);
void HnswInit(void); void HnswInit(void);
List *HnswSearchLayer(Datum q, List *ep, int ef, int lc, Relation index, FmgrInfo *procinfo, Oid collation, int m, bool inserting, HnswElement skipElement); List *HnswSearchLayer(Datum q, List *ep, int ef, int lc, Relation index, FmgrInfo *procinfo, Oid collation, int m, bool inserting, HnswElement skipElement);
HnswElement HnswGetEntryPoint(Relation index); HnswElement HnswGetEntryPoint(Relation index);
@@ -331,12 +330,12 @@ HnswElement HnswInitElementFromBlock(BlockNumber blkno, OffsetNumber offno);
void HnswInsertElement(HnswElement element, HnswElement entryPoint, Relation index, FmgrInfo *procinfo, Oid collation, int m, int efConstruction, bool existing); void HnswInsertElement(HnswElement element, HnswElement entryPoint, Relation index, FmgrInfo *procinfo, Oid collation, int m, int efConstruction, bool existing);
HnswElement HnswFindDuplicate(HnswElement e); HnswElement HnswFindDuplicate(HnswElement e);
HnswCandidate *HnswEntryCandidate(HnswElement em, Datum q, Relation rel, FmgrInfo *procinfo, Oid collation, bool loadVec); HnswCandidate *HnswEntryCandidate(HnswElement em, Datum q, Relation rel, FmgrInfo *procinfo, Oid collation, bool loadVec);
void HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum); void HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum, bool building);
void HnswSetNeighborTuple(HnswNeighborTuple ntup, HnswElement e, int m); void HnswSetNeighborTuple(HnswNeighborTuple ntup, HnswElement e, int m);
void HnswAddHeapTid(HnswElement element, ItemPointer heaptid); void HnswAddHeapTid(HnswElement element, ItemPointer heaptid);
void HnswInitNeighbors(HnswElement element, int m); void HnswInitNeighbors(HnswElement element, int m);
bool HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel); bool HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel, bool building);
void HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting); void HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting, bool building);
void HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHeaptids, bool loadVec); void HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHeaptids, bool loadVec);
void HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec); void HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec);
void HnswSetElementTuple(HnswElementTuple etup, HnswElement element); void HnswSetElementTuple(HnswElementTuple etup, HnswElement element);
@@ -360,4 +359,31 @@ void hnswrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys,
bool hnswgettuple(IndexScanDesc scan, ScanDirection dir); bool hnswgettuple(IndexScanDesc scan, ScanDirection dir);
void hnswendscan(IndexScanDesc scan); void hnswendscan(IndexScanDesc scan);
/* Hash tables */
typedef struct TidHashEntry
{
ItemPointerData tid;
char status;
} TidHashEntry;
#define SH_PREFIX tidhash
#define SH_ELEMENT_TYPE TidHashEntry
#define SH_KEY_TYPE ItemPointerData
#define SH_SCOPE extern
#define SH_DECLARE
#include "lib/simplehash.h"
typedef struct PointerHashEntry
{
uintptr_t ptr;
char status;
} PointerHashEntry;
#define SH_PREFIX pointerhash
#define SH_ELEMENT_TYPE PointerHashEntry
#define SH_KEY_TYPE uintptr_t
#define SH_SCOPE extern
#define SH_DECLARE
#include "lib/simplehash.h"
#endif #endif

View File

@@ -56,6 +56,8 @@
#define PARALLEL_KEY_HNSW_SHARED UINT64CONST(0xA000000000000001) #define PARALLEL_KEY_HNSW_SHARED UINT64CONST(0xA000000000000001)
#define PARALLEL_KEY_QUERY_TEXT UINT64CONST(0xA000000000000002) #define PARALLEL_KEY_QUERY_TEXT UINT64CONST(0xA000000000000002)
#define LIST_MAX_LENGTH ((1 << 26) - 1)
/* /*
* Create the metapage * Create the metapage
*/ */
@@ -66,11 +68,11 @@ CreateMetaPage(HnswBuildState * buildstate)
ForkNumber forkNum = buildstate->forkNum; ForkNumber forkNum = buildstate->forkNum;
Buffer buf; Buffer buf;
Page page; Page page;
GenericXLogState *state;
HnswMetaPage metap; HnswMetaPage metap;
buf = HnswNewBuffer(index, forkNum); buf = HnswNewBuffer(index, forkNum);
HnswInitRegisterPage(index, &buf, &page, &state); page = BufferGetPage(buf);
HnswInitPage(buf, page);
/* Set metapage data */ /* Set metapage data */
metap = HnswPageGetMeta(page); metap = HnswPageGetMeta(page);
@@ -86,14 +88,14 @@ CreateMetaPage(HnswBuildState * buildstate)
((PageHeader) page)->pd_lower = ((PageHeader) page)->pd_lower =
((char *) metap + sizeof(HnswMetaPageData)) - (char *) page; ((char *) metap + sizeof(HnswMetaPageData)) - (char *) page;
HnswCommitBuffer(buf, state); UnlockReleaseBuffer(buf);
} }
/* /*
* Add a new page * Add a new page
*/ */
static void static void
HnswBuildAppendPage(Relation index, Buffer *buf, Page *page, GenericXLogState **state, ForkNumber forkNum) HnswBuildAppendPage(Relation index, Buffer *buf, Page *page, ForkNumber forkNum)
{ {
/* Add a new page */ /* Add a new page */
Buffer newbuf = HnswNewBuffer(index, forkNum); Buffer newbuf = HnswNewBuffer(index, forkNum);
@@ -102,7 +104,6 @@ HnswBuildAppendPage(Relation index, Buffer *buf, Page *page, GenericXLogState **
HnswPageGetOpaque(*page)->nextblkno = BufferGetBlockNumber(newbuf); HnswPageGetOpaque(*page)->nextblkno = BufferGetBlockNumber(newbuf);
/* Commit */ /* Commit */
GenericXLogFinish(*state);
UnlockReleaseBuffer(*buf); UnlockReleaseBuffer(*buf);
/* Can take a while, so ensure we can interrupt */ /* Can take a while, so ensure we can interrupt */
@@ -113,8 +114,7 @@ HnswBuildAppendPage(Relation index, Buffer *buf, Page *page, GenericXLogState **
/* Prepare new page */ /* Prepare new page */
*buf = newbuf; *buf = newbuf;
*state = GenericXLogStart(index); *page = BufferGetPage(*buf);
*page = GenericXLogRegisterBuffer(*state, *buf, GENERIC_XLOG_FULL_IMAGE);
HnswInitPage(*buf, *page); HnswInitPage(*buf, *page);
} }
@@ -133,7 +133,6 @@ CreateElementPages(HnswBuildState * buildstate)
BlockNumber insertPage; BlockNumber insertPage;
Buffer buf; Buffer buf;
Page page; Page page;
GenericXLogState *state;
ListCell *lc; ListCell *lc;
/* Calculate sizes */ /* Calculate sizes */
@@ -146,8 +145,7 @@ CreateElementPages(HnswBuildState * buildstate)
/* Prepare first page */ /* Prepare first page */
buf = HnswNewBuffer(index, forkNum); buf = HnswNewBuffer(index, forkNum);
state = GenericXLogStart(index); page = BufferGetPage(buf);
page = GenericXLogRegisterBuffer(state, buf, GENERIC_XLOG_FULL_IMAGE);
HnswInitPage(buf, page); HnswInitPage(buf, page);
foreach(lc, buildstate->elements) foreach(lc, buildstate->elements)
@@ -173,7 +171,7 @@ CreateElementPages(HnswBuildState * buildstate)
/* Keep element and neighbors on the same page if possible */ /* Keep element and neighbors on the same page if possible */
if (PageGetFreeSpace(page) < etupSize || (combinedSize <= maxSize && PageGetFreeSpace(page) < combinedSize)) if (PageGetFreeSpace(page) < etupSize || (combinedSize <= maxSize && PageGetFreeSpace(page) < combinedSize))
HnswBuildAppendPage(index, &buf, &page, &state, forkNum); HnswBuildAppendPage(index, &buf, &page, forkNum);
/* Calculate offsets */ /* Calculate offsets */
element->blkno = BufferGetBlockNumber(buf); element->blkno = BufferGetBlockNumber(buf);
@@ -197,7 +195,7 @@ CreateElementPages(HnswBuildState * buildstate)
/* Add new page if needed */ /* Add new page if needed */
if (PageGetFreeSpace(page) < ntupSize) if (PageGetFreeSpace(page) < ntupSize)
HnswBuildAppendPage(index, &buf, &page, &state, forkNum); HnswBuildAppendPage(index, &buf, &page, forkNum);
/* Add placeholder for neighbors */ /* Add placeholder for neighbors */
if (PageAddItem(page, (Item) ntup, ntupSize, InvalidOffsetNumber, false, false) != element->neighborOffno) if (PageAddItem(page, (Item) ntup, ntupSize, InvalidOffsetNumber, false, false) != element->neighborOffno)
@@ -207,10 +205,9 @@ CreateElementPages(HnswBuildState * buildstate)
insertPage = BufferGetBlockNumber(buf); insertPage = BufferGetBlockNumber(buf);
/* Commit */ /* Commit */
GenericXLogFinish(state);
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_ALWAYS, buildstate->entryPoint, insertPage, forkNum); HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_ALWAYS, buildstate->entryPoint, insertPage, forkNum, true);
pfree(etup); pfree(etup);
pfree(ntup); pfree(ntup);
@@ -236,7 +233,6 @@ CreateNeighborPages(HnswBuildState * buildstate)
HnswElement e = lfirst(lc); HnswElement e = lfirst(lc);
Buffer buf; Buffer buf;
Page page; Page page;
GenericXLogState *state;
Size ntupSize = HNSW_NEIGHBOR_TUPLE_SIZE(e->level, m); Size ntupSize = HNSW_NEIGHBOR_TUPLE_SIZE(e->level, m);
/* Can take a while, so ensure we can interrupt */ /* Can take a while, so ensure we can interrupt */
@@ -245,8 +241,7 @@ CreateNeighborPages(HnswBuildState * buildstate)
buf = ReadBufferExtended(index, forkNum, e->neighborPage, RBM_NORMAL, NULL); buf = ReadBufferExtended(index, forkNum, e->neighborPage, RBM_NORMAL, NULL);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
state = GenericXLogStart(index); page = BufferGetPage(buf);
page = GenericXLogRegisterBuffer(state, buf, 0);
HnswSetNeighborTuple(ntup, e, m); HnswSetNeighborTuple(ntup, e, m);
@@ -254,7 +249,6 @@ CreateNeighborPages(HnswBuildState * buildstate)
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index)); elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
/* Commit */ /* Commit */
GenericXLogFinish(state);
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
} }
@@ -381,21 +375,22 @@ BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
if (isnull[0]) if (isnull[0])
return; return;
if (buildstate->memoryLeft <= 0) if (buildstate->flushed || buildstate->memoryLeft <= 0 || list_length(buildstate->elements) == LIST_MAX_LENGTH)
{ {
if (!buildstate->flushed) if (!buildstate->flushed)
{ {
ereport(NOTICE, if (buildstate->memoryLeft <= 0)
(errmsg("hnsw graph no longer fits into maintenance_work_mem after " INT64_FORMAT " tuples", (int64) buildstate->indtuples), ereport(NOTICE,
errdetail("Building will take significantly more time."), (errmsg("hnsw graph no longer fits into maintenance_work_mem after " INT64_FORMAT " tuples", (int64) buildstate->indtuples),
errhint("Increase maintenance_work_mem to speed up builds."))); errdetail("Building will take significantly more time."),
errhint("Increase maintenance_work_mem to speed up builds.")));
FlushPages(buildstate); FlushPages(buildstate);
} }
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx); oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
if (HnswInsertTuple(buildstate->index, values, isnull, tid, buildstate->heap)) if (HnswInsertTuple(buildstate->index, values, isnull, tid, buildstate->heap, true))
{ {
if (buildstate->hnswshared) if (buildstate->hnswshared)
{ {
@@ -877,6 +872,22 @@ BuildGraph(HnswBuildState * buildstate, ForkNumber forkNum)
HnswEndParallel(buildstate->hnswleader); HnswEndParallel(buildstate->hnswleader);
} }
#if PG_VERSION_NUM < 110008
void
log_newpage_range(Relation rel, ForkNumber forkNum, BlockNumber startblk, BlockNumber endblk, bool page_std)
{
for (BlockNumber blkno = startblk; blkno < endblk; blkno++)
{
Buffer buf = ReadBufferExtended(rel, forkNum, blkno, RBM_NORMAL, NULL);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
MarkBufferDirty(buf);
log_newpage_buffer(buf, page_std);
UnlockReleaseBuffer(buf);
}
}
#endif
/* /*
* Build the index * Build the index
*/ */
@@ -892,6 +903,9 @@ BuildIndex(Relation heap, Relation index, IndexInfo *indexInfo,
if (!buildstate->flushed) if (!buildstate->flushed)
FlushPages(buildstate); FlushPages(buildstate);
if (RelationNeedsWAL(index))
log_newpage_range(index, forkNum, 0, RelationGetNumberOfBlocks(index), true);
FreeBuildState(buildstate); FreeBuildState(buildstate);
} }

View File

@@ -92,7 +92,7 @@ HnswFreeOffset(Relation index, Buffer buf, Page page, HnswElement element, Size
* Add a new page * Add a new page
*/ */
static void static void
HnswInsertAppendPage(Relation index, Buffer *nbuf, Page *npage, GenericXLogState *state, Page page) HnswInsertAppendPage(Relation index, Buffer *nbuf, Page *npage, GenericXLogState *state, Page page, bool building)
{ {
/* Add a new page */ /* Add a new page */
LockRelationForExtension(index, ExclusiveLock); LockRelationForExtension(index, ExclusiveLock);
@@ -100,7 +100,11 @@ HnswInsertAppendPage(Relation index, Buffer *nbuf, Page *npage, GenericXLogState
UnlockRelationForExtension(index, ExclusiveLock); UnlockRelationForExtension(index, ExclusiveLock);
/* Init new page */ /* Init new page */
*npage = GenericXLogRegisterBuffer(state, *nbuf, GENERIC_XLOG_FULL_IMAGE); if (building)
*npage = BufferGetPage(*nbuf);
else
*npage = GenericXLogRegisterBuffer(state, *nbuf, GENERIC_XLOG_FULL_IMAGE);
HnswInitPage(*nbuf, *npage); HnswInitPage(*nbuf, *npage);
/* Update previous buffer */ /* Update previous buffer */
@@ -111,7 +115,7 @@ HnswInsertAppendPage(Relation index, Buffer *nbuf, Page *npage, GenericXLogState
* Add to element and neighbor pages * Add to element and neighbor pages
*/ */
static void static void
WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPage, BlockNumber *updatedInsertPage) WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPage, BlockNumber *updatedInsertPage, bool building)
{ {
Buffer buf; Buffer buf;
Page page; Page page;
@@ -151,8 +155,16 @@ WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPag
buf = ReadBuffer(index, currentPage); buf = ReadBuffer(index, currentPage);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
state = GenericXLogStart(index); if (building)
page = GenericXLogRegisterBuffer(state, buf, 0); {
state = NULL;
page = BufferGetPage(buf);
}
else
{
state = GenericXLogStart(index);
page = GenericXLogRegisterBuffer(state, buf, 0);
}
/* Keep track of first page where element at level 0 can fit */ /* Keep track of first page where element at level 0 can fit */
if (!BlockNumberIsValid(newInsertPage) && PageGetFreeSpace(page) >= minCombinedSize) if (!BlockNumberIsValid(newInsertPage) && PageGetFreeSpace(page) >= minCombinedSize)
@@ -172,7 +184,12 @@ WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPag
if (HnswFreeOffset(index, buf, page, e, ntupSize, &nbuf, &npage, &freeOffno, &freeNeighborOffno, &newInsertPage)) if (HnswFreeOffset(index, buf, page, e, ntupSize, &nbuf, &npage, &freeOffno, &freeNeighborOffno, &newInsertPage))
{ {
if (nbuf != buf) if (nbuf != buf)
npage = GenericXLogRegisterBuffer(state, nbuf, 0); {
if (building)
npage = BufferGetPage(nbuf);
else
npage = GenericXLogRegisterBuffer(state, nbuf, 0);
}
break; break;
} }
@@ -181,7 +198,7 @@ WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPag
/* Skip if both tuples can fit on the same page */ /* Skip if both tuples can fit on the same page */
if (combinedSize > maxSize && PageGetFreeSpace(page) >= etupSize && !BlockNumberIsValid(HnswPageGetOpaque(page)->nextblkno)) if (combinedSize > maxSize && PageGetFreeSpace(page) >= etupSize && !BlockNumberIsValid(HnswPageGetOpaque(page)->nextblkno))
{ {
HnswInsertAppendPage(index, &nbuf, &npage, state, page); HnswInsertAppendPage(index, &nbuf, &npage, state, page, building);
break; break;
} }
@@ -190,7 +207,8 @@ WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPag
if (BlockNumberIsValid(currentPage)) if (BlockNumberIsValid(currentPage))
{ {
/* Move to next page */ /* Move to next page */
GenericXLogAbort(state); if (!building)
GenericXLogAbort(state);
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
} }
else else
@@ -198,22 +216,31 @@ WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPag
Buffer newbuf; Buffer newbuf;
Page newpage; Page newpage;
HnswInsertAppendPage(index, &newbuf, &newpage, state, page); HnswInsertAppendPage(index, &newbuf, &newpage, state, page, building);
/* Commit */ /* Commit */
GenericXLogFinish(state); if (!building)
GenericXLogFinish(state);
/* Unlock previous buffer */ /* Unlock previous buffer */
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
/* Prepare new buffer */ /* Prepare new buffer */
state = GenericXLogStart(index);
buf = newbuf; buf = newbuf;
page = GenericXLogRegisterBuffer(state, buf, 0); if (building)
{
state = NULL;
page = BufferGetPage(buf);
}
else
{
state = GenericXLogStart(index);
page = GenericXLogRegisterBuffer(state, buf, 0);
}
/* Create new page for neighbors if needed */ /* Create new page for neighbors if needed */
if (PageGetFreeSpace(page) < combinedSize) if (PageGetFreeSpace(page) < combinedSize)
HnswInsertAppendPage(index, &nbuf, &npage, state, page); HnswInsertAppendPage(index, &nbuf, &npage, state, page, building);
else else
{ {
nbuf = buf; nbuf = buf;
@@ -267,7 +294,8 @@ WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPag
} }
/* Commit */ /* Commit */
GenericXLogFinish(state); if (!building)
GenericXLogFinish(state);
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
if (nbuf != buf) if (nbuf != buf)
UnlockReleaseBuffer(nbuf); UnlockReleaseBuffer(nbuf);
@@ -301,7 +329,7 @@ ConnectionExists(HnswElement e, HnswNeighborTuple ntup, int startIdx, int lm)
* Update neighbors * Update neighbors
*/ */
void void
HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting) HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting, bool building)
{ {
for (int lc = e->level; lc >= 0; lc--) for (int lc = e->level; lc >= 0; lc--)
{ {
@@ -342,8 +370,16 @@ HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswE
/* Register page */ /* Register page */
buf = ReadBuffer(index, hc->element->neighborPage); buf = ReadBuffer(index, hc->element->neighborPage);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
state = GenericXLogStart(index); if (building)
page = GenericXLogRegisterBuffer(state, buf, 0); {
state = NULL;
page = BufferGetPage(buf);
}
else
{
state = GenericXLogStart(index);
page = GenericXLogRegisterBuffer(state, buf, 0);
}
/* Get tuple */ /* Get tuple */
itemid = PageGetItemId(page, offno); itemid = PageGetItemId(page, offno);
@@ -385,9 +421,10 @@ HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswE
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index)); elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
/* Commit */ /* Commit */
GenericXLogFinish(state); if (!building)
GenericXLogFinish(state);
} }
else else if (!building)
GenericXLogAbort(state); GenericXLogAbort(state);
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
@@ -399,7 +436,7 @@ HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswE
* Add a heap TID to an existing element * Add a heap TID to an existing element
*/ */
static bool static bool
HnswAddDuplicate(Relation index, HnswElement element, HnswElement dup) HnswAddDuplicate(Relation index, HnswElement element, HnswElement dup, bool building)
{ {
Buffer buf; Buffer buf;
Page page; Page page;
@@ -412,8 +449,16 @@ HnswAddDuplicate(Relation index, HnswElement element, HnswElement dup)
/* Read page */ /* Read page */
buf = ReadBuffer(index, dup->blkno); buf = ReadBuffer(index, dup->blkno);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
state = GenericXLogStart(index); if (building)
page = GenericXLogRegisterBuffer(state, buf, 0); {
state = NULL;
page = BufferGetPage(buf);
}
else
{
state = GenericXLogStart(index);
page = GenericXLogRegisterBuffer(state, buf, 0);
}
/* Find space */ /* Find space */
itemid = PageGetItemId(page, dup->offno); itemid = PageGetItemId(page, dup->offno);
@@ -428,7 +473,8 @@ HnswAddDuplicate(Relation index, HnswElement element, HnswElement dup)
/* Either being deleted or we lost our chance to another backend */ /* Either being deleted or we lost our chance to another backend */
if (i == 0 || i == HNSW_HEAPTIDS) if (i == 0 || i == HNSW_HEAPTIDS)
{ {
GenericXLogAbort(state); if (!building)
GenericXLogAbort(state);
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
return false; return false;
} }
@@ -441,7 +487,8 @@ HnswAddDuplicate(Relation index, HnswElement element, HnswElement dup)
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index)); elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
/* Commit */ /* Commit */
GenericXLogFinish(state); if (!building)
GenericXLogFinish(state);
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
return true; return true;
@@ -451,37 +498,37 @@ HnswAddDuplicate(Relation index, HnswElement element, HnswElement dup)
* Write changes to disk * Write changes to disk
*/ */
static void static void
WriteElement(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement element, int m, int efConstruction, HnswElement dup, HnswElement entryPoint) WriteElement(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement element, int m, int efConstruction, HnswElement dup, HnswElement entryPoint, bool building)
{ {
BlockNumber newInsertPage = InvalidBlockNumber; BlockNumber newInsertPage = InvalidBlockNumber;
/* Try to add to existing page */ /* Try to add to existing page */
if (dup != NULL) if (dup != NULL)
{ {
if (HnswAddDuplicate(index, element, dup)) if (HnswAddDuplicate(index, element, dup, building))
return; return;
} }
/* Write element and neighbor tuples */ /* Write element and neighbor tuples */
WriteNewElementPages(index, element, m, GetInsertPage(index), &newInsertPage); WriteNewElementPages(index, element, m, GetInsertPage(index), &newInsertPage, building);
/* Update insert page if needed */ /* Update insert page if needed */
if (BlockNumberIsValid(newInsertPage)) if (BlockNumberIsValid(newInsertPage))
HnswUpdateMetaPage(index, 0, NULL, newInsertPage, MAIN_FORKNUM); HnswUpdateMetaPage(index, 0, NULL, newInsertPage, MAIN_FORKNUM, building);
/* Update neighbors */ /* Update neighbors */
HnswUpdateNeighborPages(index, procinfo, collation, element, m, false); HnswUpdateNeighborPages(index, procinfo, collation, element, m, false, building);
/* Update metapage if needed */ /* Update metapage if needed */
if (entryPoint == NULL || element->level > entryPoint->level) if (entryPoint == NULL || element->level > entryPoint->level)
HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_GREATER, element, InvalidBlockNumber, MAIN_FORKNUM); HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_GREATER, element, InvalidBlockNumber, MAIN_FORKNUM, building);
} }
/* /*
* Insert a tuple into the index * Insert a tuple into the index
*/ */
bool bool
HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel) HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel, bool building)
{ {
Datum value; Datum value;
FmgrInfo *normprocinfo; FmgrInfo *normprocinfo;
@@ -540,7 +587,7 @@ HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_ti
dup = HnswFindDuplicate(element); dup = HnswFindDuplicate(element);
/* Write to disk */ /* Write to disk */
WriteElement(index, procinfo, collation, element, m, efConstruction, dup, entryPoint); WriteElement(index, procinfo, collation, element, m, efConstruction, dup, entryPoint, building);
/* Release lock */ /* Release lock */
UnlockPage(index, HNSW_UPDATE_LOCK, lockmode); UnlockPage(index, HNSW_UPDATE_LOCK, lockmode);
@@ -574,7 +621,7 @@ hnswinsert(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid,
oldCtx = MemoryContextSwitchTo(insertCtx); oldCtx = MemoryContextSwitchTo(insertCtx);
/* Insert tuple */ /* Insert tuple */
HnswInsertTuple(index, values, isnull, heap_tid, heap); HnswInsertTuple(index, values, isnull, heap_tid, heap, false);
/* Delete memory context */ /* Delete memory context */
MemoryContextSwitchTo(oldCtx); MemoryContextSwitchTo(oldCtx);

View File

@@ -7,6 +7,89 @@
#include "utils/datum.h" #include "utils/datum.h"
#include "vector.h" #include "vector.h"
#if PG_VERSION_NUM >= 130000
#include "common/hashfn.h"
#else
#include "utils/hashutils.h"
#endif
#if PG_VERSION_NUM < 170000
static inline uint64
murmurhash64(uint64 data)
{
uint64 h = data;
h ^= h >> 33;
h *= 0xff51afd7ed558ccd;
h ^= h >> 33;
h *= 0xc4ceb9fe1a85ec53;
h ^= h >> 33;
return h;
}
#endif
/* TID hash table */
static uint32
hash_tid(ItemPointerData tid)
{
union
{
uint64 i;
ItemPointerData tid;
} x;
/* Initialize unused bytes */
x.i = 0;
x.tid = tid;
return murmurhash64(x.i);
}
#define SH_PREFIX tidhash
#define SH_ELEMENT_TYPE TidHashEntry
#define SH_KEY_TYPE ItemPointerData
#define SH_KEY tid
#define SH_HASH_KEY(tb, key) hash_tid(key)
#define SH_EQUAL(tb, a, b) ItemPointerEquals(&a, &b)
#define SH_SCOPE extern
#define SH_DEFINE
#include "lib/simplehash.h"
/* Needed to include simplehash.h twice */
#if PG_VERSION_NUM < 120000
#undef SH_EQUAL
#define sh_log2 pointerhash_sh_log2
#define sh_pow2 pointerhash_sh_pow2
#endif
/* Pointer hash table */
static uint32
hash_pointer(uintptr_t ptr)
{
#if SIZEOF_VOID_P == 8
return murmurhash64((uint64) ptr);
#else
return murmurhash32((uint32) ptr);
#endif
}
#define SH_PREFIX pointerhash
#define SH_ELEMENT_TYPE PointerHashEntry
#define SH_KEY_TYPE uintptr_t
#define SH_KEY ptr
#define SH_HASH_KEY(tb, key) hash_pointer(key)
#define SH_EQUAL(tb, a, b) (a == b)
#define SH_SCOPE extern
#define SH_DEFINE
#include "lib/simplehash.h"
typedef union
{
pointerhash_hash *pointers;
tidhash_hash *tids;
} visited_hash;
/* /*
* Get the max number of connections in an upper layer for each element in the index * Get the max number of connections in an upper layer for each element in the index
*/ */
@@ -101,27 +184,6 @@ HnswInitPage(Buffer buf, Page page)
HnswPageGetOpaque(page)->page_id = HNSW_PAGE_ID; HnswPageGetOpaque(page)->page_id = HNSW_PAGE_ID;
} }
/*
* Init and register page
*/
void
HnswInitRegisterPage(Relation index, Buffer *buf, Page *page, GenericXLogState **state)
{
*state = GenericXLogStart(index);
*page = GenericXLogRegisterBuffer(*state, *buf, GENERIC_XLOG_FULL_IMAGE);
HnswInitPage(*buf, *page);
}
/*
* Commit buffer
*/
void
HnswCommitBuffer(Buffer buf, GenericXLogState *state)
{
GenericXLogFinish(state);
UnlockReleaseBuffer(buf);
}
/* /*
* Allocate neighbors * Allocate neighbors
*/ */
@@ -177,6 +239,8 @@ HnswInitElement(ItemPointer heaptid, int m, double ml, int maxLevel)
HnswInitNeighbors(element, m); HnswInitNeighbors(element, m);
element->value = PointerGetDatum(NULL);
return element; return element;
} }
@@ -294,7 +358,7 @@ HnswUpdateMetaPageInfo(Page page, int updateEntry, HnswElement entryPoint, Block
* Update the metapage * Update the metapage
*/ */
void void
HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum) HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum, bool building)
{ {
Buffer buf; Buffer buf;
Page page; Page page;
@@ -302,12 +366,22 @@ HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, Bloc
buf = ReadBufferExtended(index, forkNum, HNSW_METAPAGE_BLKNO, RBM_NORMAL, NULL); buf = ReadBufferExtended(index, forkNum, HNSW_METAPAGE_BLKNO, RBM_NORMAL, NULL);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
state = GenericXLogStart(index); if (building)
page = GenericXLogRegisterBuffer(state, buf, 0); {
state = NULL;
page = BufferGetPage(buf);
}
else
{
state = GenericXLogStart(index);
page = GenericXLogRegisterBuffer(state, buf, 0);
}
HnswUpdateMetaPageInfo(page, updateEntry, entryPoint, insertPage); HnswUpdateMetaPageInfo(page, updateEntry, entryPoint, insertPage);
HnswCommitBuffer(buf, state); if (!building)
GenericXLogFinish(state);
UnlockReleaseBuffer(buf);
} }
/* /*
@@ -551,16 +625,22 @@ CreatePairingHeapNode(HnswCandidate * c)
* Add to visited * Add to visited
*/ */
static inline void static inline void
AddToVisited(HTAB *v, HnswCandidate * hc, Relation index, bool *found) AddToVisited(visited_hash v, HnswCandidate * hc, Relation index, bool *found)
{ {
if (index == NULL) if (index == NULL)
hash_search(v, &hc->element, HASH_ENTER, found); {
#if PG_VERSION_NUM >= 130000
pointerhash_insert_hash(v.pointers, (uintptr_t) hc->element, hc->element->hash, found);
#else
pointerhash_insert(v.pointers, (uintptr_t) hc->element, found);
#endif
}
else else
{ {
ItemPointerData indextid; ItemPointerData indextid;
ItemPointerSet(&indextid, hc->element->blkno, hc->element->offno); ItemPointerSet(&indextid, hc->element->blkno, hc->element->offno);
hash_search(v, &indextid, HASH_ENTER, found); tidhash_insert(v.tids, indextid, found);
} }
} }
@@ -576,30 +656,21 @@ HnswSearchLayer(Datum q, List *ep, int ef, int lc, Relation index, FmgrInfo *pro
pairingheap *C = pairingheap_allocate(CompareNearestCandidates, NULL); pairingheap *C = pairingheap_allocate(CompareNearestCandidates, NULL);
pairingheap *W = pairingheap_allocate(CompareFurthestCandidates, NULL); pairingheap *W = pairingheap_allocate(CompareFurthestCandidates, NULL);
int wlen = 0; int wlen = 0;
HASHCTL hash_ctl; visited_hash v;
HTAB *v;
/* Create hash table */ /* Create hash table */
if (index == NULL) if (index == NULL)
{ v.pointers = pointerhash_create(CurrentMemoryContext, ef * m * 2, NULL);
hash_ctl.keysize = sizeof(HnswElement *);
hash_ctl.entrysize = sizeof(HnswElement *);
}
else else
{ v.tids = tidhash_create(CurrentMemoryContext, ef * m * 2, NULL);
hash_ctl.keysize = sizeof(ItemPointerData);
hash_ctl.entrysize = sizeof(ItemPointerData);
}
hash_ctl.hcxt = CurrentMemoryContext;
v = hash_create("hnsw visited", 256, &hash_ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
/* Add entry points to v, C, and W */ /* Add entry points to v, C, and W */
foreach(lc2, ep) foreach(lc2, ep)
{ {
HnswCandidate *hc = (HnswCandidate *) lfirst(lc2); HnswCandidate *hc = (HnswCandidate *) lfirst(lc2);
bool found;
AddToVisited(v, hc, index, NULL); AddToVisited(v, hc, index, &found);
pairingheap_add(C, &(CreatePairingHeapNode(hc)->ph_node)); pairingheap_add(C, &(CreatePairingHeapNode(hc)->ph_node));
pairingheap_add(W, &(CreatePairingHeapNode(hc)->ph_node)); pairingheap_add(W, &(CreatePairingHeapNode(hc)->ph_node));
@@ -1019,6 +1090,12 @@ HnswInsertElement(HnswElement element, HnswElement entryPoint, Relation index, F
Datum q = element->value; Datum q = element->value;
HnswElement skipElement = existing ? element : NULL; HnswElement skipElement = existing ? element : NULL;
#if PG_VERSION_NUM >= 130000
/* Precompute hash */
if (index == NULL)
element->hash = hash_pointer((uintptr_t) element);
#endif
/* No neighbors if no entry point */ /* No neighbors if no entry point */
if (entryPoint == NULL) if (entryPoint == NULL)
return; return;

View File

@@ -12,12 +12,9 @@
* Check if deleted list contains an index TID * Check if deleted list contains an index TID
*/ */
static bool static bool
DeletedContains(HTAB *deleted, ItemPointer indextid) DeletedContains(tidhash_hash * deleted, ItemPointer indextid)
{ {
bool found; return tidhash_lookup(deleted, *indextid) != NULL;
hash_search(deleted, indextid, HASH_FIND, &found);
return found;
} }
/* /*
@@ -110,11 +107,13 @@ RemoveHeapTids(HnswVacuumState * vacuumstate)
if (!ItemPointerIsValid(&etup->heaptids[0])) if (!ItemPointerIsValid(&etup->heaptids[0]))
{ {
ItemPointerData ip; ItemPointerData ip;
bool found;
/* Add to deleted list */ /* Add to deleted list */
ItemPointerSet(&ip, blkno, offno); ItemPointerSet(&ip, blkno, offno);
(void) hash_search(vacuumstate->deleted, &ip, HASH_ENTER, NULL); tidhash_insert(vacuumstate->deleted, ip, &found);
Assert(!found);
} }
else if (etup->level > highestLevel && !(entryPoint != NULL && blkno == entryPoint->blkno && offno == entryPoint->offno)) else if (etup->level > highestLevel && !(entryPoint != NULL && blkno == entryPoint->blkno && offno == entryPoint->offno))
{ {
@@ -231,7 +230,7 @@ RepairGraphElement(HnswVacuumState * vacuumstate, HnswElement element, HnswEleme
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
/* Update neighbors */ /* Update neighbors */
HnswUpdateNeighborPages(index, procinfo, collation, element, m, true); HnswUpdateNeighborPages(index, procinfo, collation, element, m, true, false);
} }
/* /*
@@ -287,7 +286,7 @@ RepairGraphEntryPoint(HnswVacuumState * vacuumstate)
* point is outdated and empty, the entry point will be empty * point is outdated and empty, the entry point will be empty
* until an element is repaired. * until an element is repaired.
*/ */
HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_ALWAYS, highestPoint, InvalidBlockNumber, MAIN_FORKNUM); HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_ALWAYS, highestPoint, InvalidBlockNumber, MAIN_FORKNUM, false);
} }
else else
{ {
@@ -420,7 +419,7 @@ RepairGraph(HnswVacuumState * vacuumstate)
* was replaced and highest point was outdated. * was replaced and highest point was outdated.
*/ */
if (entryPoint == NULL || element->level > entryPoint->level) if (entryPoint == NULL || element->level > entryPoint->level)
HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_GREATER, element, InvalidBlockNumber, MAIN_FORKNUM); HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_GREATER, element, InvalidBlockNumber, MAIN_FORKNUM, false);
/* Release lock */ /* Release lock */
UnlockPage(index, HNSW_UPDATE_LOCK, lockmode); UnlockPage(index, HNSW_UPDATE_LOCK, lockmode);
@@ -565,7 +564,7 @@ MarkDeleted(HnswVacuumState * vacuumstate)
} }
/* Update insert page last, after everything has been marked as deleted */ /* Update insert page last, after everything has been marked as deleted */
HnswUpdateMetaPage(index, 0, NULL, insertPage, MAIN_FORKNUM); HnswUpdateMetaPage(index, 0, NULL, insertPage, MAIN_FORKNUM, false);
} }
/* /*
@@ -575,7 +574,6 @@ static void
InitVacuumState(HnswVacuumState * vacuumstate, IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state) InitVacuumState(HnswVacuumState * vacuumstate, IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
{ {
Relation index = info->index; Relation index = info->index;
HASHCTL hash_ctl;
if (stats == NULL) if (stats == NULL)
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult)); stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
@@ -597,10 +595,7 @@ InitVacuumState(HnswVacuumState * vacuumstate, IndexVacuumInfo *info, IndexBulkD
HnswGetMetaPageInfo(index, &vacuumstate->m, NULL); HnswGetMetaPageInfo(index, &vacuumstate->m, NULL);
/* Create hash table */ /* Create hash table */
hash_ctl.keysize = sizeof(ItemPointerData); vacuumstate->deleted = tidhash_create(CurrentMemoryContext, 256, NULL);
hash_ctl.entrysize = sizeof(ItemPointerData);
hash_ctl.hcxt = CurrentMemoryContext;
vacuumstate->deleted = hash_create("hnswbulkdelete indextids", 256, &hash_ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
} }
/* /*
@@ -609,7 +604,7 @@ InitVacuumState(HnswVacuumState * vacuumstate, IndexVacuumInfo *info, IndexBulkD
static void static void
FreeVacuumState(HnswVacuumState * vacuumstate) FreeVacuumState(HnswVacuumState * vacuumstate)
{ {
hash_destroy(vacuumstate->deleted); tidhash_destroy(vacuumstate->deleted);
FreeAccessStrategy(vacuumstate->bas); FreeAccessStrategy(vacuumstate->bas);
pfree(vacuumstate->ntup); pfree(vacuumstate->ntup);
MemoryContextDelete(vacuumstate->tmpCtx); MemoryContextDelete(vacuumstate->tmpCtx);

View File

@@ -724,7 +724,7 @@ vector_spherical_distance(PG_FUNCTION_ARGS)
} }
/* /*
* Get the L1 distance between vectors * Get the L1 distance between two vectors
*/ */
PGDLLEXPORT PG_FUNCTION_INFO_V1(l1_distance); PGDLLEXPORT PG_FUNCTION_INFO_V1(l1_distance);
Datum Datum
@@ -903,8 +903,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_lt);
Datum Datum
vector_lt(PG_FUNCTION_ARGS) vector_lt(PG_FUNCTION_ARGS)
{ {
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
PG_RETURN_BOOL(vector_cmp_internal(a, b) < 0); PG_RETURN_BOOL(vector_cmp_internal(a, b) < 0);
} }
@@ -916,8 +916,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_le);
Datum Datum
vector_le(PG_FUNCTION_ARGS) vector_le(PG_FUNCTION_ARGS)
{ {
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
PG_RETURN_BOOL(vector_cmp_internal(a, b) <= 0); PG_RETURN_BOOL(vector_cmp_internal(a, b) <= 0);
} }
@@ -929,8 +929,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_eq);
Datum Datum
vector_eq(PG_FUNCTION_ARGS) vector_eq(PG_FUNCTION_ARGS)
{ {
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
PG_RETURN_BOOL(vector_cmp_internal(a, b) == 0); PG_RETURN_BOOL(vector_cmp_internal(a, b) == 0);
} }
@@ -942,8 +942,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ne);
Datum Datum
vector_ne(PG_FUNCTION_ARGS) vector_ne(PG_FUNCTION_ARGS)
{ {
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
PG_RETURN_BOOL(vector_cmp_internal(a, b) != 0); PG_RETURN_BOOL(vector_cmp_internal(a, b) != 0);
} }
@@ -955,8 +955,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ge);
Datum Datum
vector_ge(PG_FUNCTION_ARGS) vector_ge(PG_FUNCTION_ARGS)
{ {
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
PG_RETURN_BOOL(vector_cmp_internal(a, b) >= 0); PG_RETURN_BOOL(vector_cmp_internal(a, b) >= 0);
} }
@@ -968,8 +968,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_gt);
Datum Datum
vector_gt(PG_FUNCTION_ARGS) vector_gt(PG_FUNCTION_ARGS)
{ {
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
PG_RETURN_BOOL(vector_cmp_internal(a, b) > 0); PG_RETURN_BOOL(vector_cmp_internal(a, b) > 0);
} }
@@ -981,8 +981,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_cmp);
Datum Datum
vector_cmp(PG_FUNCTION_ARGS) vector_cmp(PG_FUNCTION_ARGS)
{ {
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
PG_RETURN_INT32(vector_cmp_internal(a, b)); PG_RETURN_INT32(vector_cmp_internal(a, b));
} }