Compare commits

..

1 Commits

Author SHA1 Message Date
Andrew Kane
f6edf9245a Added support for adaptive beam search [skip ci] 2025-12-05 15:26:49 -08:00
34 changed files with 164 additions and 638 deletions

View File

@@ -23,7 +23,7 @@ jobs:
- postgres: 13 - postgres: 13
os: ubuntu-22.04 os: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v5
- uses: ankane/setup-postgres@v1 - uses: ankane/setup-postgres@v1
with: with:
postgres-version: ${{ matrix.postgres }} postgres-version: ${{ matrix.postgres }}
@@ -53,7 +53,7 @@ jobs:
- postgres: 14 - postgres: 14
os: macos-15-intel os: macos-15-intel
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v5
- uses: ankane/setup-postgres@v1 - uses: ankane/setup-postgres@v1
with: with:
postgres-version: ${{ matrix.postgres }} postgres-version: ${{ matrix.postgres }}
@@ -72,7 +72,7 @@ jobs:
tar xf $TAG.tar.gz tar xf $TAG.tar.gz
mv postgres-$TAG postgres mv postgres-$TAG postgres
env: env:
TAG: ${{ matrix.postgres == 18 && 'REL_18_2' || 'REL_14_21' }} TAG: ${{ matrix.postgres == 18 && 'REL_18_0' || 'REL_14_19' }}
- run: make prove_installcheck PROVE_FLAGS="-I ./postgres/src/test/perl -I ./test/perl" - run: make prove_installcheck PROVE_FLAGS="-I ./postgres/src/test/perl -I ./test/perl"
env: env:
PERL5LIB: /Users/runner/perl5/lib/perl5 PERL5LIB: /Users/runner/perl5/lib/perl5
@@ -92,12 +92,12 @@ jobs:
- postgres: 14 - postgres: 14
os: windows-2022 os: windows-2022
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v5
- uses: ankane/setup-postgres@v1 - uses: ankane/setup-postgres@v1
with: with:
postgres-version: ${{ matrix.postgres }} postgres-version: ${{ matrix.postgres }}
- run: | - run: |
call "C:\Program Files\Microsoft Visual Studio\${{ matrix.os == 'windows-2025' && 18 || 2022 }}\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && ^ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && ^
nmake /NOLOGO /F Makefile.win && ^ nmake /NOLOGO /F Makefile.win && ^
nmake /NOLOGO /F Makefile.win install && ^ nmake /NOLOGO /F Makefile.win install && ^
nmake /NOLOGO /F Makefile.win installcheck ${{ matrix.postgres != 17 && 'PG_REGRESS=$(PGROOT)\bin\pg_regress' || '' }} && ^ nmake /NOLOGO /F Makefile.win installcheck ${{ matrix.postgres != 17 && 'PG_REGRESS=$(PGROOT)\bin\pg_regress' || '' }} && ^
@@ -133,7 +133,7 @@ jobs:
if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }} if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v5
- uses: ankane/setup-postgres-valgrind@v1 - uses: ankane/setup-postgres-valgrind@v1
with: with:
postgres-version: 18 postgres-version: 18

View File

@@ -1,11 +1,5 @@
## 0.8.3 (2026-06-17) ## 0.8.2 (unreleased)
- Fixed possible index corruption with HNSW vacuuming
- Fixed performance regression with Hamming distance and Jaccard distance with Postgres 18
## 0.8.2 (2026-02-25)
- Fixed buffer overflow with parallel HNSW index build - [more info](https://github.com/pgvector/pgvector/issues/959)
- Improved `install` target on Windows - Improved `install` target on Windows
- Fixed `Index Searches` in `EXPLAIN` output for Postgres 18 - Fixed `Index Searches` in `EXPLAIN` output for Postgres 18

View File

@@ -5,7 +5,7 @@ ARG DEBIAN_CODENAME=bookworm
FROM postgres:$PG_MAJOR-$DEBIAN_CODENAME FROM postgres:$PG_MAJOR-$DEBIAN_CODENAME
ARG PG_MAJOR ARG PG_MAJOR
ADD https://github.com/pgvector/pgvector.git#v0.8.3 /tmp/pgvector ADD https://github.com/pgvector/pgvector.git#v0.8.1 /tmp/pgvector
RUN apt-get update && \ RUN apt-get update && \
apt-mark hold locales && \ apt-mark hold locales && \

View File

@@ -1,4 +1,4 @@
Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group Portions Copyright (c) 1996-2025, 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.8.3", "version": "0.8.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.8.3", "version": "0.8.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.8.3 EXTVERSION = 0.8.1
MODULE_big = vector MODULE_big = vector
DATA = $(wildcard sql/*--*--*.sql) DATA = $(wildcard sql/*--*--*.sql)
@@ -27,11 +27,6 @@ ifneq ($(filter ppc64%, $(shell uname -m)), )
OPTFLAGS = OPTFLAGS =
endif endif
# RISC-V64 doesn't support -march=native
ifeq ($(shell uname -m), riscv64)
OPTFLAGS =
endif
# For auto-vectorization: # For auto-vectorization:
# - GCC (needs -ftree-vectorize OR -O3) - https://gcc.gnu.org/projects/tree-ssa/vectorization.html # - GCC (needs -ftree-vectorize OR -O3) - https://gcc.gnu.org/projects/tree-ssa/vectorization.html
# - Clang (could use pragma instead) - https://llvm.org/docs/Vectorizers.html # - Clang (could use pragma instead) - https://llvm.org/docs/Vectorizers.html

View File

@@ -1,5 +1,5 @@
EXTENSION = vector EXTENSION = vector
EXTVERSION = 0.8.3 EXTVERSION = 0.8.1
DATA_built = sql\$(EXTENSION)--$(EXTVERSION).sql DATA_built = sql\$(EXTENSION)--$(EXTVERSION).sql
OBJS = src\bitutils.obj src\bitvec.obj src\halfutils.obj src\halfvec.obj 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\sparsevec.obj src\vector.obj OBJS = src\bitutils.obj src\bitvec.obj src\halfutils.obj src\halfvec.obj 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\sparsevec.obj src\vector.obj

View File

@@ -11,8 +11,6 @@ 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
Have a lot of vectors? Use [quantization](#scaling) to scale
[![Build Status](https://github.com/pgvector/pgvector/actions/workflows/build.yml/badge.svg)](https://github.com/pgvector/pgvector/actions) [![Build Status](https://github.com/pgvector/pgvector/actions/workflows/build.yml/badge.svg)](https://github.com/pgvector/pgvector/actions)
## Installation ## Installation
@@ -23,7 +21,7 @@ Compile and install the extension (supports Postgres 13+)
```sh ```sh
cd /tmp cd /tmp
git clone --branch v0.8.3 https://github.com/pgvector/pgvector.git git clone --branch v0.8.1 https://github.com/pgvector/pgvector.git
cd pgvector cd pgvector
make make
make install # may need sudo make install # may need sudo
@@ -40,7 +38,7 @@ Ensure [C++ support in Visual Studio](https://learn.microsoft.com/en-us/cpp/buil
```cmd ```cmd
set "PGROOT=C:\Program Files\PostgreSQL\18" set "PGROOT=C:\Program Files\PostgreSQL\18"
cd %TEMP% cd %TEMP%
git clone --branch v0.8.3 https://github.com/pgvector/pgvector.git git clone --branch v0.8.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
@@ -316,8 +314,6 @@ For a large number of workers, you may need to increase `max_parallel_workers` (
The [index options](#index-options) also have a significant impact on build time (use the defaults unless seeing low recall) The [index options](#index-options) also have a significant impact on build time (use the defaults unless seeing low recall)
Use [binary quantization](#binary-quantization) for faster build times at scale
### Indexing Progress ### Indexing Progress
Check [indexing progress](https://www.postgresql.org/docs/current/progress-reporting.html#CREATE-INDEX-PROGRESS-REPORTING) Check [indexing progress](https://www.postgresql.org/docs/current/progress-reporting.html#CREATE-INDEX-PROGRESS-REPORTING)
@@ -447,7 +443,13 @@ Exact indexes work well for conditions that match a low percentage of rows. Othe
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops); CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
``` ```
With approximate indexes, filtering is applied *after* the index is scanned. If a condition matches 10% of rows, with HNSW and the default `hnsw.ef_search` of 40, only 4 rows will match on average. For more rows, enable [iterative index scans](#iterative-index-scans), which will automatically scan more of the index when needed. With approximate indexes, filtering is applied *after* the index is scanned. If a condition matches 10% of rows, with HNSW and the default `hnsw.ef_search` of 40, only 4 rows will match on average. For more rows, increase `hnsw.ef_search`.
```sql
SET hnsw.ef_search = 200;
```
Starting with 0.8.0, you can enable [iterative index scans](#iterative-index-scans), which will automatically scan more of the index when needed.
```sql ```sql
SET hnsw.iterative_scan = strict_order; SET hnsw.iterative_scan = strict_order;
@@ -671,10 +673,6 @@ SHOW shared_buffers;
Be sure to restart Postgres for changes to take effect. Be sure to restart Postgres for changes to take effect.
### Storing
Use the `halfvec` type instead of `vector` for a smaller working set.
### Loading ### Loading
Use `COPY` for bulk loading data ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/loading/example.py)). Use `COPY` for bulk loading data ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/loading/example.py)).
@@ -689,8 +687,6 @@ Add any indexes *after* loading the initial data for best performance.
See index build time for [HNSW](#index-build-time) and [IVFFlat](#index-build-time-1). See index build time for [HNSW](#index-build-time) and [IVFFlat](#index-build-time-1).
Use [binary quantization](#binary-quantization) for smaller indexes and faster build times at scale.
In production environments, create indexes concurrently to avoid blocking writes. In production environments, create indexes concurrently to avoid blocking writes.
```sql ```sql
@@ -721,8 +717,6 @@ SELECT * FROM items ORDER BY embedding <#> '[3,1,2]' LIMIT 5;
#### Approximate Search #### Approximate Search
Use [binary quantization](#binary-quantization) with re-ranking to keep indexes in-memory at scale.
To speed up queries with an IVFFlat index, increase the number of inverted lists (at the expense of recall). To speed up queries with an IVFFlat index, increase the number of inverted lists (at the expense of recall).
```sql ```sql
@@ -738,20 +732,21 @@ REINDEX INDEX CONCURRENTLY index_name;
VACUUM table_name; VACUUM table_name;
``` ```
## Scaling
For a smaller working set:
1. Use the `halfvec` type instead of `vector` for tables
2. Use [binary quantization](#binary-quantization) for indexes (with re-ranking for search)
Scale vertically by increasing memory, CPU, and storage on a single instance. Use existing tools to [tune parameters](#tuning) and [monitor performance](#monitoring).
Scale horizontally with [replicas](https://www.postgresql.org/docs/current/hot-standby.html), or use [Citus](https://github.com/citusdata/citus), [PgDog](https://github.com/pgdogdev/pgdog), or another approach for sharding ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/citus/example.py)).
## Monitoring ## Monitoring
Use existing tools like [pg_stat_statements](https://www.postgresql.org/docs/current/pgstatstatements.html) or [PgHero](https://github.com/ankane/pghero) to monitor performance. Monitor performance with [pg_stat_statements](https://www.postgresql.org/docs/current/pgstatstatements.html) (be sure to add it to `shared_preload_libraries`).
```sql
CREATE EXTENSION pg_stat_statements;
```
Get the most time-consuming queries with:
```sql
SELECT query, calls, ROUND((total_plan_time + total_exec_time) / calls) AS avg_time_ms,
ROUND((total_plan_time + total_exec_time) / 60000) AS total_time_min
FROM pg_stat_statements ORDER BY total_plan_time + total_exec_time DESC LIMIT 20;
```
Monitor recall by comparing results from approximate search with exact search. Monitor recall by comparing results from approximate search with exact search.
@@ -762,6 +757,14 @@ SELECT ...
COMMIT; COMMIT;
``` ```
## Scaling
Scale pgvector the same way you scale Postgres.
Scale vertically by increasing memory, CPU, and storage on a single instance. Use existing tools to [tune parameters](#tuning) and [monitor performance](#monitoring).
Scale horizontally with [replicas](https://www.postgresql.org/docs/current/hot-standby.html), or use [Citus](https://github.com/citusdata/citus) or another approach for sharding ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/citus/example.py)).
## Languages ## 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.
@@ -773,7 +776,6 @@ Algol | [pgvector-algol](https://github.com/pgvector/pgvector-algol)
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#, F#, Visual Basic | [pgvector-dotnet](https://github.com/pgvector/pgvector-dotnet) C#, F#, Visual Basic | [pgvector-dotnet](https://github.com/pgvector/pgvector-dotnet)
COBOL | [pgvector-cobol](https://github.com/pgvector/pgvector-cobol)
Crystal | [pgvector-crystal](https://github.com/pgvector/pgvector-crystal) Crystal | [pgvector-crystal](https://github.com/pgvector/pgvector-crystal)
D | [pgvector-d](https://github.com/pgvector/pgvector-d) D | [pgvector-d](https://github.com/pgvector/pgvector-d)
Dart | [pgvector-dart](https://github.com/pgvector/pgvector-dart) Dart | [pgvector-dart](https://github.com/pgvector/pgvector-dart)
@@ -801,7 +803,6 @@ Raku | [pgvector-raku](https://github.com/pgvector/pgvector-raku)
Ruby | [pgvector-ruby](https://github.com/pgvector/pgvector-ruby), [Neighbor](https://github.com/ankane/neighbor) Ruby | [pgvector-ruby](https://github.com/pgvector/pgvector-ruby), [Neighbor](https://github.com/ankane/neighbor)
Rust | [pgvector-rust](https://github.com/pgvector/pgvector-rust) Rust | [pgvector-rust](https://github.com/pgvector/pgvector-rust)
Swift | [pgvector-swift](https://github.com/pgvector/pgvector-swift) Swift | [pgvector-swift](https://github.com/pgvector/pgvector-swift)
Tcl | [pgvector-tcl](https://github.com/pgvector/pgvector-tcl)
Zig | [pgvector-zig](https://github.com/pgvector/pgvector-zig) Zig | [pgvector-zig](https://github.com/pgvector/pgvector-zig)
## Frequently Asked Questions ## Frequently Asked Questions
@@ -875,8 +876,6 @@ No, but like other index types, youll likely see better performance if they d
SELECT pg_size_pretty(pg_relation_size('index_name')); SELECT pg_size_pretty(pg_relation_size('index_name'));
``` ```
Use [half-precision indexing](#half-precision-indexing) or [binary quantization](#binary-quantization) for smaller indexes.
## Troubleshooting ## Troubleshooting
#### Why isnt a query using an index? #### Why isnt a query using an index?
@@ -1151,23 +1150,23 @@ This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (r
Supported tags are: Supported tags are:
- `pg18-trixie`, `0.8.3-pg18-trixie` - `pg18-trixie`, `0.8.1-pg18-trixie`
- `pg18-bookworm`, `0.8.3-pg18-bookworm`, `pg18`, `0.8.3-pg18` - `pg18-bookworm`, `0.8.1-pg18-bookworm`, `pg18`, `0.8.1-pg18`
- `pg17-trixie`, `0.8.3-pg17-trixie` - `pg17-trixie`, `0.8.1-pg17-trixie`
- `pg17-bookworm`, `0.8.3-pg17-bookworm`, `pg17`, `0.8.3-pg17` - `pg17-bookworm`, `0.8.1-pg17-bookworm`, `pg17`, `0.8.1-pg17`
- `pg16-trixie`, `0.8.3-pg16-trixie` - `pg16-trixie`, `0.8.1-pg16-trixie`
- `pg16-bookworm`, `0.8.3-pg16-bookworm`, `pg16`, `0.8.3-pg16` - `pg16-bookworm`, `0.8.1-pg16-bookworm`, `pg16`, `0.8.1-pg16`
- `pg15-trixie`, `0.8.3-pg15-trixie` - `pg15-trixie`, `0.8.1-pg15-trixie`
- `pg15-bookworm`, `0.8.3-pg15-bookworm`, `pg15`, `0.8.3-pg15` - `pg15-bookworm`, `0.8.1-pg15-bookworm`, `pg15`, `0.8.1-pg15`
- `pg14-trixie`, `0.8.3-pg14-trixie` - `pg14-trixie`, `0.8.1-pg14-trixie`
- `pg14-bookworm`, `0.8.3-pg14-bookworm`, `pg14`, `0.8.3-pg14` - `pg14-bookworm`, `0.8.1-pg14-bookworm`, `pg14`, `0.8.1-pg14`
- `pg13-trixie`, `0.8.3-pg13-trixie` - `pg13-trixie`, `0.8.1-pg13-trixie`
- `pg13-bookworm`, `0.8.3-pg13-bookworm`, `pg13`, `0.8.3-pg13` - `pg13-bookworm`, `0.8.1-pg13-bookworm`, `pg13`, `0.8.1-pg13`
You can also build the image manually: You can also build the image manually:
```sh ```sh
git clone --branch v0.8.3 https://github.com/pgvector/pgvector.git git clone --branch v0.8.1 https://github.com/pgvector/pgvector.git
cd pgvector cd pgvector
docker build --pull --build-arg PG_MAJOR=18 -t myuser/pgvector . docker build --pull --build-arg PG_MAJOR=18 -t myuser/pgvector .
``` ```
@@ -1283,6 +1282,7 @@ Thanks to:
- [k-means++: The Advantage of Careful Seeding](https://theory.stanford.edu/~sergei/papers/kMeansPP-soda.pdf) - [k-means++: The Advantage of Careful Seeding](https://theory.stanford.edu/~sergei/papers/kMeansPP-soda.pdf)
- [Concept Decompositions for Large Sparse Text Data using Clustering](https://www.cs.utexas.edu/users/inderjit/public_papers/concept_mlj.pdf) - [Concept Decompositions for Large Sparse Text Data using Clustering](https://www.cs.utexas.edu/users/inderjit/public_papers/concept_mlj.pdf)
- [Efficient and Robust Approximate Nearest Neighbor Search using Hierarchical Navigable Small World Graphs](https://arxiv.org/ftp/arxiv/papers/1603/1603.09320.pdf) - [Efficient and Robust Approximate Nearest Neighbor Search using Hierarchical Navigable Small World Graphs](https://arxiv.org/ftp/arxiv/papers/1603/1603.09320.pdf)
- [Distance Adaptive Beam Search for Provably Accurate Graph-Based Nearest Neighbor Search](https://arxiv.org/pdf/2505.15636)
## History ## History
@@ -1329,7 +1329,7 @@ make clean && PG_CFLAGS="-DUSE_ASSERT_CHECKING" make && make install
To enable benchmarking: To enable benchmarking:
```sh ```sh
make clean && PG_CFLAGS="-DHNSW_BENCH -DIVFFLAT_BENCH" make && make install make clean && PG_CFLAGS="-DIVFFLAT_BENCH" make && make install
``` ```
To show memory usage: To show memory usage:

View File

@@ -1,2 +0,0 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION vector UPDATE TO '0.8.2'" to load this file. \quit

View File

@@ -1,2 +0,0 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION vector UPDATE TO '0.8.3'" to load this file. \quit

View File

@@ -31,12 +31,10 @@
#define BIT_TARGET_CLONES #define BIT_TARGET_CLONES
#endif #endif
/* Use built-ins when possible for Postgres < 19 for inlining */ /* Use built-ins when possible for inlining */
#if PG_VERSION_NUM >= 190000 #if defined(HAVE__BUILTIN_POPCOUNT) && defined(HAVE_LONG_INT_64)
#define popcount64(x) pg_popcount64(x)
#elif defined(HAVE__BUILTIN_POPCOUNT) && (defined(HAVE_LONG_INT_64) || SIZEOF_LONG == 8)
#define popcount64(x) __builtin_popcountl(x) #define popcount64(x) __builtin_popcountl(x)
#elif defined(HAVE__BUILTIN_POPCOUNT) && (defined(HAVE_LONG_LONG_INT_64) || SIZEOF_LONG_LONG == 8) #elif defined(HAVE__BUILTIN_POPCOUNT) && defined(HAVE_LONG_LONG_INT_64)
#define popcount64(x) __builtin_popcountll(x) #define popcount64(x) __builtin_popcountll(x)
#elif !defined(_MSC_VER) #elif !defined(_MSC_VER)
/* Fails to resolve with MSVC */ /* Fails to resolve with MSVC */
@@ -171,7 +169,7 @@ BitJaccardDistanceAvx512Popcount(uint32 bytes, unsigned char *ax, unsigned char
#endif #endif
TARGET_XSAVE static bool TARGET_XSAVE static bool
SupportsAvx512Popcount(void) SupportsAvx512Popcount()
{ {
unsigned int exx[4] = {0, 0, 0, 0}; unsigned int exx[4] = {0, 0, 0, 0};

View File

@@ -2,7 +2,6 @@
#include "bitutils.h" #include "bitutils.h"
#include "bitvec.h" #include "bitvec.h"
#include "fmgr.h"
#include "utils/varbit.h" #include "utils/varbit.h"
#include "vector.h" #include "vector.h"

View File

@@ -1,7 +1,5 @@
#include "postgres.h" #include "postgres.h"
#include <math.h>
#include "halfutils.h" #include "halfutils.h"
#include "halfvec.h" #include "halfvec.h"

View File

@@ -13,20 +13,12 @@
#include "port.h" /* for strtof() */ #include "port.h" /* for strtof() */
#include "sparsevec.h" #include "sparsevec.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/builtins.h"
#include "utils/float.h" #include "utils/float.h"
#include "utils/fmgrprotos.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/varbit.h" #include "utils/numeric.h"
#include "vector.h" #include "vector.h"
#if PG_VERSION_NUM >= 160000
#include "varatt.h"
#endif
#if PG_VERSION_NUM >= 170000
#include "parser/scansup.h"
#endif
#define STATE_DIMS(x) (ARR_DIMS(x)[0] - 1) #define STATE_DIMS(x) (ARR_DIMS(x)[0] - 1)
#define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1)) #define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1))
@@ -137,9 +129,9 @@ InitHalfVector(int dim)
return result; return result;
} }
#if PG_VERSION_NUM >= 170000 /*
#define halfvec_isspace(ch) scanner_isspace(ch) * Check for whitespace, since array_isspace() is static
#else */
static inline bool static inline bool
halfvec_isspace(char ch) halfvec_isspace(char ch)
{ {
@@ -152,7 +144,6 @@ halfvec_isspace(char ch)
return true; return true;
return false; return false;
} }
#endif
/* /*
* Check state array * Check state array

View File

@@ -1,25 +1,18 @@
#include "postgres.h" #include "postgres.h"
#include <float.h> #include <float.h>
#include <limits.h>
#include <math.h> #include <math.h>
#include "access/amapi.h" #include "access/amapi.h"
#include "access/genam.h"
#include "access/reloptions.h" #include "access/reloptions.h"
#include "commands/progress.h" #include "commands/progress.h"
#include "commands/vacuum.h" #include "commands/vacuum.h"
#include "fmgr.h"
#include "hnsw.h" #include "hnsw.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/pg_list.h"
#include "storage/lwlock.h"
#include "utils/float.h" #include "utils/float.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/relcache.h"
#include "utils/selfuncs.h" #include "utils/selfuncs.h"
#include "utils/spccache.h" #include "utils/spccache.h"
#include "vector.h"
#if PG_VERSION_NUM < 150000 #if PG_VERSION_NUM < 150000
#define MarkGUCPrefixReserved(x) EmitWarningsOnPlaceholders(x) #define MarkGUCPrefixReserved(x) EmitWarningsOnPlaceholders(x)
@@ -267,64 +260,6 @@ FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hnswhandler);
Datum Datum
hnswhandler(PG_FUNCTION_ARGS) hnswhandler(PG_FUNCTION_ARGS)
{ {
#if PG_VERSION_NUM >= 190000
static const IndexAmRoutine amroutine = {
.type = T_IndexAmRoutine,
.amstrategies = 0,
.amsupport = 3,
.amoptsprocnum = 0,
.amcanorder = false,
.amcanorderbyop = true,
.amcanhash = false,
.amconsistentequality = false,
.amconsistentordering = false,
.amcanbackward = false,
.amcanunique = false,
.amcanmulticol = false,
.amoptionalkey = true,
.amsearcharray = false,
.amsearchnulls = false,
.amstorage = false,
.amclusterable = false,
.ampredlocks = false,
.amcanparallel = false,
.amcanbuildparallel = true,
.amcaninclude = false,
.amusemaintenanceworkmem = false,
.amsummarizing = false,
.amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL,
.amkeytype = InvalidOid,
.ambuild = hnswbuild,
.ambuildempty = hnswbuildempty,
.aminsert = hnswinsert,
.aminsertcleanup = NULL,
.ambulkdelete = hnswbulkdelete,
.amvacuumcleanup = hnswvacuumcleanup,
.amcanreturn = NULL,
.amcostestimate = hnswcostestimate,
.amgettreeheight = NULL,
.amoptions = hnswoptions,
.amproperty = NULL,
.ambuildphasename = hnswbuildphasename,
.amvalidate = hnswvalidate,
.amadjustmembers = NULL,
.ambeginscan = hnswbeginscan,
.amrescan = hnswrescan,
.amgettuple = hnswgettuple,
.amgetbitmap = NULL,
.amendscan = hnswendscan,
.ammarkpos = NULL,
.amrestrpos = NULL,
.amestimateparallelscan = NULL,
.aminitparallelscan = NULL,
.amparallelrescan = NULL,
.amtranslatestrategy = NULL,
.amtranslatecmptype = NULL,
};
PG_RETURN_POINTER(&amroutine);
#else
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine); IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
amroutine->amstrategies = 0; amroutine->amstrategies = 0;
@@ -398,5 +333,4 @@ hnswhandler(PG_FUNCTION_ARGS)
#endif #endif
PG_RETURN_POINTER(amroutine); PG_RETURN_POINTER(amroutine);
#endif
} }

View File

@@ -3,29 +3,15 @@
#include "postgres.h" #include "postgres.h"
#include <math.h>
#include "access/genam.h" #include "access/genam.h"
#include "access/parallel.h" #include "access/parallel.h"
#include "lib/pairingheap.h" #include "lib/pairingheap.h"
#include "nodes/execnodes.h" #include "nodes/execnodes.h"
#include "port.h" /* for random() */ #include "port.h" /* for random() */
#include "storage/bufpage.h"
#include "storage/condition_variable.h"
#include "storage/lwlock.h"
#include "storage/s_lock.h"
#include "utils/relptr.h" #include "utils/relptr.h"
#include "utils/sampling.h" #include "utils/sampling.h"
#include "vector.h" #include "vector.h"
#ifdef HNSW_BENCH
#include "portability/instr_time.h"
#endif
#if PG_VERSION_NUM >= 190000
typedef Pointer Item;
#endif
#define HNSW_MAX_DIM 2000 #define HNSW_MAX_DIM 2000
#define HNSW_MAX_NNZ 1000 #define HNSW_MAX_NNZ 1000
@@ -82,21 +68,6 @@ typedef Pointer Item;
#define HnswPageGetOpaque(page) ((HnswPageOpaque) PageGetSpecialPointer(page)) #define HnswPageGetOpaque(page) ((HnswPageOpaque) PageGetSpecialPointer(page))
#define HnswPageGetMeta(page) ((HnswMetaPageData *) PageGetContents(page)) #define HnswPageGetMeta(page) ((HnswMetaPageData *) PageGetContents(page))
#ifdef HNSW_BENCH
#define HnswBench(name, code) \
do { \
instr_time start; \
instr_time duration; \
INSTR_TIME_SET_CURRENT(start); \
(code); \
INSTR_TIME_SET_CURRENT(duration); \
INSTR_TIME_SUBTRACT(duration, start); \
elog(INFO, "%s: %.3f ms", name, INSTR_TIME_GET_MILLISEC(duration)); \
} while (0)
#else
#define HnswBench(name, code) (code)
#endif
#if PG_VERSION_NUM >= 150000 #if PG_VERSION_NUM >= 150000
#define RandomDouble() pg_prng_double(&pg_global_prng_state) #define RandomDouble() pg_prng_double(&pg_global_prng_state)
#define SeedRandom(seed) pg_prng_seed(&pg_global_prng_state, seed) #define SeedRandom(seed) pg_prng_seed(&pg_global_prng_state, seed)
@@ -431,7 +402,6 @@ typedef struct HnswVacuumState
BufferAccessStrategy bas; BufferAccessStrategy bas;
HnswNeighborTuple ntup; HnswNeighborTuple ntup;
HnswElementData highestPoint; HnswElementData highestPoint;
HnswElementData fallbackPoint;
/* Memory */ /* Memory */
MemoryContext tmpCtx; MemoryContext tmpCtx;
@@ -454,7 +424,7 @@ void *HnswAlloc(HnswAllocator * allocator, Size size);
HnswElement HnswInitElement(char *base, ItemPointer tid, int m, double ml, int maxLevel, HnswAllocator * alloc); HnswElement HnswInitElement(char *base, ItemPointer tid, int m, double ml, int maxLevel, HnswAllocator * alloc);
HnswElement HnswInitElementFromBlock(BlockNumber blkno, OffsetNumber offno); HnswElement HnswInitElementFromBlock(BlockNumber blkno, OffsetNumber offno);
void HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, HnswSupport * support, int m, int efConstruction, bool existing); void HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, HnswSupport * support, int m, int efConstruction, bool existing);
HnswSearchCandidate *HnswEntryCandidate(char *base, HnswElement entryPoint, HnswQuery * q, Relation index, HnswSupport * support, bool loadVec); HnswSearchCandidate *HnswEntryCandidate(char *base, HnswElement em, HnswQuery * q, Relation rel, HnswSupport * support, bool loadVec);
void HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum, bool building); void HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum, bool building);
void HnswSetNeighborTuple(char *base, HnswNeighborTuple ntup, HnswElement e, int m); void HnswSetNeighborTuple(char *base, HnswNeighborTuple ntup, HnswElement e, int m);
void HnswAddHeapTid(HnswElement element, ItemPointer heaptid); void HnswAddHeapTid(HnswElement element, ItemPointer heaptid);

View File

@@ -36,14 +36,11 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include <limits.h> #include <math.h>
#include "access/genam.h"
#include "access/parallel.h" #include "access/parallel.h"
#include "access/relscan.h"
#include "access/table.h" #include "access/table.h"
#include "access/tableam.h" #include "access/tableam.h"
#include "access/tupdesc.h"
#include "access/xact.h" #include "access/xact.h"
#include "access/xloginsert.h" #include "access/xloginsert.h"
#include "catalog/index.h" #include "catalog/index.h"
@@ -51,15 +48,11 @@
#include "commands/progress.h" #include "commands/progress.h"
#include "hnsw.h" #include "hnsw.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/execnodes.h"
#include "optimizer/optimizer.h" #include "optimizer/optimizer.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/condition_variable.h"
#include "tcop/tcopprot.h" #include "tcop/tcopprot.h"
#include "utils/datum.h" #include "utils/datum.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#if PG_VERSION_NUM >= 160000 #if PG_VERSION_NUM >= 160000
#include "varatt.h" #include "varatt.h"
@@ -80,8 +73,6 @@
#define PARALLEL_KEY_HNSW_AREA UINT64CONST(0xA000000000000002) #define PARALLEL_KEY_HNSW_AREA UINT64CONST(0xA000000000000002)
#define PARALLEL_KEY_QUERY_TEXT UINT64CONST(0xA000000000000003) #define PARALLEL_KEY_QUERY_TEXT UINT64CONST(0xA000000000000003)
#define HNSW_MAX_GRAPH_MEMORY (SIZE_MAX / 2)
/* /*
* Create the metapage * Create the metapage
*/ */
@@ -494,7 +485,6 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
LWLock *flushLock = &graph->flushLock; LWLock *flushLock = &graph->flushLock;
char *base = buildstate->hnswarea; char *base = buildstate->hnswarea;
Datum value; Datum value;
Size memoryMargin;
/* Form index value */ /* Form index value */
if (!HnswFormIndexValue(&value, values, isnull, buildstate->typeInfo, support)) if (!HnswFormIndexValue(&value, values, isnull, buildstate->typeInfo, support))
@@ -503,9 +493,6 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
/* Get datum size */ /* Get datum size */
valueSize = VARSIZE_ANY(DatumGetPointer(value)); valueSize = VARSIZE_ANY(DatumGetPointer(value));
/* In a parallel build, add a margin so allocations never fail */
memoryMargin = base == NULL ? 0 : 1024 * 1024;
/* Ensure graph not flushed when inserting */ /* Ensure graph not flushed when inserting */
LWLockAcquire(flushLock, LW_SHARED); LWLockAcquire(flushLock, LW_SHARED);
@@ -527,7 +514,7 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
* Check that we have enough memory available for the new element now that * Check that we have enough memory available for the new element now that
* we have the allocator lock, and flush pages if needed. * we have the allocator lock, and flush pages if needed.
*/ */
if (graph->memoryUsed + memoryMargin >= graph->memoryTotal) if (graph->memoryUsed >= graph->memoryTotal)
{ {
LWLockRelease(&graph->allocatorLock); LWLockRelease(&graph->allocatorLock);
@@ -562,7 +549,7 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
/* Copy the datum */ /* Copy the datum */
memcpy(valuePtr, DatumGetPointer(value), valueSize); memcpy(valuePtr, DatumGetPointer(value), valueSize);
HnswPtrStore(base, element->value, (char *) valuePtr); HnswPtrStore(base, element->value, valuePtr);
/* Create a lock for the element */ /* Create a lock for the element */
LWLockInitialize(&element->lock, hnsw_lock_tranche_id); LWLockInitialize(&element->lock, hnsw_lock_tranche_id);
@@ -620,7 +607,7 @@ InitGraph(HnswGraph * graph, char *base, Size memoryTotal)
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;
graph->memoryTotal = Min(memoryTotal, HNSW_MAX_GRAPH_MEMORY); graph->memoryTotal = memoryTotal;
graph->flushed = false; graph->flushed = false;
graph->indtuples = 0; graph->indtuples = 0;
SpinLockInit(&graph->lock); SpinLockInit(&graph->lock);
@@ -661,17 +648,9 @@ static void *
HnswSharedMemoryAlloc(Size size, void *state) HnswSharedMemoryAlloc(Size size, void *state)
{ {
HnswBuildState *buildstate = (HnswBuildState *) state; HnswBuildState *buildstate = (HnswBuildState *) state;
Size alignedSize = MAXALIGN(size); void *chunk = buildstate->hnswarea + buildstate->graph->memoryUsed;
void *chunk;
if (alignedSize > 1024 * 1024) buildstate->graph->memoryUsed += MAXALIGN(size);
elog(ERROR, "hnsw allocation too large");
if (buildstate->graph->memoryUsed + alignedSize > buildstate->graph->memoryTotal)
elog(ERROR, "hnsw allocator out of memory");
chunk = buildstate->hnswarea + buildstate->graph->memoryUsed;
buildstate->graph->memoryUsed += alignedSize;
return chunk; return chunk;
} }
@@ -803,11 +782,7 @@ HnswParallelScanAndInsert(Relation heapRel, Relation indexRel, HnswShared * hnsw
buildstate.hnswarea = hnswarea; buildstate.hnswarea = hnswarea;
InitAllocator(&buildstate.allocator, &HnswSharedMemoryAlloc, &buildstate); InitAllocator(&buildstate.allocator, &HnswSharedMemoryAlloc, &buildstate);
scan = table_beginscan_parallel(heapRel, scan = table_beginscan_parallel(heapRel,
ParallelTableScanFromHnswShared(hnswshared) ParallelTableScanFromHnswShared(hnswshared));
#if PG_VERSION_NUM >= 190000
,SO_NONE
#endif
);
reltuples = table_index_build_scan(heapRel, indexRel, indexInfo, reltuples = table_index_build_scan(heapRel, indexRel, indexInfo,
true, progress, BuildCallback, true, progress, BuildCallback,
(void *) &buildstate, scan); (void *) &buildstate, scan);
@@ -961,8 +936,6 @@ HnswBeginParallel(HnswBuildState * buildstate, bool isconcurrent, int request)
if (esthnswarea > estother) if (esthnswarea > estother)
esthnswarea -= estother; esthnswarea -= estother;
esthnswarea = Min(esthnswarea, HNSW_MAX_GRAPH_MEMORY);
shm_toc_estimate_chunk(&pcxt->estimator, esthnswarea); shm_toc_estimate_chunk(&pcxt->estimator, esthnswarea);
shm_toc_estimate_keys(&pcxt->estimator, 2); shm_toc_estimate_keys(&pcxt->estimator, 2);
@@ -1005,7 +978,8 @@ HnswBeginParallel(HnswBuildState * buildstate, bool isconcurrent, int request)
snapshot); snapshot);
hnswarea = (char *) shm_toc_allocate(pcxt->toc, esthnswarea); hnswarea = (char *) shm_toc_allocate(pcxt->toc, esthnswarea);
InitGraph(&hnswshared->graphData, hnswarea, esthnswarea); /* Report less than allocated so never fails */
InitGraph(&hnswshared->graphData, hnswarea, esthnswarea - 1024 * 1024);
/* /*
* Avoid base address for relptr for Postgres < 14.5 * Avoid base address for relptr for Postgres < 14.5

View File

@@ -1,15 +1,13 @@
#include "postgres.h" #include "postgres.h"
#include "access/genam.h" #include <math.h>
#include "access/generic_xlog.h" #include "access/generic_xlog.h"
#include "hnsw.h" #include "hnsw.h"
#include "nodes/execnodes.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "storage/lwlock.h"
#include "utils/datum.h" #include "utils/datum.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/rel.h"
#if PG_VERSION_NUM >= 160000 #if PG_VERSION_NUM >= 160000
#include "varatt.h" #include "varatt.h"
@@ -714,7 +712,7 @@ HnswInsertTupleOnDisk(Relation index, HnswSupport * support, Datum value, ItemPo
/* Create an element */ /* Create an element */
element = HnswInitElement(base, heaptid, m, HnswGetMl(m), HnswGetMaxLevel(m), NULL); element = HnswInitElement(base, heaptid, m, HnswGetMl(m), HnswGetMaxLevel(m), NULL);
HnswPtrStore(base, element->value, (char *) DatumGetPointer(value)); HnswPtrStore(base, element->value, DatumGetPointer(value));
/* Prevent concurrent inserts when likely updating entry point */ /* Prevent concurrent inserts when likely updating entry point */
if (entryPoint == NULL || element->level > entryPoint->level) if (entryPoint == NULL || element->level > entryPoint->level)

View File

@@ -1,23 +1,12 @@
#include "postgres.h" #include "postgres.h"
#include <limits.h>
#include "access/genam.h"
#include "access/relscan.h" #include "access/relscan.h"
#include "hnsw.h" #include "hnsw.h"
#include "lib/pairingheap.h"
#include "miscadmin.h"
#include "nodes/pg_list.h"
#include "pgstat.h" #include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "utils/float.h" #include "utils/float.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/relcache.h"
#include "utils/snapmgr.h"
#if PG_VERSION_NUM >= 160000
#include "varatt.h"
#endif
/* /*
* Algorithm 5 from paper * Algorithm 5 from paper
@@ -153,7 +142,7 @@ hnswbeginscan(Relation index, int nkeys, int norderbys)
/* Calculate max memory */ /* Calculate max memory */
/* Add 256 extra bytes to fill last block when close */ /* Add 256 extra bytes to fill last block when close */
maxMemory = (double) work_mem * hnsw_scan_mem_multiplier * 1024.0 + 256; maxMemory = (double) work_mem * hnsw_scan_mem_multiplier * 1024.0 + 256;
so->maxMemory = Min(maxMemory, (double) (SIZE_MAX / 2)); so->maxMemory = Min(maxMemory, (double) SIZE_MAX);
scan->opaque = so; scan->opaque = so;

View File

@@ -2,20 +2,18 @@
#include <math.h> #include <math.h>
#include "access/genam.h"
#include "access/generic_xlog.h" #include "access/generic_xlog.h"
#include "catalog/pg_type.h"
#include "catalog/pg_type_d.h"
#include "common/hashfn.h" #include "common/hashfn.h"
#include "fmgr.h" #include "fmgr.h"
#include "hnsw.h" #include "hnsw.h"
#include "lib/pairingheap.h" #include "lib/pairingheap.h"
#include "nodes/pg_list.h"
#include "port/atomics.h"
#include "sparsevec.h" #include "sparsevec.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "utils/datum.h" #include "utils/datum.h"
#include "utils/memdebug.h" #include "utils/memdebug.h"
#include "utils/rel.h" #include "utils/rel.h"
#include "vector.h"
#if PG_VERSION_NUM >= 160000 #if PG_VERSION_NUM >= 160000
#include "varatt.h" #include "varatt.h"
@@ -262,7 +260,7 @@ HnswInitElement(char *base, ItemPointer heaptid, int m, double ml, int maxLevel,
HnswInitNeighbors(base, element, m, allocator); HnswInitNeighbors(base, element, m, allocator);
HnswPtrStore(base, element->value, (char *) NULL); HnswPtrStore(base, element->value, (Pointer) NULL);
return element; return element;
} }
@@ -288,7 +286,7 @@ HnswInitElementFromBlock(BlockNumber blkno, OffsetNumber offno)
element->blkno = blkno; element->blkno = blkno;
element->offno = offno; element->offno = offno;
HnswPtrStore(base, element->neighbors, (HnswNeighborArrayPtr *) NULL); HnswPtrStore(base, element->neighbors, (HnswNeighborArrayPtr *) NULL);
HnswPtrStore(base, element->value, (char *) NULL); HnswPtrStore(base, element->value, (Pointer) NULL);
return element; return element;
} }
@@ -514,7 +512,7 @@ HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHe
char *base = NULL; char *base = NULL;
Datum value = datumCopy(PointerGetDatum(&etup->data), false, -1); Datum value = datumCopy(PointerGetDatum(&etup->data), false, -1);
HnswPtrStore(base, element->value, (char *) DatumGetPointer(value)); HnswPtrStore(base, element->value, DatumGetPointer(value));
} }
} }
@@ -546,9 +544,6 @@ HnswLoadElementImpl(BlockNumber blkno, OffsetNumber offno, double *distance, Hns
Assert(HnswIsElementTuple(etup)); Assert(HnswIsElementTuple(etup));
if (unlikely(etup->deleted))
elog(ERROR, "cannot load deleted element");
/* Calculate distance */ /* Calculate distance */
if (distance != NULL) if (distance != NULL)
{ {
@@ -834,6 +829,7 @@ HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation in
HnswUnvisited *unvisited = palloc(lm * sizeof(HnswUnvisited)); HnswUnvisited *unvisited = palloc(lm * sizeof(HnswUnvisited));
int unvisitedLength; int unvisitedLength;
bool inMemory = index == NULL; bool inMemory = index == NULL;
double lambda = 0.12;
if (v == NULL) if (v == NULL)
{ {
@@ -889,7 +885,7 @@ HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation in
HnswSearchCandidate *f = HnswGetSearchCandidate(w_node, pairingheap_first(W)); HnswSearchCandidate *f = HnswGetSearchCandidate(w_node, pairingheap_first(W));
HnswElement cElement; HnswElement cElement;
if (c->distance > f->distance) if (c->distance > f->distance * (1 + lambda))
break; break;
cElement = HnswPtrAccess(base, c->element); cElement = HnswPtrAccess(base, c->element);
@@ -909,8 +905,10 @@ HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation in
HnswSearchCandidate *e; HnswSearchCandidate *e;
double eDistance; double eDistance;
bool alwaysAdd = wlen < ef; bool alwaysAdd = wlen < ef;
double fDistance;
f = HnswGetSearchCandidate(w_node, pairingheap_first(W)); f = HnswGetSearchCandidate(w_node, pairingheap_first(W));
fDistance = f->distance * (1 + lambda);
if (inMemory) if (inMemory)
{ {
@@ -925,13 +923,13 @@ HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation in
/* Avoid any allocations if not adding */ /* Avoid any allocations if not adding */
eElement = NULL; eElement = NULL;
HnswLoadElementImpl(blkno, offno, &eDistance, q, index, support, inserting, alwaysAdd || discarded != NULL ? NULL : &f->distance, &eElement); HnswLoadElementImpl(blkno, offno, &eDistance, q, index, support, inserting, alwaysAdd || discarded != NULL ? NULL : &fDistance, &eElement);
if (eElement == NULL) if (eElement == NULL)
continue; continue;
} }
if (!(eDistance < f->distance || alwaysAdd)) if (eElement == NULL || !(eDistance < fDistance || alwaysAdd))
{ {
if (discarded != NULL) if (discarded != NULL)
{ {

View File

@@ -1,14 +1,13 @@
#include "postgres.h" #include "postgres.h"
#include "access/genam.h" #include <math.h>
#include "access/generic_xlog.h" #include "access/generic_xlog.h"
#include "commands/vacuum.h" #include "commands/vacuum.h"
#include "hnsw.h" #include "hnsw.h"
#include "nodes/pg_list.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/rel.h"
#if PG_VERSION_NUM >= 160000 #if PG_VERSION_NUM >= 160000
#include "varatt.h" #include "varatt.h"
@@ -37,20 +36,17 @@ RemoveHeapTids(HnswVacuumState * vacuumstate)
{ {
BlockNumber blkno = HNSW_HEAD_BLKNO; BlockNumber blkno = HNSW_HEAD_BLKNO;
HnswElement highestPoint = &vacuumstate->highestPoint; HnswElement highestPoint = &vacuumstate->highestPoint;
HnswElement fallbackPoint = &vacuumstate->fallbackPoint;
Relation index = vacuumstate->index; Relation index = vacuumstate->index;
BufferAccessStrategy bas = vacuumstate->bas; BufferAccessStrategy bas = vacuumstate->bas;
HnswElement entryPoint = HnswGetEntryPoint(vacuumstate->index);
IndexBulkDeleteResult *stats = vacuumstate->stats; IndexBulkDeleteResult *stats = vacuumstate->stats;
/* Store separately since HnswElement level is uint8 */ /* Store separately since highestPoint.level is uint8 */
int highestLevel = -1; int highestLevel = -1;
int fallbackLevel = -1;
/* Initialize highest point and fallback point */ /* Initialize highest point */
highestPoint->blkno = InvalidBlockNumber; highestPoint->blkno = InvalidBlockNumber;
highestPoint->offno = InvalidOffsetNumber; highestPoint->offno = InvalidOffsetNumber;
fallbackPoint->blkno = InvalidBlockNumber;
fallbackPoint->offno = InvalidOffsetNumber;
while (BlockNumberIsValid(blkno)) while (BlockNumberIsValid(blkno))
{ {
@@ -122,31 +118,14 @@ RemoveHeapTids(HnswVacuumState * vacuumstate)
tidhash_insert(vacuumstate->deleted, ip, &found); tidhash_insert(vacuumstate->deleted, ip, &found);
Assert(!found); Assert(!found);
} }
else if (etup->level > highestLevel) else if (etup->level > highestLevel && !(entryPoint != NULL && blkno == entryPoint->blkno && offno == entryPoint->offno))
{ {
if (BlockNumberIsValid(highestPoint->blkno)) /* Keep track of highest non-entry point */
{
/* Current highest point becomes fallback */
fallbackPoint->blkno = highestPoint->blkno;
fallbackPoint->offno = highestPoint->offno;
fallbackPoint->level = highestPoint->level;
fallbackLevel = highestLevel;
}
/* Keep track of highest point */
highestPoint->blkno = blkno; highestPoint->blkno = blkno;
highestPoint->offno = offno; highestPoint->offno = offno;
highestPoint->level = etup->level; highestPoint->level = etup->level;
highestLevel = etup->level; highestLevel = etup->level;
} }
else if (etup->level > fallbackLevel)
{
/* Keep track of second highest point */
fallbackPoint->blkno = blkno;
fallbackPoint->offno = offno;
fallbackPoint->level = etup->level;
fallbackLevel = etup->level;
}
} }
blkno = HnswPageGetOpaque(page)->nextblkno; blkno = HnswPageGetOpaque(page)->nextblkno;
@@ -158,10 +137,6 @@ RemoveHeapTids(HnswVacuumState * vacuumstate)
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
} }
#ifdef HNSW_MEMORY
elog(INFO, "memory: %zu KB", MemoryContextMemAllocated(CurrentMemoryContext, true) / 1024);
#endif
} }
/* /*
@@ -203,12 +178,7 @@ NeedsUpdated(HnswVacuumState * vacuumstate, HnswElement element)
/* Also update if layer 0 is not full */ /* Also update if layer 0 is not full */
/* This could indicate too many candidates being deleted during insert */ /* This could indicate too many candidates being deleted during insert */
if (!needsUpdated) if (!needsUpdated)
{
/* Keep clang-tidy happy */
Assert(ntup->count > 0);
needsUpdated = !ItemPointerIsValid(&ntup->indextids[ntup->count - 1]); needsUpdated = !ItemPointerIsValid(&ntup->indextids[ntup->count - 1]);
}
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
@@ -293,27 +263,12 @@ RepairGraphEntryPoint(HnswVacuumState * vacuumstate)
/* Get a shared lock */ /* Get a shared lock */
LockPage(index, HNSW_UPDATE_LOCK, ShareLock); LockPage(index, HNSW_UPDATE_LOCK, ShareLock);
/* Get latest entry point */
entryPoint = HnswGetEntryPoint(index);
/* Use fallback point if highest point is entry point */
if (entryPoint != NULL && entryPoint->blkno == highestPoint->blkno && entryPoint->offno == highestPoint->offno)
{
highestPoint = &vacuumstate->fallbackPoint;
if (!BlockNumberIsValid(highestPoint->blkno))
highestPoint = NULL;
}
if (highestPoint != NULL)
{
/* Load element */ /* Load element */
HnswLoadElement(highestPoint, NULL, NULL, index, support, true, NULL); HnswLoadElement(highestPoint, NULL, NULL, index, support, true, NULL);
/* Repair if needed */ /* Repair if needed */
if (NeedsUpdated(vacuumstate, highestPoint)) if (NeedsUpdated(vacuumstate, highestPoint))
RepairGraphElement(vacuumstate, highestPoint, entryPoint); RepairGraphElement(vacuumstate, highestPoint, HnswGetEntryPoint(index));
}
/* Release lock */ /* Release lock */
UnlockPage(index, HNSW_UPDATE_LOCK, ShareLock); UnlockPage(index, HNSW_UPDATE_LOCK, ShareLock);
@@ -480,99 +435,6 @@ RepairGraph(HnswVacuumState * vacuumstate)
/* Reset memory context */ /* Reset memory context */
MemoryContextSwitchTo(oldCtx); MemoryContextSwitchTo(oldCtx);
MemoryContextReset(vacuumstate->tmpCtx); MemoryContextReset(vacuumstate->tmpCtx);
#ifdef HNSW_VACUUM_PROGRESS
if (!BlockNumberIsValid(blkno) || (blkno - HNSW_HEAD_BLKNO) % 1000 == 0)
{
BlockNumber totalBlocks = RelationGetNumberOfBlocks(index);
BlockNumber currentBlocks = BlockNumberIsValid(blkno) ? blkno : totalBlocks;
elog(INFO, "hnsw vacuum progress: %.1f%%", 100.0 * currentBlocks / totalBlocks);
}
#endif
}
}
/*
* Confirm graph was repaired
*/
static void
ConfirmRepaired(HnswVacuumState * vacuumstate)
{
BlockNumber blkno = HNSW_HEAD_BLKNO;
Relation index = vacuumstate->index;
BufferAccessStrategy bas = vacuumstate->bas;
while (BlockNumberIsValid(blkno))
{
Buffer buf;
Page page;
OffsetNumber offno;
OffsetNumber maxoffno;
vacuum_delay_point();
buf = ReadBufferExtended(index, MAIN_FORKNUM, blkno, RBM_NORMAL, bas);
LockBuffer(buf, BUFFER_LOCK_SHARE);
page = BufferGetPage(buf);
maxoffno = PageGetMaxOffsetNumber(page);
/* Iterate over nodes */
for (offno = FirstOffsetNumber; offno <= maxoffno; offno = OffsetNumberNext(offno))
{
HnswElementTuple etup = (HnswElementTuple) PageGetItem(page, PageGetItemId(page, offno));
HnswNeighborTuple ntup;
Buffer nbuf;
Page npage;
BlockNumber neighborPage;
OffsetNumber neighborOffno;
/* Skip neighbor tuples */
if (!HnswIsElementTuple(etup))
continue;
/* Skip if being deleted */
if (!ItemPointerIsValid(&etup->heaptids[0]))
continue;
/* Get neighbor page */
neighborPage = ItemPointerGetBlockNumber(&etup->neighbortid);
neighborOffno = ItemPointerGetOffsetNumber(&etup->neighbortid);
if (neighborPage == blkno)
{
nbuf = buf;
npage = page;
}
else
{
nbuf = ReadBufferExtended(index, MAIN_FORKNUM, neighborPage, RBM_NORMAL, bas);
LockBuffer(nbuf, BUFFER_LOCK_SHARE);
npage = BufferGetPage(nbuf);
}
ntup = (HnswNeighborTuple) PageGetItem(npage, PageGetItemId(npage, neighborOffno));
/* Check neighbors */
for (int i = 0; i < ntup->count; i++)
{
ItemPointer indextid = &ntup->indextids[i];
if (!ItemPointerIsValid(indextid))
continue;
/* Check if in deleted list */
if (DeletedContains(vacuumstate->deleted, indextid))
elog(ERROR, "hnsw graph not repaired");
}
if (nbuf != buf)
UnlockReleaseBuffer(nbuf);
}
blkno = HnswPageGetOpaque(page)->nextblkno;
UnlockReleaseBuffer(buf);
} }
} }
@@ -666,9 +528,8 @@ MarkDeleted(HnswVacuumState * vacuumstate)
ntup = (HnswNeighborTuple) PageGetItem(npage, PageGetItemId(npage, neighborOffno)); ntup = (HnswNeighborTuple) PageGetItem(npage, PageGetItemId(npage, neighborOffno));
/* Overwrite element */ /* Overwrite element */
/* Use memset instead of MemSet to keep clang-tidy happy */
etup->deleted = 1; etup->deleted = 1;
memset(&etup->data, 0, VARSIZE_ANY(&etup->data)); MemSet(&etup->data, 0, VARSIZE_ANY(&etup->data));
/* Overwrite neighbors */ /* Overwrite neighbors */
for (int i = 0; i < ntup->count; i++) for (int i = 0; i < ntup->count; i++)
@@ -766,16 +627,13 @@ hnswbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
InitVacuumState(&vacuumstate, info, stats, callback, callback_state); InitVacuumState(&vacuumstate, info, stats, callback, callback_state);
/* Pass 1: Remove heap TIDs */ /* Pass 1: Remove heap TIDs */
HnswBench("RemoveHeapTids", RemoveHeapTids(&vacuumstate)); RemoveHeapTids(&vacuumstate);
/* Pass 2: Repair graph */ /* Pass 2: Repair graph */
HnswBench("RepairGraph", RepairGraph(&vacuumstate)); RepairGraph(&vacuumstate);
/* Pass 3: Confirm repaired */ /* Pass 3: Mark as deleted */
HnswBench("ConfirmRepaired", ConfirmRepaired(&vacuumstate)); MarkDeleted(&vacuumstate);
/* Pass 4: Mark as deleted */
HnswBench("MarkDeleted", MarkDeleted(&vacuumstate));
FreeVacuumState(&vacuumstate); FreeVacuumState(&vacuumstate);

View File

@@ -2,33 +2,23 @@
#include <float.h> #include <float.h>
#include "access/genam.h"
#include "access/generic_xlog.h"
#include "access/itup.h"
#include "access/relscan.h"
#include "access/table.h" #include "access/table.h"
#include "access/tableam.h" #include "access/tableam.h"
#include "access/tupdesc.h"
#include "access/parallel.h" #include "access/parallel.h"
#include "access/xact.h" #include "access/xact.h"
#include "access/xloginsert.h" #include "bitvec.h"
#include "catalog/index.h" #include "catalog/index.h"
#include "catalog/pg_operator_d.h" #include "catalog/pg_operator_d.h"
#include "catalog/pg_type_d.h" #include "catalog/pg_type_d.h"
#include "commands/progress.h" #include "commands/progress.h"
#include "fmgr.h" #include "halfvec.h"
#include "ivfflat.h" #include "ivfflat.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/execnodes.h"
#include "optimizer/optimizer.h" #include "optimizer/optimizer.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/condition_variable.h"
#include "tcop/tcopprot.h" #include "tcop/tcopprot.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/rel.h" #include "vector.h"
#include "utils/sampling.h"
#include "utils/snapmgr.h"
#include "utils/tuplesort.h"
#if PG_VERSION_NUM >= 160000 #if PG_VERSION_NUM >= 160000
#include "varatt.h" #include "varatt.h"
@@ -63,13 +53,15 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0])); Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
/* /*
* Check with KMEANS_NORM_PROC that the value can be normalized since * Normalize with KMEANS_NORM_PROC since spherical distance function
* spherical distance function expects unit vectors * expects unit vectors
*/ */
if (buildstate->kmeansnormprocinfo != NULL) if (buildstate->kmeansnormprocinfo != NULL)
{ {
if (!IvfflatCheckNorm(buildstate->kmeansnormprocinfo, buildstate->collation, value)) if (!IvfflatCheckNorm(buildstate->kmeansnormprocinfo, buildstate->collation, value))
return; return;
value = IvfflatNormValue(buildstate->typeInfo, buildstate->collation, value);
} }
if (samples->length < targsamples) if (samples->length < targsamples)
@@ -80,7 +72,7 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
else else
{ {
if (buildstate->rowstoskip < 0) if (buildstate->rowstoskip < 0)
buildstate->rowstoskip = reservoir_get_next_S(&buildstate->rstate, buildstate->samplerows, targsamples); buildstate->rowstoskip = reservoir_get_next_S(&buildstate->rstate, samples->length, targsamples);
if (buildstate->rowstoskip <= 0) if (buildstate->rowstoskip <= 0)
{ {
@@ -96,9 +88,6 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
buildstate->rowstoskip -= 1; buildstate->rowstoskip -= 1;
} }
/* Increment after reservoir_get_next_S */
buildstate->samplerows += 1;
} }
/* /*
@@ -135,7 +124,6 @@ SampleRows(IvfflatBuildState * buildstate)
int targsamples = buildstate->samples->maxlen; int targsamples = buildstate->samples->maxlen;
BlockNumber totalblocks = RelationGetNumberOfBlocks(buildstate->heap); BlockNumber totalblocks = RelationGetNumberOfBlocks(buildstate->heap);
buildstate->samplerows = 0;
buildstate->rowstoskip = -1; buildstate->rowstoskip = -1;
BlockSampler_Init(&buildstate->bs, totalblocks, targsamples, RandomInt()); BlockSampler_Init(&buildstate->bs, totalblocks, targsamples, RandomInt());
@@ -145,24 +133,8 @@ SampleRows(IvfflatBuildState * buildstate)
{ {
BlockNumber targblock = BlockSampler_Next(&buildstate->bs); BlockNumber targblock = BlockSampler_Next(&buildstate->bs);
/* Set anyvisible to false like table_index_build_scan */
table_index_build_range_scan(buildstate->heap, buildstate->index, buildstate->indexInfo, table_index_build_range_scan(buildstate->heap, buildstate->index, buildstate->indexInfo,
false, false, false, targblock, 1, SampleCallback, (void *) buildstate, NULL); false, true, false, targblock, 1, SampleCallback, (void *) buildstate, NULL);
}
/* Normalize if needed */
if (buildstate->kmeansnormprocinfo != NULL)
{
VectorArray samples = buildstate->samples;
for (int i = 0; i < samples->length; i++)
{
Datum value = PointerGetDatum(VectorArrayGet(samples, i));
Datum normValue = IvfflatNormValue(buildstate->typeInfo, buildstate->collation, value);
VectorArraySet(samples, i, DatumGetPointer(normValue));
pfree(DatumGetPointer(normValue));
}
} }
} }
@@ -393,13 +365,9 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 1, "list", INT4OID, -1, 0); TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 1, "list", INT4OID, -1, 0);
TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 2, "tid", TIDOID, -1, 0); TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 2, "tid", TIDOID, -1, 0);
TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 3, "vector", TupleDescAttr(buildstate->tupdesc, 0)->atttypid, -1, 0); TupleDescInitEntry(buildstate->sortdesc, (AttrNumber) 3, "vector", TupleDescAttr(buildstate->tupdesc, 0)->atttypid, -1, 0);
#if PG_VERSION_NUM >= 190000
TupleDescFinalize(buildstate->sortdesc);
#endif
buildstate->slot = MakeSingleTupleTableSlot(buildstate->sortdesc, &TTSOpsVirtual); buildstate->slot = MakeSingleTupleTableSlot(buildstate->sortdesc, &TTSOpsVirtual);
/* TODO Ensure within maintenance_work_mem */
buildstate->centers = VectorArrayInit(buildstate->lists, buildstate->dimensions, buildstate->typeInfo->itemSize(buildstate->dimensions)); buildstate->centers = VectorArrayInit(buildstate->lists, buildstate->dimensions, buildstate->typeInfo->itemSize(buildstate->dimensions));
buildstate->listInfo = palloc(sizeof(ListInfo) * buildstate->lists); buildstate->listInfo = palloc(sizeof(ListInfo) * buildstate->lists);
@@ -458,7 +426,7 @@ ComputeCenters(IvfflatBuildState * buildstate)
buildstate->samples = VectorArrayInit(numSamples, buildstate->dimensions, buildstate->centers->itemsize); buildstate->samples = VectorArrayInit(numSamples, buildstate->dimensions, buildstate->centers->itemsize);
if (buildstate->heap != NULL) if (buildstate->heap != NULL)
{ {
IvfflatBench("sample rows", SampleRows(buildstate)); SampleRows(buildstate);
if (buildstate->samples->length < buildstate->lists) if (buildstate->samples->length < buildstate->lists)
{ {
@@ -673,11 +641,7 @@ IvfflatParallelScanAndSort(IvfflatSpool * ivfspool, IvfflatShared * ivfshared, S
ivfspool->sortstate = InitBuildSortState(buildstate.sortdesc, sortmem, coordinate); ivfspool->sortstate = InitBuildSortState(buildstate.sortdesc, sortmem, coordinate);
buildstate.sortstate = ivfspool->sortstate; buildstate.sortstate = ivfspool->sortstate;
scan = table_beginscan_parallel(ivfspool->heap, scan = table_beginscan_parallel(ivfspool->heap,
ParallelTableScanFromIvfflatShared(ivfshared) ParallelTableScanFromIvfflatShared(ivfshared));
#if PG_VERSION_NUM >= 190000
,SO_NONE
#endif
);
reltuples = table_index_build_scan(ivfspool->heap, ivfspool->index, indexInfo, reltuples = table_index_build_scan(ivfspool->heap, ivfspool->index, indexInfo,
true, progress, BuildCallback, true, progress, BuildCallback,
(void *) &buildstate, scan); (void *) &buildstate, scan);

View File

@@ -3,19 +3,14 @@
#include <float.h> #include <float.h>
#include "access/amapi.h" #include "access/amapi.h"
#include "access/genam.h"
#include "access/reloptions.h" #include "access/reloptions.h"
#include "commands/progress.h" #include "commands/progress.h"
#include "commands/vacuum.h" #include "commands/vacuum.h"
#include "fmgr.h"
#include "ivfflat.h" #include "ivfflat.h"
#include "nodes/pg_list.h"
#include "utils/float.h" #include "utils/float.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/relcache.h"
#include "utils/selfuncs.h" #include "utils/selfuncs.h"
#include "utils/spccache.h" #include "utils/spccache.h"
#include "vector.h"
#if PG_VERSION_NUM < 150000 #if PG_VERSION_NUM < 150000
#define MarkGUCPrefixReserved(x) EmitWarningsOnPlaceholders(x) #define MarkGUCPrefixReserved(x) EmitWarningsOnPlaceholders(x)
@@ -184,64 +179,6 @@ FUNCTION_PREFIX PG_FUNCTION_INFO_V1(ivfflathandler);
Datum Datum
ivfflathandler(PG_FUNCTION_ARGS) ivfflathandler(PG_FUNCTION_ARGS)
{ {
#if PG_VERSION_NUM >= 190000
static const IndexAmRoutine amroutine = {
.type = T_IndexAmRoutine,
.amstrategies = 0,
.amsupport = 5,
.amoptsprocnum = 0,
.amcanorder = false,
.amcanorderbyop = true,
.amcanhash = false,
.amconsistentequality = false,
.amconsistentordering = false,
.amcanbackward = false,
.amcanunique = false,
.amcanmulticol = false,
.amoptionalkey = true,
.amsearcharray = false,
.amsearchnulls = false,
.amstorage = false,
.amclusterable = false,
.ampredlocks = false,
.amcanparallel = false,
.amcanbuildparallel = true,
.amcaninclude = false,
.amusemaintenanceworkmem = false,
.amsummarizing = false,
.amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL,
.amkeytype = InvalidOid,
.ambuild = ivfflatbuild,
.ambuildempty = ivfflatbuildempty,
.aminsert = ivfflatinsert,
.aminsertcleanup = NULL,
.ambulkdelete = ivfflatbulkdelete,
.amvacuumcleanup = ivfflatvacuumcleanup,
.amcanreturn = NULL,
.amcostestimate = ivfflatcostestimate,
.amgettreeheight = NULL,
.amoptions = ivfflatoptions,
.amproperty = NULL,
.ambuildphasename = ivfflatbuildphasename,
.amvalidate = ivfflatvalidate,
.amadjustmembers = NULL,
.ambeginscan = ivfflatbeginscan,
.amrescan = ivfflatrescan,
.amgettuple = ivfflatgettuple,
.amgetbitmap = NULL,
.amendscan = ivfflatendscan,
.ammarkpos = NULL,
.amrestrpos = NULL,
.amestimateparallelscan = NULL,
.aminitparallelscan = NULL,
.amparallelrescan = NULL,
.amtranslatestrategy = NULL,
.amtranslatecmptype = NULL,
};
PG_RETURN_POINTER(&amroutine);
#else
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine); IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
amroutine->amstrategies = 0; amroutine->amstrategies = 0;
@@ -315,5 +252,4 @@ ivfflathandler(PG_FUNCTION_ARGS)
#endif #endif
PG_RETURN_POINTER(amroutine); PG_RETURN_POINTER(amroutine);
#endif
} }

View File

@@ -9,7 +9,6 @@
#include "lib/pairingheap.h" #include "lib/pairingheap.h"
#include "nodes/execnodes.h" #include "nodes/execnodes.h"
#include "port.h" /* for random() */ #include "port.h" /* for random() */
#include "storage/condition_variable.h"
#include "utils/sampling.h" #include "utils/sampling.h"
#include "utils/tuplesort.h" #include "utils/tuplesort.h"
#include "vector.h" #include "vector.h"
@@ -26,10 +25,6 @@
#include "portability/instr_time.h" #include "portability/instr_time.h"
#endif #endif
#if PG_VERSION_NUM >= 190000
typedef Pointer Item;
#endif
#define IVFFLAT_MAX_DIM 2000 #define IVFFLAT_MAX_DIM 2000
/* Support functions */ /* Support functions */
@@ -214,8 +209,7 @@ typedef struct IvfflatBuildState
/* Sampling */ /* Sampling */
BlockSamplerData bs; BlockSamplerData bs;
ReservoirStateData rstate; ReservoirStateData rstate;
double samplerows; int rowstoskip;
double rowstoskip;
/* Sorting */ /* Sorting */
Tuplesortstate *sortstate; Tuplesortstate *sortstate;

View File

@@ -2,16 +2,11 @@
#include <float.h> #include <float.h>
#include "access/genam.h"
#include "access/generic_xlog.h" #include "access/generic_xlog.h"
#include "access/itup.h"
#include "fmgr.h"
#include "ivfflat.h" #include "ivfflat.h"
#include "nodes/execnodes.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/rel.h"
/* /*
* Find the list that minimizes the distance function * Find the list that minimizes the distance function

View File

@@ -1,15 +1,17 @@
#include "postgres.h" #include "postgres.h"
#include <float.h> #include <float.h>
#include <limits.h>
#include <math.h> #include <math.h>
#include "access/genam.h" #include "bitvec.h"
#include "fmgr.h" #include "halfutils.h"
#include "halfvec.h"
#include "ivfflat.h" #include "ivfflat.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/relcache.h" #include "vector.h"
#if PG_VERSION_NUM >= 160000 #if PG_VERSION_NUM >= 160000
#include "varatt.h" #include "varatt.h"

View File

@@ -2,26 +2,15 @@
#include <float.h> #include <float.h>
#include "access/genam.h"
#include "access/itup.h"
#include "access/relscan.h" #include "access/relscan.h"
#include "access/tupdesc.h"
#include "catalog/pg_operator_d.h" #include "catalog/pg_operator_d.h"
#include "catalog/pg_type_d.h" #include "catalog/pg_type_d.h"
#include "fmgr.h"
#include "lib/pairingheap.h" #include "lib/pairingheap.h"
#include "ivfflat.h" #include "ivfflat.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/tuplesort.h"
#if PG_VERSION_NUM >= 160000
#include "varatt.h"
#endif
#define GetScanList(ptr) pairingheap_container(IvfflatScanList, ph_node, ptr) #define GetScanList(ptr) pairingheap_container(IvfflatScanList, ph_node, ptr)
#define GetScanListConst(ptr) pairingheap_const_container(IvfflatScanList, ph_node, ptr) #define GetScanListConst(ptr) pairingheap_const_container(IvfflatScanList, ph_node, ptr)
@@ -298,9 +287,6 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
so->tupdesc = CreateTemplateTupleDesc(2); so->tupdesc = CreateTemplateTupleDesc(2);
TupleDescInitEntry(so->tupdesc, (AttrNumber) 1, "distance", FLOAT8OID, -1, 0); TupleDescInitEntry(so->tupdesc, (AttrNumber) 1, "distance", FLOAT8OID, -1, 0);
TupleDescInitEntry(so->tupdesc, (AttrNumber) 2, "heaptid", TIDOID, -1, 0); TupleDescInitEntry(so->tupdesc, (AttrNumber) 2, "heaptid", TIDOID, -1, 0);
#if PG_VERSION_NUM >= 190000
TupleDescFinalize(so->tupdesc);
#endif
/* Prep sort */ /* Prep sort */
so->sortstate = InitScanSortState(so->tupdesc); so->sortstate = InitScanSortState(so->tupdesc);

View File

@@ -1,19 +1,13 @@
#include "postgres.h" #include "postgres.h"
#include "access/genam.h"
#include "access/generic_xlog.h" #include "access/generic_xlog.h"
#include "bitvec.h"
#include "catalog/pg_type.h"
#include "fmgr.h" #include "fmgr.h"
#include "halfutils.h" #include "halfutils.h"
#include "halfvec.h" #include "halfvec.h"
#include "ivfflat.h" #include "ivfflat.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "utils/relcache.h"
#include "utils/varbit.h"
#include "vector.h"
#if PG_VERSION_NUM >= 160000
#include "varatt.h"
#endif
/* /*
* Allocate a vector array * Allocate a vector array

View File

@@ -1,12 +1,9 @@
#include "postgres.h" #include "postgres.h"
#include "access/genam.h"
#include "access/generic_xlog.h" #include "access/generic_xlog.h"
#include "access/itup.h"
#include "commands/vacuum.h" #include "commands/vacuum.h"
#include "ivfflat.h" #include "ivfflat.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "utils/relcache.h"
#if PG_VERSION_NUM >= 180000 #if PG_VERSION_NUM >= 180000
#define vacuum_delay_point() vacuum_delay_point(false) #define vacuum_delay_point() vacuum_delay_point(false)

View File

@@ -5,27 +5,18 @@
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "common/shortest_dec.h" #include "common/shortest_dec.h"
#include "common/string.h"
#include "fmgr.h" #include "fmgr.h"
#include "halfutils.h" #include "halfutils.h"
#include "halfvec.h" #include "halfvec.h"
#include "lib/stringinfo.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
#include "sparsevec.h" #include "sparsevec.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/float.h" #include "utils/float.h"
#include "utils/fmgrprotos.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "vector.h" #include "vector.h"
#if PG_VERSION_NUM >= 160000
#include "varatt.h"
#endif
#if PG_VERSION_NUM >= 170000
#include "parser/scansup.h"
#endif
typedef struct SparseInputElement typedef struct SparseInputElement
{ {
int32 index; int32 index;
@@ -159,9 +150,9 @@ InitSparseVector(int dim, int nnz)
return result; return result;
} }
#if PG_VERSION_NUM >= 170000 /*
#define sparsevec_isspace(ch) scanner_isspace(ch) * Check for whitespace, since array_isspace() is static
#else */
static inline bool static inline bool
sparsevec_isspace(char ch) sparsevec_isspace(char ch)
{ {
@@ -174,7 +165,6 @@ sparsevec_isspace(char ch)
return true; return true;
return false; return false;
} }
#endif
/* /*
* Compare indices * Compare indices
@@ -182,10 +172,10 @@ sparsevec_isspace(char ch)
static int static int
CompareIndices(const void *a, const void *b) CompareIndices(const void *a, const void *b)
{ {
if (((const SparseInputElement *) a)->index < ((const SparseInputElement *) b)->index) if (((SparseInputElement *) a)->index < ((SparseInputElement *) b)->index)
return -1; return -1;
if (((const SparseInputElement *) a)->index > ((const SparseInputElement *) b)->index) if (((SparseInputElement *) a)->index > ((SparseInputElement *) b)->index)
return 1; return 1;
return 0; return 0;

View File

@@ -16,20 +16,16 @@
#include "port.h" /* for strtof() */ #include "port.h" /* for strtof() */
#include "sparsevec.h" #include "sparsevec.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/builtins.h"
#include "utils/float.h" #include "utils/float.h"
#include "utils/fmgrprotos.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/varbit.h" #include "utils/numeric.h"
#include "vector.h" #include "vector.h"
#if PG_VERSION_NUM >= 160000 #if PG_VERSION_NUM >= 160000
#include "varatt.h" #include "varatt.h"
#endif #endif
#if PG_VERSION_NUM >= 170000
#include "parser/scansup.h"
#endif
#define STATE_DIMS(x) (ARR_DIMS(x)[0] - 1) #define STATE_DIMS(x) (ARR_DIMS(x)[0] - 1)
#define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1)) #define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1))
@@ -40,7 +36,7 @@
#endif #endif
#if PG_VERSION_NUM >= 180000 #if PG_VERSION_NUM >= 180000
PG_MODULE_MAGIC_EXT(.name = "vector", .version = "0.8.3"); PG_MODULE_MAGIC_EXT(.name = "vector",.version = "0.8.1");
#else #else
PG_MODULE_MAGIC; PG_MODULE_MAGIC;
#endif #endif
@@ -133,9 +129,9 @@ InitVector(int dim)
return result; return result;
} }
#if PG_VERSION_NUM >= 170000 /*
#define vector_isspace(ch) scanner_isspace(ch) * Check for whitespace, since array_isspace() is static
#else */
static inline bool static inline bool
vector_isspace(char ch) vector_isspace(char ch)
{ {
@@ -148,7 +144,6 @@ vector_isspace(char ch)
return true; return true;
return false; return false;
} }
#endif
/* /*
* Check state array * Check state array

View File

@@ -16,8 +16,17 @@ $node->safe_psql("postgres",
"INSERT INTO tst SELECT i, ARRAY[random(), random(), random()] FROM generate_series(1, 100000) i;" "INSERT INTO tst SELECT i, ARRAY[random(), random(), random()] FROM generate_series(1, 100000) i;"
); );
# Check each index type
my @operators = ("<->", "<#>", "<=>");
my @opclasses = ("vector_l2_ops", "vector_ip_ops", "vector_cosine_ops");
for my $i (0 .. $#operators)
{
my $operator = $operators[$i];
my $opclass = $opclasses[$i];
# Add index # Add index
$node->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v vector_l2_ops);"); $node->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v $opclass);");
# Test 100% recall # Test 100% recall
for (1 .. 20) for (1 .. 20)
@@ -30,5 +39,6 @@ for (1 .. 20)
)); ));
is($res, $query); is($res, $query);
} }
}
done_testing(); done_testing();

View File

@@ -1,29 +0,0 @@
use strict;
use warnings FATAL => 'all';
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
# Initialize node
my $node = PostgreSQL::Test::Cluster->new('node');
$node->init;
$node->start;
# Create table
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
$node->safe_psql("postgres", "CREATE TABLE tst (v vector(3));");
$node->safe_psql("postgres",
"INSERT INTO tst SELECT ARRAY[random(), random(), random()] FROM generate_series(1, 1000) i;"
);
my ($ret, $stdout, $stderr) = $node->psql("postgres", qq(
SET client_min_messages = DEBUG;
SET maintenance_work_mem = '3073kB';
ALTER TABLE tst SET (parallel_workers = 1);
CREATE INDEX ON tst USING hnsw (v vector_l2_ops);
));
is($ret, 0, $stderr);
like($stderr, qr/using \d+ parallel workers/);
like($stderr, qr/hnsw graph no longer fits into maintenance_work_mem after 0 tuples/);
done_testing();

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.8.3' default_version = '0.8.1'
module_pathname = '$libdir/vector' module_pathname = '$libdir/vector'
relocatable = true relocatable = true