Compare commits

..

3 Commits

Author SHA1 Message Date
Andrew Kane
3f75886bb2 Switched to runtime check [skip ci] 2024-01-28 22:08:53 -08:00
Andrew Kane
ebf7de0c16 Added comment [skip ci] 2024-01-28 21:35:53 -08:00
Andrew Kane
142a32af96 Update neighbor tuples in-place for HNSW vacuum 2024-01-28 21:27:17 -08:00
20 changed files with 125 additions and 298 deletions

View File

@@ -28,7 +28,7 @@ jobs:
dev-files: true dev-files: true
- run: make - run: make
env: env:
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare PG_CFLAGS: -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
- run: | - run: |
export PG_CONFIG=`which pg_config` export PG_CONFIG=`which pg_config`
sudo --preserve-env=PG_CONFIG make install sudo --preserve-env=PG_CONFIG make install
@@ -49,7 +49,7 @@ jobs:
postgres-version: 14 postgres-version: 14
- run: make - run: make
env: env:
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter PG_CFLAGS: -Wall -Wextra -Werror -Wno-unused-parameter
- run: make install - run: make install
- run: make installcheck - run: make installcheck
- if: ${{ failure() }} - if: ${{ failure() }}
@@ -60,9 +60,7 @@ jobs:
wget -q https://github.com/postgres/postgres/archive/refs/tags/REL_14_10.tar.gz wget -q https://github.com/postgres/postgres/archive/refs/tags/REL_14_10.tar.gz
tar xf REL_14_10.tar.gz tar xf REL_14_10.tar.gz
- run: make prove_installcheck PROVE_FLAGS="-I ./postgres-REL_14_10/src/test/perl" PERL5LIB="/Users/runner/perl5/lib/perl5" - run: make prove_installcheck PROVE_FLAGS="-I ./postgres-REL_14_10/src/test/perl" PERL5LIB="/Users/runner/perl5/lib/perl5"
- run: make clean && /usr/local/opt/llvm@15/bin/scan-build --status-bugs make - run: make clean && /usr/local/opt/llvm@15/bin/scan-build --status-bugs make PG_CFLAGS="-DUSE_ASSERT_CHECKING"
env:
PG_CFLAGS: -DUSE_ASSERT_CHECKING
windows: windows:
runs-on: windows-latest runs-on: windows-latest
if: ${{ !startsWith(github.ref_name, 'mac') }} if: ${{ !startsWith(github.ref_name, 'mac') }}
@@ -99,15 +97,4 @@ jobs:
sudo -u postgres make installcheck sudo -u postgres make installcheck
sudo -u postgres make prove_installcheck sudo -u postgres make prove_installcheck
env: env:
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare PG_CFLAGS: -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
valgrind:
if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ankane/setup-postgres-valgrind@v1
with:
postgres-version: 16
- run: make
- run: sudo --preserve-env=PG_CONFIG make install
- run: make installcheck

View File

@@ -1,21 +1,11 @@
## 0.6.2 (unreleased) ## 0.6.0 (unreleased)
- Reduced lock contention with parallel HNSW index builds If upgrading with Postgres 12 or Docker, see [these notes](https://github.com/pgvector/pgvector#060-unreleased).
## 0.6.1 (2024-03-04)
- Fixed error with `ANALYZE` and vectors with different dimensions
- Fixed error with `shared_preload_libraries`
- Fixed vector subtraction being marked as commutative
## 0.6.0 (2024-01-29)
If upgrading with Postgres 12 or Docker, see [these notes](https://github.com/pgvector/pgvector#060).
- Added support for parallel index builds for HNSW
- Added validation for GUC parameters
- Changed storage for vector from `extended` to `external` - Changed storage for vector from `extended` to `external`
- Improved performance of HNSW - Improved performance of HNSW
- Added support for parallel index builds for HNSW
- Added validation for GUC parameters
- Reduced memory usage for HNSW index builds - Reduced memory usage for HNSW index builds
- Reduced WAL generation for HNSW index builds - Reduced WAL generation for HNSW index builds
- Fixed error with logical replication - Fixed error with logical replication

View File

@@ -1,4 +1,4 @@
ARG PG_MAJOR=16 ARG PG_MAJOR=15
FROM postgres:$PG_MAJOR FROM postgres:$PG_MAJOR
ARG PG_MAJOR ARG PG_MAJOR

View File

@@ -1,4 +1,4 @@
Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
Portions Copyright (c) 1994, The Regents of the University of California Portions Copyright (c) 1994, The Regents of the University of California

View File

@@ -2,7 +2,7 @@
"name": "vector", "name": "vector",
"abstract": "Open-source vector similarity search for Postgres", "abstract": "Open-source vector similarity search for Postgres",
"description": "Supports L2 distance, inner product, and cosine distance", "description": "Supports L2 distance, inner product, and cosine distance",
"version": "0.6.1", "version": "0.5.1",
"maintainer": [ "maintainer": [
"Andrew Kane <andrew@ankane.org>" "Andrew Kane <andrew@ankane.org>"
], ],
@@ -20,7 +20,7 @@
"vector": { "vector": {
"file": "sql/vector.sql", "file": "sql/vector.sql",
"docfile": "README.md", "docfile": "README.md",
"version": "0.6.1", "version": "0.5.1",
"abstract": "Open-source vector similarity search for Postgres" "abstract": "Open-source vector similarity search for Postgres"
} }
}, },

View File

@@ -1,5 +1,5 @@
EXTENSION = vector EXTENSION = vector
EXTVERSION = 0.6.1 EXTVERSION = 0.5.1
MODULE_big = vector MODULE_big = vector
DATA = $(wildcard sql/*--*.sql) DATA = $(wildcard sql/*--*.sql)
@@ -71,9 +71,11 @@ PG_MAJOR ?= 16
.PHONY: docker .PHONY: docker
docker: docker:
docker build --pull --no-cache --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:pg$(PG_MAJOR) -t pgvector/pgvector:$(EXTVERSION)-pg$(PG_MAJOR) . docker build --pull --no-cache --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:pg$(PG_MAJOR) .
docker build --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:$(EXTVERSION)-pg$(PG_MAJOR) .
.PHONY: docker-release .PHONY: docker-release
docker-release: docker-release:
docker buildx build --push --pull --no-cache --platform linux/amd64,linux/arm64 --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:pg$(PG_MAJOR) -t pgvector/pgvector:$(EXTVERSION)-pg$(PG_MAJOR) . docker buildx build --push --pull --no-cache --platform linux/amd64,linux/arm64 --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:pg$(PG_MAJOR) .
docker buildx build --push --platform linux/amd64,linux/arm64 --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:$(EXTVERSION)-pg$(PG_MAJOR) .

View File

@@ -1,5 +1,5 @@
EXTENSION = vector EXTENSION = vector
EXTVERSION = 0.6.1 EXTVERSION = 0.5.1
OBJS = src\hnsw.obj src\hnswbuild.obj src\hnswinsert.obj src\hnswscan.obj src\hnswutils.obj src\hnswvacuum.obj src\ivfbuild.obj src\ivfflat.obj src\ivfinsert.obj src\ivfkmeans.obj src\ivfscan.obj src\ivfutils.obj src\ivfvacuum.obj src\vector.obj OBJS = src\hnsw.obj src\hnswbuild.obj src\hnswinsert.obj src\hnswscan.obj src\hnswutils.obj src\hnswvacuum.obj src\ivfbuild.obj src\ivfflat.obj src\ivfinsert.obj src\ivfkmeans.obj src\ivfscan.obj src\ivfutils.obj src\ivfvacuum.obj src\vector.obj
HEADERS = src\vector.h HEADERS = src\vector.h

View File

@@ -16,11 +16,11 @@ Plus [ACID](https://en.wikipedia.org/wiki/ACID) compliance, point-in-time recove
### Linux and Mac ### Linux and Mac
Compile and install the extension (supports Postgres 12+) Compile and install the extension (supports Postgres 11+)
```sh ```sh
cd /tmp cd /tmp
git clone --branch v0.6.1 https://github.com/pgvector/pgvector.git git clone --branch v0.5.1 https://github.com/pgvector/pgvector.git
cd pgvector cd pgvector
make make
make install # may need sudo make install # may need sudo
@@ -28,7 +28,7 @@ make install # may need sudo
See the [installation notes](#installation-notes) if you run into issues See the [installation notes](#installation-notes) if you run into issues
You can also install it with [Docker](#docker), [Homebrew](#homebrew), [PGXN](#pgxn), [APT](#apt), [Yum](#yum), [pkg](#pkg), or [conda-forge](#conda-forge), and it comes preinstalled with [Postgres.app](#postgresapp) and many [hosted providers](#hosted-postgres). There are also instructions for [GitHub Actions](https://github.com/pgvector/setup-pgvector). You can also install it with [Docker](#docker), [Homebrew](#homebrew), [PGXN](#pgxn), [APT](#apt), [Yum](#yum), or [conda-forge](#conda-forge), and it comes preinstalled with [Postgres.app](#postgresapp) and many [hosted providers](#hosted-postgres). There are also instructions for [GitHub Actions](https://github.com/pgvector/setup-pgvector).
### Windows ### Windows
@@ -44,7 +44,7 @@ Then use `nmake` to build:
```cmd ```cmd
set "PGROOT=C:\Program Files\PostgreSQL\16" set "PGROOT=C:\Program Files\PostgreSQL\16"
git clone --branch v0.6.1 https://github.com/pgvector/pgvector.git git clone --branch v0.5.1 https://github.com/pgvector/pgvector.git
cd pgvector cd pgvector
nmake /F Makefile.win nmake /F Makefile.win
nmake /F Makefile.win install nmake /F Makefile.win install
@@ -102,12 +102,6 @@ Insert vectors
INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]'); INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]');
``` ```
Or load vectors in bulk using `COPY` ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/bulk_loading.py))
```sql
COPY items (embedding) FROM STDIN WITH (FORMAT BINARY);
```
Upsert vectors Upsert vectors
```sql ```sql
@@ -270,9 +264,7 @@ HINT: Increase maintenance_work_mem to speed up builds.
Note: Do not set `maintenance_work_mem` so high that it exhausts the memory on the server Note: Do not set `maintenance_work_mem` so high that it exhausts the memory on the server
Like other index types, its faster to create an index after loading your initial data Starting with 0.6.0 [unreleased], you can also speed up index creation by increasing the number of parallel workers (2 by default)
Starting with 0.6.0, you can also speed up index creation by increasing the number of parallel workers (2 by default)
```sql ```sql
SET max_parallel_maintenance_workers = 7; -- plus leader SET max_parallel_maintenance_workers = 7; -- plus leader
@@ -438,15 +430,6 @@ To speed up queries with an IVFFlat index, increase the number of inverted lists
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 1000); CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 1000);
``` ```
## Vacuuming
Vacuuming can take a while for HNSW indexes. Speed it up by reindexing first.
```sql
REINDEX INDEX CONCURRENTLY index_name;
VACUUM table_name;
```
## Languages ## 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. Use pgvector from any language with a Postgres client. You can even generate and store vectors in one language and query them in another.
@@ -581,10 +564,6 @@ or choose to store vectors inline:
ALTER TABLE items ALTER COLUMN embedding SET STORAGE PLAIN; ALTER TABLE items ALTER COLUMN embedding SET STORAGE PLAIN;
``` ```
#### Why are there less results for a query after adding an HNSW index?
Results are limited by the size of the dynamic candidate list (`hnsw.ef_search`). There may be even less results due to dead tuples or filtering conditions in the query. We recommend setting `hnsw.ef_search` to at least twice the `LIMIT` of the query. If you need more than 500 results, use an IVFFlat index instead.
#### Why are there less results for a query after adding an IVFFlat index? #### Why are there less results for a query after adding an IVFFlat index?
The index was likely created with too little data for the number of lists. Drop the index until the table has more data. The index was likely created with too little data for the number of lists. Drop the index until the table has more data.
@@ -593,8 +572,6 @@ The index was likely created with too little data for the number of lists. Drop
DROP INDEX index_name; DROP INDEX index_name;
``` ```
Results can also be limited by the number of probes (`ivfflat.probes`).
## Reference ## Reference
### Vector Type ### Vector Type
@@ -670,32 +647,24 @@ Note: Replace `16` with your Postgres server version
If compilation fails and the output includes `warning: no such sysroot directory` on Mac, reinstall Xcode Command Line Tools. If compilation fails and the output includes `warning: no such sysroot directory` on Mac, reinstall Xcode Command Line Tools.
### Portability
To compile for portability, use:
```sh
make OPTFLAGS=""
```
## Additional Installation Methods ## Additional Installation Methods
### Docker ### Docker
Get the [Docker image](https://hub.docker.com/r/pgvector/pgvector) with: Get the [Docker image](https://hub.docker.com/r/ankane/pgvector) with:
```sh ```sh
docker pull pgvector/pgvector:pg16 docker pull ankane/pgvector
``` ```
This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (replace `16` with your Postgres server version, and run it the same way). This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (run it the same way).
You can also build the image manually: You can also build the image manually:
```sh ```sh
git clone --branch v0.6.1 https://github.com/pgvector/pgvector.git git clone --branch v0.5.1 https://github.com/pgvector/pgvector.git
cd pgvector cd pgvector
docker build --build-arg PG_MAJOR=16 -t myuser/pgvector . docker build --build-arg PG_MAJOR=15 -t myuser/pgvector .
``` ```
### Homebrew ### Homebrew
@@ -738,21 +707,6 @@ sudo dnf install pgvector_16
Note: Replace `16` with your Postgres server version Note: Replace `16` with your Postgres server version
### pkg
Install the FreeBSD package with:
```sh
pkg install postgresql15-pg_vector
```
or the port with:
```sh
cd /usr/ports/databases/pgvector
make install
```
### conda-forge ### conda-forge
With Conda Postgres, install from [conda-forge](https://anaconda.org/conda-forge/pgvector) with: With Conda Postgres, install from [conda-forge](https://anaconda.org/conda-forge/pgvector) with:
@@ -787,7 +741,7 @@ SELECT extversion FROM pg_extension WHERE extname = 'vector';
## Upgrade Notes ## Upgrade Notes
### 0.6.0 ### 0.6.0 [unreleased]
#### Postgres 12 #### Postgres 12

View File

@@ -1,16 +0,0 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION vector UPDATE TO '0.6.1'" to load this file. \quit
DROP OPERATOR - (vector, vector);
CREATE OPERATOR - (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_sub
);
ALTER OPERATOR <= (vector, vector) SET (
RESTRICT = scalarlesel, JOIN = scalarlejoinsel
);
ALTER OPERATOR >= (vector, vector) SET (
RESTRICT = scalargesel, JOIN = scalargejoinsel
);

View File

@@ -180,7 +180,8 @@ CREATE OPERATOR + (
); );
CREATE OPERATOR - ( CREATE OPERATOR - (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_sub LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_sub,
COMMUTATOR = -
); );
CREATE OPERATOR * ( CREATE OPERATOR * (
@@ -194,10 +195,11 @@ CREATE OPERATOR < (
RESTRICT = scalarltsel, JOIN = scalarltjoinsel RESTRICT = scalarltsel, JOIN = scalarltjoinsel
); );
-- should use scalarlesel and scalarlejoinsel, but not supported in Postgres < 11
CREATE OPERATOR <= ( CREATE OPERATOR <= (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_le, LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_le,
COMMUTATOR = >= , NEGATOR = > , COMMUTATOR = >= , NEGATOR = > ,
RESTRICT = scalarlesel, JOIN = scalarlejoinsel RESTRICT = scalarltsel, JOIN = scalarltjoinsel
); );
CREATE OPERATOR = ( CREATE OPERATOR = (
@@ -212,10 +214,11 @@ CREATE OPERATOR <> (
RESTRICT = eqsel, JOIN = eqjoinsel RESTRICT = eqsel, JOIN = eqjoinsel
); );
-- should use scalargesel and scalargejoinsel, but not supported in Postgres < 11
CREATE OPERATOR >= ( CREATE OPERATOR >= (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_ge, LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_ge,
COMMUTATOR = <= , NEGATOR = < , COMMUTATOR = <= , NEGATOR = < ,
RESTRICT = scalargesel, JOIN = scalargejoinsel RESTRICT = scalargtsel, JOIN = scalargtjoinsel
); );
CREATE OPERATOR > ( CREATE OPERATOR > (

View File

@@ -8,7 +8,6 @@
#include "commands/progress.h" #include "commands/progress.h"
#include "commands/vacuum.h" #include "commands/vacuum.h"
#include "hnsw.h" #include "hnsw.h"
#include "miscadmin.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/selfuncs.h" #include "utils/selfuncs.h"
@@ -29,7 +28,7 @@ static relopt_kind hnsw_relopt_kind;
* this grows bigger, we should use a shmem_request_hook and * this grows bigger, we should use a shmem_request_hook and
* RequestAddinShmemSpace() to pre-reserve space for this. * RequestAddinShmemSpace() to pre-reserve space for this.
*/ */
void static void
HnswInitLockTranche(void) HnswInitLockTranche(void)
{ {
int *tranche_ids; int *tranche_ids;
@@ -54,8 +53,7 @@ HnswInitLockTranche(void)
void void
HnswInit(void) HnswInit(void)
{ {
if (!process_shared_preload_libraries_in_progress) HnswInitLockTranche();
HnswInitLockTranche();
hnsw_relopt_kind = add_reloption_kind(); hnsw_relopt_kind = add_reloption_kind();
add_int_reloption(hnsw_relopt_kind, "m", "Max number of connections", add_int_reloption(hnsw_relopt_kind, "m", "Max number of connections",

View File

@@ -60,7 +60,6 @@
#define PROGRESS_HNSW_PHASE_LOAD 2 #define PROGRESS_HNSW_PHASE_LOAD 2
#define HNSW_MAX_SIZE (BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(HnswPageOpaqueData)) - sizeof(ItemIdData)) #define HNSW_MAX_SIZE (BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(HnswPageOpaqueData)) - sizeof(ItemIdData))
#define HNSW_TUPLE_ALLOC_SIZE BLCKSZ
#define HNSW_ELEMENT_TUPLE_SIZE(size) MAXALIGN(offsetof(HnswElementTupleData, data) + (size)) #define HNSW_ELEMENT_TUPLE_SIZE(size) MAXALIGN(offsetof(HnswElementTupleData, data) + (size))
#define HNSW_NEIGHBOR_TUPLE_SIZE(level, m) MAXALIGN(offsetof(HnswNeighborTupleData, indextids) + ((level) + 2) * (m) * sizeof(ItemPointerData)) #define HNSW_NEIGHBOR_TUPLE_SIZE(level, m) MAXALIGN(offsetof(HnswNeighborTupleData, indextids) + ((level) + 2) * (m) * sizeof(ItemPointerData))
@@ -80,7 +79,7 @@
#if PG_VERSION_NUM < 130000 #if PG_VERSION_NUM < 130000
#define list_delete_last(list) list_truncate(list, list_length(list) - 1) #define list_delete_last(list) list_truncate(list, list_length(list) - 1)
#define list_sort(list, cmp) ((list) = list_qsort(list, cmp)) #define list_sort(list, cmp) list_qsort(list, cmp)
#endif #endif
#define HnswIsElementTuple(tup) ((tup)->type == HNSW_ELEMENT_TUPLE_TYPE) #define HnswIsElementTuple(tup) ((tup)->type == HNSW_ELEMENT_TUPLE_TYPE)
@@ -185,7 +184,6 @@ typedef struct HnswGraph
/* Entry state */ /* Entry state */
LWLock entryLock; LWLock entryLock;
LWLock entryWaitLock;
HnswElementPtr entryPoint; HnswElementPtr entryPoint;
/* Allocations state */ /* Allocations state */
@@ -356,7 +354,6 @@ typedef struct HnswVacuumState
/* Variables */ /* Variables */
struct tidhash_hash *deleted; struct tidhash_hash *deleted;
BufferAccessStrategy bas; BufferAccessStrategy bas;
HnswNeighborTuple ntup;
HnswElementData highestPoint; HnswElementData highestPoint;
/* Memory */ /* Memory */
@@ -390,7 +387,6 @@ void HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation i
void HnswSetElementTuple(char *base, HnswElementTuple etup, HnswElement element); void HnswSetElementTuple(char *base, HnswElementTuple etup, HnswElement element);
void HnswUpdateConnection(char *base, HnswElement element, HnswCandidate * hc, int lm, int lc, int *updateIdx, Relation index, FmgrInfo *procinfo, Oid collation); void HnswUpdateConnection(char *base, HnswElement element, HnswCandidate * hc, int lm, int lc, int *updateIdx, Relation index, FmgrInfo *procinfo, Oid collation);
void HnswLoadNeighbors(HnswElement element, Relation index, int m); void HnswLoadNeighbors(HnswElement element, Relation index, int m);
void HnswInitLockTranche(void);
PGDLLEXPORT void HnswParallelBuildMain(dsm_segment *seg, shm_toc *toc); PGDLLEXPORT void HnswParallelBuildMain(dsm_segment *seg, shm_toc *toc);
/* Index access methods */ /* Index access methods */

View File

@@ -148,6 +148,7 @@ CreateGraphPages(HnswBuildState * buildstate)
{ {
Relation index = buildstate->index; Relation index = buildstate->index;
ForkNumber forkNum = buildstate->forkNum; ForkNumber forkNum = buildstate->forkNum;
Size etupAllocSize;
Size maxSize; Size maxSize;
HnswElementTuple etup; HnswElementTuple etup;
HnswNeighborTuple ntup; HnswNeighborTuple ntup;
@@ -159,11 +160,12 @@ CreateGraphPages(HnswBuildState * buildstate)
char *base = buildstate->hnswarea; char *base = buildstate->hnswarea;
/* Calculate sizes */ /* Calculate sizes */
etupAllocSize = BLCKSZ;
maxSize = HNSW_MAX_SIZE; maxSize = HNSW_MAX_SIZE;
/* Allocate once */ /* Allocate once */
etup = palloc0(HNSW_TUPLE_ALLOC_SIZE); etup = palloc0(etupAllocSize);
ntup = palloc0(HNSW_TUPLE_ALLOC_SIZE); ntup = palloc0(BLCKSZ);
/* Prepare first page */ /* Prepare first page */
buf = HnswNewBuffer(index, forkNum); buf = HnswNewBuffer(index, forkNum);
@@ -176,13 +178,13 @@ CreateGraphPages(HnswBuildState * buildstate)
Size etupSize; Size etupSize;
Size ntupSize; Size ntupSize;
Size combinedSize; Size combinedSize;
Pointer valuePtr = HnswPtrAccess(base, element->value); void *valuePtr = HnswPtrAccess(base, element->value);
/* Update iterator */ /* Update iterator */
iter = element->next; iter = element->next;
/* Zero memory for each element */ /* Zero memory for each element */
MemSet(etup, 0, HNSW_TUPLE_ALLOC_SIZE); MemSet(etup, 0, etupAllocSize);
/* Calculate sizes */ /* Calculate sizes */
etupSize = HNSW_ELEMENT_TUPLE_SIZE(VARSIZE_ANY(valuePtr)); etupSize = HNSW_ELEMENT_TUPLE_SIZE(VARSIZE_ANY(valuePtr));
@@ -190,7 +192,7 @@ CreateGraphPages(HnswBuildState * buildstate)
combinedSize = etupSize + ntupSize + sizeof(ItemIdData); combinedSize = etupSize + ntupSize + sizeof(ItemIdData);
/* Initial size check */ /* Initial size check */
if (etupSize > HNSW_TUPLE_ALLOC_SIZE) if (etupSize > etupAllocSize)
elog(ERROR, "index tuple too large"); elog(ERROR, "index tuple too large");
HnswSetElementTuple(base, etup, element); HnswSetElementTuple(base, etup, element);
@@ -252,43 +254,38 @@ WriteNeighborTuples(HnswBuildState * buildstate)
int m = buildstate->m; int m = buildstate->m;
HnswElementPtr iter = buildstate->graph->head; HnswElementPtr iter = buildstate->graph->head;
char *base = buildstate->hnswarea; char *base = buildstate->hnswarea;
HnswNeighborTuple ntup;
/* Allocate once */
ntup = palloc0(HNSW_TUPLE_ALLOC_SIZE);
while (!HnswPtrIsNull(base, iter)) while (!HnswPtrIsNull(base, iter))
{ {
HnswElement element = HnswPtrAccess(base, iter); HnswElement e = HnswPtrAccess(base, iter);
Buffer buf; Buffer buf;
Page page; Page page;
Size ntupSize = HNSW_NEIGHBOR_TUPLE_SIZE(element->level, m); ItemId itemid;
HnswNeighborTuple ntup;
/* Update iterator */ /* Update iterator */
iter = element->next; iter = e->next;
/* Zero memory for each element */
MemSet(ntup, 0, HNSW_TUPLE_ALLOC_SIZE);
/* Can take a while, so ensure we can interrupt */ /* Can take a while, so ensure we can interrupt */
/* Needs to be called when no buffer locks are held */ /* Needs to be called when no buffer locks are held */
CHECK_FOR_INTERRUPTS(); CHECK_FOR_INTERRUPTS();
buf = ReadBufferExtended(index, forkNum, element->neighborPage, RBM_NORMAL, NULL); buf = ReadBufferExtended(index, forkNum, e->neighborPage, RBM_NORMAL, NULL);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
page = BufferGetPage(buf); page = BufferGetPage(buf);
itemid = PageGetItemId(page, e->neighborOffno);
ntup = (HnswNeighborTuple) PageGetItem(page, itemid);
HnswSetNeighborTuple(base, ntup, element, m); /* Check expected size */
Assert(ItemIdGetLength(itemid) == HNSW_NEIGHBOR_TUPLE_SIZE(e->level, m));
if (!PageIndexTupleOverwrite(page, element->neighborOffno, (Item) ntup, ntupSize)) /* Update page in-place */
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index)); HnswSetNeighborTuple(base, ntup, e, m);
/* Commit */ /* Commit */
MarkBufferDirty(buf); MarkBufferDirty(buf);
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
} }
pfree(ntup);
} }
/* /*
@@ -431,15 +428,10 @@ InsertTupleInMemory(HnswBuildState * buildstate, HnswElement element)
HnswGraph *graph = buildstate->graph; HnswGraph *graph = buildstate->graph;
HnswElement entryPoint; HnswElement entryPoint;
LWLock *entryLock = &graph->entryLock; LWLock *entryLock = &graph->entryLock;
LWLock *entryWaitLock = &graph->entryWaitLock;
int efConstruction = buildstate->efConstruction; int efConstruction = buildstate->efConstruction;
int m = buildstate->m; int m = buildstate->m;
char *base = buildstate->hnswarea; char *base = buildstate->hnswarea;
/* Wait if another process needs exclusive lock */
LWLockAcquire(entryWaitLock, LW_EXCLUSIVE);
LWLockRelease(entryWaitLock);
/* Get entry point */ /* Get entry point */
LWLockAcquire(entryLock, LW_SHARED); LWLockAcquire(entryLock, LW_SHARED);
entryPoint = HnswPtrAccess(base, graph->entryPoint); entryPoint = HnswPtrAccess(base, graph->entryPoint);
@@ -451,9 +443,7 @@ InsertTupleInMemory(HnswBuildState * buildstate, HnswElement element)
LWLockRelease(entryLock); LWLockRelease(entryLock);
/* Get exclusive lock */ /* Get exclusive lock */
LWLockAcquire(entryWaitLock, LW_EXCLUSIVE);
LWLockAcquire(entryLock, LW_EXCLUSIVE); LWLockAcquire(entryLock, LW_EXCLUSIVE);
LWLockRelease(entryWaitLock);
/* Get latest entry point after lock is acquired */ /* Get latest entry point after lock is acquired */
entryPoint = HnswPtrAccess(base, graph->entryPoint); entryPoint = HnswPtrAccess(base, graph->entryPoint);
@@ -608,9 +598,6 @@ BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
static void static void
InitGraph(HnswGraph * graph, char *base, long memoryTotal) InitGraph(HnswGraph * graph, char *base, long memoryTotal)
{ {
/* Initialize the lock tranche if needed */
HnswInitLockTranche();
HnswPtrStore(base, graph->head, (HnswElement) NULL); HnswPtrStore(base, graph->head, (HnswElement) NULL);
HnswPtrStore(base, graph->entryPoint, (HnswElement) NULL); HnswPtrStore(base, graph->entryPoint, (HnswElement) NULL);
graph->memoryUsed = 0; graph->memoryUsed = 0;
@@ -619,7 +606,6 @@ InitGraph(HnswGraph * graph, char *base, long memoryTotal)
graph->indtuples = 0; graph->indtuples = 0;
SpinLockInit(&graph->lock); SpinLockInit(&graph->lock);
LWLockInitialize(&graph->entryLock, hnsw_lock_tranche_id); LWLockInitialize(&graph->entryLock, hnsw_lock_tranche_id);
LWLockInitialize(&graph->entryWaitLock, hnsw_lock_tranche_id);
LWLockInitialize(&graph->allocatorLock, hnsw_lock_tranche_id); LWLockInitialize(&graph->allocatorLock, hnsw_lock_tranche_id);
LWLockInitialize(&graph->flushLock, hnsw_lock_tranche_id); LWLockInitialize(&graph->flushLock, hnsw_lock_tranche_id);
} }
@@ -985,14 +971,6 @@ HnswBeginParallel(HnswBuildState * buildstate, bool isconcurrent, int request)
/* Report less than allocated so never fails */ /* Report less than allocated so never fails */
InitGraph(&hnswshared->graphData, hnswarea, esthnswarea - 1024 * 1024); InitGraph(&hnswshared->graphData, hnswarea, esthnswarea - 1024 * 1024);
/*
* Avoid base address for relptr for Postgres < 14.5
* https://github.com/postgres/postgres/commit/7201cd18627afc64850537806da7f22150d1a83b
*/
#if PG_VERSION_NUM < 140005
hnswshared->graphData.memoryUsed += MAXALIGN(1);
#endif
shm_toc_insert(pcxt->toc, PARALLEL_KEY_HNSW_SHARED, hnswshared); shm_toc_insert(pcxt->toc, PARALLEL_KEY_HNSW_SHARED, hnswshared);
shm_toc_insert(pcxt->toc, PARALLEL_KEY_HNSW_AREA, hnswarea); shm_toc_insert(pcxt->toc, PARALLEL_KEY_HNSW_AREA, hnswarea);

View File

@@ -355,7 +355,9 @@ HnswUpdateNeighborsOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, Hns
Buffer buf; Buffer buf;
Page page; Page page;
GenericXLogState *state; GenericXLogState *state;
ItemId itemid;
HnswNeighborTuple ntup; HnswNeighborTuple ntup;
Size ntupSize;
int idx = -1; int idx = -1;
int startIdx; int startIdx;
HnswElement neighborElement = HnswPtrAccess(base, hc->element); HnswElement neighborElement = HnswPtrAccess(base, hc->element);
@@ -394,7 +396,9 @@ HnswUpdateNeighborsOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, Hns
} }
/* Get tuple */ /* Get tuple */
ntup = (HnswNeighborTuple) PageGetItem(page, PageGetItemId(page, offno)); itemid = PageGetItemId(page, offno);
ntup = (HnswNeighborTuple) PageGetItem(page, itemid);
ntupSize = ItemIdGetLength(itemid);
/* Calculate index for update */ /* Calculate index for update */
startIdx = (neighborElement->level - lc) * m; startIdx = (neighborElement->level - lc) * m;
@@ -423,9 +427,13 @@ HnswUpdateNeighborsOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, Hns
{ {
ItemPointer indextid = &ntup->indextids[idx]; ItemPointer indextid = &ntup->indextids[idx];
/* Update neighbor on the buffer */ /* Update neighbor */
ItemPointerSet(indextid, e->blkno, e->offno); ItemPointerSet(indextid, e->blkno, e->offno);
/* Overwrite tuple */
if (!PageIndexTupleOverwrite(page, offno, (Item) ntup, ntupSize))
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
/* Commit */ /* Commit */
if (building) if (building)
MarkBufferDirty(buf); MarkBufferDirty(buf);
@@ -449,7 +457,9 @@ AddDuplicateOnDisk(Relation index, HnswElement element, HnswElement dup, bool bu
Buffer buf; Buffer buf;
Page page; Page page;
GenericXLogState *state; GenericXLogState *state;
ItemId itemid;
HnswElementTuple etup; HnswElementTuple etup;
Size etupSize;
int i; int i;
/* Read page */ /* Read page */
@@ -467,7 +477,9 @@ AddDuplicateOnDisk(Relation index, HnswElement element, HnswElement dup, bool bu
} }
/* Find space */ /* Find space */
etup = (HnswElementTuple) PageGetItem(page, PageGetItemId(page, dup->offno)); itemid = PageGetItemId(page, dup->offno);
etup = (HnswElementTuple) PageGetItem(page, itemid);
etupSize = ItemIdGetLength(itemid);
for (i = 0; i < HNSW_HEAPTIDS; i++) for (i = 0; i < HNSW_HEAPTIDS; i++)
{ {
if (!ItemPointerIsValid(&etup->heaptids[i])) if (!ItemPointerIsValid(&etup->heaptids[i]))
@@ -483,9 +495,13 @@ AddDuplicateOnDisk(Relation index, HnswElement element, HnswElement dup, bool bu
return false; return false;
} }
/* Add heap TID, modifying the tuple on the page directly */ /* Add heap TID */
etup->heaptids[i] = element->heaptids[0]; etup->heaptids[i] = element->heaptids[0];
/* Overwrite tuple */
if (!PageIndexTupleOverwrite(page, dup->offno, (Item) etup, etupSize))
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
/* Commit */ /* Commit */
if (building) if (building)
MarkBufferDirty(buf); MarkBufferDirty(buf);

View File

@@ -860,15 +860,12 @@ HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, F
static int static int
#if PG_VERSION_NUM >= 130000 #if PG_VERSION_NUM >= 130000
CompareCandidateDistances(const ListCell *a, const ListCell *b) CompareCandidateDistances(const ListCell *a, const ListCell *b)
{
HnswCandidate *hca = lfirst(a);
HnswCandidate *hcb = lfirst(b);
#else #else
CompareCandidateDistances(const void *a, const void *b) CompareCandidateDistances(const void *a, const void *b)
{
HnswCandidate *hca = lfirst(*(ListCell **) a);
HnswCandidate *hcb = lfirst(*(ListCell **) b);
#endif #endif
{
HnswCandidate *hca = lfirst((ListCell *) a);
HnswCandidate *hcb = lfirst((ListCell *) b);
if (hca->distance < hcb->distance) if (hca->distance < hcb->distance)
return 1; return 1;
@@ -891,15 +888,12 @@ CompareCandidateDistances(const void *a, const void *b)
static int static int
#if PG_VERSION_NUM >= 130000 #if PG_VERSION_NUM >= 130000
CompareCandidateDistancesOffset(const ListCell *a, const ListCell *b) CompareCandidateDistancesOffset(const ListCell *a, const ListCell *b)
{
HnswCandidate *hca = lfirst(a);
HnswCandidate *hcb = lfirst(b);
#else #else
CompareCandidateDistancesOffset(const void *a, const void *b) CompareCandidateDistancesOffset(const void *a, const void *b)
{
HnswCandidate *hca = lfirst(*(ListCell **) a);
HnswCandidate *hcb = lfirst(*(ListCell **) b);
#endif #endif
{
HnswCandidate *hca = lfirst((ListCell *) a);
HnswCandidate *hcb = lfirst((ListCell *) b);
if (hca->distance < hcb->distance) if (hca->distance < hcb->distance)
return 1; return 1;
@@ -958,9 +952,7 @@ SelectNeighbors(char *base, List *c, int lm, int lc, FmgrInfo *procinfo, Oid col
{ {
List *r = NIL; List *r = NIL;
List *w = list_copy(c); List *w = list_copy(c);
HnswCandidate **wd; pairingheap *wd;
int wdlen = 0;
int wdoff = 0;
HnswNeighborArray *neighbors = HnswGetNeighbors(base, e2, lc); HnswNeighborArray *neighbors = HnswGetNeighbors(base, e2, lc);
bool mustCalculate = !neighbors->closerSet; bool mustCalculate = !neighbors->closerSet;
List *added = NIL; List *added = NIL;
@@ -969,7 +961,7 @@ SelectNeighbors(char *base, List *c, int lm, int lc, FmgrInfo *procinfo, Oid col
if (list_length(w) <= lm) if (list_length(w) <= lm)
return w; return w;
wd = palloc(sizeof(HnswCandidate *) * list_length(w)); wd = pairingheap_allocate(CompareNearestCandidates, NULL);
/* Ensure order of candidates is deterministic for closer caching */ /* Ensure order of candidates is deterministic for closer caching */
if (sortCandidates) if (sortCandidates)
@@ -1035,21 +1027,21 @@ SelectNeighbors(char *base, List *c, int lm, int lc, FmgrInfo *procinfo, Oid col
if (e->closer) if (e->closer)
r = lappend(r, e); r = lappend(r, e);
else else
wd[wdlen++] = e; pairingheap_add(wd, &(CreatePairingHeapNode(e)->ph_node));
} }
/* Cached value can only be used in future if sorted deterministically */ /* Cached value can only be used in future if sorted deterministically */
neighbors->closerSet = sortCandidates; neighbors->closerSet = sortCandidates;
/* Keep pruned connections */ /* Keep pruned connections */
while (wdoff < wdlen && list_length(r) < lm) while (!pairingheap_is_empty(wd) && list_length(r) < lm)
r = lappend(r, wd[wdoff++]); r = lappend(r, ((HnswPairingHeapNode *) pairingheap_remove_first(wd))->inner);
/* Return pruned for update connections */ /* Return pruned for update connections */
if (pruned != NULL) if (pruned != NULL)
{ {
if (wdoff < wdlen) if (!pairingheap_is_empty(wd))
*pruned = wd[wdoff]; *pruned = ((HnswPairingHeapNode *) pairingheap_first(wd))->inner;
else else
*pruned = linitial(w); *pruned = linitial(w);
} }

View File

@@ -60,7 +60,8 @@ RemoveHeapTids(HnswVacuumState * vacuumstate)
/* Iterate over nodes */ /* Iterate over nodes */
for (offno = FirstOffsetNumber; offno <= maxoffno; offno = OffsetNumberNext(offno)) for (offno = FirstOffsetNumber; offno <= maxoffno; offno = OffsetNumberNext(offno))
{ {
HnswElementTuple etup = (HnswElementTuple) PageGetItem(page, PageGetItemId(page, offno)); ItemId itemid = PageGetItemId(page, offno);
HnswElementTuple etup = (HnswElementTuple) PageGetItem(page, itemid);
int idx = 0; int idx = 0;
bool itemUpdated = false; bool itemUpdated = false;
@@ -91,10 +92,15 @@ RemoveHeapTids(HnswVacuumState * vacuumstate)
if (itemUpdated) if (itemUpdated)
{ {
Size etupSize = ItemIdGetLength(itemid);
/* Mark rest as invalid */ /* Mark rest as invalid */
for (int i = idx; i < HNSW_HEAPTIDS; i++) for (int i = idx; i < HNSW_HEAPTIDS; i++)
ItemPointerSetInvalid(&etup->heaptids[i]); ItemPointerSetInvalid(&etup->heaptids[i]);
if (!PageIndexTupleOverwrite(page, offno, (Item) etup, etupSize))
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
updated = true; updated = true;
} }
} }
@@ -192,8 +198,8 @@ RepairGraphElement(HnswVacuumState * vacuumstate, HnswElement element, HnswEleme
FmgrInfo *procinfo = vacuumstate->procinfo; FmgrInfo *procinfo = vacuumstate->procinfo;
Oid collation = vacuumstate->collation; Oid collation = vacuumstate->collation;
BufferAccessStrategy bas = vacuumstate->bas; BufferAccessStrategy bas = vacuumstate->bas;
HnswNeighborTuple ntup = vacuumstate->ntup; ItemId itemid;
Size ntupSize = HNSW_NEIGHBOR_TUPLE_SIZE(element->level, m); HnswNeighborTuple ntup;
char *base = NULL; char *base = NULL;
/* Skip if element is entry point */ /* Skip if element is entry point */
@@ -207,23 +213,23 @@ RepairGraphElement(HnswVacuumState * vacuumstate, HnswElement element, HnswEleme
/* Find neighbors for element, skipping itself */ /* Find neighbors for element, skipping itself */
HnswFindElementNeighbors(base, element, entryPoint, index, procinfo, collation, m, efConstruction, true); HnswFindElementNeighbors(base, element, entryPoint, index, procinfo, collation, m, efConstruction, true);
/* Zero memory for each element */
MemSet(ntup, 0, HNSW_TUPLE_ALLOC_SIZE);
/* Update neighbor tuple */
/* Do this before getting page to minimize locking */
HnswSetNeighborTuple(base, ntup, element, m);
/* Get neighbor page */ /* Get neighbor page */
buf = ReadBufferExtended(index, MAIN_FORKNUM, element->neighborPage, RBM_NORMAL, bas); buf = ReadBufferExtended(index, MAIN_FORKNUM, element->neighborPage, RBM_NORMAL, bas);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
state = GenericXLogStart(index); state = GenericXLogStart(index);
page = GenericXLogRegisterBuffer(state, buf, 0); page = GenericXLogRegisterBuffer(state, buf, 0);
/* Overwrite tuple */ /* Get neighbor tuple */
if (!PageIndexTupleOverwrite(page, element->neighborOffno, (Item) ntup, ntupSize)) itemid = PageGetItemId(page, element->neighborOffno);
ntup = (HnswNeighborTuple) PageGetItem(page, itemid);
/* Check expected size */
if (ItemIdGetLength(itemid) != HNSW_NEIGHBOR_TUPLE_SIZE(element->level, m))
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index)); elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
/* Update page in-place */
HnswSetNeighborTuple(base, ntup, element, m);
/* Commit */ /* Commit */
GenericXLogFinish(state); GenericXLogFinish(state);
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
@@ -476,8 +482,11 @@ MarkDeleted(HnswVacuumState * vacuumstate)
/* Update element and neighbors together */ /* Update element and neighbors together */
for (offno = FirstOffsetNumber; offno <= maxoffno; offno = OffsetNumberNext(offno)) for (offno = FirstOffsetNumber; offno <= maxoffno; offno = OffsetNumberNext(offno))
{ {
HnswElementTuple etup = (HnswElementTuple) PageGetItem(page, PageGetItemId(page, offno)); ItemId itemid = PageGetItemId(page, offno);
HnswElementTuple etup = (HnswElementTuple) PageGetItem(page, itemid);
HnswNeighborTuple ntup; HnswNeighborTuple ntup;
Size etupSize;
Size ntupSize;
Buffer nbuf; Buffer nbuf;
Page npage; Page npage;
BlockNumber neighborPage; BlockNumber neighborPage;
@@ -501,6 +510,10 @@ MarkDeleted(HnswVacuumState * vacuumstate)
if (ItemPointerIsValid(&etup->heaptids[0])) if (ItemPointerIsValid(&etup->heaptids[0]))
continue; continue;
/* Calculate sizes */
etupSize = ItemIdGetLength(itemid);
ntupSize = HNSW_NEIGHBOR_TUPLE_SIZE(etup->level, vacuumstate->m);
/* Get neighbor page */ /* Get neighbor page */
neighborPage = ItemPointerGetBlockNumber(&etup->neighbortid); neighborPage = ItemPointerGetBlockNumber(&etup->neighbortid);
neighborOffno = ItemPointerGetOffsetNumber(&etup->neighbortid); neighborOffno = ItemPointerGetOffsetNumber(&etup->neighbortid);
@@ -527,10 +540,13 @@ MarkDeleted(HnswVacuumState * vacuumstate)
for (int i = 0; i < ntup->count; i++) for (int i = 0; i < ntup->count; i++)
ItemPointerSetInvalid(&ntup->indextids[i]); ItemPointerSetInvalid(&ntup->indextids[i]);
/* /* Overwrite element tuple */
* We modified the tuples in place, no need to call if (!PageIndexTupleOverwrite(page, offno, (Item) etup, etupSize))
* PageIndexTupleOverwrite elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
*/
/* Overwrite neighbor tuple */
if (!PageIndexTupleOverwrite(npage, neighborOffno, (Item) ntup, ntupSize))
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
/* Commit */ /* Commit */
GenericXLogFinish(state); GenericXLogFinish(state);
@@ -575,7 +591,6 @@ InitVacuumState(HnswVacuumState * vacuumstate, IndexVacuumInfo *info, IndexBulkD
vacuumstate->bas = GetAccessStrategy(BAS_BULKREAD); vacuumstate->bas = GetAccessStrategy(BAS_BULKREAD);
vacuumstate->procinfo = index_getprocinfo(index, 1, HNSW_DISTANCE_PROC); vacuumstate->procinfo = index_getprocinfo(index, 1, HNSW_DISTANCE_PROC);
vacuumstate->collation = index->rd_indcollation[0]; vacuumstate->collation = index->rd_indcollation[0];
vacuumstate->ntup = palloc0(HNSW_TUPLE_ALLOC_SIZE);
vacuumstate->tmpCtx = AllocSetContextCreate(CurrentMemoryContext, vacuumstate->tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
"Hnsw vacuum temporary context", "Hnsw vacuum temporary context",
ALLOCSET_DEFAULT_SIZES); ALLOCSET_DEFAULT_SIZES);
@@ -595,7 +610,6 @@ FreeVacuumState(HnswVacuumState * vacuumstate)
{ {
tidhash_destroy(vacuumstate->deleted); tidhash_destroy(vacuumstate->deleted);
FreeAccessStrategy(vacuumstate->bas); FreeAccessStrategy(vacuumstate->bas);
pfree(vacuumstate->ntup);
MemoryContextDelete(vacuumstate->tmpCtx); MemoryContextDelete(vacuumstate->tmpCtx);
} }

View File

@@ -866,10 +866,9 @@ vector_mul(PG_FUNCTION_ARGS)
int int
vector_cmp_internal(Vector * a, Vector * b) vector_cmp_internal(Vector * a, Vector * b)
{ {
int dim = Min(a->dim, b->dim); CheckDims(a, b);
/* Check values before dimensions to be consistent with Postgres arrays */ for (int i = 0; i < a->dim; i++)
for (int i = 0; i < dim; i++)
{ {
if (a->x[i] < b->x[i]) if (a->x[i] < b->x[i])
return -1; return -1;
@@ -877,13 +876,6 @@ vector_cmp_internal(Vector * a, Vector * b)
if (a->x[i] > b->x[i]) if (a->x[i] > b->x[i])
return 1; return 1;
} }
if (a->dim < b->dim)
return -1;
if (a->dim > b->dim)
return 1;
return 0; return 0;
} }
@@ -897,9 +889,6 @@ vector_lt(PG_FUNCTION_ARGS)
Vector *a = PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
/* TODO Remove in 0.7.0 */
CheckDims(a, b);
PG_RETURN_BOOL(vector_cmp_internal(a, b) < 0); PG_RETURN_BOOL(vector_cmp_internal(a, b) < 0);
} }
@@ -913,9 +902,6 @@ vector_le(PG_FUNCTION_ARGS)
Vector *a = PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
/* TODO Remove in 0.7.0 */
CheckDims(a, b);
PG_RETURN_BOOL(vector_cmp_internal(a, b) <= 0); PG_RETURN_BOOL(vector_cmp_internal(a, b) <= 0);
} }
@@ -929,9 +915,6 @@ vector_eq(PG_FUNCTION_ARGS)
Vector *a = PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
/* TODO Remove in 0.7.0 */
CheckDims(a, b);
PG_RETURN_BOOL(vector_cmp_internal(a, b) == 0); PG_RETURN_BOOL(vector_cmp_internal(a, b) == 0);
} }
@@ -945,9 +928,6 @@ vector_ne(PG_FUNCTION_ARGS)
Vector *a = PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
/* TODO Remove in 0.7.0 */
CheckDims(a, b);
PG_RETURN_BOOL(vector_cmp_internal(a, b) != 0); PG_RETURN_BOOL(vector_cmp_internal(a, b) != 0);
} }
@@ -961,9 +941,6 @@ vector_ge(PG_FUNCTION_ARGS)
Vector *a = PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
/* TODO Remove in 0.7.0 */
CheckDims(a, b);
PG_RETURN_BOOL(vector_cmp_internal(a, b) >= 0); PG_RETURN_BOOL(vector_cmp_internal(a, b) >= 0);
} }
@@ -977,9 +954,6 @@ vector_gt(PG_FUNCTION_ARGS)
Vector *a = PG_GETARG_VECTOR_P(0); Vector *a = PG_GETARG_VECTOR_P(0);
Vector *b = PG_GETARG_VECTOR_P(1); Vector *b = PG_GETARG_VECTOR_P(1);
/* TODO Remove in 0.7.0 */
CheckDims(a, b);
PG_RETURN_BOOL(vector_cmp_internal(a, b) > 0); PG_RETURN_BOOL(vector_cmp_internal(a, b) > 0);
} }

View File

@@ -24,56 +24,6 @@ SELECT '[1e37]'::vector * '[1e37]';
ERROR: value out of range: overflow ERROR: value out of range: overflow
SELECT '[1e-37]'::vector * '[1e-37]'; SELECT '[1e-37]'::vector * '[1e-37]';
ERROR: value out of range: underflow ERROR: value out of range: underflow
SELECT '[1,2,3]'::vector = '[1,2,3]';
?column?
----------
t
(1 row)
SELECT '[1,2,3]'::vector = '[1,2]';
ERROR: different vector dimensions 3 and 2
SELECT vector_cmp('[1,2,3]', '[1,2,3]');
vector_cmp
------------
0
(1 row)
SELECT vector_cmp('[1,2,3]', '[0,0,0]');
vector_cmp
------------
1
(1 row)
SELECT vector_cmp('[0,0,0]', '[1,2,3]');
vector_cmp
------------
-1
(1 row)
SELECT vector_cmp('[1,2]', '[1,2,3]');
vector_cmp
------------
-1
(1 row)
SELECT vector_cmp('[1,2,3]', '[1,2]');
vector_cmp
------------
1
(1 row)
SELECT vector_cmp('[1,2]', '[2,3,4]');
vector_cmp
------------
-1
(1 row)
SELECT vector_cmp('[2,3]', '[1,2,3]');
vector_cmp
------------
1
(1 row)
SELECT vector_dims('[1,2,3]'); SELECT vector_dims('[1,2,3]');
vector_dims vector_dims
------------- -------------

View File

@@ -6,17 +6,6 @@ SELECT '[1,2,3]'::vector * '[4,5,6]';
SELECT '[1e37]'::vector * '[1e37]'; SELECT '[1e37]'::vector * '[1e37]';
SELECT '[1e-37]'::vector * '[1e-37]'; SELECT '[1e-37]'::vector * '[1e-37]';
SELECT '[1,2,3]'::vector = '[1,2,3]';
SELECT '[1,2,3]'::vector = '[1,2]';
SELECT vector_cmp('[1,2,3]', '[1,2,3]');
SELECT vector_cmp('[1,2,3]', '[0,0,0]');
SELECT vector_cmp('[0,0,0]', '[1,2,3]');
SELECT vector_cmp('[1,2]', '[1,2,3]');
SELECT vector_cmp('[1,2,3]', '[1,2]');
SELECT vector_cmp('[1,2]', '[2,3,4]');
SELECT vector_cmp('[2,3]', '[1,2,3]');
SELECT vector_dims('[1,2,3]'); SELECT vector_dims('[1,2,3]');
SELECT round(vector_norm('[1,1]')::numeric, 5); SELECT round(vector_norm('[1,1]')::numeric, 5);

View File

@@ -1,4 +1,4 @@
comment = 'vector data type and ivfflat and hnsw access methods' comment = 'vector data type and ivfflat and hnsw access methods'
default_version = '0.6.1' default_version = '0.5.1'
module_pathname = '$libdir/vector' module_pathname = '$libdir/vector'
relocatable = true relocatable = true