mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 12:07:34 +08:00
Compare commits
3 Commits
ivfflat-se
...
hnsw-less-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dcdb5a75a | ||
|
|
2fdadebce0 | ||
|
|
385f4a707e |
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -8,18 +8,18 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# - postgres: 18
|
|
||||||
# os: ubuntu-24.04
|
|
||||||
- postgres: 17
|
- postgres: 17
|
||||||
os: ubuntu-24.04
|
os: ubuntu-22.04
|
||||||
- postgres: 16
|
- postgres: 16
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
- postgres: 15
|
- postgres: 15
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
- postgres: 14
|
- postgres: 14
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
- postgres: 13
|
- postgres: 13
|
||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
|
- postgres: 12
|
||||||
|
os: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: ankane/setup-postgres@v1
|
- uses: ankane/setup-postgres@v1
|
||||||
|
|||||||
23
CHANGELOG.md
23
CHANGELOG.md
@@ -1,25 +1,4 @@
|
|||||||
## 0.8.0 (unreleased)
|
## 0.7.1 (unreleased)
|
||||||
|
|
||||||
- Added casts for arrays to `sparsevec`
|
|
||||||
- Reduced memory usage for HNSW index scans
|
|
||||||
- Dropped support for Postgres 12
|
|
||||||
|
|
||||||
## 0.7.4 (2024-08-05)
|
|
||||||
|
|
||||||
- Fixed locking for parallel HNSW index builds
|
|
||||||
- Fixed compilation error with GCC 14 on i386 when SSE2 is not enabled
|
|
||||||
|
|
||||||
## 0.7.3 (2024-07-22)
|
|
||||||
|
|
||||||
- Fixed `failed to add index item` error with `sparsevec`
|
|
||||||
- Fixed compilation error with FreeBSD ARM
|
|
||||||
- Fixed compilation warning with MSVC and Postgres 16
|
|
||||||
|
|
||||||
## 0.7.2 (2024-06-11)
|
|
||||||
|
|
||||||
- Fixed initialization fork for indexes on unlogged tables
|
|
||||||
|
|
||||||
## 0.7.1 (2024-06-03)
|
|
||||||
|
|
||||||
- Improved performance of on-disk HNSW index builds
|
- Improved performance of on-disk HNSW index builds
|
||||||
- Fixed `undefined symbol` error with GCC 8
|
- Fixed `undefined symbol` error with GCC 8
|
||||||
|
|||||||
@@ -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.7.4",
|
"version": "0.7.0",
|
||||||
"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.7.4",
|
"version": "0.7.0",
|
||||||
"abstract": "Open-source vector similarity search for Postgres"
|
"abstract": "Open-source vector similarity search for Postgres"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
9
Makefile
9
Makefile
@@ -1,9 +1,8 @@
|
|||||||
EXTENSION = vector
|
EXTENSION = vector
|
||||||
EXTVERSION = 0.7.4
|
EXTVERSION = 0.7.0
|
||||||
|
|
||||||
MODULE_big = vector
|
MODULE_big = vector
|
||||||
DATA = $(wildcard sql/*--*--*.sql)
|
DATA = $(wildcard sql/*--*.sql)
|
||||||
DATA_built = sql/$(EXTENSION)--$(EXTVERSION).sql
|
|
||||||
OBJS = src/bitutils.o src/bitvec.o src/halfutils.o src/halfvec.o src/hnsw.o src/hnswbuild.o src/hnswinsert.o src/hnswscan.o src/hnswutils.o src/hnswvacuum.o src/ivfbuild.o src/ivfflat.o src/ivfinsert.o src/ivfkmeans.o src/ivfscan.o src/ivfutils.o src/ivfvacuum.o src/sparsevec.o src/vector.o
|
OBJS = src/bitutils.o src/bitvec.o src/halfutils.o src/halfvec.o src/hnsw.o src/hnswbuild.o src/hnswinsert.o src/hnswscan.o src/hnswutils.o src/hnswvacuum.o src/ivfbuild.o src/ivfflat.o src/ivfinsert.o src/ivfkmeans.o src/ivfscan.o src/ivfutils.o src/ivfvacuum.o src/sparsevec.o src/vector.o
|
||||||
HEADERS = src/halfvec.h src/sparsevec.h src/vector.h
|
HEADERS = src/halfvec.h src/sparsevec.h src/vector.h
|
||||||
|
|
||||||
@@ -43,6 +42,8 @@ all: sql/$(EXTENSION)--$(EXTVERSION).sql
|
|||||||
sql/$(EXTENSION)--$(EXTVERSION).sql: sql/$(EXTENSION).sql
|
sql/$(EXTENSION)--$(EXTVERSION).sql: sql/$(EXTENSION).sql
|
||||||
cp $< $@
|
cp $< $@
|
||||||
|
|
||||||
|
EXTRA_CLEAN = sql/$(EXTENSION)--$(EXTVERSION).sql
|
||||||
|
|
||||||
PG_CONFIG ?= pg_config
|
PG_CONFIG ?= pg_config
|
||||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
include $(PGXS)
|
include $(PGXS)
|
||||||
@@ -52,7 +53,7 @@ ifeq ($(PROVE),)
|
|||||||
PROVE = prove
|
PROVE = prove
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# for Postgres < 15
|
# for Postgres 15
|
||||||
PROVE_FLAGS += -I ./test/perl
|
PROVE_FLAGS += -I ./test/perl
|
||||||
|
|
||||||
prove_installcheck:
|
prove_installcheck:
|
||||||
|
|||||||
17
Makefile.win
17
Makefile.win
@@ -1,7 +1,6 @@
|
|||||||
EXTENSION = vector
|
EXTENSION = vector
|
||||||
EXTVERSION = 0.7.4
|
EXTVERSION = 0.7.0
|
||||||
|
|
||||||
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
|
||||||
HEADERS = src\halfvec.h src\sparsevec.h src\vector.h
|
HEADERS = src\halfvec.h src\sparsevec.h src\vector.h
|
||||||
|
|
||||||
@@ -20,6 +19,11 @@ PG_CFLAGS = $(PG_CFLAGS) $(OPTFLAGS) /O2 /fp:fast
|
|||||||
# https://learn.microsoft.com/en-us/cpp/error-messages/tool-errors/vectorizer-and-parallelizer-messages
|
# https://learn.microsoft.com/en-us/cpp/error-messages/tool-errors/vectorizer-and-parallelizer-messages
|
||||||
# PG_CFLAGS = $(PG_CFLAGS) /Qvec-report:2
|
# PG_CFLAGS = $(PG_CFLAGS) /Qvec-report:2
|
||||||
|
|
||||||
|
all: sql\$(EXTENSION)--$(EXTVERSION).sql
|
||||||
|
|
||||||
|
sql\$(EXTENSION)--$(EXTVERSION).sql: sql\$(EXTENSION).sql
|
||||||
|
copy sql\$(EXTENSION).sql $@
|
||||||
|
|
||||||
# TODO use pg_config
|
# TODO use pg_config
|
||||||
!ifndef PGROOT
|
!ifndef PGROOT
|
||||||
!error PGROOT is not set
|
!error PGROOT is not set
|
||||||
@@ -39,18 +43,15 @@ SHLIB = $(EXTENSION).dll
|
|||||||
|
|
||||||
LIBS = "$(LIBDIR)\postgres.lib"
|
LIBS = "$(LIBDIR)\postgres.lib"
|
||||||
|
|
||||||
all: $(SHLIB) $(DATA_built)
|
|
||||||
|
|
||||||
.c.obj:
|
.c.obj:
|
||||||
$(CC) $(CFLAGS) /c $< /Fo$@
|
$(CC) $(CFLAGS) /c $< /Fo$@
|
||||||
|
|
||||||
$(SHLIB): $(OBJS)
|
$(SHLIB): $(OBJS)
|
||||||
$(CC) $(CFLAGS) $(OBJS) $(LIBS) /link /DLL /OUT:$(SHLIB)
|
$(CC) $(CFLAGS) $(OBJS) $(LIBS) /link /DLL /OUT:$(SHLIB)
|
||||||
|
|
||||||
sql\$(EXTENSION)--$(EXTVERSION).sql: sql\$(EXTENSION).sql
|
all: $(SHLIB)
|
||||||
copy sql\$(EXTENSION).sql $@
|
|
||||||
|
|
||||||
install: all
|
install:
|
||||||
copy $(SHLIB) "$(PKGLIBDIR)"
|
copy $(SHLIB) "$(PKGLIBDIR)"
|
||||||
copy $(EXTENSION).control "$(SHAREDIR)\extension"
|
copy $(EXTENSION).control "$(SHAREDIR)\extension"
|
||||||
copy sql\$(EXTENSION)--*.sql "$(SHAREDIR)\extension"
|
copy sql\$(EXTENSION)--*.sql "$(SHAREDIR)\extension"
|
||||||
@@ -69,6 +70,6 @@ uninstall:
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
del /f $(SHLIB) $(EXTENSION).lib $(EXTENSION).exp
|
del /f $(SHLIB) $(EXTENSION).lib $(EXTENSION).exp
|
||||||
del /f $(DATA_built)
|
|
||||||
del /f $(OBJS)
|
del /f $(OBJS)
|
||||||
|
del /f sql\$(EXTENSION)--$(EXTVERSION).sql
|
||||||
del /f /s /q results regression.diffs regression.out tmp_check tmp_check_iso log output_iso
|
del /f /s /q results regression.diffs regression.out tmp_check tmp_check_iso log output_iso
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -21,7 +21,7 @@ Compile and install the extension (supports Postgres 12+)
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /tmp
|
cd /tmp
|
||||||
git clone --branch v0.7.4 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.7.0 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
make
|
make
|
||||||
make install # may need sudo
|
make install # may need sudo
|
||||||
@@ -46,7 +46,7 @@ Then use `nmake` to build:
|
|||||||
```cmd
|
```cmd
|
||||||
set "PGROOT=C:\Program Files\PostgreSQL\16"
|
set "PGROOT=C:\Program Files\PostgreSQL\16"
|
||||||
cd %TEMP%
|
cd %TEMP%
|
||||||
git clone --branch v0.7.4 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.7.0 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
|
||||||
@@ -106,7 +106,7 @@ 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/loading/example.py))
|
Or load vectors in bulk using `COPY` ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/bulk_loading.py))
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
COPY items (embedding) FROM STDIN WITH (FORMAT BINARY);
|
COPY items (embedding) FROM STDIN WITH (FORMAT BINARY);
|
||||||
@@ -202,7 +202,7 @@ You can add an index to use approximate nearest neighbor search, which trades so
|
|||||||
|
|
||||||
Supported index types are:
|
Supported index types are:
|
||||||
|
|
||||||
- [HNSW](#hnsw)
|
- [HNSW](#hnsw) - added in 0.5.0
|
||||||
- [IVFFlat](#ivfflat)
|
- [IVFFlat](#ivfflat)
|
||||||
|
|
||||||
## HNSW
|
## HNSW
|
||||||
@@ -473,7 +473,7 @@ SELECT * FROM items ORDER BY embedding::halfvec(3) <-> '[1,2,3]' LIMIT 5;
|
|||||||
|
|
||||||
## Binary Vectors
|
## Binary Vectors
|
||||||
|
|
||||||
Use the `bit` type to store binary vectors ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/imagehash/example.py))
|
Use the `bit` type to store binary vectors ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/hash_image_search.py))
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE items (id bigserial PRIMARY KEY, embedding bit(3));
|
CREATE TABLE items (id bigserial PRIMARY KEY, embedding bit(3));
|
||||||
@@ -551,7 +551,7 @@ SELECT id, content FROM items, plainto_tsquery('hello search') query
|
|||||||
WHERE textsearch @@ query ORDER BY ts_rank_cd(textsearch, query) DESC LIMIT 5;
|
WHERE textsearch @@ query ORDER BY ts_rank_cd(textsearch, query) DESC LIMIT 5;
|
||||||
```
|
```
|
||||||
|
|
||||||
You can use [Reciprocal Rank Fusion](https://github.com/pgvector/pgvector-python/blob/master/examples/hybrid_search/rrf.py) or a [cross-encoder](https://github.com/pgvector/pgvector-python/blob/master/examples/hybrid_search/cross_encoder.py) to combine results.
|
You can use [Reciprocal Rank Fusion](https://github.com/pgvector/pgvector-python/blob/master/examples/hybrid_search_rrf.py) or a [cross-encoder](https://github.com/pgvector/pgvector-python/blob/master/examples/hybrid_search.py) to combine results.
|
||||||
|
|
||||||
## Indexing Subvectors
|
## Indexing Subvectors
|
||||||
|
|
||||||
@@ -597,7 +597,7 @@ Be sure to restart Postgres for changes to take effect.
|
|||||||
|
|
||||||
### 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/bulk_loading.py)).
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
COPY items (embedding) FROM STDIN WITH (FORMAT BINARY);
|
COPY items (embedding) FROM STDIN WITH (FORMAT BINARY);
|
||||||
@@ -687,7 +687,7 @@ 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 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)).
|
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.py)).
|
||||||
|
|
||||||
## Languages
|
## Languages
|
||||||
|
|
||||||
@@ -1051,7 +1051,7 @@ This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (r
|
|||||||
You can also build the image manually:
|
You can also build the image manually:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone --branch v0.7.4 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.7.0 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
docker build --pull --build-arg PG_MAJOR=16 -t myuser/pgvector .
|
docker build --pull --build-arg PG_MAJOR=16 -t myuser/pgvector .
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
|
||||||
\echo Use "ALTER EXTENSION vector UPDATE TO '0.7.1'" to load this file. \quit
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
|
||||||
\echo Use "ALTER EXTENSION vector UPDATE TO '0.7.2'" to load this file. \quit
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
|
||||||
\echo Use "ALTER EXTENSION vector UPDATE TO '0.7.3'" to load this file. \quit
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
|
||||||
\echo Use "ALTER EXTENSION vector UPDATE TO '0.7.4'" to load this file. \quit
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
|
||||||
\echo Use "ALTER EXTENSION vector UPDATE TO '0.8.0'" to load this file. \quit
|
|
||||||
|
|
||||||
CREATE FUNCTION array_to_sparsevec(integer[], integer, boolean) RETURNS sparsevec
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION array_to_sparsevec(real[], integer, boolean) RETURNS sparsevec
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION array_to_sparsevec(double precision[], integer, boolean) RETURNS sparsevec
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION array_to_sparsevec(numeric[], integer, boolean) RETURNS sparsevec
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE CAST (integer[] AS sparsevec)
|
|
||||||
WITH FUNCTION array_to_sparsevec(integer[], integer, boolean) AS ASSIGNMENT;
|
|
||||||
|
|
||||||
CREATE CAST (real[] AS sparsevec)
|
|
||||||
WITH FUNCTION array_to_sparsevec(real[], integer, boolean) AS ASSIGNMENT;
|
|
||||||
|
|
||||||
CREATE CAST (double precision[] AS sparsevec)
|
|
||||||
WITH FUNCTION array_to_sparsevec(double precision[], integer, boolean) AS ASSIGNMENT;
|
|
||||||
|
|
||||||
CREATE CAST (numeric[] AS sparsevec)
|
|
||||||
WITH FUNCTION array_to_sparsevec(numeric[], integer, boolean) AS ASSIGNMENT;
|
|
||||||
@@ -782,18 +782,6 @@ CREATE FUNCTION halfvec_to_sparsevec(halfvec, integer, boolean) RETURNS sparseve
|
|||||||
CREATE FUNCTION sparsevec_to_halfvec(sparsevec, integer, boolean) RETURNS halfvec
|
CREATE FUNCTION sparsevec_to_halfvec(sparsevec, integer, boolean) RETURNS halfvec
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
CREATE FUNCTION array_to_sparsevec(integer[], integer, boolean) RETURNS sparsevec
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION array_to_sparsevec(real[], integer, boolean) RETURNS sparsevec
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION array_to_sparsevec(double precision[], integer, boolean) RETURNS sparsevec
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION array_to_sparsevec(numeric[], integer, boolean) RETURNS sparsevec
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
-- sparsevec casts
|
-- sparsevec casts
|
||||||
|
|
||||||
CREATE CAST (sparsevec AS sparsevec)
|
CREATE CAST (sparsevec AS sparsevec)
|
||||||
@@ -811,18 +799,6 @@ CREATE CAST (sparsevec AS halfvec)
|
|||||||
CREATE CAST (halfvec AS sparsevec)
|
CREATE CAST (halfvec AS sparsevec)
|
||||||
WITH FUNCTION halfvec_to_sparsevec(halfvec, integer, boolean) AS IMPLICIT;
|
WITH FUNCTION halfvec_to_sparsevec(halfvec, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
CREATE CAST (integer[] AS sparsevec)
|
|
||||||
WITH FUNCTION array_to_sparsevec(integer[], integer, boolean) AS ASSIGNMENT;
|
|
||||||
|
|
||||||
CREATE CAST (real[] AS sparsevec)
|
|
||||||
WITH FUNCTION array_to_sparsevec(real[], integer, boolean) AS ASSIGNMENT;
|
|
||||||
|
|
||||||
CREATE CAST (double precision[] AS sparsevec)
|
|
||||||
WITH FUNCTION array_to_sparsevec(double precision[], integer, boolean) AS ASSIGNMENT;
|
|
||||||
|
|
||||||
CREATE CAST (numeric[] AS sparsevec)
|
|
||||||
WITH FUNCTION array_to_sparsevec(numeric[], integer, boolean) AS ASSIGNMENT;
|
|
||||||
|
|
||||||
-- sparsevec operators
|
-- sparsevec operators
|
||||||
|
|
||||||
CREATE OPERATOR <-> (
|
CREATE OPERATOR <-> (
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
/* Check version in first header */
|
/* Check version in first header */
|
||||||
#if PG_VERSION_NUM < 130000
|
#if PG_VERSION_NUM < 120000
|
||||||
#error "Requires PostgreSQL 13+"
|
#error "Requires PostgreSQL 12+"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern uint64 (*BitHammingDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance);
|
extern uint64 (*BitHammingDistance) (uint32 bytes, unsigned char *ax, unsigned char *bx, uint64 distance);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include "bitutils.h"
|
#include "bitutils.h"
|
||||||
#include "bitvec.h"
|
#include "bitvec.h"
|
||||||
#include "utils/varbit.h"
|
#include "utils/varbit.h"
|
||||||
#include "vector.h"
|
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 160000
|
#if PG_VERSION_NUM >= 160000
|
||||||
#include "varatt.h"
|
#include "varatt.h"
|
||||||
@@ -41,7 +40,7 @@ CheckDims(VarBit *a, VarBit *b)
|
|||||||
/*
|
/*
|
||||||
* Get the Hamming distance between two bit vectors
|
* Get the Hamming distance between two bit vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hamming_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(hamming_distance);
|
||||||
Datum
|
Datum
|
||||||
hamming_distance(PG_FUNCTION_ARGS)
|
hamming_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -56,7 +55,7 @@ hamming_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the Jaccard distance between two bit vectors
|
* Get the Jaccard distance between two bit vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(jaccard_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(jaccard_distance);
|
||||||
Datum
|
Datum
|
||||||
jaccard_distance(PG_FUNCTION_ARGS)
|
jaccard_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,6 +19,11 @@
|
|||||||
#include "utils/numeric.h"
|
#include "utils/numeric.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 130000
|
||||||
|
#define TYPALIGN_DOUBLE 'd'
|
||||||
|
#define TYPALIGN_INT 'i'
|
||||||
|
#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))
|
||||||
|
|
||||||
@@ -180,7 +185,7 @@ float_underflow_error(void)
|
|||||||
/*
|
/*
|
||||||
* Convert textual representation to internal representation
|
* Convert textual representation to internal representation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_in);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_in);
|
||||||
Datum
|
Datum
|
||||||
halfvec_in(PG_FUNCTION_ARGS)
|
halfvec_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -294,7 +299,7 @@ halfvec_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to textual representation
|
* Convert internal representation to textual representation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_out);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_out);
|
||||||
Datum
|
Datum
|
||||||
halfvec_out(PG_FUNCTION_ARGS)
|
halfvec_out(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -340,7 +345,7 @@ halfvec_out(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert type modifier
|
* Convert type modifier
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_typmod_in);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_typmod_in);
|
||||||
Datum
|
Datum
|
||||||
halfvec_typmod_in(PG_FUNCTION_ARGS)
|
halfvec_typmod_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -371,7 +376,7 @@ halfvec_typmod_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert external binary representation to internal representation
|
* Convert external binary representation to internal representation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_recv);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_recv);
|
||||||
Datum
|
Datum
|
||||||
halfvec_recv(PG_FUNCTION_ARGS)
|
halfvec_recv(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -405,7 +410,7 @@ halfvec_recv(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to the external binary representation
|
* Convert internal representation to the external binary representation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_send);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_send);
|
||||||
Datum
|
Datum
|
||||||
halfvec_send(PG_FUNCTION_ARGS)
|
halfvec_send(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -425,7 +430,7 @@ halfvec_send(PG_FUNCTION_ARGS)
|
|||||||
* Convert half vector to half vector
|
* Convert half vector to half vector
|
||||||
* This is needed to check the type modifier
|
* This is needed to check the type modifier
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec);
|
||||||
Datum
|
Datum
|
||||||
halfvec(PG_FUNCTION_ARGS)
|
halfvec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -440,7 +445,7 @@ halfvec(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert array to half vector
|
* Convert array to half vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(array_to_halfvec);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(array_to_halfvec);
|
||||||
Datum
|
Datum
|
||||||
array_to_halfvec(PG_FUNCTION_ARGS)
|
array_to_halfvec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -514,7 +519,7 @@ array_to_halfvec(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert half vector to float4[]
|
* Convert half vector to float4[]
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_to_float4);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_to_float4);
|
||||||
Datum
|
Datum
|
||||||
halfvec_to_float4(PG_FUNCTION_ARGS)
|
halfvec_to_float4(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -538,7 +543,7 @@ halfvec_to_float4(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert vector to half vec
|
* Convert vector to half vec
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_to_halfvec);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_to_halfvec);
|
||||||
Datum
|
Datum
|
||||||
vector_to_halfvec(PG_FUNCTION_ARGS)
|
vector_to_halfvec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -560,7 +565,7 @@ vector_to_halfvec(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 distance between half vectors
|
* Get the L2 distance between half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l2_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_distance);
|
||||||
Datum
|
Datum
|
||||||
halfvec_l2_distance(PG_FUNCTION_ARGS)
|
halfvec_l2_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -575,7 +580,7 @@ halfvec_l2_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 squared distance between half vectors
|
* Get the L2 squared distance between half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l2_squared_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_squared_distance);
|
||||||
Datum
|
Datum
|
||||||
halfvec_l2_squared_distance(PG_FUNCTION_ARGS)
|
halfvec_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -590,7 +595,7 @@ halfvec_l2_squared_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the inner product of two half vectors
|
* Get the inner product of two half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_inner_product);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_inner_product);
|
||||||
Datum
|
Datum
|
||||||
halfvec_inner_product(PG_FUNCTION_ARGS)
|
halfvec_inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -605,7 +610,7 @@ halfvec_inner_product(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the negative inner product of two half vectors
|
* Get the negative inner product of two half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_negative_inner_product);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_negative_inner_product);
|
||||||
Datum
|
Datum
|
||||||
halfvec_negative_inner_product(PG_FUNCTION_ARGS)
|
halfvec_negative_inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -620,7 +625,7 @@ halfvec_negative_inner_product(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the cosine distance between two half vectors
|
* Get the cosine distance between two half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_cosine_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_cosine_distance);
|
||||||
Datum
|
Datum
|
||||||
halfvec_cosine_distance(PG_FUNCTION_ARGS)
|
halfvec_cosine_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -652,7 +657,7 @@ halfvec_cosine_distance(PG_FUNCTION_ARGS)
|
|||||||
* Currently uses angular distance since needs to satisfy triangle inequality
|
* Currently uses angular distance since needs to satisfy triangle inequality
|
||||||
* Assumes inputs are unit vectors (skips norm)
|
* Assumes inputs are unit vectors (skips norm)
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_spherical_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_spherical_distance);
|
||||||
Datum
|
Datum
|
||||||
halfvec_spherical_distance(PG_FUNCTION_ARGS)
|
halfvec_spherical_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -676,7 +681,7 @@ halfvec_spherical_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L1 distance between two half vectors
|
* Get the L1 distance between two half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l1_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l1_distance);
|
||||||
Datum
|
Datum
|
||||||
halfvec_l1_distance(PG_FUNCTION_ARGS)
|
halfvec_l1_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -691,7 +696,7 @@ halfvec_l1_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the dimensions of a half vector
|
* Get the dimensions of a half vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_vector_dims);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_vector_dims);
|
||||||
Datum
|
Datum
|
||||||
halfvec_vector_dims(PG_FUNCTION_ARGS)
|
halfvec_vector_dims(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -703,7 +708,7 @@ halfvec_vector_dims(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 norm of a half vector
|
* Get the L2 norm of a half vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l2_norm);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_norm);
|
||||||
Datum
|
Datum
|
||||||
halfvec_l2_norm(PG_FUNCTION_ARGS)
|
halfvec_l2_norm(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -725,7 +730,7 @@ halfvec_l2_norm(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Normalize a half vector with the L2 norm
|
* Normalize a half vector with the L2 norm
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l2_normalize);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_normalize);
|
||||||
Datum
|
Datum
|
||||||
halfvec_l2_normalize(PG_FUNCTION_ARGS)
|
halfvec_l2_normalize(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -764,7 +769,7 @@ halfvec_l2_normalize(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Add half vectors
|
* Add half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_add);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_add);
|
||||||
Datum
|
Datum
|
||||||
halfvec_add(PG_FUNCTION_ARGS)
|
halfvec_add(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -803,7 +808,7 @@ halfvec_add(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Subtract half vectors
|
* Subtract half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_sub);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_sub);
|
||||||
Datum
|
Datum
|
||||||
halfvec_sub(PG_FUNCTION_ARGS)
|
halfvec_sub(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -842,7 +847,7 @@ halfvec_sub(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Multiply half vectors
|
* Multiply half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_mul);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_mul);
|
||||||
Datum
|
Datum
|
||||||
halfvec_mul(PG_FUNCTION_ARGS)
|
halfvec_mul(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -884,7 +889,7 @@ halfvec_mul(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Concatenate half vectors
|
* Concatenate half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_concat);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_concat);
|
||||||
Datum
|
Datum
|
||||||
halfvec_concat(PG_FUNCTION_ARGS)
|
halfvec_concat(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -908,7 +913,7 @@ halfvec_concat(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Quantize a half vector
|
* Quantize a half vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_binary_quantize);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_binary_quantize);
|
||||||
Datum
|
Datum
|
||||||
halfvec_binary_quantize(PG_FUNCTION_ARGS)
|
halfvec_binary_quantize(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -926,7 +931,7 @@ halfvec_binary_quantize(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get a subvector
|
* Get a subvector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_subvector);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_subvector);
|
||||||
Datum
|
Datum
|
||||||
halfvec_subvector(PG_FUNCTION_ARGS)
|
halfvec_subvector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1000,7 +1005,7 @@ halfvec_cmp_internal(HalfVector * a, HalfVector * b)
|
|||||||
/*
|
/*
|
||||||
* Less than
|
* Less than
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_lt);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_lt);
|
||||||
Datum
|
Datum
|
||||||
halfvec_lt(PG_FUNCTION_ARGS)
|
halfvec_lt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1013,7 +1018,7 @@ halfvec_lt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Less than or equal
|
* Less than or equal
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_le);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_le);
|
||||||
Datum
|
Datum
|
||||||
halfvec_le(PG_FUNCTION_ARGS)
|
halfvec_le(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1026,7 +1031,7 @@ halfvec_le(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Equal
|
* Equal
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_eq);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_eq);
|
||||||
Datum
|
Datum
|
||||||
halfvec_eq(PG_FUNCTION_ARGS)
|
halfvec_eq(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1039,7 +1044,7 @@ halfvec_eq(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Not equal
|
* Not equal
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_ne);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ne);
|
||||||
Datum
|
Datum
|
||||||
halfvec_ne(PG_FUNCTION_ARGS)
|
halfvec_ne(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1052,7 +1057,7 @@ halfvec_ne(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than or equal
|
* Greater than or equal
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_ge);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ge);
|
||||||
Datum
|
Datum
|
||||||
halfvec_ge(PG_FUNCTION_ARGS)
|
halfvec_ge(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1065,7 +1070,7 @@ halfvec_ge(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than
|
* Greater than
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_gt);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_gt);
|
||||||
Datum
|
Datum
|
||||||
halfvec_gt(PG_FUNCTION_ARGS)
|
halfvec_gt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1078,7 +1083,7 @@ halfvec_gt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Compare half vectors
|
* Compare half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_cmp);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_cmp);
|
||||||
Datum
|
Datum
|
||||||
halfvec_cmp(PG_FUNCTION_ARGS)
|
halfvec_cmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1091,7 +1096,7 @@ halfvec_cmp(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Accumulate half vectors
|
* Accumulate half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_accum);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_accum);
|
||||||
Datum
|
Datum
|
||||||
halfvec_accum(PG_FUNCTION_ARGS)
|
halfvec_accum(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1152,7 +1157,7 @@ halfvec_accum(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Average half vectors
|
* Average half vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_avg);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_avg);
|
||||||
Datum
|
Datum
|
||||||
halfvec_avg(PG_FUNCTION_ARGS)
|
halfvec_avg(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1186,7 +1191,7 @@ halfvec_avg(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert sparse vector to half vector
|
* Convert sparse vector to half vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_to_halfvec);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_to_halfvec);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_to_halfvec(PG_FUNCTION_ARGS)
|
sparsevec_to_halfvec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
/* F16C has better performance than _Float16 (on x86-64) */
|
/* F16C has better performance than _Float16 (on x86-64) */
|
||||||
#if defined(__F16C__)
|
#if defined(__F16C__)
|
||||||
#define F16C_SUPPORT
|
#define F16C_SUPPORT
|
||||||
#elif defined(__FLT16_MAX__) && !defined(HALFVEC_DISPATCH) && !defined(__FreeBSD__) && (!defined(__i386__) || defined(__SSE2__))
|
#elif defined(__FLT16_MAX__) && !defined(HALFVEC_DISPATCH)
|
||||||
#define FLT16_SUPPORT
|
#define FLT16_SUPPORT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
45
src/hnsw.c
45
src/hnsw.c
@@ -9,7 +9,6 @@
|
|||||||
#include "commands/vacuum.h"
|
#include "commands/vacuum.h"
|
||||||
#include "hnsw.h"
|
#include "hnsw.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "utils/float.h"
|
|
||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
#include "utils/selfuncs.h"
|
#include "utils/selfuncs.h"
|
||||||
|
|
||||||
@@ -60,9 +59,17 @@ HnswInit(void)
|
|||||||
|
|
||||||
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",
|
||||||
HNSW_DEFAULT_M, HNSW_MIN_M, HNSW_MAX_M, AccessExclusiveLock);
|
HNSW_DEFAULT_M, HNSW_MIN_M, HNSW_MAX_M
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
|
,AccessExclusiveLock
|
||||||
|
#endif
|
||||||
|
);
|
||||||
add_int_reloption(hnsw_relopt_kind, "ef_construction", "Size of the dynamic candidate list for construction",
|
add_int_reloption(hnsw_relopt_kind, "ef_construction", "Size of the dynamic candidate list for construction",
|
||||||
HNSW_DEFAULT_EF_CONSTRUCTION, HNSW_MIN_EF_CONSTRUCTION, HNSW_MAX_EF_CONSTRUCTION, AccessExclusiveLock);
|
HNSW_DEFAULT_EF_CONSTRUCTION, HNSW_MIN_EF_CONSTRUCTION, HNSW_MAX_EF_CONSTRUCTION
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
|
,AccessExclusiveLock
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
DefineCustomIntVariable("hnsw.ef_search", "Sets the size of the dynamic candidate list for search",
|
DefineCustomIntVariable("hnsw.ef_search", "Sets the size of the dynamic candidate list for search",
|
||||||
"Valid range is 1..1000.", &hnsw_ef_search,
|
"Valid range is 1..1000.", &hnsw_ef_search,
|
||||||
@@ -105,8 +112,8 @@ hnswcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|||||||
/* Never use index without order */
|
/* Never use index without order */
|
||||||
if (path->indexorderbys == NULL)
|
if (path->indexorderbys == NULL)
|
||||||
{
|
{
|
||||||
*indexStartupCost = get_float8_infinity();
|
*indexStartupCost = DBL_MAX;
|
||||||
*indexTotalCost = get_float8_infinity();
|
*indexTotalCost = DBL_MAX;
|
||||||
*indexSelectivity = 0;
|
*indexSelectivity = 0;
|
||||||
*indexCorrelation = 0;
|
*indexCorrelation = 0;
|
||||||
*indexPages = 0;
|
*indexPages = 0;
|
||||||
@@ -147,10 +154,23 @@ hnswoptions(Datum reloptions, bool validate)
|
|||||||
{"ef_construction", RELOPT_TYPE_INT, offsetof(HnswOptions, efConstruction)},
|
{"ef_construction", RELOPT_TYPE_INT, offsetof(HnswOptions, efConstruction)},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
return (bytea *) build_reloptions(reloptions, validate,
|
return (bytea *) build_reloptions(reloptions, validate,
|
||||||
hnsw_relopt_kind,
|
hnsw_relopt_kind,
|
||||||
sizeof(HnswOptions),
|
sizeof(HnswOptions),
|
||||||
tab, lengthof(tab));
|
tab, lengthof(tab));
|
||||||
|
#else
|
||||||
|
relopt_value *options;
|
||||||
|
int numoptions;
|
||||||
|
HnswOptions *rdopts;
|
||||||
|
|
||||||
|
options = parseRelOptions(reloptions, validate, hnsw_relopt_kind, &numoptions);
|
||||||
|
rdopts = allocateReloptStruct(sizeof(HnswOptions), options, numoptions);
|
||||||
|
fillRelOptions((void *) rdopts, sizeof(HnswOptions), options, numoptions,
|
||||||
|
validate, tab, lengthof(tab));
|
||||||
|
|
||||||
|
return (bytea *) rdopts;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -167,7 +187,7 @@ hnswvalidate(Oid opclassoid)
|
|||||||
*
|
*
|
||||||
* See https://www.postgresql.org/docs/current/index-api.html
|
* See https://www.postgresql.org/docs/current/index-api.html
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hnswhandler);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(hnswhandler);
|
||||||
Datum
|
Datum
|
||||||
hnswhandler(PG_FUNCTION_ARGS)
|
hnswhandler(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -175,7 +195,9 @@ hnswhandler(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
amroutine->amstrategies = 0;
|
amroutine->amstrategies = 0;
|
||||||
amroutine->amsupport = 3;
|
amroutine->amsupport = 3;
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
amroutine->amoptsprocnum = 0;
|
amroutine->amoptsprocnum = 0;
|
||||||
|
#endif
|
||||||
amroutine->amcanorder = false;
|
amroutine->amcanorder = false;
|
||||||
amroutine->amcanorderbyop = true;
|
amroutine->amcanorderbyop = true;
|
||||||
amroutine->amcanbackward = false; /* can change direction mid-scan */
|
amroutine->amcanbackward = false; /* can change direction mid-scan */
|
||||||
@@ -188,24 +210,17 @@ hnswhandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amclusterable = false;
|
amroutine->amclusterable = false;
|
||||||
amroutine->ampredlocks = false;
|
amroutine->ampredlocks = false;
|
||||||
amroutine->amcanparallel = false;
|
amroutine->amcanparallel = false;
|
||||||
#if PG_VERSION_NUM >= 170000
|
|
||||||
amroutine->amcanbuildparallel = true;
|
|
||||||
#endif
|
|
||||||
amroutine->amcaninclude = false;
|
amroutine->amcaninclude = false;
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
|
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
|
||||||
#if PG_VERSION_NUM >= 160000
|
|
||||||
amroutine->amsummarizing = false;
|
|
||||||
#endif
|
|
||||||
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
|
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
|
||||||
|
#endif
|
||||||
amroutine->amkeytype = InvalidOid;
|
amroutine->amkeytype = InvalidOid;
|
||||||
|
|
||||||
/* Interface functions */
|
/* Interface functions */
|
||||||
amroutine->ambuild = hnswbuild;
|
amroutine->ambuild = hnswbuild;
|
||||||
amroutine->ambuildempty = hnswbuildempty;
|
amroutine->ambuildempty = hnswbuildempty;
|
||||||
amroutine->aminsert = hnswinsert;
|
amroutine->aminsert = hnswinsert;
|
||||||
#if PG_VERSION_NUM >= 170000
|
|
||||||
amroutine->aminsertcleanup = NULL;
|
|
||||||
#endif
|
|
||||||
amroutine->ambulkdelete = hnswbulkdelete;
|
amroutine->ambulkdelete = hnswbulkdelete;
|
||||||
amroutine->amvacuumcleanup = hnswvacuumcleanup;
|
amroutine->amvacuumcleanup = hnswvacuumcleanup;
|
||||||
amroutine->amcanreturn = NULL;
|
amroutine->amcanreturn = NULL;
|
||||||
|
|||||||
17
src/hnsw.h
17
src/hnsw.h
@@ -76,6 +76,11 @@
|
|||||||
#define SeedRandom(seed) srandom(seed)
|
#define SeedRandom(seed) srandom(seed)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 130000
|
||||||
|
#define list_delete_last(list) list_truncate(list, list_length(list) - 1)
|
||||||
|
#define list_sort(list, cmp) ((list) = list_qsort(list, cmp))
|
||||||
|
#endif
|
||||||
|
|
||||||
#define HnswIsElementTuple(tup) ((tup)->type == HNSW_ELEMENT_TUPLE_TYPE)
|
#define HnswIsElementTuple(tup) ((tup)->type == HNSW_ELEMENT_TUPLE_TYPE)
|
||||||
#define HnswIsNeighborTuple(tup) ((tup)->type == HNSW_NEIGHBOR_TUPLE_TYPE)
|
#define HnswIsNeighborTuple(tup) ((tup)->type == HNSW_NEIGHBOR_TUPLE_TYPE)
|
||||||
|
|
||||||
@@ -155,13 +160,11 @@ struct HnswNeighborArray
|
|||||||
HnswCandidate items[FLEXIBLE_ARRAY_MEMBER];
|
HnswCandidate items[FLEXIBLE_ARRAY_MEMBER];
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct HnswSearchCandidate
|
typedef struct HnswPairingHeapNode
|
||||||
{
|
{
|
||||||
pairingheap_node c_node;
|
pairingheap_node ph_node;
|
||||||
pairingheap_node w_node;
|
HnswCandidate *inner;
|
||||||
HnswElementPtr element;
|
} HnswPairingHeapNode;
|
||||||
float distance;
|
|
||||||
} HnswSearchCandidate;
|
|
||||||
|
|
||||||
/* HNSW index options */
|
/* HNSW index options */
|
||||||
typedef struct HnswOptions
|
typedef struct HnswOptions
|
||||||
@@ -382,7 +385,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, FmgrInfo *procinfo, Oid collation, int m, int efConstruction, bool existing);
|
void HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, FmgrInfo *procinfo, Oid collation, int m, int efConstruction, bool existing);
|
||||||
HnswSearchCandidate *HnswEntryCandidate(char *base, HnswElement em, Datum q, Relation rel, FmgrInfo *procinfo, Oid collation, bool loadVec);
|
HnswCandidate *HnswEntryCandidate(char *base, HnswElement em, Datum q, Relation rel, FmgrInfo *procinfo, Oid collation, 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);
|
||||||
|
|||||||
@@ -60,6 +60,12 @@
|
|||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
|
#define CALLBACK_ITEM_POINTER ItemPointer tid
|
||||||
|
#else
|
||||||
|
#define CALLBACK_ITEM_POINTER HeapTuple hup
|
||||||
|
#endif
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 140000
|
#if PG_VERSION_NUM >= 140000
|
||||||
#include "utils/backend_status.h"
|
#include "utils/backend_status.h"
|
||||||
#include "utils/wait_event.h"
|
#include "utils/wait_event.h"
|
||||||
@@ -69,6 +75,10 @@
|
|||||||
#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)
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 130000
|
||||||
|
#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T + SIZEOF_VOID_P * 2)
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the metapage
|
* Create the metapage
|
||||||
*/
|
*/
|
||||||
@@ -182,9 +192,7 @@ CreateGraphPages(HnswBuildState * buildstate)
|
|||||||
|
|
||||||
/* Initial size check */
|
/* Initial size check */
|
||||||
if (etupSize > HNSW_TUPLE_ALLOC_SIZE)
|
if (etupSize > HNSW_TUPLE_ALLOC_SIZE)
|
||||||
ereport(ERROR,
|
elog(ERROR, "index tuple too large");
|
||||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
|
||||||
errmsg("index tuple too large")));
|
|
||||||
|
|
||||||
HnswSetElementTuple(base, etup, element);
|
HnswSetElementTuple(base, etup, element);
|
||||||
|
|
||||||
@@ -371,13 +379,7 @@ UpdateNeighborsInMemory(char *base, FmgrInfo *procinfo, Oid collation, HnswEleme
|
|||||||
for (int lc = e->level; lc >= 0; lc--)
|
for (int lc = e->level; lc >= 0; lc--)
|
||||||
{
|
{
|
||||||
int lm = HnswGetLayerM(m, lc);
|
int lm = HnswGetLayerM(m, lc);
|
||||||
Size neighborsSize = HNSW_NEIGHBOR_ARRAY_SIZE(lm);
|
HnswNeighborArray *neighbors = HnswGetNeighbors(base, e, lc);
|
||||||
HnswNeighborArray *neighbors = palloc(neighborsSize);
|
|
||||||
|
|
||||||
/* Copy neighbors to local memory */
|
|
||||||
LWLockAcquire(&e->lock, LW_SHARED);
|
|
||||||
memcpy(neighbors, HnswGetNeighbors(base, e, lc), neighborsSize);
|
|
||||||
LWLockRelease(&e->lock);
|
|
||||||
|
|
||||||
for (int i = 0; i < neighbors->length; i++)
|
for (int i = 0; i < neighbors->length; i++)
|
||||||
{
|
{
|
||||||
@@ -387,6 +389,7 @@ UpdateNeighborsInMemory(char *base, FmgrInfo *procinfo, Oid collation, HnswEleme
|
|||||||
/* Keep scan-build happy on Mac x86-64 */
|
/* Keep scan-build happy on Mac x86-64 */
|
||||||
Assert(neighborElement);
|
Assert(neighborElement);
|
||||||
|
|
||||||
|
/* Use element for lock instead of hc since hc can be replaced */
|
||||||
LWLockAcquire(&neighborElement->lock, LW_EXCLUSIVE);
|
LWLockAcquire(&neighborElement->lock, LW_EXCLUSIVE);
|
||||||
HnswUpdateConnection(base, e, hc, lm, lc, NULL, NULL, procinfo, collation);
|
HnswUpdateConnection(base, e, hc, lm, lc, NULL, NULL, procinfo, collation);
|
||||||
LWLockRelease(&neighborElement->lock);
|
LWLockRelease(&neighborElement->lock);
|
||||||
@@ -575,13 +578,17 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
|
|||||||
* Callback for table_index_build_scan
|
* Callback for table_index_build_scan
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
BuildCallback(Relation index, ItemPointer tid, Datum *values,
|
BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
||||||
bool *isnull, bool tupleIsAlive, void *state)
|
bool *isnull, bool tupleIsAlive, void *state)
|
||||||
{
|
{
|
||||||
HnswBuildState *buildstate = (HnswBuildState *) state;
|
HnswBuildState *buildstate = (HnswBuildState *) state;
|
||||||
HnswGraph *graph = buildstate->graph;
|
HnswGraph *graph = buildstate->graph;
|
||||||
MemoryContext oldCtx;
|
MemoryContext oldCtx;
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 130000
|
||||||
|
ItemPointer tid = &hup->t_self;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Skip nulls */
|
/* Skip nulls */
|
||||||
if (isnull[0])
|
if (isnull[0])
|
||||||
return;
|
return;
|
||||||
@@ -644,7 +651,11 @@ HnswMemoryContextAlloc(Size size, void *state)
|
|||||||
HnswBuildState *buildstate = (HnswBuildState *) state;
|
HnswBuildState *buildstate = (HnswBuildState *) state;
|
||||||
void *chunk = MemoryContextAlloc(buildstate->graphCtx, size);
|
void *chunk = MemoryContextAlloc(buildstate->graphCtx, size);
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
buildstate->graphData.memoryUsed = MemoryContextMemAllocated(buildstate->graphCtx, false);
|
buildstate->graphData.memoryUsed = MemoryContextMemAllocated(buildstate->graphCtx, false);
|
||||||
|
#else
|
||||||
|
buildstate->graphData.memoryUsed += MAXALIGN(size);
|
||||||
|
#endif
|
||||||
|
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
@@ -680,25 +691,17 @@ InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, Index
|
|||||||
|
|
||||||
/* Disallow varbit since require fixed dimensions */
|
/* Disallow varbit since require fixed dimensions */
|
||||||
if (TupleDescAttr(index->rd_att, 0)->atttypid == VARBITOID)
|
if (TupleDescAttr(index->rd_att, 0)->atttypid == VARBITOID)
|
||||||
ereport(ERROR,
|
elog(ERROR, "type not supported for hnsw index");
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("type not supported for hnsw index")));
|
|
||||||
|
|
||||||
/* Require column to have dimensions to be indexed */
|
/* Require column to have dimensions to be indexed */
|
||||||
if (buildstate->dimensions < 0)
|
if (buildstate->dimensions < 0)
|
||||||
ereport(ERROR,
|
elog(ERROR, "column does not have dimensions");
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("column does not have dimensions")));
|
|
||||||
|
|
||||||
if (buildstate->dimensions > buildstate->typeInfo->maxDimensions)
|
if (buildstate->dimensions > buildstate->typeInfo->maxDimensions)
|
||||||
ereport(ERROR,
|
elog(ERROR, "column cannot have more than %d dimensions for hnsw index", buildstate->typeInfo->maxDimensions);
|
||||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
|
||||||
errmsg("column cannot have more than %d dimensions for hnsw index", buildstate->typeInfo->maxDimensions)));
|
|
||||||
|
|
||||||
if (buildstate->efConstruction < 2 * buildstate->m)
|
if (buildstate->efConstruction < 2 * buildstate->m)
|
||||||
ereport(ERROR,
|
elog(ERROR, "ef_construction must be greater than or equal to 2 * m");
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("ef_construction must be greater than or equal to 2 * m")));
|
|
||||||
|
|
||||||
buildstate->reltuples = 0;
|
buildstate->reltuples = 0;
|
||||||
buildstate->indtuples = 0;
|
buildstate->indtuples = 0;
|
||||||
@@ -1118,8 +1121,8 @@ BuildIndex(Relation heap, Relation index, IndexInfo *indexInfo,
|
|||||||
|
|
||||||
BuildGraph(buildstate, forkNum);
|
BuildGraph(buildstate, forkNum);
|
||||||
|
|
||||||
if (RelationNeedsWAL(index) || forkNum == INIT_FORKNUM)
|
if (RelationNeedsWAL(index))
|
||||||
log_newpage_range(index, forkNum, 0, RelationGetNumberOfBlocksInFork(index, forkNum), true);
|
log_newpage_range(index, forkNum, 0, RelationGetNumberOfBlocks(index), true);
|
||||||
|
|
||||||
FreeBuildState(buildstate);
|
FreeBuildState(buildstate);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,15 +36,14 @@ GetInsertPage(Relation index)
|
|||||||
* Check for a free offset
|
* Check for a free offset
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
HnswFreeOffset(Relation index, Buffer buf, Page page, HnswElement element, Size etupSize, Size ntupSize, Buffer *nbuf, Page *npage, OffsetNumber *freeOffno, OffsetNumber *freeNeighborOffno, BlockNumber *newInsertPage)
|
HnswFreeOffset(Relation index, Buffer buf, Page page, HnswElement element, Size ntupSize, Buffer *nbuf, Page *npage, OffsetNumber *freeOffno, OffsetNumber *freeNeighborOffno, BlockNumber *newInsertPage)
|
||||||
{
|
{
|
||||||
OffsetNumber offno;
|
OffsetNumber offno;
|
||||||
OffsetNumber maxoffno = PageGetMaxOffsetNumber(page);
|
OffsetNumber maxoffno = PageGetMaxOffsetNumber(page);
|
||||||
|
|
||||||
for (offno = FirstOffsetNumber; offno <= maxoffno; offno = OffsetNumberNext(offno))
|
for (offno = FirstOffsetNumber; offno <= maxoffno; offno = OffsetNumberNext(offno))
|
||||||
{
|
{
|
||||||
ItemId eitemid = PageGetItemId(page, offno);
|
HnswElementTuple etup = (HnswElementTuple) PageGetItem(page, PageGetItemId(page, offno));
|
||||||
HnswElementTuple etup = (HnswElementTuple) PageGetItem(page, eitemid);
|
|
||||||
|
|
||||||
/* Skip neighbor tuples */
|
/* Skip neighbor tuples */
|
||||||
if (!HnswIsElementTuple(etup))
|
if (!HnswIsElementTuple(etup))
|
||||||
@@ -55,9 +54,7 @@ HnswFreeOffset(Relation index, Buffer buf, Page page, HnswElement element, Size
|
|||||||
BlockNumber elementPage = BufferGetBlockNumber(buf);
|
BlockNumber elementPage = BufferGetBlockNumber(buf);
|
||||||
BlockNumber neighborPage = ItemPointerGetBlockNumber(&etup->neighbortid);
|
BlockNumber neighborPage = ItemPointerGetBlockNumber(&etup->neighbortid);
|
||||||
OffsetNumber neighborOffno = ItemPointerGetOffsetNumber(&etup->neighbortid);
|
OffsetNumber neighborOffno = ItemPointerGetOffsetNumber(&etup->neighbortid);
|
||||||
ItemId nitemid;
|
ItemId itemid;
|
||||||
Size pageFree;
|
|
||||||
Size npageFree;
|
|
||||||
|
|
||||||
if (!BlockNumberIsValid(*newInsertPage))
|
if (!BlockNumberIsValid(*newInsertPage))
|
||||||
*newInsertPage = elementPage;
|
*newInsertPage = elementPage;
|
||||||
@@ -76,25 +73,10 @@ HnswFreeOffset(Relation index, Buffer buf, Page page, HnswElement element, Size
|
|||||||
*npage = BufferGetPage(*nbuf);
|
*npage = BufferGetPage(*nbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
nitemid = PageGetItemId(*npage, neighborOffno);
|
itemid = PageGetItemId(*npage, neighborOffno);
|
||||||
|
|
||||||
/* Ensure aligned for space check */
|
/* Check for space on neighbor tuple page */
|
||||||
Assert(etupSize == MAXALIGN(etupSize));
|
if (PageGetFreeSpace(*npage) + ItemIdGetLength(itemid) - sizeof(ItemIdData) >= ntupSize)
|
||||||
Assert(ntupSize == MAXALIGN(ntupSize));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate free space individually since tuples are overwritten
|
|
||||||
* individually (in separate calls to PageIndexTupleOverwrite)
|
|
||||||
*/
|
|
||||||
pageFree = ItemIdGetLength(eitemid) + PageGetExactFreeSpace(page);
|
|
||||||
npageFree = ItemIdGetLength(nitemid);
|
|
||||||
if (neighborPage != elementPage)
|
|
||||||
npageFree += PageGetExactFreeSpace(*npage);
|
|
||||||
else if (pageFree >= etupSize)
|
|
||||||
npageFree += pageFree - etupSize;
|
|
||||||
|
|
||||||
/* Check for space */
|
|
||||||
if (pageFree >= etupSize && npageFree >= ntupSize)
|
|
||||||
{
|
{
|
||||||
*freeOffno = offno;
|
*freeOffno = offno;
|
||||||
*freeNeighborOffno = neighborOffno;
|
*freeNeighborOffno = neighborOffno;
|
||||||
@@ -202,7 +184,7 @@ AddElementOnDisk(Relation index, HnswElement e, int m, BlockNumber insertPage, B
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Next, try space from a deleted element */
|
/* Next, try space from a deleted element */
|
||||||
if (HnswFreeOffset(index, buf, page, e, etupSize, ntupSize, &nbuf, &npage, &freeOffno, &freeNeighborOffno, &newInsertPage))
|
if (HnswFreeOffset(index, buf, page, e, ntupSize, &nbuf, &npage, &freeOffno, &freeNeighborOffno, &newInsertPage))
|
||||||
{
|
{
|
||||||
if (nbuf != buf)
|
if (nbuf != buf)
|
||||||
{
|
{
|
||||||
@@ -379,12 +361,8 @@ HnswUpdateNeighborsOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, Hns
|
|||||||
HnswElement neighborElement = HnswPtrAccess(base, hc->element);
|
HnswElement neighborElement = HnswPtrAccess(base, hc->element);
|
||||||
OffsetNumber offno = neighborElement->neighborOffno;
|
OffsetNumber offno = neighborElement->neighborOffno;
|
||||||
|
|
||||||
/*
|
/* Get latest neighbors since they may have changed */
|
||||||
* Get latest neighbors since they may have changed. Do not lock
|
/* Do not lock yet since selecting neighbors can take time */
|
||||||
* yet since selecting neighbors can take time. Could use
|
|
||||||
* optimistic locking to retry if another update occurs before
|
|
||||||
* getting exclusive lock.
|
|
||||||
*/
|
|
||||||
HnswLoadNeighbors(neighborElement, index, m);
|
HnswLoadNeighbors(neighborElement, index, m);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -160,15 +160,15 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir)
|
|||||||
|
|
||||||
so->first = false;
|
so->first = false;
|
||||||
|
|
||||||
#if defined(HNSW_MEMORY)
|
#if defined(HNSW_MEMORY) && PG_VERSION_NUM >= 130000
|
||||||
elog(INFO, "memory: %zu KB", MemoryContextMemAllocated(so->tmpCtx, false) / 1024);
|
elog(INFO, "memory: %zu MB", MemoryContextMemAllocated(so->tmpCtx, false) / (1024 * 1024));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
while (list_length(so->w) > 0)
|
while (list_length(so->w) > 0)
|
||||||
{
|
{
|
||||||
char *base = NULL;
|
char *base = NULL;
|
||||||
HnswSearchCandidate *hc = llast(so->w);
|
HnswCandidate *hc = llast(so->w);
|
||||||
HnswElement element = HnswPtrAccess(base, hc->element);
|
HnswElement element = HnswPtrAccess(base, hc->element);
|
||||||
ItemPointer heaptid;
|
ItemPointer heaptid;
|
||||||
|
|
||||||
|
|||||||
329
src/hnswutils.c
329
src/hnswutils.c
@@ -5,7 +5,6 @@
|
|||||||
#include "access/generic_xlog.h"
|
#include "access/generic_xlog.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "catalog/pg_type_d.h"
|
#include "catalog/pg_type_d.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"
|
||||||
@@ -15,6 +14,12 @@
|
|||||||
#include "utils/memdebug.h"
|
#include "utils/memdebug.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
|
#include "common/hashfn.h"
|
||||||
|
#else
|
||||||
|
#include "utils/hashutils.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 170000
|
#if PG_VERSION_NUM < 170000
|
||||||
static inline uint64
|
static inline uint64
|
||||||
murmurhash64(uint64 data)
|
murmurhash64(uint64 data)
|
||||||
@@ -107,12 +112,6 @@ typedef union
|
|||||||
tidhash_hash *tids;
|
tidhash_hash *tids;
|
||||||
} visited_hash;
|
} visited_hash;
|
||||||
|
|
||||||
typedef union
|
|
||||||
{
|
|
||||||
HnswElement element;
|
|
||||||
ItemPointerData indextid;
|
|
||||||
} HnswUnvisited;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the max number of connections in an upper layer for each element in the index
|
* Get the max number of connections in an upper layer for each element in the index
|
||||||
*/
|
*/
|
||||||
@@ -301,9 +300,6 @@ HnswGetMetaPageInfo(Relation index, int *m, HnswElement * entryPoint)
|
|||||||
page = BufferGetPage(buf);
|
page = BufferGetPage(buf);
|
||||||
metap = HnswPageGetMeta(page);
|
metap = HnswPageGetMeta(page);
|
||||||
|
|
||||||
if (unlikely(metap->magicNumber != HNSW_MAGIC_NUMBER))
|
|
||||||
elog(ERROR, "hnsw index is not valid");
|
|
||||||
|
|
||||||
if (m != NULL)
|
if (m != NULL)
|
||||||
*m = metap->m;
|
*m = metap->m;
|
||||||
|
|
||||||
@@ -548,19 +544,19 @@ HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHe
|
|||||||
/*
|
/*
|
||||||
* Load an element and optionally get its distance from q
|
* Load an element and optionally get its distance from q
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
HnswLoadElementImpl(BlockNumber blkno, OffsetNumber offno, float *distance, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec, float *maxDistance, HnswElement * element)
|
HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec, float *maxDistance)
|
||||||
{
|
{
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
Page page;
|
Page page;
|
||||||
HnswElementTuple etup;
|
HnswElementTuple etup;
|
||||||
|
|
||||||
/* Read vector */
|
/* Read vector */
|
||||||
buf = ReadBuffer(index, blkno);
|
buf = ReadBuffer(index, element->blkno);
|
||||||
LockBuffer(buf, BUFFER_LOCK_SHARE);
|
LockBuffer(buf, BUFFER_LOCK_SHARE);
|
||||||
page = BufferGetPage(buf);
|
page = BufferGetPage(buf);
|
||||||
|
|
||||||
etup = (HnswElementTuple) PageGetItem(page, PageGetItemId(page, offno));
|
etup = (HnswElementTuple) PageGetItem(page, PageGetItemId(page, element->offno));
|
||||||
|
|
||||||
Assert(HnswIsElementTuple(etup));
|
Assert(HnswIsElementTuple(etup));
|
||||||
|
|
||||||
@@ -575,32 +571,19 @@ HnswLoadElementImpl(BlockNumber blkno, OffsetNumber offno, float *distance, Datu
|
|||||||
|
|
||||||
/* Load element */
|
/* Load element */
|
||||||
if (distance == NULL || maxDistance == NULL || *distance < *maxDistance)
|
if (distance == NULL || maxDistance == NULL || *distance < *maxDistance)
|
||||||
{
|
HnswLoadElementFromTuple(element, etup, true, loadVec);
|
||||||
if (*element == NULL)
|
|
||||||
*element = HnswInitElementFromBlock(blkno, offno);
|
|
||||||
|
|
||||||
HnswLoadElementFromTuple(*element, etup, true, loadVec);
|
|
||||||
}
|
|
||||||
|
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load an element and optionally get its distance from q
|
* Get the distance for a candidate
|
||||||
*/
|
|
||||||
void
|
|
||||||
HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec, float *maxDistance)
|
|
||||||
{
|
|
||||||
HnswLoadElementImpl(element->blkno, element->offno, distance, q, index, procinfo, collation, loadVec, maxDistance, &element);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the distance for an element
|
|
||||||
*/
|
*/
|
||||||
static float
|
static float
|
||||||
GetElementDistance(char *base, HnswElement element, Datum q, FmgrInfo *procinfo, Oid collation)
|
GetCandidateDistance(char *base, HnswCandidate * hc, Datum q, FmgrInfo *procinfo, Oid collation)
|
||||||
{
|
{
|
||||||
Datum value = HnswGetValue(base, element);
|
HnswElement hce = HnswPtrAccess(base, hc->element);
|
||||||
|
Datum value = HnswGetValue(base, hce);
|
||||||
|
|
||||||
return DatumGetFloat8(FunctionCall2Coll(procinfo, collation, q, value));
|
return DatumGetFloat8(FunctionCall2Coll(procinfo, collation, q, value));
|
||||||
}
|
}
|
||||||
@@ -608,32 +591,29 @@ GetElementDistance(char *base, HnswElement element, Datum q, FmgrInfo *procinfo,
|
|||||||
/*
|
/*
|
||||||
* Create a candidate for the entry point
|
* Create a candidate for the entry point
|
||||||
*/
|
*/
|
||||||
HnswSearchCandidate *
|
HnswCandidate *
|
||||||
HnswEntryCandidate(char *base, HnswElement entryPoint, Datum q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec)
|
HnswEntryCandidate(char *base, HnswElement entryPoint, Datum q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec)
|
||||||
{
|
{
|
||||||
HnswSearchCandidate *hc = palloc(sizeof(HnswSearchCandidate));
|
HnswCandidate *hc = palloc(sizeof(HnswCandidate));
|
||||||
|
|
||||||
HnswPtrStore(base, hc->element, entryPoint);
|
HnswPtrStore(base, hc->element, entryPoint);
|
||||||
if (index == NULL)
|
if (index == NULL)
|
||||||
hc->distance = GetElementDistance(base, entryPoint, q, procinfo, collation);
|
hc->distance = GetCandidateDistance(base, hc, q, procinfo, collation);
|
||||||
else
|
else
|
||||||
HnswLoadElement(entryPoint, &hc->distance, &q, index, procinfo, collation, loadVec, NULL);
|
HnswLoadElement(entryPoint, &hc->distance, &q, index, procinfo, collation, loadVec, NULL);
|
||||||
return hc;
|
return hc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HnswGetSearchCandidate(membername, ptr) pairingheap_container(HnswSearchCandidate, membername, ptr)
|
|
||||||
#define HnswGetSearchCandidateConst(membername, ptr) pairingheap_const_container(HnswSearchCandidate, membername, ptr)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compare candidate distances
|
* Compare candidate distances
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
CompareNearestCandidates(const pairingheap_node *a, const pairingheap_node *b, void *arg)
|
CompareNearestCandidates(const pairingheap_node *a, const pairingheap_node *b, void *arg)
|
||||||
{
|
{
|
||||||
if (HnswGetSearchCandidateConst(c_node, a)->distance < HnswGetSearchCandidateConst(c_node, b)->distance)
|
if (((const HnswPairingHeapNode *) a)->inner->distance < ((const HnswPairingHeapNode *) b)->inner->distance)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (HnswGetSearchCandidateConst(c_node, a)->distance > HnswGetSearchCandidateConst(c_node, b)->distance)
|
if (((const HnswPairingHeapNode *) a)->inner->distance > ((const HnswPairingHeapNode *) b)->inner->distance)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -645,15 +625,27 @@ CompareNearestCandidates(const pairingheap_node *a, const pairingheap_node *b, v
|
|||||||
static int
|
static int
|
||||||
CompareFurthestCandidates(const pairingheap_node *a, const pairingheap_node *b, void *arg)
|
CompareFurthestCandidates(const pairingheap_node *a, const pairingheap_node *b, void *arg)
|
||||||
{
|
{
|
||||||
if (HnswGetSearchCandidateConst(w_node, a)->distance < HnswGetSearchCandidateConst(w_node, b)->distance)
|
if (((const HnswPairingHeapNode *) a)->inner->distance < ((const HnswPairingHeapNode *) b)->inner->distance)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (HnswGetSearchCandidateConst(w_node, a)->distance > HnswGetSearchCandidateConst(w_node, b)->distance)
|
if (((const HnswPairingHeapNode *) a)->inner->distance > ((const HnswPairingHeapNode *) b)->inner->distance)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a pairing heap node for a candidate
|
||||||
|
*/
|
||||||
|
static HnswPairingHeapNode *
|
||||||
|
CreatePairingHeapNode(HnswCandidate * c)
|
||||||
|
{
|
||||||
|
HnswPairingHeapNode *node = palloc(sizeof(HnswPairingHeapNode));
|
||||||
|
|
||||||
|
node->inner = c;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Init visited
|
* Init visited
|
||||||
*/
|
*/
|
||||||
@@ -672,11 +664,11 @@ InitVisited(char *base, visited_hash * v, Relation index, int ef, int m)
|
|||||||
* Add to visited
|
* Add to visited
|
||||||
*/
|
*/
|
||||||
static inline void
|
static inline void
|
||||||
AddToVisited(char *base, visited_hash * v, HnswElementPtr elementPtr, Relation index, bool *found)
|
AddToVisited(char *base, visited_hash * v, HnswCandidate * hc, Relation index, bool *found)
|
||||||
{
|
{
|
||||||
if (index != NULL)
|
if (index != NULL)
|
||||||
{
|
{
|
||||||
HnswElement element = HnswPtrAccess(base, elementPtr);
|
HnswElement element = HnswPtrAccess(base, hc->element);
|
||||||
ItemPointerData indextid;
|
ItemPointerData indextid;
|
||||||
|
|
||||||
ItemPointerSet(&indextid, element->blkno, element->offno);
|
ItemPointerSet(&indextid, element->blkno, element->offno);
|
||||||
@@ -684,15 +676,23 @@ AddToVisited(char *base, visited_hash * v, HnswElementPtr elementPtr, Relation i
|
|||||||
}
|
}
|
||||||
else if (base != NULL)
|
else if (base != NULL)
|
||||||
{
|
{
|
||||||
HnswElement element = HnswPtrAccess(base, elementPtr);
|
#if PG_VERSION_NUM >= 130000
|
||||||
|
HnswElement element = HnswPtrAccess(base, hc->element);
|
||||||
|
|
||||||
offsethash_insert_hash(v->offsets, HnswPtrOffset(elementPtr), element->hash, found);
|
offsethash_insert_hash(v->offsets, HnswPtrOffset(hc->element), element->hash, found);
|
||||||
|
#else
|
||||||
|
offsethash_insert(v->offsets, HnswPtrOffset(hc->element), found);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
HnswElement element = HnswPtrAccess(base, elementPtr);
|
#if PG_VERSION_NUM >= 130000
|
||||||
|
HnswElement element = HnswPtrAccess(base, hc->element);
|
||||||
|
|
||||||
pointerhash_insert_hash(v->pointers, (uintptr_t) HnswPtrPointer(elementPtr), element->hash, found);
|
pointerhash_insert_hash(v->pointers, (uintptr_t) HnswPtrPointer(hc->element), element->hash, found);
|
||||||
|
#else
|
||||||
|
pointerhash_insert(v->pointers, (uintptr_t) HnswPtrPointer(hc->element), found);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -700,89 +700,20 @@ AddToVisited(char *base, visited_hash * v, HnswElementPtr elementPtr, Relation i
|
|||||||
* Count element towards ef
|
* Count element towards ef
|
||||||
*/
|
*/
|
||||||
static inline bool
|
static inline bool
|
||||||
CountElement(HnswElement skipElement, HnswElement e)
|
CountElement(char *base, HnswElement skipElement, HnswCandidate * hc)
|
||||||
{
|
{
|
||||||
|
HnswElement e;
|
||||||
|
|
||||||
if (skipElement == NULL)
|
if (skipElement == NULL)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
/* Ensure does not access heaptidsLength during in-memory build */
|
/* Ensure does not access heaptidsLength during in-memory build */
|
||||||
pg_memory_barrier();
|
pg_memory_barrier();
|
||||||
|
|
||||||
/* Keep scan-build happy on Mac x86-64 */
|
e = HnswPtrAccess(base, hc->element);
|
||||||
Assert(e);
|
|
||||||
|
|
||||||
return e->heaptidsLength != 0;
|
return e->heaptidsLength != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Load unvisited neighbors from memory
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
HnswLoadUnvisitedFromMemory(char *base, HnswElement element, HnswUnvisited * unvisited, int *unvisitedLength, visited_hash * v, int lc, HnswNeighborArray * localNeighborhood, Size neighborhoodSize)
|
|
||||||
{
|
|
||||||
/* Get the neighborhood at layer lc */
|
|
||||||
HnswNeighborArray *neighborhood = HnswGetNeighbors(base, element, lc);
|
|
||||||
|
|
||||||
/* Copy neighborhood to local memory */
|
|
||||||
LWLockAcquire(&element->lock, LW_SHARED);
|
|
||||||
memcpy(localNeighborhood, neighborhood, neighborhoodSize);
|
|
||||||
LWLockRelease(&element->lock);
|
|
||||||
|
|
||||||
*unvisitedLength = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < localNeighborhood->length; i++)
|
|
||||||
{
|
|
||||||
HnswCandidate *hc = &localNeighborhood->items[i];
|
|
||||||
bool found;
|
|
||||||
|
|
||||||
AddToVisited(base, v, hc->element, NULL, &found);
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
unvisited[(*unvisitedLength)++].element = HnswPtrAccess(base, hc->element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Load unvisited neighbors from disk
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
HnswLoadUnvisitedFromDisk(HnswElement element, HnswUnvisited * unvisited, int *unvisitedLength, visited_hash * v, Relation index, int m, int lm, int lc)
|
|
||||||
{
|
|
||||||
Buffer buf;
|
|
||||||
Page page;
|
|
||||||
HnswNeighborTuple ntup;
|
|
||||||
int start;
|
|
||||||
ItemPointerData indextids[HNSW_MAX_M * 2];
|
|
||||||
|
|
||||||
buf = ReadBuffer(index, element->neighborPage);
|
|
||||||
LockBuffer(buf, BUFFER_LOCK_SHARE);
|
|
||||||
page = BufferGetPage(buf);
|
|
||||||
|
|
||||||
ntup = (HnswNeighborTuple) PageGetItem(page, PageGetItemId(page, element->neighborOffno));
|
|
||||||
start = (element->level - lc) * m;
|
|
||||||
|
|
||||||
/* Copy to minimize lock time */
|
|
||||||
memcpy(&indextids, ntup->indextids + start, lm * sizeof(ItemPointerData));
|
|
||||||
|
|
||||||
UnlockReleaseBuffer(buf);
|
|
||||||
|
|
||||||
*unvisitedLength = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < lm; i++)
|
|
||||||
{
|
|
||||||
ItemPointer indextid = &indextids[i];
|
|
||||||
bool found;
|
|
||||||
|
|
||||||
if (!ItemPointerIsValid(indextid))
|
|
||||||
break;
|
|
||||||
|
|
||||||
tidhash_insert(v->tids, *indextid, &found);
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
unvisited[(*unvisitedLength)++].indextid = *indextid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Algorithm 2 from paper
|
* Algorithm 2 from paper
|
||||||
*/
|
*/
|
||||||
@@ -795,45 +726,43 @@ HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, F
|
|||||||
int wlen = 0;
|
int wlen = 0;
|
||||||
visited_hash v;
|
visited_hash v;
|
||||||
ListCell *lc2;
|
ListCell *lc2;
|
||||||
HnswNeighborArray *localNeighborhood = NULL;
|
HnswNeighborArray *neighborhoodData = NULL;
|
||||||
Size neighborhoodSize = 0;
|
Size neighborhoodSize;
|
||||||
int lm = HnswGetLayerM(m, lc);
|
|
||||||
HnswUnvisited *unvisited = palloc(lm * sizeof(HnswUnvisited));
|
|
||||||
int unvisitedLength;
|
|
||||||
|
|
||||||
InitVisited(base, &v, index, ef, m);
|
InitVisited(base, &v, index, ef, m);
|
||||||
|
|
||||||
/* Create local memory for neighborhood if needed */
|
/* Create local memory for neighborhood if needed */
|
||||||
if (index == NULL)
|
if (index == NULL)
|
||||||
{
|
{
|
||||||
neighborhoodSize = HNSW_NEIGHBOR_ARRAY_SIZE(lm);
|
neighborhoodSize = HNSW_NEIGHBOR_ARRAY_SIZE(HnswGetLayerM(m, lc));
|
||||||
localNeighborhood = palloc(neighborhoodSize);
|
neighborhoodData = palloc(neighborhoodSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add entry points to v, C, and W */
|
/* Add entry points to v, C, and W */
|
||||||
foreach(lc2, ep)
|
foreach(lc2, ep)
|
||||||
{
|
{
|
||||||
HnswSearchCandidate *hc = (HnswSearchCandidate *) lfirst(lc2);
|
HnswCandidate *hc = (HnswCandidate *) lfirst(lc2);
|
||||||
bool found;
|
bool found;
|
||||||
|
|
||||||
AddToVisited(base, &v, hc->element, index, &found);
|
AddToVisited(base, &v, hc, index, &found);
|
||||||
|
|
||||||
pairingheap_add(C, &hc->c_node);
|
pairingheap_add(C, &(CreatePairingHeapNode(hc)->ph_node));
|
||||||
pairingheap_add(W, &hc->w_node);
|
pairingheap_add(W, &(CreatePairingHeapNode(hc)->ph_node));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do not count elements being deleted towards ef when vacuuming. It
|
* Do not count elements being deleted towards ef when vacuuming. It
|
||||||
* would be ideal to do this for inserts as well, but this could
|
* would be ideal to do this for inserts as well, but this could
|
||||||
* affect insert performance.
|
* affect insert performance.
|
||||||
*/
|
*/
|
||||||
if (CountElement(skipElement, HnswPtrAccess(base, hc->element)))
|
if (CountElement(base, skipElement, hc))
|
||||||
wlen++;
|
wlen++;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!pairingheap_is_empty(C))
|
while (!pairingheap_is_empty(C))
|
||||||
{
|
{
|
||||||
HnswSearchCandidate *c = HnswGetSearchCandidate(c_node, pairingheap_remove_first(C));
|
HnswNeighborArray *neighborhood;
|
||||||
HnswSearchCandidate *f = HnswGetSearchCandidate(w_node, pairingheap_first(W));
|
HnswCandidate *c = ((HnswPairingHeapNode *) pairingheap_remove_first(C))->inner;
|
||||||
|
HnswCandidate *f = ((HnswPairingHeapNode *) pairingheap_first(W))->inner;
|
||||||
HnswElement cElement;
|
HnswElement cElement;
|
||||||
|
|
||||||
if (c->distance > f->distance)
|
if (c->distance > f->distance)
|
||||||
@@ -841,41 +770,43 @@ HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, F
|
|||||||
|
|
||||||
cElement = HnswPtrAccess(base, c->element);
|
cElement = HnswPtrAccess(base, c->element);
|
||||||
|
|
||||||
if (index == NULL)
|
if (HnswPtrIsNull(base, cElement->neighbors))
|
||||||
HnswLoadUnvisitedFromMemory(base, cElement, unvisited, &unvisitedLength, &v, lc, localNeighborhood, neighborhoodSize);
|
HnswLoadNeighbors(cElement, index, m);
|
||||||
else
|
|
||||||
HnswLoadUnvisitedFromDisk(cElement, unvisited, &unvisitedLength, &v, index, m, lm, lc);
|
|
||||||
|
|
||||||
for (int i = 0; i < unvisitedLength; i++)
|
/* Get the neighborhood at layer lc */
|
||||||
|
neighborhood = HnswGetNeighbors(base, cElement, lc);
|
||||||
|
|
||||||
|
/* Copy neighborhood to local memory if needed */
|
||||||
|
if (index == NULL)
|
||||||
|
{
|
||||||
|
LWLockAcquire(&cElement->lock, LW_SHARED);
|
||||||
|
memcpy(neighborhoodData, neighborhood, neighborhoodSize);
|
||||||
|
LWLockRelease(&cElement->lock);
|
||||||
|
neighborhood = neighborhoodData;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < neighborhood->length; i++)
|
||||||
|
{
|
||||||
|
HnswCandidate *e = &neighborhood->items[i];
|
||||||
|
bool visited;
|
||||||
|
|
||||||
|
AddToVisited(base, &v, e, index, &visited);
|
||||||
|
|
||||||
|
if (!visited)
|
||||||
{
|
{
|
||||||
HnswElement eElement;
|
|
||||||
HnswSearchCandidate *e;
|
|
||||||
float eDistance;
|
float eDistance;
|
||||||
bool alwaysAdd = wlen < ef;
|
HnswElement eElement = HnswPtrAccess(base, e->element);
|
||||||
|
|
||||||
f = HnswGetSearchCandidate(w_node, pairingheap_first(W));
|
f = ((HnswPairingHeapNode *) pairingheap_first(W))->inner;
|
||||||
|
|
||||||
if (index == NULL)
|
if (index == NULL)
|
||||||
{
|
eDistance = GetCandidateDistance(base, e, q, procinfo, collation);
|
||||||
eElement = unvisited[i].element;
|
|
||||||
eDistance = GetElementDistance(base, eElement, q, procinfo, collation);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
|
HnswLoadElement(eElement, &eDistance, &q, index, procinfo, collation, inserting, wlen >= ef ? &f->distance : NULL);
|
||||||
|
|
||||||
|
if (eDistance < f->distance || wlen < ef)
|
||||||
{
|
{
|
||||||
ItemPointer indextid = &unvisited[i].indextid;
|
HnswCandidate *ec;
|
||||||
BlockNumber blkno = ItemPointerGetBlockNumber(indextid);
|
|
||||||
OffsetNumber offno = ItemPointerGetOffsetNumber(indextid);
|
|
||||||
|
|
||||||
/* Avoid any allocations if not adding */
|
|
||||||
eElement = NULL;
|
|
||||||
HnswLoadElementImpl(blkno, offno, &eDistance, &q, index, procinfo, collation, inserting, alwaysAdd ? NULL : &f->distance, &eElement);
|
|
||||||
|
|
||||||
if (eElement == NULL)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(eDistance < f->distance || alwaysAdd))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Assert(!eElement->deleted);
|
Assert(!eElement->deleted);
|
||||||
|
|
||||||
@@ -883,19 +814,20 @@ HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, F
|
|||||||
if (eElement->level < lc)
|
if (eElement->level < lc)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Create a new candidate */
|
/* Copy e */
|
||||||
e = palloc(sizeof(HnswSearchCandidate));
|
ec = palloc(sizeof(HnswCandidate));
|
||||||
HnswPtrStore(base, e->element, eElement);
|
HnswPtrStore(base, ec->element, eElement);
|
||||||
e->distance = eDistance;
|
ec->distance = eDistance;
|
||||||
pairingheap_add(C, &e->c_node);
|
|
||||||
pairingheap_add(W, &e->w_node);
|
pairingheap_add(C, &(CreatePairingHeapNode(ec)->ph_node));
|
||||||
|
pairingheap_add(W, &(CreatePairingHeapNode(ec)->ph_node));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do not count elements being deleted towards ef when vacuuming.
|
* Do not count elements being deleted towards ef when
|
||||||
* It would be ideal to do this for inserts as well, but this
|
* vacuuming. It would be ideal to do this for inserts as
|
||||||
* could affect insert performance.
|
* well, but this could affect insert performance.
|
||||||
*/
|
*/
|
||||||
if (CountElement(skipElement, eElement))
|
if (CountElement(base, skipElement, e))
|
||||||
{
|
{
|
||||||
wlen++;
|
wlen++;
|
||||||
|
|
||||||
@@ -905,11 +837,13 @@ HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, F
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Add each element of W to w */
|
/* Add each element of W to w */
|
||||||
while (!pairingheap_is_empty(W))
|
while (!pairingheap_is_empty(W))
|
||||||
{
|
{
|
||||||
HnswSearchCandidate *hc = HnswGetSearchCandidate(w_node, pairingheap_remove_first(W));
|
HnswCandidate *hc = ((HnswPairingHeapNode *) pairingheap_remove_first(W))->inner;
|
||||||
|
|
||||||
w = lappend(w, hc);
|
w = lappend(w, hc);
|
||||||
}
|
}
|
||||||
@@ -921,10 +855,17 @@ HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, F
|
|||||||
* Compare candidate distances with pointer tie-breaker
|
* Compare candidate distances with pointer tie-breaker
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
CompareCandidateDistances(const ListCell *a, const ListCell *b)
|
CompareCandidateDistances(const ListCell *a, const ListCell *b)
|
||||||
{
|
{
|
||||||
HnswCandidate *hca = lfirst(a);
|
HnswCandidate *hca = lfirst(a);
|
||||||
HnswCandidate *hcb = lfirst(b);
|
HnswCandidate *hcb = lfirst(b);
|
||||||
|
#else
|
||||||
|
CompareCandidateDistances(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
HnswCandidate *hca = lfirst(*(ListCell **) a);
|
||||||
|
HnswCandidate *hcb = lfirst(*(ListCell **) b);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (hca->distance < hcb->distance)
|
if (hca->distance < hcb->distance)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -945,10 +886,17 @@ CompareCandidateDistances(const ListCell *a, const ListCell *b)
|
|||||||
* Compare candidate distances with offset tie-breaker
|
* Compare candidate distances with offset tie-breaker
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
CompareCandidateDistancesOffset(const ListCell *a, const ListCell *b)
|
CompareCandidateDistancesOffset(const ListCell *a, const ListCell *b)
|
||||||
{
|
{
|
||||||
HnswCandidate *hca = lfirst(a);
|
HnswCandidate *hca = lfirst(a);
|
||||||
HnswCandidate *hcb = lfirst(b);
|
HnswCandidate *hcb = lfirst(b);
|
||||||
|
#else
|
||||||
|
CompareCandidateDistancesOffset(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
HnswCandidate *hca = lfirst(*(ListCell **) a);
|
||||||
|
HnswCandidate *hcb = lfirst(*(ListCell **) b);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (hca->distance < hcb->distance)
|
if (hca->distance < hcb->distance)
|
||||||
return 1;
|
return 1;
|
||||||
@@ -1158,7 +1106,7 @@ HnswUpdateConnection(char *base, HnswElement element, HnswCandidate * hc, int lm
|
|||||||
if (HnswPtrIsNull(base, hc3Element->value))
|
if (HnswPtrIsNull(base, hc3Element->value))
|
||||||
HnswLoadElement(hc3Element, &hc3->distance, &q, index, procinfo, collation, true, NULL);
|
HnswLoadElement(hc3Element, &hc3->distance, &q, index, procinfo, collation, true, NULL);
|
||||||
else
|
else
|
||||||
hc3->distance = GetElementDistance(base, hc3Element, q, procinfo, collation);
|
hc3->distance = GetCandidateDistance(base, hc3, q, procinfo, collation);
|
||||||
|
|
||||||
/* Prune element if being deleted */
|
/* Prune element if being deleted */
|
||||||
if (hc3Element->heaptidsLength == 0)
|
if (hc3Element->heaptidsLength == 0)
|
||||||
@@ -1230,6 +1178,7 @@ RemoveElements(char *base, List *w, HnswElement skipElement)
|
|||||||
return w2;
|
return w2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
/*
|
/*
|
||||||
* Precompute hash
|
* Precompute hash
|
||||||
*/
|
*/
|
||||||
@@ -1245,6 +1194,7 @@ PrecomputeHash(char *base, HnswElement element)
|
|||||||
else
|
else
|
||||||
element->hash = hash_offset(HnswPtrOffset(ptr));
|
element->hash = hash_offset(HnswPtrOffset(ptr));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Algorithm 1 from paper
|
* Algorithm 1 from paper
|
||||||
@@ -1259,9 +1209,11 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
|
|||||||
Datum q = HnswGetValue(base, element);
|
Datum q = HnswGetValue(base, element);
|
||||||
HnswElement skipElement = existing ? element : NULL;
|
HnswElement skipElement = existing ? element : NULL;
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
/* Precompute hash */
|
/* Precompute hash */
|
||||||
if (index == NULL)
|
if (index == NULL)
|
||||||
PrecomputeHash(base, element);
|
PrecomputeHash(base, element);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* No neighbors if no entry point */
|
/* No neighbors if no entry point */
|
||||||
if (entryPoint == NULL)
|
if (entryPoint == NULL)
|
||||||
@@ -1290,27 +1242,16 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
|
|||||||
{
|
{
|
||||||
int lm = HnswGetLayerM(m, lc);
|
int lm = HnswGetLayerM(m, lc);
|
||||||
List *neighbors;
|
List *neighbors;
|
||||||
List *lw = NIL;
|
List *lw;
|
||||||
ListCell *lc2;
|
|
||||||
|
|
||||||
w = HnswSearchLayer(base, q, ep, efConstruction, lc, index, procinfo, collation, m, true, skipElement);
|
w = HnswSearchLayer(base, q, ep, efConstruction, lc, index, procinfo, collation, m, true, skipElement);
|
||||||
|
|
||||||
/* Convert search candidates to candidates */
|
|
||||||
foreach(lc2, w)
|
|
||||||
{
|
|
||||||
HnswSearchCandidate *sc = lfirst(lc2);
|
|
||||||
HnswCandidate *hc = palloc(sizeof(HnswCandidate));
|
|
||||||
|
|
||||||
hc->element = sc->element;
|
|
||||||
hc->distance = sc->distance;
|
|
||||||
|
|
||||||
lw = lappend(lw, hc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Elements being deleted or skipped can help with search */
|
/* Elements being deleted or skipped can help with search */
|
||||||
/* but should be removed before selecting neighbors */
|
/* but should be removed before selecting neighbors */
|
||||||
if (index != NULL)
|
if (index != NULL)
|
||||||
lw = RemoveElements(base, lw, skipElement);
|
lw = RemoveElements(base, w, skipElement);
|
||||||
|
else
|
||||||
|
lw = w;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Candidates are sorted, but not deterministically. Could set
|
* Candidates are sorted, but not deterministically. Could set
|
||||||
@@ -1335,9 +1276,7 @@ SparsevecCheckValue(Pointer v)
|
|||||||
SparseVector *vec = (SparseVector *) v;
|
SparseVector *vec = (SparseVector *) v;
|
||||||
|
|
||||||
if (vec->nnz > HNSW_MAX_NNZ)
|
if (vec->nnz > HNSW_MAX_NNZ)
|
||||||
ereport(ERROR,
|
elog(ERROR, "sparsevec cannot have more than %d non-zero elements for hnsw index", HNSW_MAX_NNZ);
|
||||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
|
||||||
errmsg("sparsevec cannot have more than %d non-zero elements for hnsw index", HNSW_MAX_NNZ)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1362,7 +1301,7 @@ HnswGetTypeInfo(Relation index)
|
|||||||
return (const HnswTypeInfo *) DatumGetPointer(FunctionCall0Coll(procinfo, InvalidOid));
|
return (const HnswTypeInfo *) DatumGetPointer(FunctionCall0Coll(procinfo, InvalidOid));
|
||||||
}
|
}
|
||||||
|
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hnsw_halfvec_support);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(hnsw_halfvec_support);
|
||||||
Datum
|
Datum
|
||||||
hnsw_halfvec_support(PG_FUNCTION_ARGS)
|
hnsw_halfvec_support(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1375,7 +1314,7 @@ hnsw_halfvec_support(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_POINTER(&typeInfo);
|
PG_RETURN_POINTER(&typeInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hnsw_bit_support);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(hnsw_bit_support);
|
||||||
Datum
|
Datum
|
||||||
hnsw_bit_support(PG_FUNCTION_ARGS)
|
hnsw_bit_support(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1388,7 +1327,7 @@ hnsw_bit_support(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_POINTER(&typeInfo);
|
PG_RETURN_POINTER(&typeInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hnsw_sparsevec_support);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(hnsw_sparsevec_support);
|
||||||
Datum
|
Datum
|
||||||
hnsw_sparsevec_support(PG_FUNCTION_ARGS)
|
hnsw_sparsevec_support(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,6 +26,12 @@
|
|||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
|
#define CALLBACK_ITEM_POINTER ItemPointer tid
|
||||||
|
#else
|
||||||
|
#define CALLBACK_ITEM_POINTER HeapTuple hup
|
||||||
|
#endif
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 140000
|
#if PG_VERSION_NUM >= 140000
|
||||||
#include "utils/backend_status.h"
|
#include "utils/backend_status.h"
|
||||||
#include "utils/wait_event.h"
|
#include "utils/wait_event.h"
|
||||||
@@ -90,7 +96,7 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
|
|||||||
* Callback for sampling
|
* Callback for sampling
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
SampleCallback(Relation index, ItemPointer tid, Datum *values,
|
SampleCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
||||||
bool *isnull, bool tupleIsAlive, void *state)
|
bool *isnull, bool tupleIsAlive, void *state)
|
||||||
{
|
{
|
||||||
IvfflatBuildState *buildstate = (IvfflatBuildState *) state;
|
IvfflatBuildState *buildstate = (IvfflatBuildState *) state;
|
||||||
@@ -201,12 +207,16 @@ AddTupleToSort(Relation index, ItemPointer tid, Datum *values, IvfflatBuildState
|
|||||||
* Callback for table_index_build_scan
|
* Callback for table_index_build_scan
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
BuildCallback(Relation index, ItemPointer tid, Datum *values,
|
BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
||||||
bool *isnull, bool tupleIsAlive, void *state)
|
bool *isnull, bool tupleIsAlive, void *state)
|
||||||
{
|
{
|
||||||
IvfflatBuildState *buildstate = (IvfflatBuildState *) state;
|
IvfflatBuildState *buildstate = (IvfflatBuildState *) state;
|
||||||
MemoryContext oldCtx;
|
MemoryContext oldCtx;
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 130000
|
||||||
|
ItemPointer tid = &hup->t_self;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Skip nulls */
|
/* Skip nulls */
|
||||||
if (isnull[0])
|
if (isnull[0])
|
||||||
return;
|
return;
|
||||||
@@ -325,20 +335,14 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
|
|||||||
|
|
||||||
/* Disallow varbit since require fixed dimensions */
|
/* Disallow varbit since require fixed dimensions */
|
||||||
if (TupleDescAttr(index->rd_att, 0)->atttypid == VARBITOID)
|
if (TupleDescAttr(index->rd_att, 0)->atttypid == VARBITOID)
|
||||||
ereport(ERROR,
|
elog(ERROR, "type not supported for ivfflat index");
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("type not supported for ivfflat index")));
|
|
||||||
|
|
||||||
/* Require column to have dimensions to be indexed */
|
/* Require column to have dimensions to be indexed */
|
||||||
if (buildstate->dimensions < 0)
|
if (buildstate->dimensions < 0)
|
||||||
ereport(ERROR,
|
elog(ERROR, "column does not have dimensions");
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("column does not have dimensions")));
|
|
||||||
|
|
||||||
if (buildstate->dimensions > buildstate->typeInfo->maxDimensions)
|
if (buildstate->dimensions > buildstate->typeInfo->maxDimensions)
|
||||||
ereport(ERROR,
|
elog(ERROR, "column cannot have more than %d dimensions for ivfflat index", buildstate->typeInfo->maxDimensions);
|
||||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
|
||||||
errmsg("column cannot have more than %d dimensions for ivfflat index", buildstate->typeInfo->maxDimensions)));
|
|
||||||
|
|
||||||
buildstate->reltuples = 0;
|
buildstate->reltuples = 0;
|
||||||
buildstate->indtuples = 0;
|
buildstate->indtuples = 0;
|
||||||
@@ -351,9 +355,7 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
|
|||||||
|
|
||||||
/* Require more than one dimension for spherical k-means */
|
/* Require more than one dimension for spherical k-means */
|
||||||
if (buildstate->kmeansnormprocinfo != NULL && buildstate->dimensions == 1)
|
if (buildstate->kmeansnormprocinfo != NULL && buildstate->dimensions == 1)
|
||||||
ereport(ERROR,
|
elog(ERROR, "dimensions must be greater than one for this opclass");
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("dimensions must be greater than one for this opclass")));
|
|
||||||
|
|
||||||
/* Create tuple description for sorting */
|
/* Create tuple description for sorting */
|
||||||
buildstate->tupdesc = CreateTemplateTupleDesc(3);
|
buildstate->tupdesc = CreateTemplateTupleDesc(3);
|
||||||
@@ -560,20 +562,6 @@ PrintKmeansMetrics(IvfflatBuildState * buildstate)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize build sort state
|
|
||||||
*/
|
|
||||||
static Tuplesortstate *
|
|
||||||
InitBuildSortState(TupleDesc tupdesc, int memory, SortCoordinate coordinate)
|
|
||||||
{
|
|
||||||
AttrNumber attNums[] = {1};
|
|
||||||
Oid sortOperators[] = {Int4LessOperator};
|
|
||||||
Oid sortCollations[] = {InvalidOid};
|
|
||||||
bool nullsFirstFlags[] = {false};
|
|
||||||
|
|
||||||
return tuplesort_begin_heap(tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, memory, coordinate, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Within leader, wait for end of heap scan
|
* Within leader, wait for end of heap scan
|
||||||
*/
|
*/
|
||||||
@@ -621,6 +609,12 @@ IvfflatParallelScanAndSort(IvfflatSpool * ivfspool, IvfflatShared * ivfshared, S
|
|||||||
double reltuples;
|
double reltuples;
|
||||||
IndexInfo *indexInfo;
|
IndexInfo *indexInfo;
|
||||||
|
|
||||||
|
/* Sort options, which must match AssignTuples */
|
||||||
|
AttrNumber attNums[] = {1};
|
||||||
|
Oid sortOperators[] = {Int4LessOperator};
|
||||||
|
Oid sortCollations[] = {InvalidOid};
|
||||||
|
bool nullsFirstFlags[] = {false};
|
||||||
|
|
||||||
/* Initialize local tuplesort coordination state */
|
/* Initialize local tuplesort coordination state */
|
||||||
coordinate = palloc0(sizeof(SortCoordinateData));
|
coordinate = palloc0(sizeof(SortCoordinateData));
|
||||||
coordinate->isWorker = true;
|
coordinate->isWorker = true;
|
||||||
@@ -633,7 +627,7 @@ IvfflatParallelScanAndSort(IvfflatSpool * ivfspool, IvfflatShared * ivfshared, S
|
|||||||
InitBuildState(&buildstate, ivfspool->heap, ivfspool->index, indexInfo);
|
InitBuildState(&buildstate, ivfspool->heap, ivfspool->index, indexInfo);
|
||||||
memcpy(buildstate.centers->items, ivfcenters, buildstate.centers->itemsize * buildstate.centers->maxlen);
|
memcpy(buildstate.centers->items, ivfcenters, buildstate.centers->itemsize * buildstate.centers->maxlen);
|
||||||
buildstate.centers->length = buildstate.centers->maxlen;
|
buildstate.centers->length = buildstate.centers->maxlen;
|
||||||
ivfspool->sortstate = InitBuildSortState(buildstate.tupdesc, sortmem, coordinate);
|
ivfspool->sortstate = tuplesort_begin_heap(buildstate.tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, sortmem, coordinate, false);
|
||||||
buildstate.sortstate = ivfspool->sortstate;
|
buildstate.sortstate = ivfspool->sortstate;
|
||||||
scan = table_beginscan_parallel(ivfspool->heap,
|
scan = table_beginscan_parallel(ivfspool->heap,
|
||||||
ParallelTableScanFromIvfflatShared(ivfshared));
|
ParallelTableScanFromIvfflatShared(ivfshared));
|
||||||
@@ -930,6 +924,12 @@ AssignTuples(IvfflatBuildState * buildstate)
|
|||||||
int parallel_workers = 0;
|
int parallel_workers = 0;
|
||||||
SortCoordinate coordinate = NULL;
|
SortCoordinate coordinate = NULL;
|
||||||
|
|
||||||
|
/* Sort options, which must match IvfflatParallelScanAndSort */
|
||||||
|
AttrNumber attNums[] = {1};
|
||||||
|
Oid sortOperators[] = {Int4LessOperator};
|
||||||
|
Oid sortCollations[] = {InvalidOid};
|
||||||
|
bool nullsFirstFlags[] = {false};
|
||||||
|
|
||||||
pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_ASSIGN);
|
pgstat_progress_update_param(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_ASSIGN);
|
||||||
|
|
||||||
/* Calculate parallel workers */
|
/* Calculate parallel workers */
|
||||||
@@ -950,7 +950,7 @@ AssignTuples(IvfflatBuildState * buildstate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Begin serial/leader tuplesort */
|
/* Begin serial/leader tuplesort */
|
||||||
buildstate->sortstate = InitBuildSortState(buildstate->tupdesc, maintenance_work_mem, coordinate);
|
buildstate->sortstate = tuplesort_begin_heap(buildstate->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, maintenance_work_mem, coordinate, false);
|
||||||
|
|
||||||
/* Add tuples to sort */
|
/* Add tuples to sort */
|
||||||
if (buildstate->heap != NULL)
|
if (buildstate->heap != NULL)
|
||||||
@@ -1006,10 +1006,6 @@ BuildIndex(Relation heap, Relation index, IndexInfo *indexInfo,
|
|||||||
CreateListPages(index, buildstate->centers, buildstate->dimensions, buildstate->lists, forkNum, &buildstate->listInfo);
|
CreateListPages(index, buildstate->centers, buildstate->dimensions, buildstate->lists, forkNum, &buildstate->listInfo);
|
||||||
CreateEntryPages(buildstate, forkNum);
|
CreateEntryPages(buildstate, forkNum);
|
||||||
|
|
||||||
/* Write WAL for initialization fork since GenericXLog functions do not */
|
|
||||||
if (forkNum == INIT_FORKNUM)
|
|
||||||
log_newpage_range(index, forkNum, 0, RelationGetNumberOfBlocksInFork(index, forkNum), true);
|
|
||||||
|
|
||||||
FreeBuildState(buildstate);
|
FreeBuildState(buildstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include "commands/progress.h"
|
#include "commands/progress.h"
|
||||||
#include "commands/vacuum.h"
|
#include "commands/vacuum.h"
|
||||||
#include "ivfflat.h"
|
#include "ivfflat.h"
|
||||||
#include "utils/float.h"
|
|
||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
#include "utils/selfuncs.h"
|
#include "utils/selfuncs.h"
|
||||||
#include "utils/spccache.h"
|
#include "utils/spccache.h"
|
||||||
@@ -27,7 +26,11 @@ IvfflatInit(void)
|
|||||||
{
|
{
|
||||||
ivfflat_relopt_kind = add_reloption_kind();
|
ivfflat_relopt_kind = add_reloption_kind();
|
||||||
add_int_reloption(ivfflat_relopt_kind, "lists", "Number of inverted lists",
|
add_int_reloption(ivfflat_relopt_kind, "lists", "Number of inverted lists",
|
||||||
IVFFLAT_DEFAULT_LISTS, IVFFLAT_MIN_LISTS, IVFFLAT_MAX_LISTS, AccessExclusiveLock);
|
IVFFLAT_DEFAULT_LISTS, IVFFLAT_MIN_LISTS, IVFFLAT_MAX_LISTS
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
|
,AccessExclusiveLock
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
DefineCustomIntVariable("ivfflat.probes", "Sets the number of probes",
|
DefineCustomIntVariable("ivfflat.probes", "Sets the number of probes",
|
||||||
"Valid range is 1..lists.", &ivfflat_probes,
|
"Valid range is 1..lists.", &ivfflat_probes,
|
||||||
@@ -57,39 +60,6 @@ ivfflatbuildphasename(int64 phasenum)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Estimate the number of probes needed to get requested tuples
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
EstimateProbes(PlannerInfo *root, IndexPath *path, int lists)
|
|
||||||
{
|
|
||||||
double selectivity = 1;
|
|
||||||
ListCell *lc;
|
|
||||||
double tuplesPerList;
|
|
||||||
|
|
||||||
/* Cannot estimate without limit */
|
|
||||||
/* limit_tuples includes offset */
|
|
||||||
if (root->limit_tuples < 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Get the selectivity of non-index conditions */
|
|
||||||
foreach(lc, path->indexinfo->indrestrictinfo)
|
|
||||||
{
|
|
||||||
RestrictInfo *rinfo = lfirst(lc);
|
|
||||||
|
|
||||||
/* Skip DEFAULT_INEQ_SEL since it may be a distance filter */
|
|
||||||
if (rinfo->norm_selec >= 0 && rinfo->norm_selec <= 1 && rinfo->norm_selec != (Selectivity) DEFAULT_INEQ_SEL)
|
|
||||||
selectivity *= rinfo->norm_selec;
|
|
||||||
}
|
|
||||||
|
|
||||||
tuplesPerList = path->indexinfo->tuples * selectivity / (double) lists;
|
|
||||||
if (tuplesPerList == 0)
|
|
||||||
return lists;
|
|
||||||
|
|
||||||
/* No need to cap at this point */
|
|
||||||
return root->limit_tuples / tuplesPerList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Estimate the cost of an index scan
|
* Estimate the cost of an index scan
|
||||||
*/
|
*/
|
||||||
@@ -101,7 +71,6 @@ ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|||||||
{
|
{
|
||||||
GenericCosts costs;
|
GenericCosts costs;
|
||||||
int lists;
|
int lists;
|
||||||
int probes;
|
|
||||||
double ratio;
|
double ratio;
|
||||||
double spc_seq_page_cost;
|
double spc_seq_page_cost;
|
||||||
Relation index;
|
Relation index;
|
||||||
@@ -109,8 +78,8 @@ ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|||||||
/* Never use index without order */
|
/* Never use index without order */
|
||||||
if (path->indexorderbys == NULL)
|
if (path->indexorderbys == NULL)
|
||||||
{
|
{
|
||||||
*indexStartupCost = get_float8_infinity();
|
*indexStartupCost = DBL_MAX;
|
||||||
*indexTotalCost = get_float8_infinity();
|
*indexTotalCost = DBL_MAX;
|
||||||
*indexSelectivity = 0;
|
*indexSelectivity = 0;
|
||||||
*indexCorrelation = 0;
|
*indexCorrelation = 0;
|
||||||
*indexPages = 0;
|
*indexPages = 0;
|
||||||
@@ -123,10 +92,8 @@ ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|||||||
IvfflatGetMetaPageInfo(index, &lists, NULL);
|
IvfflatGetMetaPageInfo(index, &lists, NULL);
|
||||||
index_close(index, NoLock);
|
index_close(index, NoLock);
|
||||||
|
|
||||||
probes = Max(ivfflat_probes, EstimateProbes(root, path, lists));
|
|
||||||
|
|
||||||
/* Get the ratio of lists that we need to visit */
|
/* Get the ratio of lists that we need to visit */
|
||||||
ratio = ((double) probes) / lists;
|
ratio = ((double) ivfflat_probes) / lists;
|
||||||
if (ratio > 1.0)
|
if (ratio > 1.0)
|
||||||
ratio = 1.0;
|
ratio = 1.0;
|
||||||
|
|
||||||
@@ -181,10 +148,23 @@ ivfflatoptions(Datum reloptions, bool validate)
|
|||||||
{"lists", RELOPT_TYPE_INT, offsetof(IvfflatOptions, lists)},
|
{"lists", RELOPT_TYPE_INT, offsetof(IvfflatOptions, lists)},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
return (bytea *) build_reloptions(reloptions, validate,
|
return (bytea *) build_reloptions(reloptions, validate,
|
||||||
ivfflat_relopt_kind,
|
ivfflat_relopt_kind,
|
||||||
sizeof(IvfflatOptions),
|
sizeof(IvfflatOptions),
|
||||||
tab, lengthof(tab));
|
tab, lengthof(tab));
|
||||||
|
#else
|
||||||
|
relopt_value *options;
|
||||||
|
int numoptions;
|
||||||
|
IvfflatOptions *rdopts;
|
||||||
|
|
||||||
|
options = parseRelOptions(reloptions, validate, ivfflat_relopt_kind, &numoptions);
|
||||||
|
rdopts = allocateReloptStruct(sizeof(IvfflatOptions), options, numoptions);
|
||||||
|
fillRelOptions((void *) rdopts, sizeof(IvfflatOptions), options, numoptions,
|
||||||
|
validate, tab, lengthof(tab));
|
||||||
|
|
||||||
|
return (bytea *) rdopts;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -201,7 +181,7 @@ ivfflatvalidate(Oid opclassoid)
|
|||||||
*
|
*
|
||||||
* See https://www.postgresql.org/docs/current/index-api.html
|
* See https://www.postgresql.org/docs/current/index-api.html
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(ivfflathandler);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(ivfflathandler);
|
||||||
Datum
|
Datum
|
||||||
ivfflathandler(PG_FUNCTION_ARGS)
|
ivfflathandler(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -209,7 +189,9 @@ ivfflathandler(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
amroutine->amstrategies = 0;
|
amroutine->amstrategies = 0;
|
||||||
amroutine->amsupport = 5;
|
amroutine->amsupport = 5;
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
amroutine->amoptsprocnum = 0;
|
amroutine->amoptsprocnum = 0;
|
||||||
|
#endif
|
||||||
amroutine->amcanorder = false;
|
amroutine->amcanorder = false;
|
||||||
amroutine->amcanorderbyop = true;
|
amroutine->amcanorderbyop = true;
|
||||||
amroutine->amcanbackward = false; /* can change direction mid-scan */
|
amroutine->amcanbackward = false; /* can change direction mid-scan */
|
||||||
@@ -222,24 +204,17 @@ ivfflathandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amclusterable = false;
|
amroutine->amclusterable = false;
|
||||||
amroutine->ampredlocks = false;
|
amroutine->ampredlocks = false;
|
||||||
amroutine->amcanparallel = false;
|
amroutine->amcanparallel = false;
|
||||||
#if PG_VERSION_NUM >= 170000
|
|
||||||
amroutine->amcanbuildparallel = true;
|
|
||||||
#endif
|
|
||||||
amroutine->amcaninclude = false;
|
amroutine->amcaninclude = false;
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
|
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
|
||||||
#if PG_VERSION_NUM >= 160000
|
|
||||||
amroutine->amsummarizing = false;
|
|
||||||
#endif
|
|
||||||
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
|
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
|
||||||
|
#endif
|
||||||
amroutine->amkeytype = InvalidOid;
|
amroutine->amkeytype = InvalidOid;
|
||||||
|
|
||||||
/* Interface functions */
|
/* Interface functions */
|
||||||
amroutine->ambuild = ivfflatbuild;
|
amroutine->ambuild = ivfflatbuild;
|
||||||
amroutine->ambuildempty = ivfflatbuildempty;
|
amroutine->ambuildempty = ivfflatbuildempty;
|
||||||
amroutine->aminsert = ivfflatinsert;
|
amroutine->aminsert = ivfflatinsert;
|
||||||
#if PG_VERSION_NUM >= 170000
|
|
||||||
amroutine->aminsertcleanup = NULL;
|
|
||||||
#endif
|
|
||||||
amroutine->ambulkdelete = ivfflatbulkdelete;
|
amroutine->ambulkdelete = ivfflatbulkdelete;
|
||||||
amroutine->amvacuumcleanup = ivfflatvacuumcleanup;
|
amroutine->amvacuumcleanup = ivfflatvacuumcleanup;
|
||||||
amroutine->amcanreturn = NULL; /* tuple not included in heapsort */
|
amroutine->amcanreturn = NULL; /* tuple not included in heapsort */
|
||||||
|
|||||||
@@ -253,9 +253,8 @@ typedef struct IvfflatScanOpaqueData
|
|||||||
/* Sorting */
|
/* Sorting */
|
||||||
Tuplesortstate *sortstate;
|
Tuplesortstate *sortstate;
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
TupleTableSlot *vslot;
|
TupleTableSlot *slot;
|
||||||
TupleTableSlot *mslot;
|
bool isnull;
|
||||||
BufferAccessStrategy bas;
|
|
||||||
|
|
||||||
/* Support functions */
|
/* Support functions */
|
||||||
FmgrInfo *procinfo;
|
FmgrInfo *procinfo;
|
||||||
|
|||||||
@@ -94,9 +94,6 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, R
|
|||||||
value = IvfflatNormValue(typeInfo, collation, value);
|
value = IvfflatNormValue(typeInfo, collation, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure index is valid */
|
|
||||||
IvfflatGetMetaPageInfo(index, NULL, NULL);
|
|
||||||
|
|
||||||
/* Find the insert page - sets the page and list info */
|
/* Find the insert page - sets the page and list info */
|
||||||
FindInsertPage(index, values, &insertPage, &listInfo);
|
FindInsertPage(index, values, &insertPage, &listInfo);
|
||||||
Assert(BlockNumberIsValid(insertPage));
|
Assert(BlockNumberIsValid(insertPage));
|
||||||
|
|||||||
@@ -151,8 +151,12 @@ RandomCenters(Relation index, VectorArray centers, const IvfflatTypeInfo * typeI
|
|||||||
static void
|
static void
|
||||||
ShowMemoryUsage(MemoryContext context, Size estimatedSize)
|
ShowMemoryUsage(MemoryContext context, Size estimatedSize)
|
||||||
{
|
{
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
elog(INFO, "total memory: %zu MB",
|
elog(INFO, "total memory: %zu MB",
|
||||||
MemoryContextMemAllocated(context, true) / (1024 * 1024));
|
MemoryContextMemAllocated(context, true) / (1024 * 1024));
|
||||||
|
#else
|
||||||
|
MemoryContextStats(context);
|
||||||
|
#endif
|
||||||
elog(INFO, "estimated memory: %zu MB", estimatedSize / (1024 * 1024));
|
elog(INFO, "estimated memory: %zu MB", estimatedSize / (1024 * 1024));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -323,7 +327,7 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, const Ivff
|
|||||||
newCenters->length = numCenters;
|
newCenters->length = numCenters;
|
||||||
|
|
||||||
#ifdef IVFFLAT_MEMORY
|
#ifdef IVFFLAT_MEMORY
|
||||||
ShowMemoryUsage(MemoryContextGetParent(CurrentMemoryContext), totalSize);
|
ShowMemoryUsage(MemoryContextGetParent(CurrentMemoryContext));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Pick initial centers */
|
/* Pick initial centers */
|
||||||
|
|||||||
@@ -11,23 +11,16 @@
|
|||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
|
|
||||||
#ifdef IVFFLAT_MEMORY
|
|
||||||
#include "utils/memutils.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define GetScanList(ptr) pairingheap_container(IvfflatScanList, ph_node, ptr)
|
|
||||||
#define GetScanListConst(ptr) pairingheap_const_container(IvfflatScanList, ph_node, ptr)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compare list distances
|
* Compare list distances
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
CompareLists(const pairingheap_node *a, const pairingheap_node *b, void *arg)
|
CompareLists(const pairingheap_node *a, const pairingheap_node *b, void *arg)
|
||||||
{
|
{
|
||||||
if (GetScanListConst(a)->distance > GetScanListConst(b)->distance)
|
if (((const IvfflatScanList *) a)->distance > ((const IvfflatScanList *) b)->distance)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (GetScanListConst(a)->distance < GetScanListConst(b)->distance)
|
if (((const IvfflatScanList *) a)->distance < ((const IvfflatScanList *) b)->distance)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -79,14 +72,14 @@ GetScanLists(IndexScanDesc scan, Datum value)
|
|||||||
|
|
||||||
/* Calculate max distance */
|
/* Calculate max distance */
|
||||||
if (listCount == so->probes)
|
if (listCount == so->probes)
|
||||||
maxDistance = GetScanList(pairingheap_first(so->listQueue))->distance;
|
maxDistance = ((IvfflatScanList *) pairingheap_first(so->listQueue))->distance;
|
||||||
}
|
}
|
||||||
else if (distance < maxDistance)
|
else if (distance < maxDistance)
|
||||||
{
|
{
|
||||||
IvfflatScanList *scanlist;
|
IvfflatScanList *scanlist;
|
||||||
|
|
||||||
/* Remove */
|
/* Remove */
|
||||||
scanlist = GetScanList(pairingheap_remove_first(so->listQueue));
|
scanlist = (IvfflatScanList *) pairingheap_remove_first(so->listQueue);
|
||||||
|
|
||||||
/* Reuse */
|
/* Reuse */
|
||||||
scanlist->startPage = list->startPage;
|
scanlist->startPage = list->startPage;
|
||||||
@@ -94,7 +87,7 @@ GetScanLists(IndexScanDesc scan, Datum value)
|
|||||||
pairingheap_add(so->listQueue, &scanlist->ph_node);
|
pairingheap_add(so->listQueue, &scanlist->ph_node);
|
||||||
|
|
||||||
/* Update max distance */
|
/* Update max distance */
|
||||||
maxDistance = GetScanList(pairingheap_first(so->listQueue))->distance;
|
maxDistance = ((IvfflatScanList *) pairingheap_first(so->listQueue))->distance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,12 +106,19 @@ GetScanItems(IndexScanDesc scan, Datum value)
|
|||||||
IvfflatScanOpaque so = (IvfflatScanOpaque) scan->opaque;
|
IvfflatScanOpaque so = (IvfflatScanOpaque) scan->opaque;
|
||||||
TupleDesc tupdesc = RelationGetDescr(scan->indexRelation);
|
TupleDesc tupdesc = RelationGetDescr(scan->indexRelation);
|
||||||
double tuples = 0;
|
double tuples = 0;
|
||||||
TupleTableSlot *slot = so->vslot;
|
TupleTableSlot *slot = MakeSingleTupleTableSlot(so->tupdesc, &TTSOpsVirtual);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reuse same set of shared buffers for scan
|
||||||
|
*
|
||||||
|
* See postgres/src/backend/storage/buffer/README for description
|
||||||
|
*/
|
||||||
|
BufferAccessStrategy bas = GetAccessStrategy(BAS_BULKREAD);
|
||||||
|
|
||||||
/* Search closest probes lists */
|
/* Search closest probes lists */
|
||||||
while (!pairingheap_is_empty(so->listQueue))
|
while (!pairingheap_is_empty(so->listQueue))
|
||||||
{
|
{
|
||||||
BlockNumber searchPage = GetScanList(pairingheap_remove_first(so->listQueue))->startPage;
|
BlockNumber searchPage = ((IvfflatScanList *) pairingheap_remove_first(so->listQueue))->startPage;
|
||||||
|
|
||||||
/* Search all entry pages for list */
|
/* Search all entry pages for list */
|
||||||
while (BlockNumberIsValid(searchPage))
|
while (BlockNumberIsValid(searchPage))
|
||||||
@@ -127,7 +127,7 @@ GetScanItems(IndexScanDesc scan, Datum value)
|
|||||||
Page page;
|
Page page;
|
||||||
OffsetNumber maxoffno;
|
OffsetNumber maxoffno;
|
||||||
|
|
||||||
buf = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM, searchPage, RBM_NORMAL, so->bas);
|
buf = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM, searchPage, RBM_NORMAL, bas);
|
||||||
LockBuffer(buf, BUFFER_LOCK_SHARE);
|
LockBuffer(buf, BUFFER_LOCK_SHARE);
|
||||||
page = BufferGetPage(buf);
|
page = BufferGetPage(buf);
|
||||||
maxoffno = PageGetMaxOffsetNumber(page);
|
maxoffno = PageGetMaxOffsetNumber(page);
|
||||||
@@ -166,6 +166,8 @@ GetScanItems(IndexScanDesc scan, Datum value)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FreeAccessStrategy(bas);
|
||||||
|
|
||||||
if (tuples < 100)
|
if (tuples < 100)
|
||||||
ereport(DEBUG1,
|
ereport(DEBUG1,
|
||||||
(errmsg("index scan found few tuples"),
|
(errmsg("index scan found few tuples"),
|
||||||
@@ -215,20 +217,6 @@ GetScanValue(IndexScanDesc scan)
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize scan sort state
|
|
||||||
*/
|
|
||||||
static Tuplesortstate *
|
|
||||||
InitScanSortState(TupleDesc tupdesc)
|
|
||||||
{
|
|
||||||
AttrNumber attNums[] = {1};
|
|
||||||
Oid sortOperators[] = {Float8LessOperator};
|
|
||||||
Oid sortCollations[] = {InvalidOid};
|
|
||||||
bool nullsFirstFlags[] = {false};
|
|
||||||
|
|
||||||
return tuplesort_begin_heap(tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, work_mem, NULL, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prepare for an index scan
|
* Prepare for an index scan
|
||||||
*/
|
*/
|
||||||
@@ -239,6 +227,10 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
|
|||||||
IvfflatScanOpaque so;
|
IvfflatScanOpaque so;
|
||||||
int lists;
|
int lists;
|
||||||
int dimensions;
|
int dimensions;
|
||||||
|
AttrNumber attNums[] = {1};
|
||||||
|
Oid sortOperators[] = {Float8LessOperator};
|
||||||
|
Oid sortCollations[] = {InvalidOid};
|
||||||
|
bool nullsFirstFlags[] = {false};
|
||||||
int probes = ivfflat_probes;
|
int probes = ivfflat_probes;
|
||||||
|
|
||||||
scan = RelationGetIndexScan(index, nkeys, norderbys);
|
scan = RelationGetIndexScan(index, nkeys, norderbys);
|
||||||
@@ -266,18 +258,9 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
|
|||||||
TupleDescInitEntry(so->tupdesc, (AttrNumber) 2, "heaptid", TIDOID, -1, 0);
|
TupleDescInitEntry(so->tupdesc, (AttrNumber) 2, "heaptid", TIDOID, -1, 0);
|
||||||
|
|
||||||
/* Prep sort */
|
/* Prep sort */
|
||||||
so->sortstate = InitScanSortState(so->tupdesc);
|
so->sortstate = tuplesort_begin_heap(so->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, work_mem, NULL, false);
|
||||||
|
|
||||||
/* Need separate slots for puttuple and gettuple */
|
so->slot = MakeSingleTupleTableSlot(so->tupdesc, &TTSOpsMinimalTuple);
|
||||||
so->vslot = MakeSingleTupleTableSlot(so->tupdesc, &TTSOpsVirtual);
|
|
||||||
so->mslot = MakeSingleTupleTableSlot(so->tupdesc, &TTSOpsMinimalTuple);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reuse same set of shared buffers for scan
|
|
||||||
*
|
|
||||||
* See postgres/src/backend/storage/buffer/README for description
|
|
||||||
*/
|
|
||||||
so->bas = GetAccessStrategy(BAS_BULKREAD);
|
|
||||||
|
|
||||||
so->listQueue = pairingheap_allocate(CompareLists, scan);
|
so->listQueue = pairingheap_allocate(CompareLists, scan);
|
||||||
|
|
||||||
@@ -294,8 +277,10 @@ ivfflatrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int
|
|||||||
{
|
{
|
||||||
IvfflatScanOpaque so = (IvfflatScanOpaque) scan->opaque;
|
IvfflatScanOpaque so = (IvfflatScanOpaque) scan->opaque;
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 130000
|
||||||
if (!so->first)
|
if (!so->first)
|
||||||
tuplesort_reset(so->sortstate);
|
tuplesort_reset(so->sortstate);
|
||||||
|
#endif
|
||||||
|
|
||||||
so->first = true;
|
so->first = true;
|
||||||
pairingheap_reset(so->listQueue);
|
pairingheap_reset(so->listQueue);
|
||||||
@@ -342,19 +327,14 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
|
|||||||
IvfflatBench("GetScanItems", GetScanItems(scan, value));
|
IvfflatBench("GetScanItems", GetScanItems(scan, value));
|
||||||
so->first = false;
|
so->first = false;
|
||||||
|
|
||||||
#if defined(IVFFLAT_MEMORY)
|
|
||||||
elog(INFO, "memory: %zu MB", MemoryContextMemAllocated(CurrentMemoryContext, true) / (1024 * 1024));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Clean up if we allocated a new value */
|
/* Clean up if we allocated a new value */
|
||||||
if (value != scan->orderByData->sk_argument)
|
if (value != scan->orderByData->sk_argument)
|
||||||
pfree(DatumGetPointer(value));
|
pfree(DatumGetPointer(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tuplesort_gettupleslot(so->sortstate, true, false, so->mslot, NULL))
|
if (tuplesort_gettupleslot(so->sortstate, true, false, so->slot, NULL))
|
||||||
{
|
{
|
||||||
bool isnull;
|
ItemPointer heaptid = (ItemPointer) DatumGetPointer(slot_getattr(so->slot, 2, &so->isnull));
|
||||||
ItemPointer heaptid = (ItemPointer) DatumGetPointer(slot_getattr(so->mslot, 2, &isnull));
|
|
||||||
|
|
||||||
scan->xs_heaptid = *heaptid;
|
scan->xs_heaptid = *heaptid;
|
||||||
scan->xs_recheck = false;
|
scan->xs_recheck = false;
|
||||||
@@ -375,10 +355,6 @@ ivfflatendscan(IndexScanDesc scan)
|
|||||||
|
|
||||||
pairingheap_free(so->listQueue);
|
pairingheap_free(so->listQueue);
|
||||||
tuplesort_end(so->sortstate);
|
tuplesort_end(so->sortstate);
|
||||||
FreeAccessStrategy(so->bas);
|
|
||||||
FreeTupleDesc(so->tupdesc);
|
|
||||||
|
|
||||||
/* TODO Free vslot and mslot without freeing TupleDesc */
|
|
||||||
|
|
||||||
pfree(so);
|
pfree(so);
|
||||||
scan->opaque = NULL;
|
scan->opaque = NULL;
|
||||||
|
|||||||
@@ -170,10 +170,6 @@ IvfflatGetMetaPageInfo(Relation index, int *lists, int *dimensions)
|
|||||||
page = BufferGetPage(buf);
|
page = BufferGetPage(buf);
|
||||||
metap = IvfflatPageGetMeta(page);
|
metap = IvfflatPageGetMeta(page);
|
||||||
|
|
||||||
if (unlikely(metap->magicNumber != IVFFLAT_MAGIC_NUMBER))
|
|
||||||
elog(ERROR, "ivfflat index is not valid");
|
|
||||||
|
|
||||||
if (lists != NULL)
|
|
||||||
*lists = metap->lists;
|
*lists = metap->lists;
|
||||||
|
|
||||||
if (dimensions != NULL)
|
if (dimensions != NULL)
|
||||||
@@ -342,7 +338,7 @@ IvfflatGetTypeInfo(Relation index)
|
|||||||
return (const IvfflatTypeInfo *) DatumGetPointer(FunctionCall0Coll(procinfo, InvalidOid));
|
return (const IvfflatTypeInfo *) DatumGetPointer(FunctionCall0Coll(procinfo, InvalidOid));
|
||||||
}
|
}
|
||||||
|
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(ivfflat_halfvec_support);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(ivfflat_halfvec_support);
|
||||||
Datum
|
Datum
|
||||||
ivfflat_halfvec_support(PG_FUNCTION_ARGS)
|
ivfflat_halfvec_support(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -357,7 +353,7 @@ ivfflat_halfvec_support(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_POINTER(&typeInfo);
|
PG_RETURN_POINTER(&typeInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(ivfflat_bit_support);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(ivfflat_bit_support);
|
||||||
Datum
|
Datum
|
||||||
ivfflat_bit_support(PG_FUNCTION_ARGS)
|
ivfflat_bit_support(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
|||||||
179
src/sparsevec.c
179
src/sparsevec.c
@@ -3,7 +3,6 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include "catalog/pg_type.h"
|
|
||||||
#include "common/string.h"
|
#include "common/string.h"
|
||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
#include "halfutils.h"
|
#include "halfutils.h"
|
||||||
@@ -12,7 +11,6 @@
|
|||||||
#include "sparsevec.h"
|
#include "sparsevec.h"
|
||||||
#include "utils/array.h"
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/lsyscache.h"
|
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 120000
|
#if PG_VERSION_NUM >= 120000
|
||||||
@@ -190,7 +188,7 @@ CompareIndices(const void *a, const void *b)
|
|||||||
/*
|
/*
|
||||||
* Convert textual representation to internal representation
|
* Convert textual representation to internal representation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_in);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_in);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_in(PG_FUNCTION_ARGS)
|
sparsevec_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -411,7 +409,7 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to textual representation
|
* Convert internal representation to textual representation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_out);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_out);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_out(PG_FUNCTION_ARGS)
|
sparsevec_out(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -464,7 +462,7 @@ sparsevec_out(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert type modifier
|
* Convert type modifier
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_typmod_in);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_typmod_in);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_typmod_in(PG_FUNCTION_ARGS)
|
sparsevec_typmod_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -495,7 +493,7 @@ sparsevec_typmod_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert external binary representation to internal representation
|
* Convert external binary representation to internal representation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_recv);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_recv);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_recv(PG_FUNCTION_ARGS)
|
sparsevec_recv(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -547,7 +545,7 @@ sparsevec_recv(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to the external binary representation
|
* Convert internal representation to the external binary representation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_send);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_send);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_send(PG_FUNCTION_ARGS)
|
sparsevec_send(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -574,7 +572,7 @@ sparsevec_send(PG_FUNCTION_ARGS)
|
|||||||
* Convert sparse vector to sparse vector
|
* Convert sparse vector to sparse vector
|
||||||
* This is needed to check the type modifier
|
* This is needed to check the type modifier
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec);
|
||||||
Datum
|
Datum
|
||||||
sparsevec(PG_FUNCTION_ARGS)
|
sparsevec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -589,7 +587,7 @@ sparsevec(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert dense vector to sparse vector
|
* Convert dense vector to sparse vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_to_sparsevec);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_to_sparsevec);
|
||||||
Datum
|
Datum
|
||||||
vector_to_sparsevec(PG_FUNCTION_ARGS)
|
vector_to_sparsevec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -632,7 +630,7 @@ vector_to_sparsevec(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert half vector to sparse vector
|
* Convert half vector to sparse vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_to_sparsevec);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_to_sparsevec);
|
||||||
Datum
|
Datum
|
||||||
halfvec_to_sparsevec(PG_FUNCTION_ARGS)
|
halfvec_to_sparsevec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -672,137 +670,6 @@ halfvec_to_sparsevec(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_POINTER(result);
|
PG_RETURN_POINTER(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert array to sparse vector
|
|
||||||
*/
|
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(array_to_sparsevec);
|
|
||||||
Datum
|
|
||||||
array_to_sparsevec(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
ArrayType *array = PG_GETARG_ARRAYTYPE_P(0);
|
|
||||||
int32 typmod = PG_GETARG_INT32(1);
|
|
||||||
SparseVector *result;
|
|
||||||
int16 typlen;
|
|
||||||
bool typbyval;
|
|
||||||
char typalign;
|
|
||||||
Datum *elemsp;
|
|
||||||
int nelemsp;
|
|
||||||
int nnz = 0;
|
|
||||||
float *values;
|
|
||||||
int j = 0;
|
|
||||||
|
|
||||||
if (ARR_NDIM(array) > 1)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
|
||||||
errmsg("array must be 1-D")));
|
|
||||||
|
|
||||||
if (ARR_HASNULL(array) && array_contains_nulls(array))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
|
||||||
errmsg("array must not contain nulls")));
|
|
||||||
|
|
||||||
get_typlenbyvalalign(ARR_ELEMTYPE(array), &typlen, &typbyval, &typalign);
|
|
||||||
deconstruct_array(array, ARR_ELEMTYPE(array), typlen, typbyval, typalign, &elemsp, NULL, &nelemsp);
|
|
||||||
|
|
||||||
CheckDim(nelemsp);
|
|
||||||
CheckExpectedDim(typmod, nelemsp);
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
/* /fp:fast may not propagate +/-Infinity or NaN */
|
|
||||||
#define IS_NOT_ZERO(v) (isnan((float) (v)) || isinf((float) (v)) || ((float) (v)) != 0)
|
|
||||||
#else
|
|
||||||
#define IS_NOT_ZERO(v) (((float) (v)) != 0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ARR_ELEMTYPE(array) == INT4OID)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < nelemsp; i++)
|
|
||||||
nnz += IS_NOT_ZERO(DatumGetInt32(elemsp[i]));
|
|
||||||
}
|
|
||||||
else if (ARR_ELEMTYPE(array) == FLOAT8OID)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < nelemsp; i++)
|
|
||||||
nnz += IS_NOT_ZERO(DatumGetFloat8(elemsp[i]));
|
|
||||||
}
|
|
||||||
else if (ARR_ELEMTYPE(array) == FLOAT4OID)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < nelemsp; i++)
|
|
||||||
nnz += IS_NOT_ZERO(DatumGetFloat4(elemsp[i]));
|
|
||||||
}
|
|
||||||
else if (ARR_ELEMTYPE(array) == NUMERICOID)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < nelemsp; i++)
|
|
||||||
nnz += IS_NOT_ZERO(DirectFunctionCall1(numeric_float4, elemsp[i]));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
|
||||||
errmsg("unsupported array type")));
|
|
||||||
}
|
|
||||||
|
|
||||||
result = InitSparseVector(nelemsp, nnz);
|
|
||||||
values = SPARSEVEC_VALUES(result);
|
|
||||||
|
|
||||||
#define PROCESS_ARRAY_ELEM(elem) \
|
|
||||||
do { \
|
|
||||||
float v = (float) (elem); \
|
|
||||||
if (IS_NOT_ZERO(v)) { \
|
|
||||||
/* Safety check */ \
|
|
||||||
if (j >= result->nnz) \
|
|
||||||
elog(ERROR, "safety check failed"); \
|
|
||||||
result->indices[j] = i; \
|
|
||||||
values[j] = v; \
|
|
||||||
j++; \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
if (ARR_ELEMTYPE(array) == INT4OID)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < nelemsp; i++)
|
|
||||||
PROCESS_ARRAY_ELEM(DatumGetInt32(elemsp[i]));
|
|
||||||
}
|
|
||||||
else if (ARR_ELEMTYPE(array) == FLOAT8OID)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < nelemsp; i++)
|
|
||||||
PROCESS_ARRAY_ELEM(DatumGetFloat8(elemsp[i]));
|
|
||||||
}
|
|
||||||
else if (ARR_ELEMTYPE(array) == FLOAT4OID)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < nelemsp; i++)
|
|
||||||
PROCESS_ARRAY_ELEM(DatumGetFloat4(elemsp[i]));
|
|
||||||
}
|
|
||||||
else if (ARR_ELEMTYPE(array) == NUMERICOID)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < nelemsp; i++)
|
|
||||||
PROCESS_ARRAY_ELEM(DatumGetFloat4(DirectFunctionCall1(numeric_float4, elemsp[i])));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
|
||||||
errmsg("unsupported array type")));
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef PROCESS_ARRAY_ELEM
|
|
||||||
#undef IS_NOT_ZERO
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Free allocation from deconstruct_array. Do not free individual elements
|
|
||||||
* when pass-by-reference since they point to original array.
|
|
||||||
*/
|
|
||||||
pfree(elemsp);
|
|
||||||
|
|
||||||
if (j != result->nnz)
|
|
||||||
elog(ERROR, "correctness check failed");
|
|
||||||
|
|
||||||
/* Check elements */
|
|
||||||
for (int i = 0; i < result->nnz; i++)
|
|
||||||
CheckElement(values[i]);
|
|
||||||
|
|
||||||
PG_RETURN_POINTER(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the L2 squared distance between sparse vectors
|
* Get the L2 squared distance between sparse vectors
|
||||||
*/
|
*/
|
||||||
@@ -854,7 +721,7 @@ SparsevecL2SquaredDistance(SparseVector * a, SparseVector * b)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 distance between sparse vectors
|
* Get the L2 distance between sparse vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l2_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_distance);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_l2_distance(PG_FUNCTION_ARGS)
|
sparsevec_l2_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -870,7 +737,7 @@ sparsevec_l2_distance(PG_FUNCTION_ARGS)
|
|||||||
* Get the L2 squared distance between sparse vectors
|
* Get the L2 squared distance between sparse vectors
|
||||||
* This saves a sqrt calculation
|
* This saves a sqrt calculation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l2_squared_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_squared_distance);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_l2_squared_distance(PG_FUNCTION_ARGS)
|
sparsevec_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -921,7 +788,7 @@ SparsevecInnerProduct(SparseVector * a, SparseVector * b)
|
|||||||
/*
|
/*
|
||||||
* Get the inner product of two sparse vectors
|
* Get the inner product of two sparse vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_inner_product);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_inner_product);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_inner_product(PG_FUNCTION_ARGS)
|
sparsevec_inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -936,7 +803,7 @@ sparsevec_inner_product(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the negative inner product of two sparse vectors
|
* Get the negative inner product of two sparse vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_negative_inner_product);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_negative_inner_product);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_negative_inner_product(PG_FUNCTION_ARGS)
|
sparsevec_negative_inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -951,7 +818,7 @@ sparsevec_negative_inner_product(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the cosine distance between two sparse vectors
|
* Get the cosine distance between two sparse vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_cosine_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_cosine_distance);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_cosine_distance(PG_FUNCTION_ARGS)
|
sparsevec_cosine_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -996,7 +863,7 @@ sparsevec_cosine_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L1 distance between two sparse vectors
|
* Get the L1 distance between two sparse vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l1_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l1_distance);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_l1_distance(PG_FUNCTION_ARGS)
|
sparsevec_l1_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1045,7 +912,7 @@ sparsevec_l1_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 norm of a sparse vector
|
* Get the L2 norm of a sparse vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l2_norm);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_norm);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_l2_norm(PG_FUNCTION_ARGS)
|
sparsevec_l2_norm(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1063,7 +930,7 @@ sparsevec_l2_norm(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Normalize a sparse vector with the L2 norm
|
* Normalize a sparse vector with the L2 norm
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l2_normalize);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_normalize);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_l2_normalize(PG_FUNCTION_ARGS)
|
sparsevec_l2_normalize(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1173,7 +1040,7 @@ sparsevec_cmp_internal(SparseVector * a, SparseVector * b)
|
|||||||
/*
|
/*
|
||||||
* Less than
|
* Less than
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_lt);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_lt);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_lt(PG_FUNCTION_ARGS)
|
sparsevec_lt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1186,7 +1053,7 @@ sparsevec_lt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Less than or equal
|
* Less than or equal
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_le);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_le);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_le(PG_FUNCTION_ARGS)
|
sparsevec_le(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1199,7 +1066,7 @@ sparsevec_le(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Equal
|
* Equal
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_eq);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_eq);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_eq(PG_FUNCTION_ARGS)
|
sparsevec_eq(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1212,7 +1079,7 @@ sparsevec_eq(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Not equal
|
* Not equal
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_ne);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_ne);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_ne(PG_FUNCTION_ARGS)
|
sparsevec_ne(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1225,7 +1092,7 @@ sparsevec_ne(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than or equal
|
* Greater than or equal
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_ge);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_ge);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_ge(PG_FUNCTION_ARGS)
|
sparsevec_ge(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1238,7 +1105,7 @@ sparsevec_ge(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than
|
* Greater than
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_gt);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_gt);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_gt(PG_FUNCTION_ARGS)
|
sparsevec_gt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1251,7 +1118,7 @@ sparsevec_gt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Compare sparse vectors
|
* Compare sparse vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_cmp);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_cmp);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_cmp(PG_FUNCTION_ARGS)
|
sparsevec_cmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
|||||||
77
src/vector.c
77
src/vector.c
@@ -26,6 +26,11 @@
|
|||||||
#include "varatt.h"
|
#include "varatt.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 130000
|
||||||
|
#define TYPALIGN_DOUBLE 'd'
|
||||||
|
#define TYPALIGN_INT 'i'
|
||||||
|
#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))
|
||||||
|
|
||||||
@@ -176,7 +181,7 @@ float_underflow_error(void)
|
|||||||
/*
|
/*
|
||||||
* Convert textual representation to internal representation
|
* Convert textual representation to internal representation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_in);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_in);
|
||||||
Datum
|
Datum
|
||||||
vector_in(PG_FUNCTION_ARGS)
|
vector_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -289,7 +294,7 @@ vector_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to textual representation
|
* Convert internal representation to textual representation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_out);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_out);
|
||||||
Datum
|
Datum
|
||||||
vector_out(PG_FUNCTION_ARGS)
|
vector_out(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -343,7 +348,7 @@ PrintVector(char *msg, Vector * vector)
|
|||||||
/*
|
/*
|
||||||
* Convert type modifier
|
* Convert type modifier
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_typmod_in);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_typmod_in);
|
||||||
Datum
|
Datum
|
||||||
vector_typmod_in(PG_FUNCTION_ARGS)
|
vector_typmod_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -374,7 +379,7 @@ vector_typmod_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert external binary representation to internal representation
|
* Convert external binary representation to internal representation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_recv);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_recv);
|
||||||
Datum
|
Datum
|
||||||
vector_recv(PG_FUNCTION_ARGS)
|
vector_recv(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -408,7 +413,7 @@ vector_recv(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to the external binary representation
|
* Convert internal representation to the external binary representation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_send);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_send);
|
||||||
Datum
|
Datum
|
||||||
vector_send(PG_FUNCTION_ARGS)
|
vector_send(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -428,7 +433,7 @@ vector_send(PG_FUNCTION_ARGS)
|
|||||||
* Convert vector to vector
|
* Convert vector to vector
|
||||||
* This is needed to check the type modifier
|
* This is needed to check the type modifier
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector);
|
||||||
Datum
|
Datum
|
||||||
vector(PG_FUNCTION_ARGS)
|
vector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -443,7 +448,7 @@ vector(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert array to vector
|
* Convert array to vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(array_to_vector);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(array_to_vector);
|
||||||
Datum
|
Datum
|
||||||
array_to_vector(PG_FUNCTION_ARGS)
|
array_to_vector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -517,7 +522,7 @@ array_to_vector(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert vector to float4[]
|
* Convert vector to float4[]
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_to_float4);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_to_float4);
|
||||||
Datum
|
Datum
|
||||||
vector_to_float4(PG_FUNCTION_ARGS)
|
vector_to_float4(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -541,7 +546,7 @@ vector_to_float4(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert half vector to vector
|
* Convert half vector to vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_to_vector);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_to_vector);
|
||||||
Datum
|
Datum
|
||||||
halfvec_to_vector(PG_FUNCTION_ARGS)
|
halfvec_to_vector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -579,7 +584,7 @@ VectorL2SquaredDistance(int dim, float *ax, float *bx)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 distance between vectors
|
* Get the L2 distance between vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(l2_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(l2_distance);
|
||||||
Datum
|
Datum
|
||||||
l2_distance(PG_FUNCTION_ARGS)
|
l2_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -595,7 +600,7 @@ l2_distance(PG_FUNCTION_ARGS)
|
|||||||
* Get the L2 squared distance between vectors
|
* Get the L2 squared distance between vectors
|
||||||
* This saves a sqrt calculation
|
* This saves a sqrt calculation
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_l2_squared_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_l2_squared_distance);
|
||||||
Datum
|
Datum
|
||||||
vector_l2_squared_distance(PG_FUNCTION_ARGS)
|
vector_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -622,7 +627,7 @@ VectorInnerProduct(int dim, float *ax, float *bx)
|
|||||||
/*
|
/*
|
||||||
* Get the inner product of two vectors
|
* Get the inner product of two vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(inner_product);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(inner_product);
|
||||||
Datum
|
Datum
|
||||||
inner_product(PG_FUNCTION_ARGS)
|
inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -637,7 +642,7 @@ inner_product(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the negative inner product of two vectors
|
* Get the negative inner product of two vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_negative_inner_product);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_negative_inner_product);
|
||||||
Datum
|
Datum
|
||||||
vector_negative_inner_product(PG_FUNCTION_ARGS)
|
vector_negative_inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -671,7 +676,7 @@ VectorCosineSimilarity(int dim, float *ax, float *bx)
|
|||||||
/*
|
/*
|
||||||
* Get the cosine distance between two vectors
|
* Get the cosine distance between two vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(cosine_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(cosine_distance);
|
||||||
Datum
|
Datum
|
||||||
cosine_distance(PG_FUNCTION_ARGS)
|
cosine_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -703,7 +708,7 @@ cosine_distance(PG_FUNCTION_ARGS)
|
|||||||
* Currently uses angular distance since needs to satisfy triangle inequality
|
* Currently uses angular distance since needs to satisfy triangle inequality
|
||||||
* Assumes inputs are unit vectors (skips norm)
|
* Assumes inputs are unit vectors (skips norm)
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_spherical_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_spherical_distance);
|
||||||
Datum
|
Datum
|
||||||
vector_spherical_distance(PG_FUNCTION_ARGS)
|
vector_spherical_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -740,7 +745,7 @@ VectorL1Distance(int dim, float *ax, float *bx)
|
|||||||
/*
|
/*
|
||||||
* Get the L1 distance between two vectors
|
* Get the L1 distance between two vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(l1_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(l1_distance);
|
||||||
Datum
|
Datum
|
||||||
l1_distance(PG_FUNCTION_ARGS)
|
l1_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -755,7 +760,7 @@ l1_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the dimensions of a vector
|
* Get the dimensions of a vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_dims);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_dims);
|
||||||
Datum
|
Datum
|
||||||
vector_dims(PG_FUNCTION_ARGS)
|
vector_dims(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -767,7 +772,7 @@ vector_dims(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 norm of a vector
|
* Get the L2 norm of a vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_norm);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_norm);
|
||||||
Datum
|
Datum
|
||||||
vector_norm(PG_FUNCTION_ARGS)
|
vector_norm(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -785,7 +790,7 @@ vector_norm(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Normalize a vector with the L2 norm
|
* Normalize a vector with the L2 norm
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(l2_normalize);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(l2_normalize);
|
||||||
Datum
|
Datum
|
||||||
l2_normalize(PG_FUNCTION_ARGS)
|
l2_normalize(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -824,7 +829,7 @@ l2_normalize(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Add vectors
|
* Add vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_add);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_add);
|
||||||
Datum
|
Datum
|
||||||
vector_add(PG_FUNCTION_ARGS)
|
vector_add(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -857,7 +862,7 @@ vector_add(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Subtract vectors
|
* Subtract vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_sub);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_sub);
|
||||||
Datum
|
Datum
|
||||||
vector_sub(PG_FUNCTION_ARGS)
|
vector_sub(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -890,7 +895,7 @@ vector_sub(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Multiply vectors
|
* Multiply vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_mul);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_mul);
|
||||||
Datum
|
Datum
|
||||||
vector_mul(PG_FUNCTION_ARGS)
|
vector_mul(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -926,7 +931,7 @@ vector_mul(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Concatenate vectors
|
* Concatenate vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_concat);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_concat);
|
||||||
Datum
|
Datum
|
||||||
vector_concat(PG_FUNCTION_ARGS)
|
vector_concat(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -950,7 +955,7 @@ vector_concat(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Quantize a vector
|
* Quantize a vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(binary_quantize);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(binary_quantize);
|
||||||
Datum
|
Datum
|
||||||
binary_quantize(PG_FUNCTION_ARGS)
|
binary_quantize(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -968,7 +973,7 @@ binary_quantize(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get a subvector
|
* Get a subvector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(subvector);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(subvector);
|
||||||
Datum
|
Datum
|
||||||
subvector(PG_FUNCTION_ARGS)
|
subvector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1042,7 +1047,7 @@ vector_cmp_internal(Vector * a, Vector * b)
|
|||||||
/*
|
/*
|
||||||
* Less than
|
* Less than
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_lt);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_lt);
|
||||||
Datum
|
Datum
|
||||||
vector_lt(PG_FUNCTION_ARGS)
|
vector_lt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1055,7 +1060,7 @@ vector_lt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Less than or equal
|
* Less than or equal
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_le);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_le);
|
||||||
Datum
|
Datum
|
||||||
vector_le(PG_FUNCTION_ARGS)
|
vector_le(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1068,7 +1073,7 @@ vector_le(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Equal
|
* Equal
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_eq);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_eq);
|
||||||
Datum
|
Datum
|
||||||
vector_eq(PG_FUNCTION_ARGS)
|
vector_eq(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1081,7 +1086,7 @@ vector_eq(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Not equal
|
* Not equal
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_ne);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ne);
|
||||||
Datum
|
Datum
|
||||||
vector_ne(PG_FUNCTION_ARGS)
|
vector_ne(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1094,7 +1099,7 @@ vector_ne(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than or equal
|
* Greater than or equal
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_ge);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ge);
|
||||||
Datum
|
Datum
|
||||||
vector_ge(PG_FUNCTION_ARGS)
|
vector_ge(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1107,7 +1112,7 @@ vector_ge(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than
|
* Greater than
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_gt);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_gt);
|
||||||
Datum
|
Datum
|
||||||
vector_gt(PG_FUNCTION_ARGS)
|
vector_gt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1120,7 +1125,7 @@ vector_gt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Compare vectors
|
* Compare vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_cmp);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_cmp);
|
||||||
Datum
|
Datum
|
||||||
vector_cmp(PG_FUNCTION_ARGS)
|
vector_cmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1133,7 +1138,7 @@ vector_cmp(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Accumulate vectors
|
* Accumulate vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_accum);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_accum);
|
||||||
Datum
|
Datum
|
||||||
vector_accum(PG_FUNCTION_ARGS)
|
vector_accum(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1194,7 +1199,7 @@ vector_accum(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Combine vectors or half vectors (also used for halfvec_combine)
|
* Combine vectors or half vectors (also used for halfvec_combine)
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_combine);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_combine);
|
||||||
Datum
|
Datum
|
||||||
vector_combine(PG_FUNCTION_ARGS)
|
vector_combine(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1265,7 +1270,7 @@ vector_combine(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Average vectors
|
* Average vectors
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_avg);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_avg);
|
||||||
Datum
|
Datum
|
||||||
vector_avg(PG_FUNCTION_ARGS)
|
vector_avg(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1299,7 +1304,7 @@ vector_avg(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert sparse vector to dense vector
|
* Convert sparse vector to dense vector
|
||||||
*/
|
*/
|
||||||
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_to_vector);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_to_vector);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_to_vector(PG_FUNCTION_ARGS)
|
sparsevec_to_vector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,11 +20,4 @@ Vector *InitVector(int dim);
|
|||||||
void PrintVector(char *msg, Vector * vector);
|
void PrintVector(char *msg, Vector * vector);
|
||||||
int vector_cmp_internal(Vector * a, Vector * b);
|
int vector_cmp_internal(Vector * a, Vector * b);
|
||||||
|
|
||||||
/* TODO Move to better place */
|
|
||||||
#if PG_VERSION_NUM >= 160000
|
|
||||||
#define FUNCTION_PREFIX
|
|
||||||
#else
|
|
||||||
#define FUNCTION_PREFIX PGDLLEXPORT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -208,62 +208,6 @@ SELECT '{1:1e-8}/1'::sparsevec::halfvec;
|
|||||||
[0]
|
[0]
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT ARRAY[1,0,2,0,3,0]::sparsevec;
|
|
||||||
array
|
|
||||||
-----------------
|
|
||||||
{1:1,3:2,5:3}/6
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT ARRAY[1.0,0.0,2.0,0.0,3.0,0.0]::sparsevec;
|
|
||||||
array
|
|
||||||
-----------------
|
|
||||||
{1:1,3:2,5:3}/6
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT ARRAY[1,0,2,0,3,0]::float4[]::sparsevec;
|
|
||||||
array
|
|
||||||
-----------------
|
|
||||||
{1:1,3:2,5:3}/6
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT ARRAY[1,0,2,0,3,0]::float8[]::sparsevec;
|
|
||||||
array
|
|
||||||
-----------------
|
|
||||||
{1:1,3:2,5:3}/6
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT ARRAY[1,0,2,0,3,0]::numeric[]::sparsevec;
|
|
||||||
array
|
|
||||||
-----------------
|
|
||||||
{1:1,3:2,5:3}/6
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1,0,2,0,3,0}'::real[]::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
-----------------
|
|
||||||
{1:1,3:2,5:3}/6
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1,0,2,0,3,0}'::real[]::sparsevec(6);
|
|
||||||
sparsevec
|
|
||||||
-----------------
|
|
||||||
{1:1,3:2,5:3}/6
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1,0,2,0,3,0}'::real[]::sparsevec(5);
|
|
||||||
ERROR: expected 5 dimensions, not 6
|
|
||||||
SELECT '{NULL}'::real[]::sparsevec;
|
|
||||||
ERROR: array must not contain nulls
|
|
||||||
SELECT '{NaN}'::real[]::sparsevec;
|
|
||||||
ERROR: NaN not allowed in sparsevec
|
|
||||||
SELECT '{Infinity}'::real[]::sparsevec;
|
|
||||||
ERROR: infinite value not allowed in sparsevec
|
|
||||||
SELECT '{-Infinity}'::real[]::sparsevec;
|
|
||||||
ERROR: infinite value not allowed in sparsevec
|
|
||||||
SELECT '{}'::real[]::sparsevec;
|
|
||||||
ERROR: sparsevec must have at least 1 dimension
|
|
||||||
SELECT '{{1}}'::real[]::sparsevec;
|
|
||||||
ERROR: array must be 1-D
|
|
||||||
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
|
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
|
||||||
ERROR: vector cannot have more than 16000 dimensions
|
ERROR: vector cannot have more than 16000 dimensions
|
||||||
SELECT array_to_vector(array_agg(n), 16001, false) FROM generate_series(1, 16001) n;
|
SELECT array_to_vector(array_agg(n), 16001, false) FROM generate_series(1, 16001) n;
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package PostgreSQL::Test::Cluster;
|
|
||||||
|
|
||||||
use PostgresNode;
|
|
||||||
|
|
||||||
sub new
|
|
||||||
{
|
|
||||||
my ($class, $name) = @_;
|
|
||||||
return get_new_node($name);
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package PostgreSQL::Test::Utils;
|
|
||||||
|
|
||||||
use TestLib;
|
|
||||||
|
|
||||||
1;
|
|
||||||
8
test/perl/PostgresNode.pm
Normal file
8
test/perl/PostgresNode.pm
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
use PostgreSQL::Test::Cluster;
|
||||||
|
|
||||||
|
sub get_new_node
|
||||||
|
{
|
||||||
|
return PostgreSQL::Test::Cluster->new(@_);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
3
test/perl/TestLib.pm
Normal file
3
test/perl/TestLib.pm
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
use PostgreSQL::Test::Utils;
|
||||||
|
|
||||||
|
1;
|
||||||
@@ -58,22 +58,6 @@ SELECT '{}/16001'::sparsevec::halfvec;
|
|||||||
SELECT '{1:65520}/1'::sparsevec::halfvec;
|
SELECT '{1:65520}/1'::sparsevec::halfvec;
|
||||||
SELECT '{1:1e-8}/1'::sparsevec::halfvec;
|
SELECT '{1:1e-8}/1'::sparsevec::halfvec;
|
||||||
|
|
||||||
SELECT ARRAY[1,0,2,0,3,0]::sparsevec;
|
|
||||||
SELECT ARRAY[1.0,0.0,2.0,0.0,3.0,0.0]::sparsevec;
|
|
||||||
SELECT ARRAY[1,0,2,0,3,0]::float4[]::sparsevec;
|
|
||||||
SELECT ARRAY[1,0,2,0,3,0]::float8[]::sparsevec;
|
|
||||||
SELECT ARRAY[1,0,2,0,3,0]::numeric[]::sparsevec;
|
|
||||||
|
|
||||||
SELECT '{1,0,2,0,3,0}'::real[]::sparsevec;
|
|
||||||
SELECT '{1,0,2,0,3,0}'::real[]::sparsevec(6);
|
|
||||||
SELECT '{1,0,2,0,3,0}'::real[]::sparsevec(5);
|
|
||||||
SELECT '{NULL}'::real[]::sparsevec;
|
|
||||||
SELECT '{NaN}'::real[]::sparsevec;
|
|
||||||
SELECT '{Infinity}'::real[]::sparsevec;
|
|
||||||
SELECT '{-Infinity}'::real[]::sparsevec;
|
|
||||||
SELECT '{}'::real[]::sparsevec;
|
|
||||||
SELECT '{{1}}'::real[]::sparsevec;
|
|
||||||
|
|
||||||
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
|
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
|
||||||
SELECT array_to_vector(array_agg(n), 16001, false) FROM generate_series(1, 16001) n;
|
SELECT array_to_vector(array_agg(n), 16001, false) FROM generate_series(1, 16001) n;
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
# Test generic xlog record work for ivfflat index replication.
|
# Test generic xlog record work for ivfflat index replication.
|
||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $dim = 32;
|
my $dim = 32;
|
||||||
@@ -49,7 +49,7 @@ sub test_index_replay
|
|||||||
my $array_sql = join(",", ('random()') x $dim);
|
my $array_sql = join(",", ('random()') x $dim);
|
||||||
|
|
||||||
# Initialize primary node
|
# Initialize primary node
|
||||||
$node_primary = PostgreSQL::Test::Cluster->new('primary');
|
$node_primary = get_new_node('primary');
|
||||||
$node_primary->init(allows_streaming => 1);
|
$node_primary->init(allows_streaming => 1);
|
||||||
if ($dim > 32)
|
if ($dim > 32)
|
||||||
{
|
{
|
||||||
@@ -67,7 +67,7 @@ my $backup_name = 'my_backup';
|
|||||||
$node_primary->backup($backup_name);
|
$node_primary->backup($backup_name);
|
||||||
|
|
||||||
# Create streaming replica linking to primary
|
# Create streaming replica linking to primary
|
||||||
$node_replica = PostgreSQL::Test::Cluster->new('replica');
|
$node_replica = get_new_node('replica');
|
||||||
$node_replica->init_from_backup($node_primary, $backup_name, has_streaming => 1);
|
$node_replica->init_from_backup($node_primary, $backup_name, has_streaming => 1);
|
||||||
$node_replica->start;
|
$node_replica->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $dim = 3;
|
my $dim = 3;
|
||||||
@@ -15,7 +15,7 @@ for (1 .. $dim)
|
|||||||
my $array_sql = join(", ", @r);
|
my $array_sql = join(", ", @r);
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -49,7 +49,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -48,7 +48,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $dim = 768;
|
my $dim = 768;
|
||||||
@@ -9,7 +9,7 @@ my $dim = 768;
|
|||||||
my $array_sql = join(",", ('random()') x $dim);
|
my $array_sql = join(",", ('random()') x $dim);
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $dim = 3;
|
my $dim = 3;
|
||||||
my $nc = 100;
|
my $nc = 50;
|
||||||
my $limit = 20;
|
my $limit = 20;
|
||||||
|
|
||||||
my $array_sql = join(",", ('random()') x $dim);
|
my $array_sql = join(",", ('random()') x $dim);
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
@@ -102,13 +102,15 @@ $node->safe_psql("postgres", "CREATE INDEX attribute_idx ON tst (c);");
|
|||||||
$explain = $node->safe_psql("postgres", qq(
|
$explain = $node->safe_psql("postgres", qq(
|
||||||
EXPLAIN ANALYZE SELECT i FROM tst WHERE c = $c ORDER BY v <-> '$query' LIMIT $limit;
|
EXPLAIN ANALYZE SELECT i FROM tst WHERE c = $c ORDER BY v <-> '$query' LIMIT $limit;
|
||||||
));
|
));
|
||||||
like($explain, qr/Index Scan on attribute_idx/);
|
# TODO Use attribute index
|
||||||
|
like($explain, qr/Index Scan using idx/);
|
||||||
|
|
||||||
# Test partial index
|
# Test partial index
|
||||||
$node->safe_psql("postgres", "CREATE INDEX partial_idx ON tst USING ivfflat (v vector_l2_ops) WITH (lists = 5) WHERE (c = $c);");
|
$node->safe_psql("postgres", "CREATE INDEX partial_idx ON tst USING ivfflat (v vector_l2_ops) WITH (lists = 5) WHERE (c = $c);");
|
||||||
$explain = $node->safe_psql("postgres", qq(
|
$explain = $node->safe_psql("postgres", qq(
|
||||||
EXPLAIN ANALYZE SELECT i FROM tst WHERE c = $c ORDER BY v <-> '$query' LIMIT $limit;
|
EXPLAIN ANALYZE SELECT i FROM tst WHERE c = $c ORDER BY v <-> '$query' LIMIT $limit;
|
||||||
));
|
));
|
||||||
like($explain, qr/Index Scan using partial_idx/);
|
# TODO Use partial index
|
||||||
|
like($explain, qr/Index Scan using idx/);
|
||||||
|
|
||||||
done_testing();
|
done_testing();
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
# Test generic xlog record work for hnsw index replication.
|
# Test generic xlog record work for hnsw index replication.
|
||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $dim = 32;
|
my $dim = 32;
|
||||||
@@ -49,7 +49,7 @@ sub test_index_replay
|
|||||||
my $array_sql = join(",", ('random()') x $dim);
|
my $array_sql = join(",", ('random()') x $dim);
|
||||||
|
|
||||||
# Initialize primary node
|
# Initialize primary node
|
||||||
$node_primary = PostgreSQL::Test::Cluster->new('primary');
|
$node_primary = get_new_node('primary');
|
||||||
$node_primary->init(allows_streaming => 1);
|
$node_primary->init(allows_streaming => 1);
|
||||||
if ($dim > 32)
|
if ($dim > 32)
|
||||||
{
|
{
|
||||||
@@ -67,7 +67,7 @@ my $backup_name = 'my_backup';
|
|||||||
$node_primary->backup($backup_name);
|
$node_primary->backup($backup_name);
|
||||||
|
|
||||||
# Create streaming replica linking to primary
|
# Create streaming replica linking to primary
|
||||||
$node_replica = PostgreSQL::Test::Cluster->new('replica');
|
$node_replica = get_new_node('replica');
|
||||||
$node_replica->init_from_backup($node_primary, $backup_name, has_streaming => 1);
|
$node_replica->init_from_backup($node_primary, $backup_name, has_streaming => 1);
|
||||||
$node_replica->start;
|
$node_replica->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $dim = 3;
|
my $dim = 3;
|
||||||
@@ -15,7 +15,7 @@ for (1 .. $dim)
|
|||||||
my $array_sql = join(", ", @r);
|
my $array_sql = join(", ", @r);
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -47,7 +47,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -47,7 +47,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -48,7 +48,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
# Ensures elements and neighbors on both same and different pages
|
# Ensures elements and neighbors on both same and different pages
|
||||||
@@ -10,7 +10,7 @@ my $dim = 1900;
|
|||||||
my $array_sql = join(",", ('random()') x $dim);
|
my $array_sql = join(",", ('random()') x $dim);
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $dim = 3;
|
my $dim = 3;
|
||||||
@@ -11,7 +11,7 @@ my $limit = 20;
|
|||||||
my $array_sql = join(",", ('random()') x $dim);
|
my $array_sql = join(",", ('random()') x $dim);
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ sub test_aggregate
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
# Does not raise overflow error in this instance due to loss of precision
|
# Does not raise overflow error in this instance due to loss of precision
|
||||||
is($res, "[24576,24576,49152]");
|
is($res, "[24576,24576,49152]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $dim = 1024;
|
my $dim = 1024;
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -51,7 +51,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -51,7 +51,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -51,7 +51,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -48,7 +48,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -48,7 +48,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -48,7 +48,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -47,7 +47,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -47,7 +47,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -48,7 +48,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -51,7 +51,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
my $array_sql = join(",", ('floor(random() * 2)::int - 1') x 3);
|
my $array_sql = join(",", ('floor(random() * 2)::int - 1') x 3);
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -10,7 +10,7 @@ my $dim = 5;
|
|||||||
my $array_sql = join(",", ('floor(random() * 4)::int - 2') x $dim);
|
my $array_sql = join(",", ('floor(random() * 4)::int - 2') x $dim);
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
my $node;
|
my $node;
|
||||||
@@ -51,7 +51,7 @@ sub test_recall
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
$node = PostgreSQL::Test::Cluster->new('node');
|
$node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings;
|
||||||
use PostgreSQL::Test::Cluster;
|
use PostgresNode;
|
||||||
use PostgreSQL::Test::Utils;
|
use TestLib;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = PostgreSQL::Test::Cluster->new('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +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 and index
|
|
||||||
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
|
||||||
$node->safe_psql("postgres", "CREATE TABLE tst (i serial, v sparsevec(100000));");
|
|
||||||
$node->safe_psql("postgres", "CREATE INDEX ON tst USING hnsw (v sparsevec_l2_ops);");
|
|
||||||
|
|
||||||
for (1 .. 3)
|
|
||||||
{
|
|
||||||
for (1 .. 100)
|
|
||||||
{
|
|
||||||
my @elements;
|
|
||||||
my %indices;
|
|
||||||
for (1 .. int(rand() * 100))
|
|
||||||
{
|
|
||||||
my $index = int(rand() * (100000 - 1)) + 1;
|
|
||||||
if (!exists($indices{$index}))
|
|
||||||
{
|
|
||||||
my $value = rand();
|
|
||||||
push(@elements, "$index:$value");
|
|
||||||
$indices{$index} = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
my $embedding = "{" . join(",", @elements) . "}/100000";
|
|
||||||
$node->safe_psql("postgres", "INSERT INTO tst (v) VALUES ('$embedding');");
|
|
||||||
}
|
|
||||||
|
|
||||||
$node->safe_psql("postgres", "DELETE FROM tst WHERE i % 2 = 0;");
|
|
||||||
$node->safe_psql("postgres", "VACUUM tst;");
|
|
||||||
is(1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
done_testing();
|
|
||||||
@@ -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.7.4'
|
default_version = '0.7.0'
|
||||||
module_pathname = '$libdir/vector'
|
module_pathname = '$libdir/vector'
|
||||||
relocatable = true
|
relocatable = true
|
||||||
|
|||||||
Reference in New Issue
Block a user