mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 20:15:46 +08:00
Compare commits
272 Commits
valgrind-s
...
hnsw-strea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea689b92b2 | ||
|
|
51dad9ff2f | ||
|
|
2cbd08b6c0 | ||
|
|
d06e75f78c | ||
|
|
8e1a2715ea | ||
|
|
25fae23fc3 | ||
|
|
46d3164a30 | ||
|
|
f2b2040306 | ||
|
|
c46f078e3c | ||
|
|
fa6782985a | ||
|
|
32ab27d72a | ||
|
|
064db12de7 | ||
|
|
45a6eef9e0 | ||
|
|
17266ed409 | ||
|
|
a98534e5ab | ||
|
|
57c05c59a2 | ||
|
|
3126fbdb6f | ||
|
|
f4b67b078f | ||
|
|
c0f6570c4a | ||
|
|
1d3d0f46ac | ||
|
|
77688b4309 | ||
|
|
d5f4a0e435 | ||
|
|
57248ba128 | ||
|
|
ff6da4fcea | ||
|
|
a8b4b6675a | ||
|
|
d148b4e61b | ||
|
|
658d74e2f6 | ||
|
|
7ba593c492 | ||
|
|
caabac33b8 | ||
|
|
525e3b81e1 | ||
|
|
8eb8cdf0f3 | ||
|
|
4c72f91206 | ||
|
|
4ac86f62a1 | ||
|
|
648dd8af78 | ||
|
|
ee43ee9b16 | ||
|
|
5ce367e18b | ||
|
|
f371eb119b | ||
|
|
382a25aefb | ||
|
|
0b6214aad6 | ||
|
|
8b9333d468 | ||
|
|
f2afd11257 | ||
|
|
cae3458329 | ||
|
|
87f5e40495 | ||
|
|
8798c1474c | ||
|
|
1b337ad97d | ||
|
|
0047630baf | ||
|
|
dc23752618 | ||
|
|
8b253359ab | ||
|
|
8de5f55b0b | ||
|
|
351db562af | ||
|
|
e1c2d03dba | ||
|
|
ba8e29600b | ||
|
|
158d9340bc | ||
|
|
49e05fb5ba | ||
|
|
9b42662188 | ||
|
|
7265927fd6 | ||
|
|
ab57217f48 | ||
|
|
5f6e031ccc | ||
|
|
5ee0471ead | ||
|
|
54f8d9733d | ||
|
|
cf419f448b | ||
|
|
8a2eebd6a4 | ||
|
|
daf9c5c743 | ||
|
|
2bca4e406b | ||
|
|
74020a90da | ||
|
|
44d8d28b40 | ||
|
|
1a1221f905 | ||
|
|
54fa16e3e3 | ||
|
|
40c3e402c7 | ||
|
|
058248fdcc | ||
|
|
73c5145b77 | ||
|
|
ec4a23fe49 | ||
|
|
38207f5640 | ||
|
|
46de265a24 | ||
|
|
b8c27914d4 | ||
|
|
2d85af51a8 | ||
|
|
e0ad441306 | ||
|
|
5776a4d937 | ||
|
|
242a12b7d5 | ||
|
|
1370dd6e86 | ||
|
|
a100dc67e5 | ||
|
|
2df9f24aad | ||
|
|
8e979ed377 | ||
|
|
4e35c6abe3 | ||
|
|
77b3d1f2a8 | ||
|
|
ecd0738728 | ||
|
|
62ffc3641c | ||
|
|
11e4d040d9 | ||
|
|
87ac108bf7 | ||
|
|
b2fa625255 | ||
|
|
a8e699c927 | ||
|
|
91541fece6 | ||
|
|
f3de487da2 | ||
|
|
721d4b7e3f | ||
|
|
28066d8fe4 | ||
|
|
495041e43b | ||
|
|
52c385c03a | ||
|
|
80cbd32dab | ||
|
|
97cf990e0f | ||
|
|
55dc735e1a | ||
|
|
be4e9a9df2 | ||
|
|
d5e8fc96a5 | ||
|
|
6d2af6d3f9 | ||
|
|
a6ab5d07c0 | ||
|
|
aa77346103 | ||
|
|
b0da2d95d9 | ||
|
|
3fb05eb847 | ||
|
|
b738ffecc1 | ||
|
|
7117513532 | ||
|
|
85d877d540 | ||
|
|
05fb382031 | ||
|
|
8e1853fbf3 | ||
|
|
f9d68a061a | ||
|
|
4f8ab574c9 | ||
|
|
a15806196e | ||
|
|
5c9429a0f8 | ||
|
|
4b44d6e745 | ||
|
|
16ca608f42 | ||
|
|
8dde14a736 | ||
|
|
d74d3065bc | ||
|
|
a1b80faa67 | ||
|
|
4af5a127e0 | ||
|
|
d02d71a398 | ||
|
|
2aca04b8de | ||
|
|
e47984e616 | ||
|
|
da7d3959a3 | ||
|
|
dadbbc3758 | ||
|
|
6af0a43d62 | ||
|
|
ffcb90d094 | ||
|
|
8a312c3c8e | ||
|
|
5d86b177ab | ||
|
|
ea99957fae | ||
|
|
4cede1a9c9 | ||
|
|
d0dbc8b4d1 | ||
|
|
bb855e6cb4 | ||
|
|
103ac50f1a | ||
|
|
57fb706242 | ||
|
|
020d3edaa9 | ||
|
|
1e9e355175 | ||
|
|
f1d283f565 | ||
|
|
8684c2ba62 | ||
|
|
6c692ef23f | ||
|
|
bb424e96e7 | ||
|
|
30911edb7a | ||
|
|
5ae2bd9efb | ||
|
|
19215317a4 | ||
|
|
c7ca7f05de | ||
|
|
4733cf253b | ||
|
|
06d1fa1402 | ||
|
|
8c5a4bfb6c | ||
|
|
8772c8de68 | ||
|
|
d1694a93af | ||
|
|
61870a0244 | ||
|
|
9b89bed701 | ||
|
|
ad7cad5ecd | ||
|
|
2a8b9d689e | ||
|
|
18cd8a60c3 | ||
|
|
8c91a9f56a | ||
|
|
9249e7e2de | ||
|
|
9e91af5989 | ||
|
|
9dcf1bdc80 | ||
|
|
0eceaa3966 | ||
|
|
49c1f13095 | ||
|
|
ff9b22977e | ||
|
|
0468cbf6e6 | ||
|
|
258eaf58fd | ||
|
|
fa8d2df1cc | ||
|
|
69f49290fb | ||
|
|
ad91451266 | ||
|
|
cafd2f6641 | ||
|
|
7923c44efe | ||
|
|
9b269e2612 | ||
|
|
9894ca3e4e | ||
|
|
19cbbfdd69 | ||
|
|
24c8a2ff40 | ||
|
|
6df583a6f6 | ||
|
|
999a2e53dd | ||
|
|
3849f0fd3d | ||
|
|
df178472d1 | ||
|
|
a72511db7f | ||
|
|
b52beefbc6 | ||
|
|
1cc66543be | ||
|
|
b15930c9c0 | ||
|
|
6f2afb16ff | ||
|
|
0959e69529 | ||
|
|
1e94907179 | ||
|
|
c9fb66d54d | ||
|
|
48e68e5e42 | ||
|
|
78d32943ac | ||
|
|
ee29c51a49 | ||
|
|
cf494f15ac | ||
|
|
13bd194d00 | ||
|
|
0ddf65c2a3 | ||
|
|
1475c06902 | ||
|
|
7140a18283 | ||
|
|
7dcdaef96c | ||
|
|
abad3d81cc | ||
|
|
d516c9bd3f | ||
|
|
a172b7cafd | ||
|
|
3cbffb0e45 | ||
|
|
5e98f455e3 | ||
|
|
498a39d79b | ||
|
|
d9e22a31ca | ||
|
|
5ecf02f07b | ||
|
|
d188b56173 | ||
|
|
6247b302fc | ||
|
|
7f15221fb4 | ||
|
|
f23d7184e9 | ||
|
|
cd95d6dfa4 | ||
|
|
dc88135515 | ||
|
|
c91fc7e0f7 | ||
|
|
708da0e058 | ||
|
|
80d34830f6 | ||
|
|
68ac05e11e | ||
|
|
8daa581f42 | ||
|
|
ebc76114ad | ||
|
|
5dec500879 | ||
|
|
1fdfff7349 | ||
|
|
38e365ed58 | ||
|
|
58ec5296b0 | ||
|
|
47d5b2896e | ||
|
|
2bf1175ab0 | ||
|
|
ec640f3b57 | ||
|
|
91cf4d223e | ||
|
|
96fdf63787 | ||
|
|
914f9aa04a | ||
|
|
e9c3c42e1c | ||
|
|
c67dc6f9b0 | ||
|
|
c39cb25c32 | ||
|
|
8f00d679d6 | ||
|
|
52bfedddc2 | ||
|
|
0f4c2407dd | ||
|
|
3e924ab7ad | ||
|
|
cd8a25bc9a | ||
|
|
6bb5de3d1b | ||
|
|
15ee38456f | ||
|
|
25b98540c9 | ||
|
|
c4484c90d9 | ||
|
|
1129d23df9 | ||
|
|
cf46c3f651 | ||
|
|
fb6683ade7 | ||
|
|
558953ca6b | ||
|
|
b24ec26025 | ||
|
|
8b6eab25a3 | ||
|
|
69c3e719f7 | ||
|
|
e81547847f | ||
|
|
6ad81fc60d | ||
|
|
7ac0ac5a7f | ||
|
|
3eef1ff5c2 | ||
|
|
b8bdf317f0 | ||
|
|
78e5bcf229 | ||
|
|
4d21eea6f1 | ||
|
|
03ca9adc4c | ||
|
|
d244a040e1 | ||
|
|
c3448a25e2 | ||
|
|
053ce2ddae | ||
|
|
24c1b51099 | ||
|
|
9696835a19 | ||
|
|
b2a5259607 | ||
|
|
c198fd58ee | ||
|
|
8c408759dc | ||
|
|
14b351bc92 | ||
|
|
ad3f811fa3 | ||
|
|
281a74f54e | ||
|
|
034713c803 | ||
|
|
ed2e460f00 | ||
|
|
d136615874 | ||
|
|
d70b160e0a | ||
|
|
d1affcc667 | ||
|
|
158481ff2a | ||
|
|
794bbaecc7 | ||
|
|
8eddcfbd1d |
119
.github/workflows/build.yml
vendored
119
.github/workflows/build.yml
vendored
@@ -1,6 +1,123 @@
|
|||||||
name: build
|
name: build
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
|
ubuntu:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
# - postgres: 18
|
||||||
|
# os: ubuntu-24.04
|
||||||
|
- postgres: 17
|
||||||
|
os: ubuntu-24.04
|
||||||
|
- postgres: 16
|
||||||
|
os: ubuntu-22.04
|
||||||
|
- postgres: 15
|
||||||
|
os: ubuntu-22.04
|
||||||
|
- postgres: 14
|
||||||
|
os: ubuntu-20.04
|
||||||
|
- postgres: 13
|
||||||
|
os: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: ankane/setup-postgres@v1
|
||||||
|
with:
|
||||||
|
postgres-version: ${{ matrix.postgres }}
|
||||||
|
dev-files: true
|
||||||
|
- run: make
|
||||||
|
env:
|
||||||
|
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
|
||||||
|
- run: |
|
||||||
|
export PG_CONFIG=`which pg_config`
|
||||||
|
sudo --preserve-env=PG_CONFIG make install
|
||||||
|
- run: make installcheck
|
||||||
|
- if: ${{ failure() }}
|
||||||
|
run: cat regression.diffs
|
||||||
|
- run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install libipc-run-perl
|
||||||
|
- run: make prove_installcheck
|
||||||
|
mac:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
if: ${{ !startsWith(github.ref_name, 'windows') }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- postgres: 16
|
||||||
|
os: macos-14
|
||||||
|
- postgres: 14
|
||||||
|
os: macos-12
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: ankane/setup-postgres@v1
|
||||||
|
with:
|
||||||
|
postgres-version: ${{ matrix.postgres }}
|
||||||
|
- run: make
|
||||||
|
env:
|
||||||
|
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter
|
||||||
|
- run: make install
|
||||||
|
- run: make installcheck
|
||||||
|
- if: ${{ failure() }}
|
||||||
|
run: cat regression.diffs
|
||||||
|
# Homebrew Postgres does not enable TAP tests, so need to download
|
||||||
|
- run: |
|
||||||
|
brew install cpanm
|
||||||
|
cpanm --notest IPC::Run
|
||||||
|
wget -q https://github.com/postgres/postgres/archive/refs/tags/$TAG.tar.gz
|
||||||
|
tar xf $TAG.tar.gz
|
||||||
|
mv postgres-$TAG postgres
|
||||||
|
env:
|
||||||
|
TAG: ${{ matrix.postgres == 16 && 'REL_16_2' || 'REL_14_11' }}
|
||||||
|
- run: make prove_installcheck PROVE_FLAGS="-I ./postgres/src/test/perl -I ./test/perl"
|
||||||
|
env:
|
||||||
|
PERL5LIB: /Users/runner/perl5/lib/perl5
|
||||||
|
- run: make clean && $(brew --prefix llvm@15)/bin/scan-build --status-bugs make
|
||||||
|
env:
|
||||||
|
PG_CFLAGS: -DUSE_ASSERT_CHECKING
|
||||||
|
windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
if: ${{ !startsWith(github.ref_name, 'mac') }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: ankane/setup-postgres@v1
|
||||||
|
with:
|
||||||
|
postgres-version: 14
|
||||||
|
- run: |
|
||||||
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" && ^
|
||||||
|
nmake /NOLOGO /F Makefile.win && ^
|
||||||
|
nmake /NOLOGO /F Makefile.win install && ^
|
||||||
|
nmake /NOLOGO /F Makefile.win installcheck && ^
|
||||||
|
nmake /NOLOGO /F Makefile.win clean && ^
|
||||||
|
nmake /NOLOGO /F Makefile.win uninstall
|
||||||
|
shell: cmd
|
||||||
|
- if: ${{ failure() }}
|
||||||
|
run: cat regression.diffs
|
||||||
|
i386:
|
||||||
|
if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: debian:12
|
||||||
|
options: --platform linux/386
|
||||||
|
steps:
|
||||||
|
- run: apt-get update && apt-get install -y build-essential git libipc-run-perl postgresql-15 postgresql-server-dev-15 sudo
|
||||||
|
- run: service postgresql start
|
||||||
|
- run: |
|
||||||
|
git clone https://github.com/${{ github.repository }}.git pgvector
|
||||||
|
cd pgvector
|
||||||
|
git fetch origin ${{ github.ref }}
|
||||||
|
git reset --hard FETCH_HEAD
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
chown -R postgres .
|
||||||
|
sudo -u postgres make installcheck
|
||||||
|
sudo -u postgres make prove_installcheck
|
||||||
|
env:
|
||||||
|
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
|
||||||
|
- if: ${{ failure() }}
|
||||||
|
run: cat pgvector/regression.diffs
|
||||||
valgrind:
|
valgrind:
|
||||||
if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }}
|
if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -12,4 +129,4 @@ jobs:
|
|||||||
check-ub: yes
|
check-ub: yes
|
||||||
- run: make OPTFLAGS=""
|
- run: make OPTFLAGS=""
|
||||||
- run: sudo --preserve-env=PG_CONFIG make install
|
- run: sudo --preserve-env=PG_CONFIG make install
|
||||||
- run: make prove_installcheck PROVE_TESTS=test/t/031_hnsw_sparsevec_vacuum_recall.pl
|
- run: make installcheck
|
||||||
|
|||||||
33
CHANGELOG.md
33
CHANGELOG.md
@@ -1,4 +1,35 @@
|
|||||||
## 0.7.0 (unreleased)
|
## 0.8.0 (unreleased)
|
||||||
|
|
||||||
|
- Added support for iterative index scans
|
||||||
|
- Added casts for arrays to `sparsevec`
|
||||||
|
- Improved cost estimation
|
||||||
|
- Improved performance of HNSW inserts and on-disk index builds
|
||||||
|
- 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
|
||||||
|
- Fixed `undefined symbol` error with GCC 8
|
||||||
|
- Fixed compilation error with universal binaries on Mac
|
||||||
|
- Fixed compilation warning with Clang < 14
|
||||||
|
|
||||||
|
## 0.7.0 (2024-04-29)
|
||||||
|
|
||||||
- Added `halfvec` type
|
- Added `halfvec` type
|
||||||
- Added `sparsevec` type
|
- Added `sparsevec` type
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
ARG PG_MAJOR=16
|
ARG PG_MAJOR=17
|
||||||
FROM postgres:$PG_MAJOR
|
FROM postgres:$PG_MAJOR
|
||||||
ARG PG_MAJOR
|
ARG PG_MAJOR
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "vector",
|
"name": "vector",
|
||||||
"abstract": "Open-source vector similarity search for Postgres",
|
"abstract": "Open-source vector similarity search for Postgres",
|
||||||
"description": "Supports L2 distance, inner product, and cosine distance",
|
"description": "Supports L2 distance, inner product, and cosine distance",
|
||||||
"version": "0.6.2",
|
"version": "0.7.4",
|
||||||
"maintainer": [
|
"maintainer": [
|
||||||
"Andrew Kane <andrew@ankane.org>"
|
"Andrew Kane <andrew@ankane.org>"
|
||||||
],
|
],
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
"vector": {
|
"vector": {
|
||||||
"file": "sql/vector.sql",
|
"file": "sql/vector.sql",
|
||||||
"docfile": "README.md",
|
"docfile": "README.md",
|
||||||
"version": "0.6.2",
|
"version": "0.7.4",
|
||||||
"abstract": "Open-source vector similarity search for Postgres"
|
"abstract": "Open-source vector similarity search for Postgres"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
12
Makefile
12
Makefile
@@ -1,8 +1,9 @@
|
|||||||
EXTENSION = vector
|
EXTENSION = vector
|
||||||
EXTVERSION = 0.6.2
|
EXTVERSION = 0.7.4
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ TESTS = $(wildcard test/sql/*.sql)
|
|||||||
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
|
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
|
||||||
REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION)
|
REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION)
|
||||||
|
|
||||||
|
# To compile for portability, run: make OPTFLAGS=""
|
||||||
OPTFLAGS = -march=native
|
OPTFLAGS = -march=native
|
||||||
|
|
||||||
# Mac ARM doesn't always support -march=native
|
# Mac ARM doesn't always support -march=native
|
||||||
@@ -41,8 +43,6 @@ 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 +52,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:
|
||||||
@@ -66,7 +66,7 @@ dist:
|
|||||||
git archive --format zip --prefix=$(EXTENSION)-$(EXTVERSION)/ --output dist/$(EXTENSION)-$(EXTVERSION).zip master
|
git archive --format zip --prefix=$(EXTENSION)-$(EXTVERSION)/ --output dist/$(EXTENSION)-$(EXTVERSION).zip master
|
||||||
|
|
||||||
# for Docker
|
# for Docker
|
||||||
PG_MAJOR ?= 16
|
PG_MAJOR ?= 17
|
||||||
|
|
||||||
.PHONY: docker
|
.PHONY: docker
|
||||||
|
|
||||||
|
|||||||
19
Makefile.win
19
Makefile.win
@@ -1,10 +1,11 @@
|
|||||||
EXTENSION = vector
|
EXTENSION = vector
|
||||||
EXTVERSION = 0.6.2
|
EXTVERSION = 0.7.4
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
REGRESS = bit_functions btree_halfvec btree_sparsevec btree_vector cast copy halfvec_functions halfvec_input hnsw_bit_hamming hnsw_bit_jaccard hnsw_halfvec_cosine hnsw_halfvec_ip hnsw_halfvec_l2 hnsw_options hnsw_sparsevec_cosine hnsw_sparsevec_ip hnsw_sparsevec_l2 hnsw_unlogged hnsw_vector_cosine hnsw_vector_ip hnsw_vector_l2 ivfflat_halfvec_cosine ivfflat_halfvec_ip ivfflat_halfvec_l2 ivfflat_options ivfflat_unlogged ivfflat_vector_cosine ivfflat_vector_ip ivfflat_vector_l2 sparsevec_functions sparsevec_input vector_functions vector_input
|
REGRESS = bit btree cast copy halfvec hnsw_bit hnsw_halfvec hnsw_sparsevec hnsw_vector ivfflat_bit ivfflat_halfvec ivfflat_vector sparsevec vector_type
|
||||||
REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION)
|
REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION)
|
||||||
|
|
||||||
# For /arch flags
|
# For /arch flags
|
||||||
@@ -19,11 +20,6 @@ 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
|
||||||
@@ -43,15 +39,18 @@ 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)
|
||||||
|
|
||||||
all: $(SHLIB)
|
sql\$(EXTENSION)--$(EXTVERSION).sql: sql\$(EXTENSION).sql
|
||||||
|
copy sql\$(EXTENSION).sql $@
|
||||||
|
|
||||||
install:
|
install: all
|
||||||
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"
|
||||||
@@ -70,6 +69,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
|
||||||
|
|||||||
266
README.md
266
README.md
@@ -5,7 +5,8 @@ Open-source vector similarity search for Postgres
|
|||||||
Store your vectors with the rest of your data. Supports:
|
Store your vectors with the rest of your data. Supports:
|
||||||
|
|
||||||
- exact and approximate nearest neighbor search
|
- exact and approximate nearest neighbor search
|
||||||
- L2 distance, inner product, and cosine distance
|
- single-precision, half-precision, binary, and sparse vectors
|
||||||
|
- L2 distance, inner product, cosine distance, L1 distance, Hamming distance, and Jaccard distance
|
||||||
- any [language](#languages) with a Postgres client
|
- any [language](#languages) with a Postgres client
|
||||||
|
|
||||||
Plus [ACID](https://en.wikipedia.org/wiki/ACID) compliance, point-in-time recovery, JOINs, and all of the other [great features](https://www.postgresql.org/about/) of Postgres
|
Plus [ACID](https://en.wikipedia.org/wiki/ACID) compliance, point-in-time recovery, JOINs, and all of the other [great features](https://www.postgresql.org/about/) of Postgres
|
||||||
@@ -20,7 +21,7 @@ Compile and install the extension (supports Postgres 12+)
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /tmp
|
cd /tmp
|
||||||
git clone --branch v0.6.2 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.7.4 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
make
|
make
|
||||||
make install # may need sudo
|
make install # may need sudo
|
||||||
@@ -45,12 +46,14 @@ 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.6.2 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.7.4 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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: Postgres 17 is not supported yet due to an upstream issue
|
||||||
|
|
||||||
See the [installation notes](#installation-notes---windows) if you run into issues
|
See the [installation notes](#installation-notes---windows) if you run into issues
|
||||||
|
|
||||||
You can also install it with [Docker](#docker) or [conda-forge](#conda-forge).
|
You can also install it with [Docker](#docker) or [conda-forge](#conda-forge).
|
||||||
@@ -81,7 +84,7 @@ Get the nearest neighbors by L2 distance
|
|||||||
SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
|
SELECT * FROM items ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
|
||||||
```
|
```
|
||||||
|
|
||||||
Also supports inner product (`<#>`), cosine distance (`<=>`), and L1 distance (`<+>`, unreleased)
|
Also supports inner product (`<#>`), cosine distance (`<=>`), and L1 distance (`<+>`, added in 0.7.0)
|
||||||
|
|
||||||
Note: `<#>` returns the negative inner product since Postgres only supports `ASC` order index scans on operators
|
Note: `<#>` returns the negative inner product since Postgres only supports `ASC` order index scans on operators
|
||||||
|
|
||||||
@@ -99,13 +102,15 @@ Or add a vector column to an existing table
|
|||||||
ALTER TABLE items ADD COLUMN embedding vector(3);
|
ALTER TABLE items ADD COLUMN embedding vector(3);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Also supports [half-precision](#half-precision-vectors), [binary](#binary-vectors), and [sparse](#sparse-vectors) vectors
|
||||||
|
|
||||||
Insert vectors
|
Insert vectors
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]');
|
INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]');
|
||||||
```
|
```
|
||||||
|
|
||||||
Or load vectors in bulk using `COPY` ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/bulk_loading.py))
|
Or load vectors in bulk using `COPY` ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/loading/example.py))
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
COPY items (embedding) FROM STDIN WITH (FORMAT BINARY);
|
COPY items (embedding) FROM STDIN WITH (FORMAT BINARY);
|
||||||
@@ -143,7 +148,9 @@ Supported distance functions are:
|
|||||||
- `<->` - L2 distance
|
- `<->` - L2 distance
|
||||||
- `<#>` - (negative) inner product
|
- `<#>` - (negative) inner product
|
||||||
- `<=>` - cosine distance
|
- `<=>` - cosine distance
|
||||||
- `<+>` - L1 distance (unreleased)
|
- `<+>` - L1 distance (added in 0.7.0)
|
||||||
|
- `<~>` - Hamming distance (binary vectors, added in 0.7.0)
|
||||||
|
- `<%>` - Jaccard distance (binary vectors, added in 0.7.0)
|
||||||
|
|
||||||
Get the nearest neighbors to a row
|
Get the nearest neighbors to a row
|
||||||
|
|
||||||
@@ -201,7 +208,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) - added in 0.5.0
|
- [HNSW](#hnsw)
|
||||||
- [IVFFlat](#ivfflat)
|
- [IVFFlat](#ivfflat)
|
||||||
|
|
||||||
## HNSW
|
## HNSW
|
||||||
@@ -216,6 +223,8 @@ L2 distance
|
|||||||
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
|
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: Use `halfvec_l2_ops` for `halfvec` and `sparsevec_l2_ops` for `sparsevec` (and similar with the other distance functions)
|
||||||
|
|
||||||
Inner product
|
Inner product
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
@@ -228,19 +237,19 @@ Cosine distance
|
|||||||
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);
|
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);
|
||||||
```
|
```
|
||||||
|
|
||||||
L1 distance - unreleased
|
L1 distance - added in 0.7.0
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE INDEX ON items USING hnsw (embedding vector_l1_ops);
|
CREATE INDEX ON items USING hnsw (embedding vector_l1_ops);
|
||||||
```
|
```
|
||||||
|
|
||||||
Hamming distance - unreleased
|
Hamming distance - added in 0.7.0
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE INDEX ON items USING hnsw (embedding bit_hamming_ops);
|
CREATE INDEX ON items USING hnsw (embedding bit_hamming_ops);
|
||||||
```
|
```
|
||||||
|
|
||||||
Jaccard distance - unreleased
|
Jaccard distance - added in 0.7.0
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE INDEX ON items USING hnsw (embedding bit_jaccard_ops);
|
CREATE INDEX ON items USING hnsw (embedding bit_jaccard_ops);
|
||||||
@@ -249,9 +258,9 @@ CREATE INDEX ON items USING hnsw (embedding bit_jaccard_ops);
|
|||||||
Supported types are:
|
Supported types are:
|
||||||
|
|
||||||
- `vector` - up to 2,000 dimensions
|
- `vector` - up to 2,000 dimensions
|
||||||
- `halfvec` - up to 4,000 dimensions (unreleased)
|
- `halfvec` - up to 4,000 dimensions (added in 0.7.0)
|
||||||
- `bit` - up to 64,000 dimensions (unreleased)
|
- `bit` - up to 64,000 dimensions (added in 0.7.0)
|
||||||
- `sparsevec` - up to 1,000 non-zero elements (unreleased)
|
- `sparsevec` - up to 1,000 non-zero elements (added in 0.7.0)
|
||||||
|
|
||||||
### Index Options
|
### Index Options
|
||||||
|
|
||||||
@@ -344,6 +353,8 @@ L2 distance
|
|||||||
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);
|
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: Use `halfvec_l2_ops` for `halfvec` (and similar with the other distance functions)
|
||||||
|
|
||||||
Inner product
|
Inner product
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
@@ -356,7 +367,7 @@ Cosine distance
|
|||||||
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
|
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
|
||||||
```
|
```
|
||||||
|
|
||||||
Hamming distance - unreleased
|
Hamming distance - added in 0.7.0
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE INDEX ON items USING ivfflat (embedding bit_hamming_ops) WITH (lists = 100);
|
CREATE INDEX ON items USING ivfflat (embedding bit_hamming_ops) WITH (lists = 100);
|
||||||
@@ -365,8 +376,8 @@ CREATE INDEX ON items USING ivfflat (embedding bit_hamming_ops) WITH (lists = 10
|
|||||||
Supported types are:
|
Supported types are:
|
||||||
|
|
||||||
- `vector` - up to 2,000 dimensions
|
- `vector` - up to 2,000 dimensions
|
||||||
- `halfvec` - up to 4,000 dimensions (unreleased)
|
- `halfvec` - up to 4,000 dimensions (added in 0.7.0)
|
||||||
- `bit` - up to 64,000 dimensions (unreleased)
|
- `bit` - up to 64,000 dimensions (added in 0.7.0)
|
||||||
|
|
||||||
### Query Options
|
### Query Options
|
||||||
|
|
||||||
@@ -440,9 +451,80 @@ Use [partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html
|
|||||||
CREATE TABLE items (embedding vector(3), category_id int) PARTITION BY LIST(category_id);
|
CREATE TABLE items (embedding vector(3), category_id int) PARTITION BY LIST(category_id);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Half Vectors
|
## Iterative Search [unreleased]
|
||||||
|
|
||||||
*Unreleased*
|
*Added in 0.8.0*
|
||||||
|
|
||||||
|
With approximate indexes, you can end up with less results than expected due to filtering conditions in the query.
|
||||||
|
|
||||||
|
Starting with 0.8.0, you can enable iterative search. If too few results from the initial index scan match the query filters, it will resume scanning until enough results are found. This can significantly improve recall (at the cost of speed).
|
||||||
|
|
||||||
|
```tsql
|
||||||
|
SET hnsw.streaming = on;
|
||||||
|
-- or
|
||||||
|
SET ivfflat.streaming = on;
|
||||||
|
```
|
||||||
|
|
||||||
|
However, there are some important caveats.
|
||||||
|
|
||||||
|
### Iterative Caveats
|
||||||
|
|
||||||
|
With iterative search, it’s possible for rows to be slightly out of order by distance. For strict ordering, use:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
WITH approx_order AS MATERIALIZED (
|
||||||
|
SELECT *, embedding <-> '[1,2,3]' AS distance FROM items WHERE ... ORDER BY distance LIMIT 5
|
||||||
|
) SELECT * FROM approx_order ORDER BY distance;
|
||||||
|
```
|
||||||
|
|
||||||
|
For distance filters, use a CTE and place the filter outside it.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
WITH approx_order AS MATERIALIZED (
|
||||||
|
SELECT *, embedding <-> '[1,2,3]' AS distance FROM items WHERE ... ORDER BY distance LIMIT 5
|
||||||
|
) SELECT * FROM approx_order WHERE distance < 0.1 ORDER BY distance;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Iterative Options
|
||||||
|
|
||||||
|
Since scanning a large portion of the index is expensive, there are options to control when the scan ends.
|
||||||
|
|
||||||
|
#### HNSW
|
||||||
|
|
||||||
|
Specify the max number of additional tuples visited
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SET hnsw.ef_stream = 10000;
|
||||||
|
```
|
||||||
|
|
||||||
|
The scan will also end if reaches `work_mem`. You can see when this happens by enabling debug messages.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SET client_min_messages = debug1;
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
DEBUG: hnsw index scan exceeded work_mem after 10000 tuples
|
||||||
|
HINT: Increase work_mem to scan more tuples.
|
||||||
|
```
|
||||||
|
|
||||||
|
If the server has enough memory, you can adjust this with:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SET work_mem = '8MB';
|
||||||
|
```
|
||||||
|
|
||||||
|
#### IVFFlat
|
||||||
|
|
||||||
|
Specify the max number of probes
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SET ivfflat.max_probes = 100;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Half-Precision Vectors
|
||||||
|
|
||||||
|
*Added in 0.7.0*
|
||||||
|
|
||||||
Use the `halfvec` type to store half-precision vectors
|
Use the `halfvec` type to store half-precision vectors
|
||||||
|
|
||||||
@@ -450,11 +532,11 @@ Use the `halfvec` type to store half-precision vectors
|
|||||||
CREATE TABLE items (id bigserial PRIMARY KEY, embedding halfvec(3));
|
CREATE TABLE items (id bigserial PRIMARY KEY, embedding halfvec(3));
|
||||||
```
|
```
|
||||||
|
|
||||||
## Half Indexing
|
## Half-Precision Indexing
|
||||||
|
|
||||||
*Unreleased*
|
*Added in 0.7.0*
|
||||||
|
|
||||||
Index vectors at half precision for smaller indexes and faster build times
|
Index vectors at half precision for smaller indexes
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE INDEX ON items USING hnsw ((embedding::halfvec(3)) halfvec_l2_ops);
|
CREATE INDEX ON items USING hnsw ((embedding::halfvec(3)) halfvec_l2_ops);
|
||||||
@@ -468,30 +550,30 @@ 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/hash_image_search.py))
|
Use the `bit` type to store binary vectors ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/imagehash/example.py))
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE items (id bigserial PRIMARY KEY, embedding bit(3));
|
CREATE TABLE items (id bigserial PRIMARY KEY, embedding bit(3));
|
||||||
INSERT INTO items (embedding) VALUES ('000'), ('111');
|
INSERT INTO items (embedding) VALUES ('000'), ('111');
|
||||||
```
|
```
|
||||||
|
|
||||||
Get the nearest neighbors by Hamming distance
|
Get the nearest neighbors by Hamming distance (added in 0.7.0)
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT * FROM items ORDER BY bit_count(embedding # '101') LIMIT 5;
|
|
||||||
```
|
|
||||||
|
|
||||||
Or (unreleased)
|
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
SELECT * FROM items ORDER BY embedding <~> '101' LIMIT 5;
|
SELECT * FROM items ORDER BY embedding <~> '101' LIMIT 5;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or (before 0.7.0)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM items ORDER BY bit_count(embedding # '101') LIMIT 5;
|
||||||
|
```
|
||||||
|
|
||||||
Also supports Jaccard distance (`<%>`)
|
Also supports Jaccard distance (`<%>`)
|
||||||
|
|
||||||
## Binary Quantization
|
## Binary Quantization
|
||||||
|
|
||||||
*Unreleased*
|
*Added in 0.7.0*
|
||||||
|
|
||||||
Use expression indexing for binary quantization
|
Use expression indexing for binary quantization
|
||||||
|
|
||||||
@@ -515,7 +597,7 @@ SELECT * FROM (
|
|||||||
|
|
||||||
## Sparse Vectors
|
## Sparse Vectors
|
||||||
|
|
||||||
*Unreleased*
|
*Added in 0.7.0*
|
||||||
|
|
||||||
Use the `sparsevec` type to store sparse vectors
|
Use the `sparsevec` type to store sparse vectors
|
||||||
|
|
||||||
@@ -546,11 +628,11 @@ 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.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/cross_encoder.py) to combine results.
|
||||||
|
|
||||||
## Indexing Subvectors
|
## Indexing Subvectors
|
||||||
|
|
||||||
*Unreleased*
|
*Added in 0.7.0*
|
||||||
|
|
||||||
Use expression indexing to index subvectors
|
Use expression indexing to index subvectors
|
||||||
|
|
||||||
@@ -592,7 +674,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/bulk_loading.py)).
|
Use `COPY` for bulk loading data ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/loading/example.py)).
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
COPY items (embedding) FROM STDIN WITH (FORMAT BINARY);
|
COPY items (embedding) FROM STDIN WITH (FORMAT BINARY);
|
||||||
@@ -682,7 +764,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.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/example.py)).
|
||||||
|
|
||||||
## Languages
|
## Languages
|
||||||
|
|
||||||
@@ -726,7 +808,7 @@ Yes, pgvector uses the write-ahead log (WAL), which allows for replication and p
|
|||||||
|
|
||||||
#### What if I want to index vectors with more than 2,000 dimensions?
|
#### What if I want to index vectors with more than 2,000 dimensions?
|
||||||
|
|
||||||
You’ll need to use [dimensionality reduction](https://en.wikipedia.org/wiki/Dimensionality_reduction) at the moment.
|
You can use [half-precision indexing](#half-precision-indexing) to index up to 4,000 dimensions or [binary quantization](#binary-quantization) to index up to 64,000 dimensions. Another option is [dimensionality reduction](https://en.wikipedia.org/wiki/Dimensionality_reduction).
|
||||||
|
|
||||||
#### Can I store vectors with different dimensions in the same column?
|
#### Can I store vectors with different dimensions in the same column?
|
||||||
|
|
||||||
@@ -789,7 +871,7 @@ SELECT pg_size_pretty(pg_relation_size('index_name'));
|
|||||||
|
|
||||||
#### Why isn’t a query using an index?
|
#### Why isn’t a query using an index?
|
||||||
|
|
||||||
The query needs to have an `ORDER BY` and `LIMIT`, and the `ORDER BY` must be the result of a distance operator, not an expression.
|
The query needs to have an `ORDER BY` and `LIMIT`, and the `ORDER BY` must be the result of a distance operator (not an expression) in ascending order.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- index
|
-- index
|
||||||
@@ -864,23 +946,23 @@ Operator | Description | Added
|
|||||||
\+ | element-wise addition |
|
\+ | element-wise addition |
|
||||||
\- | element-wise subtraction |
|
\- | element-wise subtraction |
|
||||||
\* | element-wise multiplication | 0.5.0
|
\* | element-wise multiplication | 0.5.0
|
||||||
\|\| | concatenate | unreleased
|
\|\| | concatenate | 0.7.0
|
||||||
<-> | Euclidean distance |
|
<-> | Euclidean distance |
|
||||||
<#> | negative inner product |
|
<#> | negative inner product |
|
||||||
<=> | cosine distance |
|
<=> | cosine distance |
|
||||||
<+> | taxicab distance | unreleased
|
<+> | taxicab distance | 0.7.0
|
||||||
|
|
||||||
### Vector Functions
|
### Vector Functions
|
||||||
|
|
||||||
Function | Description | Added
|
Function | Description | Added
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
binary_quantize(vector) → bit | binary quantize | unreleased
|
binary_quantize(vector) → bit | binary quantize | 0.7.0
|
||||||
cosine_distance(vector, vector) → double precision | cosine distance |
|
cosine_distance(vector, vector) → double precision | cosine distance |
|
||||||
inner_product(vector, vector) → double precision | inner product |
|
inner_product(vector, vector) → double precision | inner product |
|
||||||
l1_distance(vector, vector) → double precision | taxicab distance | 0.5.0
|
l1_distance(vector, vector) → double precision | taxicab distance | 0.5.0
|
||||||
l2_distance(vector, vector) → double precision | Euclidean distance |
|
l2_distance(vector, vector) → double precision | Euclidean distance |
|
||||||
l2_normalize(vector) → vector | Normalize with Euclidean norm | unreleased
|
l2_normalize(vector) → vector | Normalize with Euclidean norm | 0.7.0
|
||||||
subvector(vector, integer, integer) → vector | subvector | unreleased
|
subvector(vector, integer, integer) → vector | subvector | 0.7.0
|
||||||
vector_dims(vector) → integer | number of dimensions |
|
vector_dims(vector) → integer | number of dimensions |
|
||||||
vector_norm(vector) → double precision | Euclidean norm |
|
vector_norm(vector) → double precision | Euclidean norm |
|
||||||
|
|
||||||
@@ -899,35 +981,35 @@ Each half vector takes `2 * dimensions + 8` bytes of storage. Each element is a
|
|||||||
|
|
||||||
Operator | Description | Added
|
Operator | Description | Added
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
\+ | element-wise addition | unreleased
|
\+ | element-wise addition | 0.7.0
|
||||||
\- | element-wise subtraction | unreleased
|
\- | element-wise subtraction | 0.7.0
|
||||||
\* | element-wise multiplication | unreleased
|
\* | element-wise multiplication | 0.7.0
|
||||||
\|\| | concatenate | unreleased
|
\|\| | concatenate | 0.7.0
|
||||||
<-> | Euclidean distance | unreleased
|
<-> | Euclidean distance | 0.7.0
|
||||||
<#> | negative inner product | unreleased
|
<#> | negative inner product | 0.7.0
|
||||||
<=> | cosine distance | unreleased
|
<=> | cosine distance | 0.7.0
|
||||||
<+> | taxicab distance | unreleased
|
<+> | taxicab distance | 0.7.0
|
||||||
|
|
||||||
### Halfvec Functions
|
### Halfvec Functions
|
||||||
|
|
||||||
Function | Description | Added
|
Function | Description | Added
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
binary_quantize(halfvec) → bit | binary quantize | unreleased
|
binary_quantize(halfvec) → bit | binary quantize | 0.7.0
|
||||||
cosine_distance(halfvec, halfvec) → double precision | cosine distance | unreleased
|
cosine_distance(halfvec, halfvec) → double precision | cosine distance | 0.7.0
|
||||||
inner_product(halfvec, halfvec) → double precision | inner product | unreleased
|
inner_product(halfvec, halfvec) → double precision | inner product | 0.7.0
|
||||||
l1_distance(halfvec, halfvec) → double precision | taxicab distance | unreleased
|
l1_distance(halfvec, halfvec) → double precision | taxicab distance | 0.7.0
|
||||||
l2_distance(halfvec, halfvec) → double precision | Euclidean distance | unreleased
|
l2_distance(halfvec, halfvec) → double precision | Euclidean distance | 0.7.0
|
||||||
l2_norm(halfvec) → double precision | Euclidean norm | unreleased
|
l2_norm(halfvec) → double precision | Euclidean norm | 0.7.0
|
||||||
l2_normalize(halfvec) → halfvec | Normalize with Euclidean norm | unreleased
|
l2_normalize(halfvec) → halfvec | Normalize with Euclidean norm | 0.7.0
|
||||||
subvector(halfvec, integer, integer) → halfvec | subvector | unreleased
|
subvector(halfvec, integer, integer) → halfvec | subvector | 0.7.0
|
||||||
vector_dims(halfvec) → integer | number of dimensions | unreleased
|
vector_dims(halfvec) → integer | number of dimensions | 0.7.0
|
||||||
|
|
||||||
### Halfvec Aggregate Functions
|
### Halfvec Aggregate Functions
|
||||||
|
|
||||||
Function | Description | Added
|
Function | Description | Added
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
avg(halfvec) → halfvec | average | unreleased
|
avg(halfvec) → halfvec | average | 0.7.0
|
||||||
sum(halfvec) → halfvec | sum | unreleased
|
sum(halfvec) → halfvec | sum | 0.7.0
|
||||||
|
|
||||||
### Bit Type
|
### Bit Type
|
||||||
|
|
||||||
@@ -937,15 +1019,15 @@ Each bit vector takes `dimensions / 8 + 8` bytes of storage. See the [Postgres d
|
|||||||
|
|
||||||
Operator | Description | Added
|
Operator | Description | Added
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
<~> | Hamming distance | unreleased
|
<~> | Hamming distance | 0.7.0
|
||||||
<%> | Jaccard distance | unreleased
|
<%> | Jaccard distance | 0.7.0
|
||||||
|
|
||||||
### Bit Functions
|
### Bit Functions
|
||||||
|
|
||||||
Function | Description | Added
|
Function | Description | Added
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
hamming_distance(bit, bit) → double precision | Hamming distance | unreleased
|
hamming_distance(bit, bit) → double precision | Hamming distance | 0.7.0
|
||||||
jaccard_distance(bit, bit) → double precision | Jaccard distance | unreleased
|
jaccard_distance(bit, bit) → double precision | Jaccard distance | 0.7.0
|
||||||
|
|
||||||
### Sparsevec Type
|
### Sparsevec Type
|
||||||
|
|
||||||
@@ -955,21 +1037,21 @@ Each sparse vector takes `8 * non-zero elements + 16` bytes of storage. Each ele
|
|||||||
|
|
||||||
Operator | Description | Added
|
Operator | Description | Added
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
<-> | Euclidean distance | unreleased
|
<-> | Euclidean distance | 0.7.0
|
||||||
<#> | negative inner product | unreleased
|
<#> | negative inner product | 0.7.0
|
||||||
<=> | cosine distance | unreleased
|
<=> | cosine distance | 0.7.0
|
||||||
<+> | taxicab distance | unreleased
|
<+> | taxicab distance | 0.7.0
|
||||||
|
|
||||||
### Sparsevec Functions
|
### Sparsevec Functions
|
||||||
|
|
||||||
Function | Description | Added
|
Function | Description | Added
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
cosine_distance(sparsevec, sparsevec) → double precision | cosine distance | unreleased
|
cosine_distance(sparsevec, sparsevec) → double precision | cosine distance | 0.7.0
|
||||||
inner_product(sparsevec, sparsevec) → double precision | inner product | unreleased
|
inner_product(sparsevec, sparsevec) → double precision | inner product | 0.7.0
|
||||||
l1_distance(sparsevec, sparsevec) → double precision | taxicab distance | unreleased
|
l1_distance(sparsevec, sparsevec) → double precision | taxicab distance | 0.7.0
|
||||||
l2_distance(sparsevec, sparsevec) → double precision | Euclidean distance | unreleased
|
l2_distance(sparsevec, sparsevec) → double precision | Euclidean distance | 0.7.0
|
||||||
l2_norm(sparsevec) → double precision | Euclidean norm | unreleased
|
l2_norm(sparsevec) → double precision | Euclidean norm | 0.7.0
|
||||||
l2_normalize(sparsevec) → sparsevec | Normalize with Euclidean norm | unreleased
|
l2_normalize(sparsevec) → sparsevec | Normalize with Euclidean norm | 0.7.0
|
||||||
|
|
||||||
## Installation Notes - Linux and Mac
|
## Installation Notes - Linux and Mac
|
||||||
|
|
||||||
@@ -978,7 +1060,7 @@ l2_normalize(sparsevec) → sparsevec | Normalize with Euclidean norm | unreleas
|
|||||||
If your machine has multiple Postgres installations, specify the path to [pg_config](https://www.postgresql.org/docs/current/app-pgconfig.html) with:
|
If your machine has multiple Postgres installations, specify the path to [pg_config](https://www.postgresql.org/docs/current/app-pgconfig.html) with:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
export PG_CONFIG=/Library/PostgreSQL/16/bin/pg_config
|
export PG_CONFIG=/Library/PostgreSQL/17/bin/pg_config
|
||||||
```
|
```
|
||||||
|
|
||||||
Then re-run the installation instructions (run `make clean` before `make` if needed). If `sudo` is needed for `make install`, use:
|
Then re-run the installation instructions (run `make clean` before `make` if needed). If `sudo` is needed for `make install`, use:
|
||||||
@@ -989,11 +1071,11 @@ sudo --preserve-env=PG_CONFIG make install
|
|||||||
|
|
||||||
A few common paths on Mac are:
|
A few common paths on Mac are:
|
||||||
|
|
||||||
- EDB installer - `/Library/PostgreSQL/16/bin/pg_config`
|
- EDB installer - `/Library/PostgreSQL/17/bin/pg_config`
|
||||||
- Homebrew (arm64) - `/opt/homebrew/opt/postgresql@16/bin/pg_config`
|
- Homebrew (arm64) - `/opt/homebrew/opt/postgresql@17/bin/pg_config`
|
||||||
- Homebrew (x86-64) - `/usr/local/opt/postgresql@16/bin/pg_config`
|
- Homebrew (x86-64) - `/usr/local/opt/postgresql@17/bin/pg_config`
|
||||||
|
|
||||||
Note: Replace `16` with your Postgres server version
|
Note: Replace `17` with your Postgres server version
|
||||||
|
|
||||||
### Missing Header
|
### Missing Header
|
||||||
|
|
||||||
@@ -1002,10 +1084,10 @@ If compilation fails with `fatal error: postgres.h: No such file or directory`,
|
|||||||
For Ubuntu and Debian, use:
|
For Ubuntu and Debian, use:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo apt install postgresql-server-dev-16
|
sudo apt install postgresql-server-dev-17
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: Replace `16` with your Postgres server version
|
Note: Replace `17` with your Postgres server version
|
||||||
|
|
||||||
### Missing SDK
|
### Missing SDK
|
||||||
|
|
||||||
@@ -1038,17 +1120,17 @@ If installation fails with `Access is denied`, re-run the installation instructi
|
|||||||
Get the [Docker image](https://hub.docker.com/r/pgvector/pgvector) with:
|
Get the [Docker image](https://hub.docker.com/r/pgvector/pgvector) with:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker pull pgvector/pgvector:pg16
|
docker pull pgvector/pgvector:pg17
|
||||||
```
|
```
|
||||||
|
|
||||||
This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (replace `16` with your Postgres server version, and run it the same way).
|
This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (replace `17` with your Postgres server version, and run it the same way).
|
||||||
|
|
||||||
You can also build the image manually:
|
You can also build the image manually:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone --branch v0.6.2 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.7.4 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
docker build --build-arg PG_MAJOR=16 -t myuser/pgvector .
|
docker build --pull --build-arg PG_MAJOR=17 -t myuser/pgvector .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Homebrew
|
### Homebrew
|
||||||
@@ -1059,7 +1141,7 @@ With Homebrew Postgres, you can use:
|
|||||||
brew install pgvector
|
brew install pgvector
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: This only adds it to the `postgresql@14` formula
|
Note: This only adds it to the `postgresql@17` and `postgresql@14` formulas
|
||||||
|
|
||||||
### PGXN
|
### PGXN
|
||||||
|
|
||||||
@@ -1074,22 +1156,22 @@ pgxn install vector
|
|||||||
Debian and Ubuntu packages are available from the [PostgreSQL APT Repository](https://wiki.postgresql.org/wiki/Apt). Follow the [setup instructions](https://wiki.postgresql.org/wiki/Apt#Quickstart) and run:
|
Debian and Ubuntu packages are available from the [PostgreSQL APT Repository](https://wiki.postgresql.org/wiki/Apt). Follow the [setup instructions](https://wiki.postgresql.org/wiki/Apt#Quickstart) and run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo apt install postgresql-16-pgvector
|
sudo apt install postgresql-17-pgvector
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: Replace `16` with your Postgres server version
|
Note: Replace `17` with your Postgres server version
|
||||||
|
|
||||||
### Yum
|
### Yum
|
||||||
|
|
||||||
RPM packages are available from the [PostgreSQL Yum Repository](https://yum.postgresql.org/). Follow the [setup instructions](https://www.postgresql.org/download/linux/redhat/) for your distribution and run:
|
RPM packages are available from the [PostgreSQL Yum Repository](https://yum.postgresql.org/). Follow the [setup instructions](https://www.postgresql.org/download/linux/redhat/) for your distribution and run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo yum install pgvector_16
|
sudo yum install pgvector_17
|
||||||
# or
|
# or
|
||||||
sudo dnf install pgvector_16
|
sudo dnf install pgvector_17
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: Replace `16` with your Postgres server version
|
Note: Replace `17` with your Postgres server version
|
||||||
|
|
||||||
### pkg
|
### pkg
|
||||||
|
|
||||||
|
|||||||
@@ -22,52 +22,26 @@ CREATE OPERATOR || (
|
|||||||
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_concat
|
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_concat
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE FUNCTION ivfflat_halfvec_support(internal) RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||||
|
|
||||||
|
CREATE FUNCTION ivfflat_bit_support(internal) RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||||
|
|
||||||
|
CREATE FUNCTION hnsw_halfvec_support(internal) RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||||
|
|
||||||
|
CREATE FUNCTION hnsw_bit_support(internal) RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||||
|
|
||||||
|
CREATE FUNCTION hnsw_sparsevec_support(internal) RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||||
|
|
||||||
CREATE OPERATOR CLASS vector_l1_ops
|
CREATE OPERATOR CLASS vector_l1_ops
|
||||||
FOR TYPE vector USING hnsw AS
|
FOR TYPE vector USING hnsw AS
|
||||||
OPERATOR 1 <+> (vector, vector) FOR ORDER BY float_ops,
|
OPERATOR 1 <+> (vector, vector) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 l1_distance(vector, vector);
|
FUNCTION 1 l1_distance(vector, vector);
|
||||||
|
|
||||||
CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION jaccard_distance(bit, bit) RETURNS float8
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION bit_ivfflat_support(internal) RETURNS internal
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION bit_hnsw_support(internal) RETURNS internal
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE OPERATOR <~> (
|
|
||||||
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = hamming_distance,
|
|
||||||
COMMUTATOR = '<~>'
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE OPERATOR <%> (
|
|
||||||
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = jaccard_distance,
|
|
||||||
COMMUTATOR = '<%>'
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS bit_hamming_ops
|
|
||||||
FOR TYPE bit USING ivfflat AS
|
|
||||||
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
|
||||||
FUNCTION 1 hamming_distance(bit, bit),
|
|
||||||
FUNCTION 3 hamming_distance(bit, bit),
|
|
||||||
FUNCTION 6 bit_ivfflat_support(internal);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS bit_hamming_ops
|
|
||||||
FOR TYPE bit USING hnsw AS
|
|
||||||
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
|
||||||
FUNCTION 1 hamming_distance(bit, bit),
|
|
||||||
FUNCTION 4 bit_hnsw_support(internal);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS bit_jaccard_ops
|
|
||||||
FOR TYPE bit USING hnsw AS
|
|
||||||
OPERATOR 1 <%> (bit, bit) FOR ORDER BY float_ops,
|
|
||||||
FUNCTION 1 jaccard_distance(bit, bit),
|
|
||||||
FUNCTION 4 bit_hnsw_support(internal);
|
|
||||||
|
|
||||||
CREATE TYPE halfvec;
|
CREATE TYPE halfvec;
|
||||||
|
|
||||||
CREATE FUNCTION halfvec_in(cstring, oid, integer) RETURNS halfvec
|
CREATE FUNCTION halfvec_in(cstring, oid, integer) RETURNS halfvec
|
||||||
@@ -169,17 +143,14 @@ CREATE FUNCTION halfvec_accum(double precision[], halfvec) RETURNS double precis
|
|||||||
CREATE FUNCTION halfvec_avg(double precision[]) RETURNS halfvec
|
CREATE FUNCTION halfvec_avg(double precision[]) RETURNS halfvec
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
CREATE FUNCTION halfvec_ivfflat_support(internal) RETURNS internal
|
CREATE FUNCTION halfvec_combine(double precision[], double precision[]) RETURNS double precision[]
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME', 'vector_combine' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
CREATE FUNCTION halfvec_hnsw_support(internal) RETURNS internal
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE AGGREGATE avg(halfvec) (
|
CREATE AGGREGATE avg(halfvec) (
|
||||||
SFUNC = halfvec_accum,
|
SFUNC = halfvec_accum,
|
||||||
STYPE = double precision[],
|
STYPE = double precision[],
|
||||||
FINALFUNC = halfvec_avg,
|
FINALFUNC = halfvec_avg,
|
||||||
COMBINEFUNC = vector_combine,
|
COMBINEFUNC = halfvec_combine,
|
||||||
INITCOND = '{0}',
|
INITCOND = '{0}',
|
||||||
PARALLEL = SAFE
|
PARALLEL = SAFE
|
||||||
);
|
);
|
||||||
@@ -327,7 +298,7 @@ CREATE OPERATOR CLASS halfvec_l2_ops
|
|||||||
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
|
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
|
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
|
||||||
FUNCTION 3 l2_distance(halfvec, halfvec),
|
FUNCTION 3 l2_distance(halfvec, halfvec),
|
||||||
FUNCTION 6 halfvec_ivfflat_support(internal);
|
FUNCTION 5 ivfflat_halfvec_support(internal);
|
||||||
|
|
||||||
CREATE OPERATOR CLASS halfvec_ip_ops
|
CREATE OPERATOR CLASS halfvec_ip_ops
|
||||||
FOR TYPE halfvec USING ivfflat AS
|
FOR TYPE halfvec USING ivfflat AS
|
||||||
@@ -335,8 +306,7 @@ CREATE OPERATOR CLASS halfvec_ip_ops
|
|||||||
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
||||||
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
|
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
|
||||||
FUNCTION 4 l2_norm(halfvec),
|
FUNCTION 4 l2_norm(halfvec),
|
||||||
FUNCTION 5 l2_normalize(halfvec),
|
FUNCTION 5 ivfflat_halfvec_support(internal);
|
||||||
FUNCTION 6 halfvec_ivfflat_support(internal);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS halfvec_cosine_ops
|
CREATE OPERATOR CLASS halfvec_cosine_ops
|
||||||
FOR TYPE halfvec USING ivfflat AS
|
FOR TYPE halfvec USING ivfflat AS
|
||||||
@@ -345,34 +315,67 @@ CREATE OPERATOR CLASS halfvec_cosine_ops
|
|||||||
FUNCTION 2 l2_norm(halfvec),
|
FUNCTION 2 l2_norm(halfvec),
|
||||||
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
|
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
|
||||||
FUNCTION 4 l2_norm(halfvec),
|
FUNCTION 4 l2_norm(halfvec),
|
||||||
FUNCTION 5 l2_normalize(halfvec),
|
FUNCTION 5 ivfflat_halfvec_support(internal);
|
||||||
FUNCTION 6 halfvec_ivfflat_support(internal);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS halfvec_l2_ops
|
CREATE OPERATOR CLASS halfvec_l2_ops
|
||||||
FOR TYPE halfvec USING hnsw AS
|
FOR TYPE halfvec USING hnsw AS
|
||||||
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
|
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
|
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
|
||||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||||
|
|
||||||
CREATE OPERATOR CLASS halfvec_ip_ops
|
CREATE OPERATOR CLASS halfvec_ip_ops
|
||||||
FOR TYPE halfvec USING hnsw AS
|
FOR TYPE halfvec USING hnsw AS
|
||||||
OPERATOR 1 <#> (halfvec, halfvec) FOR ORDER BY float_ops,
|
OPERATOR 1 <#> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
||||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||||
|
|
||||||
CREATE OPERATOR CLASS halfvec_cosine_ops
|
CREATE OPERATOR CLASS halfvec_cosine_ops
|
||||||
FOR TYPE halfvec USING hnsw AS
|
FOR TYPE halfvec USING hnsw AS
|
||||||
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops,
|
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
||||||
FUNCTION 2 l2_norm(halfvec),
|
FUNCTION 2 l2_norm(halfvec),
|
||||||
FUNCTION 3 l2_normalize(halfvec),
|
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS halfvec_l1_ops
|
CREATE OPERATOR CLASS halfvec_l1_ops
|
||||||
FOR TYPE halfvec USING hnsw AS
|
FOR TYPE halfvec USING hnsw AS
|
||||||
OPERATOR 1 <+> (halfvec, halfvec) FOR ORDER BY float_ops,
|
OPERATOR 1 <+> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 l1_distance(halfvec, halfvec),
|
FUNCTION 1 l1_distance(halfvec, halfvec),
|
||||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||||
|
|
||||||
|
CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION jaccard_distance(bit, bit) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE OPERATOR <~> (
|
||||||
|
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = hamming_distance,
|
||||||
|
COMMUTATOR = '<~>'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR <%> (
|
||||||
|
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = jaccard_distance,
|
||||||
|
COMMUTATOR = '<%>'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS bit_hamming_ops
|
||||||
|
FOR TYPE bit USING ivfflat AS
|
||||||
|
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
||||||
|
FUNCTION 1 hamming_distance(bit, bit),
|
||||||
|
FUNCTION 3 hamming_distance(bit, bit),
|
||||||
|
FUNCTION 5 ivfflat_bit_support(internal);
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS bit_hamming_ops
|
||||||
|
FOR TYPE bit USING hnsw AS
|
||||||
|
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
||||||
|
FUNCTION 1 hamming_distance(bit, bit),
|
||||||
|
FUNCTION 3 hnsw_bit_support(internal);
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS bit_jaccard_ops
|
||||||
|
FOR TYPE bit USING hnsw AS
|
||||||
|
OPERATOR 1 <%> (bit, bit) FOR ORDER BY float_ops,
|
||||||
|
FUNCTION 1 jaccard_distance(bit, bit),
|
||||||
|
FUNCTION 3 hnsw_bit_support(internal);
|
||||||
|
|
||||||
CREATE TYPE sparsevec;
|
CREATE TYPE sparsevec;
|
||||||
|
|
||||||
@@ -460,9 +463,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 sparsevec_hnsw_support(internal) RETURNS internal
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE CAST (sparsevec AS sparsevec)
|
CREATE CAST (sparsevec AS sparsevec)
|
||||||
WITH FUNCTION sparsevec(sparsevec, integer, boolean) AS IMPLICIT;
|
WITH FUNCTION sparsevec(sparsevec, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
@@ -547,24 +547,23 @@ CREATE OPERATOR CLASS sparsevec_l2_ops
|
|||||||
FOR TYPE sparsevec USING hnsw AS
|
FOR TYPE sparsevec USING hnsw AS
|
||||||
OPERATOR 1 <-> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
OPERATOR 1 <-> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 sparsevec_l2_squared_distance(sparsevec, sparsevec),
|
FUNCTION 1 sparsevec_l2_squared_distance(sparsevec, sparsevec),
|
||||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||||
|
|
||||||
CREATE OPERATOR CLASS sparsevec_ip_ops
|
CREATE OPERATOR CLASS sparsevec_ip_ops
|
||||||
FOR TYPE sparsevec USING hnsw AS
|
FOR TYPE sparsevec USING hnsw AS
|
||||||
OPERATOR 1 <#> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
OPERATOR 1 <#> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
|
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
|
||||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||||
|
|
||||||
CREATE OPERATOR CLASS sparsevec_cosine_ops
|
CREATE OPERATOR CLASS sparsevec_cosine_ops
|
||||||
FOR TYPE sparsevec USING hnsw AS
|
FOR TYPE sparsevec USING hnsw AS
|
||||||
OPERATOR 1 <=> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
OPERATOR 1 <=> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
|
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
|
||||||
FUNCTION 2 l2_norm(sparsevec),
|
FUNCTION 2 l2_norm(sparsevec),
|
||||||
FUNCTION 3 l2_normalize(sparsevec),
|
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS sparsevec_l1_ops
|
CREATE OPERATOR CLASS sparsevec_l1_ops
|
||||||
FOR TYPE sparsevec USING hnsw AS
|
FOR TYPE sparsevec USING hnsw AS
|
||||||
OPERATOR 1 <+> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
OPERATOR 1 <+> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 l1_distance(sparsevec, sparsevec),
|
FUNCTION 1 l1_distance(sparsevec, sparsevec),
|
||||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||||
|
|||||||
2
sql/vector--0.7.0--0.7.1.sql
Normal file
2
sql/vector--0.7.0--0.7.1.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-- 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
|
||||||
2
sql/vector--0.7.1--0.7.2.sql
Normal file
2
sql/vector--0.7.1--0.7.2.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-- 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
|
||||||
2
sql/vector--0.7.2--0.7.3.sql
Normal file
2
sql/vector--0.7.2--0.7.3.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-- 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
|
||||||
2
sql/vector--0.7.3--0.7.4.sql
Normal file
2
sql/vector--0.7.3--0.7.4.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-- 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
|
||||||
26
sql/vector--0.7.4--0.8.0.sql
Normal file
26
sql/vector--0.7.4--0.8.0.sql
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
-- 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;
|
||||||
165
sql/vector.sql
165
sql/vector.sql
@@ -261,6 +261,23 @@ CREATE ACCESS METHOD hnsw TYPE INDEX HANDLER hnswhandler;
|
|||||||
|
|
||||||
COMMENT ON ACCESS METHOD hnsw IS 'hnsw index access method';
|
COMMENT ON ACCESS METHOD hnsw IS 'hnsw index access method';
|
||||||
|
|
||||||
|
-- access method private functions
|
||||||
|
|
||||||
|
CREATE FUNCTION ivfflat_halfvec_support(internal) RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||||
|
|
||||||
|
CREATE FUNCTION ivfflat_bit_support(internal) RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||||
|
|
||||||
|
CREATE FUNCTION hnsw_halfvec_support(internal) RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||||
|
|
||||||
|
CREATE FUNCTION hnsw_bit_support(internal) RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||||
|
|
||||||
|
CREATE FUNCTION hnsw_sparsevec_support(internal) RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C;
|
||||||
|
|
||||||
-- vector opclasses
|
-- vector opclasses
|
||||||
|
|
||||||
CREATE OPERATOR CLASS vector_ops
|
CREATE OPERATOR CLASS vector_ops
|
||||||
@@ -314,55 +331,6 @@ CREATE OPERATOR CLASS vector_l1_ops
|
|||||||
OPERATOR 1 <+> (vector, vector) FOR ORDER BY float_ops,
|
OPERATOR 1 <+> (vector, vector) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 l1_distance(vector, vector);
|
FUNCTION 1 l1_distance(vector, vector);
|
||||||
|
|
||||||
-- bit functions
|
|
||||||
|
|
||||||
CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION jaccard_distance(bit, bit) RETURNS float8
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
-- bit private functions
|
|
||||||
|
|
||||||
CREATE FUNCTION bit_ivfflat_support(internal) RETURNS internal
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
CREATE FUNCTION bit_hnsw_support(internal) RETURNS internal
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
-- bit operators
|
|
||||||
|
|
||||||
CREATE OPERATOR <~> (
|
|
||||||
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = hamming_distance,
|
|
||||||
COMMUTATOR = '<~>'
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE OPERATOR <%> (
|
|
||||||
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = jaccard_distance,
|
|
||||||
COMMUTATOR = '<%>'
|
|
||||||
);
|
|
||||||
|
|
||||||
-- bit opclasses
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS bit_hamming_ops
|
|
||||||
FOR TYPE bit USING ivfflat AS
|
|
||||||
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
|
||||||
FUNCTION 1 hamming_distance(bit, bit),
|
|
||||||
FUNCTION 3 hamming_distance(bit, bit),
|
|
||||||
FUNCTION 6 bit_ivfflat_support(internal);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS bit_hamming_ops
|
|
||||||
FOR TYPE bit USING hnsw AS
|
|
||||||
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
|
||||||
FUNCTION 1 hamming_distance(bit, bit),
|
|
||||||
FUNCTION 4 bit_hnsw_support(internal);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS bit_jaccard_ops
|
|
||||||
FOR TYPE bit USING hnsw AS
|
|
||||||
OPERATOR 1 <%> (bit, bit) FOR ORDER BY float_ops,
|
|
||||||
FUNCTION 1 jaccard_distance(bit, bit),
|
|
||||||
FUNCTION 4 bit_hnsw_support(internal);
|
|
||||||
|
|
||||||
-- halfvec type
|
-- halfvec type
|
||||||
|
|
||||||
CREATE TYPE halfvec;
|
CREATE TYPE halfvec;
|
||||||
@@ -470,11 +438,8 @@ CREATE FUNCTION halfvec_accum(double precision[], halfvec) RETURNS double precis
|
|||||||
CREATE FUNCTION halfvec_avg(double precision[]) RETURNS halfvec
|
CREATE FUNCTION halfvec_avg(double precision[]) RETURNS halfvec
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
CREATE FUNCTION halfvec_ivfflat_support(internal) RETURNS internal
|
CREATE FUNCTION halfvec_combine(double precision[], double precision[]) RETURNS double precision[]
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME', 'vector_combine' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
CREATE FUNCTION halfvec_hnsw_support(internal) RETURNS internal
|
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
|
||||||
|
|
||||||
-- halfvec aggregates
|
-- halfvec aggregates
|
||||||
|
|
||||||
@@ -482,7 +447,7 @@ CREATE AGGREGATE avg(halfvec) (
|
|||||||
SFUNC = halfvec_accum,
|
SFUNC = halfvec_accum,
|
||||||
STYPE = double precision[],
|
STYPE = double precision[],
|
||||||
FINALFUNC = halfvec_avg,
|
FINALFUNC = halfvec_avg,
|
||||||
COMBINEFUNC = vector_combine,
|
COMBINEFUNC = halfvec_combine,
|
||||||
INITCOND = '{0}',
|
INITCOND = '{0}',
|
||||||
PARALLEL = SAFE
|
PARALLEL = SAFE
|
||||||
);
|
);
|
||||||
@@ -638,7 +603,7 @@ CREATE OPERATOR CLASS halfvec_l2_ops
|
|||||||
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
|
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
|
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
|
||||||
FUNCTION 3 l2_distance(halfvec, halfvec),
|
FUNCTION 3 l2_distance(halfvec, halfvec),
|
||||||
FUNCTION 6 halfvec_ivfflat_support(internal);
|
FUNCTION 5 ivfflat_halfvec_support(internal);
|
||||||
|
|
||||||
CREATE OPERATOR CLASS halfvec_ip_ops
|
CREATE OPERATOR CLASS halfvec_ip_ops
|
||||||
FOR TYPE halfvec USING ivfflat AS
|
FOR TYPE halfvec USING ivfflat AS
|
||||||
@@ -646,8 +611,7 @@ CREATE OPERATOR CLASS halfvec_ip_ops
|
|||||||
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
||||||
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
|
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
|
||||||
FUNCTION 4 l2_norm(halfvec),
|
FUNCTION 4 l2_norm(halfvec),
|
||||||
FUNCTION 5 l2_normalize(halfvec),
|
FUNCTION 5 ivfflat_halfvec_support(internal);
|
||||||
FUNCTION 6 halfvec_ivfflat_support(internal);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS halfvec_cosine_ops
|
CREATE OPERATOR CLASS halfvec_cosine_ops
|
||||||
FOR TYPE halfvec USING ivfflat AS
|
FOR TYPE halfvec USING ivfflat AS
|
||||||
@@ -656,34 +620,73 @@ CREATE OPERATOR CLASS halfvec_cosine_ops
|
|||||||
FUNCTION 2 l2_norm(halfvec),
|
FUNCTION 2 l2_norm(halfvec),
|
||||||
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
|
FUNCTION 3 halfvec_spherical_distance(halfvec, halfvec),
|
||||||
FUNCTION 4 l2_norm(halfvec),
|
FUNCTION 4 l2_norm(halfvec),
|
||||||
FUNCTION 5 l2_normalize(halfvec),
|
FUNCTION 5 ivfflat_halfvec_support(internal);
|
||||||
FUNCTION 6 halfvec_ivfflat_support(internal);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS halfvec_l2_ops
|
CREATE OPERATOR CLASS halfvec_l2_ops
|
||||||
FOR TYPE halfvec USING hnsw AS
|
FOR TYPE halfvec USING hnsw AS
|
||||||
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
|
OPERATOR 1 <-> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
|
FUNCTION 1 halfvec_l2_squared_distance(halfvec, halfvec),
|
||||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||||
|
|
||||||
CREATE OPERATOR CLASS halfvec_ip_ops
|
CREATE OPERATOR CLASS halfvec_ip_ops
|
||||||
FOR TYPE halfvec USING hnsw AS
|
FOR TYPE halfvec USING hnsw AS
|
||||||
OPERATOR 1 <#> (halfvec, halfvec) FOR ORDER BY float_ops,
|
OPERATOR 1 <#> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
||||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||||
|
|
||||||
CREATE OPERATOR CLASS halfvec_cosine_ops
|
CREATE OPERATOR CLASS halfvec_cosine_ops
|
||||||
FOR TYPE halfvec USING hnsw AS
|
FOR TYPE halfvec USING hnsw AS
|
||||||
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops,
|
OPERATOR 1 <=> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
FUNCTION 1 halfvec_negative_inner_product(halfvec, halfvec),
|
||||||
FUNCTION 2 l2_norm(halfvec),
|
FUNCTION 2 l2_norm(halfvec),
|
||||||
FUNCTION 3 l2_normalize(halfvec),
|
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS halfvec_l1_ops
|
CREATE OPERATOR CLASS halfvec_l1_ops
|
||||||
FOR TYPE halfvec USING hnsw AS
|
FOR TYPE halfvec USING hnsw AS
|
||||||
OPERATOR 1 <+> (halfvec, halfvec) FOR ORDER BY float_ops,
|
OPERATOR 1 <+> (halfvec, halfvec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 l1_distance(halfvec, halfvec),
|
FUNCTION 1 l1_distance(halfvec, halfvec),
|
||||||
FUNCTION 4 halfvec_hnsw_support(internal);
|
FUNCTION 3 hnsw_halfvec_support(internal);
|
||||||
|
|
||||||
|
-- bit functions
|
||||||
|
|
||||||
|
CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION jaccard_distance(bit, bit) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
-- bit operators
|
||||||
|
|
||||||
|
CREATE OPERATOR <~> (
|
||||||
|
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = hamming_distance,
|
||||||
|
COMMUTATOR = '<~>'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR <%> (
|
||||||
|
LEFTARG = bit, RIGHTARG = bit, PROCEDURE = jaccard_distance,
|
||||||
|
COMMUTATOR = '<%>'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- bit opclasses
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS bit_hamming_ops
|
||||||
|
FOR TYPE bit USING ivfflat AS
|
||||||
|
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
||||||
|
FUNCTION 1 hamming_distance(bit, bit),
|
||||||
|
FUNCTION 3 hamming_distance(bit, bit),
|
||||||
|
FUNCTION 5 ivfflat_bit_support(internal);
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS bit_hamming_ops
|
||||||
|
FOR TYPE bit USING hnsw AS
|
||||||
|
OPERATOR 1 <~> (bit, bit) FOR ORDER BY float_ops,
|
||||||
|
FUNCTION 1 hamming_distance(bit, bit),
|
||||||
|
FUNCTION 3 hnsw_bit_support(internal);
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS bit_jaccard_ops
|
||||||
|
FOR TYPE bit USING hnsw AS
|
||||||
|
OPERATOR 1 <%> (bit, bit) FOR ORDER BY float_ops,
|
||||||
|
FUNCTION 1 jaccard_distance(bit, bit),
|
||||||
|
FUNCTION 3 hnsw_bit_support(internal);
|
||||||
|
|
||||||
--- sparsevec type
|
--- sparsevec type
|
||||||
|
|
||||||
@@ -779,7 +782,16 @@ 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 sparsevec_hnsw_support(internal) RETURNS internal
|
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;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
-- sparsevec casts
|
-- sparsevec casts
|
||||||
@@ -799,6 +811,18 @@ 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 <-> (
|
||||||
@@ -872,24 +896,23 @@ CREATE OPERATOR CLASS sparsevec_l2_ops
|
|||||||
FOR TYPE sparsevec USING hnsw AS
|
FOR TYPE sparsevec USING hnsw AS
|
||||||
OPERATOR 1 <-> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
OPERATOR 1 <-> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 sparsevec_l2_squared_distance(sparsevec, sparsevec),
|
FUNCTION 1 sparsevec_l2_squared_distance(sparsevec, sparsevec),
|
||||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||||
|
|
||||||
CREATE OPERATOR CLASS sparsevec_ip_ops
|
CREATE OPERATOR CLASS sparsevec_ip_ops
|
||||||
FOR TYPE sparsevec USING hnsw AS
|
FOR TYPE sparsevec USING hnsw AS
|
||||||
OPERATOR 1 <#> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
OPERATOR 1 <#> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
|
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
|
||||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||||
|
|
||||||
CREATE OPERATOR CLASS sparsevec_cosine_ops
|
CREATE OPERATOR CLASS sparsevec_cosine_ops
|
||||||
FOR TYPE sparsevec USING hnsw AS
|
FOR TYPE sparsevec USING hnsw AS
|
||||||
OPERATOR 1 <=> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
OPERATOR 1 <=> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
|
FUNCTION 1 sparsevec_negative_inner_product(sparsevec, sparsevec),
|
||||||
FUNCTION 2 l2_norm(sparsevec),
|
FUNCTION 2 l2_norm(sparsevec),
|
||||||
FUNCTION 3 l2_normalize(sparsevec),
|
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
|
||||||
|
|
||||||
CREATE OPERATOR CLASS sparsevec_l1_ops
|
CREATE OPERATOR CLASS sparsevec_l1_ops
|
||||||
FOR TYPE sparsevec USING hnsw AS
|
FOR TYPE sparsevec USING hnsw AS
|
||||||
OPERATOR 1 <+> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
OPERATOR 1 <+> (sparsevec, sparsevec) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 l1_distance(sparsevec, sparsevec),
|
FUNCTION 1 l1_distance(sparsevec, sparsevec),
|
||||||
FUNCTION 4 sparsevec_hnsw_support(internal);
|
FUNCTION 3 hnsw_sparsevec_support(internal);
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "bitutils.h"
|
#include "bitutils.h"
|
||||||
|
#include "halfvec.h" /* for USE_DISPATCH and USE_TARGET_CLONES */
|
||||||
#include "port/pg_bitutils.h"
|
#include "port/pg_bitutils.h"
|
||||||
|
|
||||||
|
#if defined(USE_DISPATCH)
|
||||||
|
#define BIT_DISPATCH
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef BIT_DISPATCH
|
#ifdef BIT_DISPATCH
|
||||||
#include <immintrin.h>
|
#include <immintrin.h>
|
||||||
|
|
||||||
#if defined(HAVE__GET_CPUID)
|
#if defined(USE__GET_CPUID)
|
||||||
#include <cpuid.h>
|
#include <cpuid.h>
|
||||||
#else
|
#else
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
@@ -168,7 +173,7 @@ SupportsAvx512Popcount()
|
|||||||
{
|
{
|
||||||
unsigned int exx[4] = {0, 0, 0, 0};
|
unsigned int exx[4] = {0, 0, 0, 0};
|
||||||
|
|
||||||
#if defined(HAVE__GET_CPUID)
|
#if defined(USE__GET_CPUID)
|
||||||
__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
|
__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
|
||||||
#else
|
#else
|
||||||
__cpuid(exx, 1);
|
__cpuid(exx, 1);
|
||||||
@@ -182,7 +187,7 @@ SupportsAvx512Popcount()
|
|||||||
if ((_xgetbv(0) & 0xe6) != 0xe6)
|
if ((_xgetbv(0) & 0xe6) != 0xe6)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#if defined(HAVE__GET_CPUID)
|
#if defined(USE__GET_CPUID)
|
||||||
__get_cpuid_count(7, 0, &exx[0], &exx[1], &exx[2], &exx[3]);
|
__get_cpuid_count(7, 0, &exx[0], &exx[1], &exx[2], &exx[3]);
|
||||||
#else
|
#else
|
||||||
__cpuidex(exx, 7, 0);
|
__cpuidex(exx, 7, 0);
|
||||||
|
|||||||
@@ -1,26 +1,11 @@
|
|||||||
#ifndef BITUTILS_H
|
#ifndef BITUTILS_H
|
||||||
#define BITUTILS_H
|
#define BITUTILS_H
|
||||||
|
|
||||||
/* We use two types of dispatching: intrinsics and target_clones */
|
#include "postgres.h"
|
||||||
/* TODO Move to better place */
|
|
||||||
#ifndef DISABLE_DISPATCH
|
|
||||||
/* Only enable for more recent compilers to keep build process simple */
|
|
||||||
#if defined(__x86_64__) && defined(__GNUC__) && __GNUC__ >= 8
|
|
||||||
#define USE_DISPATCH
|
|
||||||
#elif defined(__x86_64__) && defined(__clang_major__) && __clang_major__ >= 7
|
|
||||||
#define USE_DISPATCH
|
|
||||||
#elif defined(_M_AMD64) && defined(_MSC_VER) && _MSC_VER >= 1920
|
|
||||||
#define USE_DISPATCH
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* target_clones requires glibc */
|
/* Check version in first header */
|
||||||
#if defined(USE_DISPATCH) && defined(__gnu_linux__)
|
#if PG_VERSION_NUM < 130000
|
||||||
#define USE_TARGET_CLONES
|
#error "Requires PostgreSQL 13+"
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(USE_DISPATCH)
|
|
||||||
#define BIT_DISPATCH
|
|
||||||
#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,6 +3,7 @@
|
|||||||
#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"
|
||||||
@@ -40,7 +41,7 @@ CheckDims(VarBit *a, VarBit *b)
|
|||||||
/*
|
/*
|
||||||
* Get the Hamming distance between two bit vectors
|
* Get the Hamming distance between two bit vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(hamming_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hamming_distance);
|
||||||
Datum
|
Datum
|
||||||
hamming_distance(PG_FUNCTION_ARGS)
|
hamming_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -55,7 +56,7 @@ hamming_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the Jaccard distance between two bit vectors
|
* Get the Jaccard distance between two bit vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(jaccard_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(jaccard_distance);
|
||||||
Datum
|
Datum
|
||||||
jaccard_distance(PG_FUNCTION_ARGS)
|
jaccard_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef BITVECTOR_H
|
#ifndef BITVEC_H
|
||||||
#define BITVECTOR_H
|
#define BITVEC_H
|
||||||
|
|
||||||
#include "utils/varbit.h"
|
#include "utils/varbit.h"
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#ifdef HALFVEC_DISPATCH
|
#ifdef HALFVEC_DISPATCH
|
||||||
#include <immintrin.h>
|
#include <immintrin.h>
|
||||||
|
|
||||||
#if defined(HAVE__GET_CPUID)
|
#if defined(USE__GET_CPUID)
|
||||||
#include <cpuid.h>
|
#include <cpuid.h>
|
||||||
#else
|
#else
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
@@ -254,7 +254,7 @@ SupportsCpuFeature(unsigned int feature)
|
|||||||
{
|
{
|
||||||
unsigned int exx[4] = {0, 0, 0, 0};
|
unsigned int exx[4] = {0, 0, 0, 0};
|
||||||
|
|
||||||
#if defined(HAVE__GET_CPUID)
|
#if defined(USE__GET_CPUID)
|
||||||
__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
|
__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
|
||||||
#else
|
#else
|
||||||
__cpuid(exx, 1);
|
__cpuid(exx, 1);
|
||||||
|
|||||||
@@ -67,9 +67,6 @@ HalfToFloat4(half num)
|
|||||||
#elif defined(FLT16_SUPPORT)
|
#elif defined(FLT16_SUPPORT)
|
||||||
return (float) num;
|
return (float) num;
|
||||||
#else
|
#else
|
||||||
/* TODO Improve performance */
|
|
||||||
|
|
||||||
/* Assumes same endianness for floats and integers */
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
float f;
|
float f;
|
||||||
@@ -154,9 +151,6 @@ Float4ToHalfUnchecked(float num)
|
|||||||
#elif defined(FLT16_SUPPORT)
|
#elif defined(FLT16_SUPPORT)
|
||||||
return (_Float16) num;
|
return (_Float16) num;
|
||||||
#else
|
#else
|
||||||
/* TODO Improve performance */
|
|
||||||
|
|
||||||
/* Assumes same endianness for floats and integers */
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
float f;
|
float f;
|
||||||
|
|||||||
126
src/halfvec.c
126
src/halfvec.c
@@ -19,11 +19,6 @@
|
|||||||
#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))
|
||||||
|
|
||||||
@@ -164,28 +159,10 @@ CheckStateArray(ArrayType *statearray, const char *caller)
|
|||||||
return (float8 *) ARR_DATA_PTR(statearray);
|
return (float8 *) ARR_DATA_PTR(statearray);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 120003
|
|
||||||
static pg_noinline void
|
|
||||||
float_overflow_error(void)
|
|
||||||
{
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("value out of range: overflow")));
|
|
||||||
}
|
|
||||||
|
|
||||||
static pg_noinline void
|
|
||||||
float_underflow_error(void)
|
|
||||||
{
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("value out of range: underflow")));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert textual representation to internal representation
|
* Convert textual representation to internal representation
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_in);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_in);
|
||||||
Datum
|
Datum
|
||||||
halfvec_in(PG_FUNCTION_ARGS)
|
halfvec_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -299,7 +276,7 @@ halfvec_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to textual representation
|
* Convert internal representation to textual representation
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_out);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_out);
|
||||||
Datum
|
Datum
|
||||||
halfvec_out(PG_FUNCTION_ARGS)
|
halfvec_out(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -328,6 +305,10 @@ halfvec_out(PG_FUNCTION_ARGS)
|
|||||||
if (i > 0)
|
if (i > 0)
|
||||||
AppendChar(ptr, ',');
|
AppendChar(ptr, ',');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use shortest decimal representation of single-precision float for
|
||||||
|
* simplicity
|
||||||
|
*/
|
||||||
AppendFloat(ptr, HalfToFloat4(vector->x[i]));
|
AppendFloat(ptr, HalfToFloat4(vector->x[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,7 +322,7 @@ halfvec_out(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert type modifier
|
* Convert type modifier
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_typmod_in);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_typmod_in);
|
||||||
Datum
|
Datum
|
||||||
halfvec_typmod_in(PG_FUNCTION_ARGS)
|
halfvec_typmod_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -372,7 +353,7 @@ halfvec_typmod_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert external binary representation to internal representation
|
* Convert external binary representation to internal representation
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_recv);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_recv);
|
||||||
Datum
|
Datum
|
||||||
halfvec_recv(PG_FUNCTION_ARGS)
|
halfvec_recv(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -406,7 +387,7 @@ halfvec_recv(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to the external binary representation
|
* Convert internal representation to the external binary representation
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_send);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_send);
|
||||||
Datum
|
Datum
|
||||||
halfvec_send(PG_FUNCTION_ARGS)
|
halfvec_send(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -426,7 +407,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
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec);
|
||||||
Datum
|
Datum
|
||||||
halfvec(PG_FUNCTION_ARGS)
|
halfvec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -441,7 +422,7 @@ halfvec(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert array to half vector
|
* Convert array to half vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(array_to_halfvec);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(array_to_halfvec);
|
||||||
Datum
|
Datum
|
||||||
array_to_halfvec(PG_FUNCTION_ARGS)
|
array_to_halfvec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -515,7 +496,7 @@ array_to_halfvec(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert half vector to float4[]
|
* Convert half vector to float4[]
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_to_float4);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_to_float4);
|
||||||
Datum
|
Datum
|
||||||
halfvec_to_float4(PG_FUNCTION_ARGS)
|
halfvec_to_float4(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -539,7 +520,7 @@ halfvec_to_float4(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert vector to half vec
|
* Convert vector to half vec
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_to_halfvec);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_to_halfvec);
|
||||||
Datum
|
Datum
|
||||||
vector_to_halfvec(PG_FUNCTION_ARGS)
|
vector_to_halfvec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -561,7 +542,7 @@ vector_to_halfvec(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 distance between half vectors
|
* Get the L2 distance between half vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l2_distance);
|
||||||
Datum
|
Datum
|
||||||
halfvec_l2_distance(PG_FUNCTION_ARGS)
|
halfvec_l2_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -576,7 +557,7 @@ halfvec_l2_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 squared distance between half vectors
|
* Get the L2 squared distance between half vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_squared_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l2_squared_distance);
|
||||||
Datum
|
Datum
|
||||||
halfvec_l2_squared_distance(PG_FUNCTION_ARGS)
|
halfvec_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -591,7 +572,7 @@ halfvec_l2_squared_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the inner product of two half vectors
|
* Get the inner product of two half vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_inner_product);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_inner_product);
|
||||||
Datum
|
Datum
|
||||||
halfvec_inner_product(PG_FUNCTION_ARGS)
|
halfvec_inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -606,7 +587,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
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_negative_inner_product);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_negative_inner_product);
|
||||||
Datum
|
Datum
|
||||||
halfvec_negative_inner_product(PG_FUNCTION_ARGS)
|
halfvec_negative_inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -621,7 +602,7 @@ halfvec_negative_inner_product(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the cosine distance between two half vectors
|
* Get the cosine distance between two half vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_cosine_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_cosine_distance);
|
||||||
Datum
|
Datum
|
||||||
halfvec_cosine_distance(PG_FUNCTION_ARGS)
|
halfvec_cosine_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -653,7 +634,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)
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_spherical_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_spherical_distance);
|
||||||
Datum
|
Datum
|
||||||
halfvec_spherical_distance(PG_FUNCTION_ARGS)
|
halfvec_spherical_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -677,7 +658,7 @@ halfvec_spherical_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L1 distance between two half vectors
|
* Get the L1 distance between two half vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l1_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l1_distance);
|
||||||
Datum
|
Datum
|
||||||
halfvec_l1_distance(PG_FUNCTION_ARGS)
|
halfvec_l1_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -692,7 +673,7 @@ halfvec_l1_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the dimensions of a half vector
|
* Get the dimensions of a half vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_vector_dims);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_vector_dims);
|
||||||
Datum
|
Datum
|
||||||
halfvec_vector_dims(PG_FUNCTION_ARGS)
|
halfvec_vector_dims(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -704,7 +685,7 @@ halfvec_vector_dims(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 norm of a half vector
|
* Get the L2 norm of a half vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_norm);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l2_norm);
|
||||||
Datum
|
Datum
|
||||||
halfvec_l2_norm(PG_FUNCTION_ARGS)
|
halfvec_l2_norm(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -726,7 +707,7 @@ halfvec_l2_norm(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Normalize a half vector with the L2 norm
|
* Normalize a half vector with the L2 norm
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_l2_normalize);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_l2_normalize);
|
||||||
Datum
|
Datum
|
||||||
halfvec_l2_normalize(PG_FUNCTION_ARGS)
|
halfvec_l2_normalize(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -765,7 +746,7 @@ halfvec_l2_normalize(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Add half vectors
|
* Add half vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_add);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_add);
|
||||||
Datum
|
Datum
|
||||||
halfvec_add(PG_FUNCTION_ARGS)
|
halfvec_add(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -804,7 +785,7 @@ halfvec_add(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Subtract half vectors
|
* Subtract half vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_sub);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_sub);
|
||||||
Datum
|
Datum
|
||||||
halfvec_sub(PG_FUNCTION_ARGS)
|
halfvec_sub(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -843,7 +824,7 @@ halfvec_sub(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Multiply half vectors
|
* Multiply half vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_mul);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_mul);
|
||||||
Datum
|
Datum
|
||||||
halfvec_mul(PG_FUNCTION_ARGS)
|
halfvec_mul(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -885,7 +866,7 @@ halfvec_mul(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Concatenate half vectors
|
* Concatenate half vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_concat);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_concat);
|
||||||
Datum
|
Datum
|
||||||
halfvec_concat(PG_FUNCTION_ARGS)
|
halfvec_concat(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -909,7 +890,7 @@ halfvec_concat(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Quantize a half vector
|
* Quantize a half vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_binary_quantize);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_binary_quantize);
|
||||||
Datum
|
Datum
|
||||||
halfvec_binary_quantize(PG_FUNCTION_ARGS)
|
halfvec_binary_quantize(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -927,24 +908,39 @@ halfvec_binary_quantize(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get a subvector
|
* Get a subvector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_subvector);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_subvector);
|
||||||
Datum
|
Datum
|
||||||
halfvec_subvector(PG_FUNCTION_ARGS)
|
halfvec_subvector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
HalfVector *a = PG_GETARG_HALFVEC_P(0);
|
HalfVector *a = PG_GETARG_HALFVEC_P(0);
|
||||||
int32 start = PG_GETARG_INT32(1);
|
int32 start = PG_GETARG_INT32(1);
|
||||||
int32 count = PG_GETARG_INT32(2);
|
int32 count = PG_GETARG_INT32(2);
|
||||||
int32 end = start + count;
|
int32 end;
|
||||||
half *ax = a->x;
|
half *ax = a->x;
|
||||||
HalfVector *result;
|
HalfVector *result;
|
||||||
int dim;
|
int32 dim;
|
||||||
|
|
||||||
|
if (count < 1)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||||
|
errmsg("halfvec must have at least 1 dimension")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if (start + count > a->dim), avoiding integer overflow. a->dim
|
||||||
|
* and count are both positive, so a->dim - count won't overflow.
|
||||||
|
*/
|
||||||
|
if (start > a->dim - count)
|
||||||
|
end = a->dim + 1;
|
||||||
|
else
|
||||||
|
end = start + count;
|
||||||
|
|
||||||
/* Indexing starts at 1, like substring */
|
/* Indexing starts at 1, like substring */
|
||||||
if (start < 1)
|
if (start < 1)
|
||||||
start = 1;
|
start = 1;
|
||||||
|
else if (start > a->dim)
|
||||||
if (end > a->dim)
|
ereport(ERROR,
|
||||||
end = a->dim + 1;
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||||
|
errmsg("halfvec must have at least 1 dimension")));
|
||||||
|
|
||||||
dim = end - start;
|
dim = end - start;
|
||||||
CheckDim(dim);
|
CheckDim(dim);
|
||||||
@@ -959,7 +955,7 @@ halfvec_subvector(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Internal helper to compare half vectors
|
* Internal helper to compare half vectors
|
||||||
*/
|
*/
|
||||||
int
|
static int
|
||||||
halfvec_cmp_internal(HalfVector * a, HalfVector * b)
|
halfvec_cmp_internal(HalfVector * a, HalfVector * b)
|
||||||
{
|
{
|
||||||
int dim = Min(a->dim, b->dim);
|
int dim = Min(a->dim, b->dim);
|
||||||
@@ -986,7 +982,7 @@ halfvec_cmp_internal(HalfVector * a, HalfVector * b)
|
|||||||
/*
|
/*
|
||||||
* Less than
|
* Less than
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_lt);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_lt);
|
||||||
Datum
|
Datum
|
||||||
halfvec_lt(PG_FUNCTION_ARGS)
|
halfvec_lt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -999,7 +995,7 @@ halfvec_lt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Less than or equal
|
* Less than or equal
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_le);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_le);
|
||||||
Datum
|
Datum
|
||||||
halfvec_le(PG_FUNCTION_ARGS)
|
halfvec_le(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1012,7 +1008,7 @@ halfvec_le(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Equal
|
* Equal
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_eq);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_eq);
|
||||||
Datum
|
Datum
|
||||||
halfvec_eq(PG_FUNCTION_ARGS)
|
halfvec_eq(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1025,7 +1021,7 @@ halfvec_eq(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Not equal
|
* Not equal
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ne);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_ne);
|
||||||
Datum
|
Datum
|
||||||
halfvec_ne(PG_FUNCTION_ARGS)
|
halfvec_ne(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1038,7 +1034,7 @@ halfvec_ne(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than or equal
|
* Greater than or equal
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ge);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_ge);
|
||||||
Datum
|
Datum
|
||||||
halfvec_ge(PG_FUNCTION_ARGS)
|
halfvec_ge(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1051,7 +1047,7 @@ halfvec_ge(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than
|
* Greater than
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_gt);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_gt);
|
||||||
Datum
|
Datum
|
||||||
halfvec_gt(PG_FUNCTION_ARGS)
|
halfvec_gt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1064,7 +1060,7 @@ halfvec_gt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Compare half vectors
|
* Compare half vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_cmp);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_cmp);
|
||||||
Datum
|
Datum
|
||||||
halfvec_cmp(PG_FUNCTION_ARGS)
|
halfvec_cmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1077,7 +1073,7 @@ halfvec_cmp(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Accumulate half vectors
|
* Accumulate half vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_accum);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_accum);
|
||||||
Datum
|
Datum
|
||||||
halfvec_accum(PG_FUNCTION_ARGS)
|
halfvec_accum(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1138,7 +1134,7 @@ halfvec_accum(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Average half vectors
|
* Average half vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_avg);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_avg);
|
||||||
Datum
|
Datum
|
||||||
halfvec_avg(PG_FUNCTION_ARGS)
|
halfvec_avg(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1172,7 +1168,7 @@ halfvec_avg(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert sparse vector to half vector
|
* Convert sparse vector to half vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_to_halfvec);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_to_halfvec);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_to_halfvec(PG_FUNCTION_ARGS)
|
sparsevec_to_halfvec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1187,7 +1183,7 @@ sparsevec_to_halfvec(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
result = InitHalfVector(dim);
|
result = InitHalfVector(dim);
|
||||||
for (int i = 0; i < svec->nnz; i++)
|
for (int i = 0; i < svec->nnz; i++)
|
||||||
result->x[svec->indices[i] - 1] = Float4ToHalf(values[i]);
|
result->x[svec->indices[i]] = Float4ToHalf(values[i]);
|
||||||
|
|
||||||
PG_RETURN_POINTER(result);
|
PG_RETURN_POINTER(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,31 @@
|
|||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
|
||||||
#include "bitutils.h"
|
/* We use two types of dispatching: intrinsics and target_clones */
|
||||||
#include "fmgr.h"
|
/* TODO Move to better place */
|
||||||
#include "vector.h"
|
#ifndef DISABLE_DISPATCH
|
||||||
|
/* Only enable for more recent compilers to keep build process simple */
|
||||||
|
#if defined(__x86_64__) && defined(__GNUC__) && __GNUC__ >= 9
|
||||||
|
#define USE_DISPATCH
|
||||||
|
#elif defined(__x86_64__) && defined(__clang_major__) && __clang_major__ >= 7
|
||||||
|
#define USE_DISPATCH
|
||||||
|
#elif defined(_M_AMD64) && defined(_MSC_VER) && _MSC_VER >= 1920
|
||||||
|
#define USE_DISPATCH
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* target_clones requires glibc */
|
||||||
|
#if defined(USE_DISPATCH) && defined(__gnu_linux__) && defined(__has_attribute)
|
||||||
|
/* Use separate line for portability */
|
||||||
|
#if __has_attribute(target_clones)
|
||||||
|
#define USE_TARGET_CLONES
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Apple clang check needed for universal binaries on Mac */
|
||||||
|
#if defined(USE_DISPATCH) && (defined(HAVE__GET_CPUID) || defined(__apple_build_version__))
|
||||||
|
#define USE__GET_CPUID
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(USE_DISPATCH)
|
#if defined(USE_DISPATCH)
|
||||||
#define HALFVEC_DISPATCH
|
#define HALFVEC_DISPATCH
|
||||||
@@ -16,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)
|
#elif defined(__FLT16_MAX__) && !defined(HALFVEC_DISPATCH) && !defined(__FreeBSD__) && (!defined(__i386__) || defined(__SSE2__))
|
||||||
#define FLT16_SUPPORT
|
#define FLT16_SUPPORT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -28,7 +50,7 @@
|
|||||||
#define HALF_MAX 65504
|
#define HALF_MAX 65504
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define HALFVEC_MAX_DIM VECTOR_MAX_DIM
|
#define HALFVEC_MAX_DIM 16000
|
||||||
|
|
||||||
#define HALFVEC_SIZE(_dim) (offsetof(HalfVector, x) + sizeof(half)*(_dim))
|
#define HALFVEC_SIZE(_dim) (offsetof(HalfVector, x) + sizeof(half)*(_dim))
|
||||||
#define DatumGetHalfVector(x) ((HalfVector *) PG_DETOAST_DATUM(x))
|
#define DatumGetHalfVector(x) ((HalfVector *) PG_DETOAST_DATUM(x))
|
||||||
@@ -39,11 +61,10 @@ typedef struct HalfVector
|
|||||||
{
|
{
|
||||||
int32 vl_len_; /* varlena header (do not touch directly!) */
|
int32 vl_len_; /* varlena header (do not touch directly!) */
|
||||||
int16 dim; /* number of dimensions */
|
int16 dim; /* number of dimensions */
|
||||||
int16 unused;
|
int16 unused; /* reserved for future use, always zero */
|
||||||
half x[FLEXIBLE_ARRAY_MEMBER];
|
half x[FLEXIBLE_ARRAY_MEMBER];
|
||||||
} HalfVector;
|
} HalfVector;
|
||||||
|
|
||||||
HalfVector *InitHalfVector(int dim);
|
HalfVector *InitHalfVector(int dim);
|
||||||
int halfvec_cmp_internal(HalfVector * a, HalfVector * b);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
144
src/hnsw.c
144
src/hnsw.c
@@ -9,14 +9,27 @@
|
|||||||
#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"
|
||||||
|
#include "utils/spccache.h"
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 150000
|
#if PG_VERSION_NUM < 150000
|
||||||
#define MarkGUCPrefixReserved(x) EmitWarningsOnPlaceholders(x)
|
#define MarkGUCPrefixReserved(x) EmitWarningsOnPlaceholders(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const struct config_enum_entry hnsw_iterative_search_options[] = {
|
||||||
|
{"off", HNSW_ITERATIVE_SEARCH_OFF, false},
|
||||||
|
{"strict", HNSW_ITERATIVE_SEARCH_STRICT, false},
|
||||||
|
{"relaxed", HNSW_ITERATIVE_SEARCH_RELAXED, false},
|
||||||
|
/* TODO Change to strict before merging */
|
||||||
|
{"on", HNSW_ITERATIVE_SEARCH_RELAXED, false},
|
||||||
|
{NULL, 0, false}
|
||||||
|
};
|
||||||
|
|
||||||
int hnsw_ef_search;
|
int hnsw_ef_search;
|
||||||
|
int hnsw_max_iterative_tuples;
|
||||||
|
int hnsw_iterative_search;
|
||||||
int hnsw_lock_tranche_id;
|
int hnsw_lock_tranche_id;
|
||||||
static relopt_kind hnsw_relopt_kind;
|
static relopt_kind hnsw_relopt_kind;
|
||||||
|
|
||||||
@@ -59,22 +72,25 @@ 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
|
HNSW_DEFAULT_M, HNSW_MIN_M, HNSW_MAX_M, AccessExclusiveLock);
|
||||||
#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
|
HNSW_DEFAULT_EF_CONSTRUCTION, HNSW_MIN_EF_CONSTRUCTION, HNSW_MAX_EF_CONSTRUCTION, AccessExclusiveLock);
|
||||||
#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,
|
||||||
HNSW_DEFAULT_EF_SEARCH, HNSW_MIN_EF_SEARCH, HNSW_MAX_EF_SEARCH, PGC_USERSET, 0, NULL, NULL, NULL);
|
HNSW_DEFAULT_EF_SEARCH, HNSW_MIN_EF_SEARCH, HNSW_MAX_EF_SEARCH, PGC_USERSET, 0, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
/* TODO Change name */
|
||||||
|
DefineCustomEnumVariable("hnsw.streaming", "Iterative search mode",
|
||||||
|
NULL, &hnsw_iterative_search,
|
||||||
|
HNSW_ITERATIVE_SEARCH_OFF, hnsw_iterative_search_options, PGC_USERSET, 0, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
/* TODO Change name */
|
||||||
|
/* TODO Ensure ivfflat.max_probes uses same value for "all" */
|
||||||
|
DefineCustomIntVariable("hnsw.ef_stream", "Sets the max number of additional candidates to visit for streaming search",
|
||||||
|
"-1 means all", &hnsw_max_iterative_tuples,
|
||||||
|
HNSW_DEFAULT_EF_STREAM, HNSW_MIN_EF_STREAM, HNSW_MAX_EF_STREAM, PGC_USERSET, 0, NULL, NULL, NULL);
|
||||||
|
|
||||||
MarkGUCPrefixReserved("hnsw");
|
MarkGUCPrefixReserved("hnsw");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,14 +122,16 @@ hnswcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|||||||
{
|
{
|
||||||
GenericCosts costs;
|
GenericCosts costs;
|
||||||
int m;
|
int m;
|
||||||
int entryLevel;
|
double ratio;
|
||||||
|
double startupPages;
|
||||||
|
double spc_seq_page_cost;
|
||||||
Relation index;
|
Relation index;
|
||||||
|
|
||||||
/* Never use index without order */
|
/* Never use index without order */
|
||||||
if (path->indexorderbys == NULL)
|
if (path->indexorderbys == NULL)
|
||||||
{
|
{
|
||||||
*indexStartupCost = DBL_MAX;
|
*indexStartupCost = get_float8_infinity();
|
||||||
*indexTotalCost = DBL_MAX;
|
*indexTotalCost = get_float8_infinity();
|
||||||
*indexSelectivity = 0;
|
*indexSelectivity = 0;
|
||||||
*indexCorrelation = 0;
|
*indexCorrelation = 0;
|
||||||
*indexPages = 0;
|
*indexPages = 0;
|
||||||
@@ -122,21 +140,71 @@ hnswcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|||||||
|
|
||||||
MemSet(&costs, 0, sizeof(costs));
|
MemSet(&costs, 0, sizeof(costs));
|
||||||
|
|
||||||
|
genericcostestimate(root, path, loop_count, &costs);
|
||||||
|
|
||||||
index = index_open(path->indexinfo->indexoid, NoLock);
|
index = index_open(path->indexinfo->indexoid, NoLock);
|
||||||
HnswGetMetaPageInfo(index, &m, NULL);
|
HnswGetMetaPageInfo(index, &m, NULL);
|
||||||
index_close(index, NoLock);
|
index_close(index, NoLock);
|
||||||
|
|
||||||
/* Approximate entry level */
|
/*
|
||||||
entryLevel = (int) -log(1.0 / path->indexinfo->tuples) * HnswGetMl(m);
|
* HNSW cost estimation follows a formula that accounts for the total
|
||||||
|
* number of tuples indexed combined with the parameters that most
|
||||||
|
* influence the duration of the index scan, namely: m - the number of
|
||||||
|
* tuples that are scanned in each step of the HNSW graph traversal
|
||||||
|
* ef_search - which influences the total number of steps taken at layer 0
|
||||||
|
*
|
||||||
|
* The source of the vector data can impact how many steps it takes to
|
||||||
|
* converge on the set of vectors to return to the executor. Currently, we
|
||||||
|
* use a hardcoded scaling factor (HNSWScanScalingFactor) to help
|
||||||
|
* influence that, but this could later become a configurable parameter
|
||||||
|
* based on the cost estimations.
|
||||||
|
*
|
||||||
|
* The tuple estimator formula is below:
|
||||||
|
*
|
||||||
|
* numIndexTuples = entryLevel * m + layer0TuplesMax * layer0Selectivity
|
||||||
|
*
|
||||||
|
* "entryLevel * m" represents the floor of tuples we need to scan to get
|
||||||
|
* to layer 0 (L0).
|
||||||
|
*
|
||||||
|
* "layer0TuplesMax" is the estimated total number of tuples we'd scan at
|
||||||
|
* L0 if we weren't discarding already visited tuples as part of the scan.
|
||||||
|
*
|
||||||
|
* "layer0Selectivity" estimates the percentage of tuples that are scanned
|
||||||
|
* at L0, accounting for previously visited tuples, multiplied by the
|
||||||
|
* "scalingFactor" (currently hardcoded).
|
||||||
|
*/
|
||||||
|
if (path->indexinfo->tuples > 0)
|
||||||
|
{
|
||||||
|
double scalingFactor = 0.55;
|
||||||
|
int entryLevel = (int) (log(path->indexinfo->tuples) * HnswGetMl(m));
|
||||||
|
int layer0TuplesMax = HnswGetLayerM(m, 0) * hnsw_ef_search;
|
||||||
|
double layer0Selectivity = scalingFactor * log(path->indexinfo->tuples) / (log(m) * (1 + log(hnsw_ef_search)));
|
||||||
|
|
||||||
/* TODO Improve estimate of visited tuples (currently underestimates) */
|
ratio = (entryLevel * m + layer0TuplesMax * layer0Selectivity) / path->indexinfo->tuples;
|
||||||
/* Account for number of tuples (or entry level), m, and ef_search */
|
|
||||||
costs.numIndexTuples = (entryLevel + 2) * m;
|
|
||||||
|
|
||||||
genericcostestimate(root, path, loop_count, &costs);
|
if (ratio > 1)
|
||||||
|
ratio = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ratio = 1;
|
||||||
|
|
||||||
/* Use total cost since most work happens before first tuple is returned */
|
get_tablespace_page_costs(path->indexinfo->reltablespace, NULL, &spc_seq_page_cost);
|
||||||
*indexStartupCost = costs.indexTotalCost;
|
|
||||||
|
/* Startup cost is cost before returning the first row */
|
||||||
|
costs.indexStartupCost = costs.indexTotalCost * ratio;
|
||||||
|
|
||||||
|
/* Adjust cost if needed since TOAST not included in seq scan cost */
|
||||||
|
startupPages = costs.numIndexPages * ratio;
|
||||||
|
if (startupPages > path->indexinfo->rel->pages && ratio < 0.5)
|
||||||
|
{
|
||||||
|
/* Change all page cost from random to sequential */
|
||||||
|
costs.indexStartupCost -= startupPages * (costs.spc_random_page_cost - spc_seq_page_cost);
|
||||||
|
|
||||||
|
/* Remove cost of extra pages */
|
||||||
|
costs.indexStartupCost -= (startupPages - path->indexinfo->rel->pages) * spc_seq_page_cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
*indexStartupCost = costs.indexStartupCost;
|
||||||
*indexTotalCost = costs.indexTotalCost;
|
*indexTotalCost = costs.indexTotalCost;
|
||||||
*indexSelectivity = costs.indexSelectivity;
|
*indexSelectivity = costs.indexSelectivity;
|
||||||
*indexCorrelation = costs.indexCorrelation;
|
*indexCorrelation = costs.indexCorrelation;
|
||||||
@@ -154,23 +222,10 @@ 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -187,17 +242,15 @@ hnswvalidate(Oid opclassoid)
|
|||||||
*
|
*
|
||||||
* See https://www.postgresql.org/docs/current/index-api.html
|
* See https://www.postgresql.org/docs/current/index-api.html
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(hnswhandler);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(hnswhandler);
|
||||||
Datum
|
Datum
|
||||||
hnswhandler(PG_FUNCTION_ARGS)
|
hnswhandler(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
|
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
|
||||||
|
|
||||||
amroutine->amstrategies = 0;
|
amroutine->amstrategies = 0;
|
||||||
amroutine->amsupport = 4;
|
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 */
|
||||||
@@ -210,17 +263,24 @@ hnswhandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amclusterable = false;
|
amroutine->amclusterable = false;
|
||||||
amroutine->ampredlocks = false;
|
amroutine->ampredlocks = false;
|
||||||
amroutine->amcanparallel = false;
|
amroutine->amcanparallel = false;
|
||||||
amroutine->amcaninclude = false;
|
#if PG_VERSION_NUM >= 170000
|
||||||
#if PG_VERSION_NUM >= 130000
|
amroutine->amcanbuildparallel = true;
|
||||||
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
|
|
||||||
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
|
|
||||||
#endif
|
#endif
|
||||||
|
amroutine->amcaninclude = false;
|
||||||
|
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
|
||||||
|
#if PG_VERSION_NUM >= 160000
|
||||||
|
amroutine->amsummarizing = false;
|
||||||
|
#endif
|
||||||
|
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
|
||||||
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;
|
||||||
|
|||||||
137
src/hnsw.h
137
src/hnsw.h
@@ -12,18 +12,13 @@
|
|||||||
#include "utils/sampling.h"
|
#include "utils/sampling.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 120000
|
|
||||||
#error "Requires PostgreSQL 12+"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define HNSW_MAX_DIM 2000
|
#define HNSW_MAX_DIM 2000
|
||||||
#define HNSW_MAX_NNZ 1000
|
#define HNSW_MAX_NNZ 1000
|
||||||
|
|
||||||
/* Support functions */
|
/* Support functions */
|
||||||
#define HNSW_DISTANCE_PROC 1
|
#define HNSW_DISTANCE_PROC 1
|
||||||
#define HNSW_NORM_PROC 2
|
#define HNSW_NORM_PROC 2
|
||||||
#define HNSW_NORMALIZE_PROC 3
|
#define HNSW_TYPE_INFO_PROC 3
|
||||||
#define HNSW_TYPE_SUPPORT_PROC 4
|
|
||||||
|
|
||||||
#define HNSW_VERSION 1
|
#define HNSW_VERSION 1
|
||||||
#define HNSW_MAGIC_NUMBER 0xA953A953
|
#define HNSW_MAGIC_NUMBER 0xA953A953
|
||||||
@@ -47,6 +42,9 @@
|
|||||||
#define HNSW_DEFAULT_EF_SEARCH 40
|
#define HNSW_DEFAULT_EF_SEARCH 40
|
||||||
#define HNSW_MIN_EF_SEARCH 1
|
#define HNSW_MIN_EF_SEARCH 1
|
||||||
#define HNSW_MAX_EF_SEARCH 1000
|
#define HNSW_MAX_EF_SEARCH 1000
|
||||||
|
#define HNSW_DEFAULT_EF_STREAM -1
|
||||||
|
#define HNSW_MIN_EF_STREAM -1
|
||||||
|
#define HNSW_MAX_EF_STREAM INT_MAX
|
||||||
|
|
||||||
/* Tuple types */
|
/* Tuple types */
|
||||||
#define HNSW_ELEMENT_TUPLE_TYPE 1
|
#define HNSW_ELEMENT_TUPLE_TYPE 1
|
||||||
@@ -58,15 +56,6 @@
|
|||||||
#define HNSW_UPDATE_ENTRY_GREATER 1
|
#define HNSW_UPDATE_ENTRY_GREATER 1
|
||||||
#define HNSW_UPDATE_ENTRY_ALWAYS 2
|
#define HNSW_UPDATE_ENTRY_ALWAYS 2
|
||||||
|
|
||||||
typedef enum HnswType
|
|
||||||
{
|
|
||||||
HNSW_TYPE_VECTOR,
|
|
||||||
HNSW_TYPE_HALFVEC,
|
|
||||||
HNSW_TYPE_BIT,
|
|
||||||
HNSW_TYPE_SPARSEVEC,
|
|
||||||
HNSW_TYPE_UNSUPPORTED
|
|
||||||
} HnswType;
|
|
||||||
|
|
||||||
/* Build phases */
|
/* Build phases */
|
||||||
/* PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE is 1 */
|
/* PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE is 1 */
|
||||||
#define PROGRESS_HNSW_PHASE_LOAD 2
|
#define PROGRESS_HNSW_PHASE_LOAD 2
|
||||||
@@ -90,11 +79,6 @@ typedef enum HnswType
|
|||||||
#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)
|
||||||
|
|
||||||
@@ -107,6 +91,9 @@ typedef enum HnswType
|
|||||||
/* Ensure fits on page and in uint8 */
|
/* Ensure fits on page and in uint8 */
|
||||||
#define HnswGetMaxLevel(m) Min(((BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(HnswPageOpaqueData)) - offsetof(HnswNeighborTupleData, indextids) - sizeof(ItemIdData)) / (sizeof(ItemPointerData)) / (m)) - 2, 255)
|
#define HnswGetMaxLevel(m) Min(((BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(HnswPageOpaqueData)) - offsetof(HnswNeighborTupleData, indextids) - sizeof(ItemIdData)) / (sizeof(ItemPointerData)) / (m)) - 2, 255)
|
||||||
|
|
||||||
|
#define HnswGetSearchCandidate(membername, ptr) pairingheap_container(HnswSearchCandidate, membername, ptr)
|
||||||
|
#define HnswGetSearchCandidateConst(membername, ptr) pairingheap_const_container(HnswSearchCandidate, membername, ptr)
|
||||||
|
|
||||||
#define HnswGetValue(base, element) PointerGetDatum(HnswPtrAccess(base, (element)->value))
|
#define HnswGetValue(base, element) PointerGetDatum(HnswPtrAccess(base, (element)->value))
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 140005
|
#if PG_VERSION_NUM < 140005
|
||||||
@@ -125,8 +112,17 @@ typedef enum HnswType
|
|||||||
|
|
||||||
/* Variables */
|
/* Variables */
|
||||||
extern int hnsw_ef_search;
|
extern int hnsw_ef_search;
|
||||||
|
extern int hnsw_max_iterative_tuples;
|
||||||
|
extern int hnsw_iterative_search;
|
||||||
extern int hnsw_lock_tranche_id;
|
extern int hnsw_lock_tranche_id;
|
||||||
|
|
||||||
|
typedef enum HnswIterativeSearchType
|
||||||
|
{
|
||||||
|
HNSW_ITERATIVE_SEARCH_OFF,
|
||||||
|
HNSW_ITERATIVE_SEARCH_STRICT,
|
||||||
|
HNSW_ITERATIVE_SEARCH_RELAXED
|
||||||
|
} HnswIterativeSearchType;
|
||||||
|
|
||||||
typedef struct HnswElementData HnswElementData;
|
typedef struct HnswElementData HnswElementData;
|
||||||
typedef struct HnswNeighborArray HnswNeighborArray;
|
typedef struct HnswNeighborArray HnswNeighborArray;
|
||||||
|
|
||||||
@@ -148,6 +144,7 @@ struct HnswElementData
|
|||||||
uint8 heaptidsLength;
|
uint8 heaptidsLength;
|
||||||
uint8 level;
|
uint8 level;
|
||||||
uint8 deleted;
|
uint8 deleted;
|
||||||
|
uint8 version;
|
||||||
uint32 hash;
|
uint32 hash;
|
||||||
HnswNeighborsPtr neighbors;
|
HnswNeighborsPtr neighbors;
|
||||||
BlockNumber blkno;
|
BlockNumber blkno;
|
||||||
@@ -174,11 +171,13 @@ struct HnswNeighborArray
|
|||||||
HnswCandidate items[FLEXIBLE_ARRAY_MEMBER];
|
HnswCandidate items[FLEXIBLE_ARRAY_MEMBER];
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct HnswPairingHeapNode
|
typedef struct HnswSearchCandidate
|
||||||
{
|
{
|
||||||
pairingheap_node ph_node;
|
pairingheap_node c_node;
|
||||||
HnswCandidate *inner;
|
pairingheap_node w_node;
|
||||||
} HnswPairingHeapNode;
|
HnswElementPtr element;
|
||||||
|
double distance;
|
||||||
|
} HnswSearchCandidate;
|
||||||
|
|
||||||
/* HNSW index options */
|
/* HNSW index options */
|
||||||
typedef struct HnswOptions
|
typedef struct HnswOptions
|
||||||
@@ -202,8 +201,8 @@ typedef struct HnswGraph
|
|||||||
|
|
||||||
/* Allocations state */
|
/* Allocations state */
|
||||||
LWLock allocatorLock;
|
LWLock allocatorLock;
|
||||||
long memoryUsed;
|
Size memoryUsed;
|
||||||
long memoryTotal;
|
Size memoryTotal;
|
||||||
|
|
||||||
/* Flushed state */
|
/* Flushed state */
|
||||||
LWLock flushLock;
|
LWLock flushLock;
|
||||||
@@ -247,6 +246,25 @@ typedef struct HnswAllocator
|
|||||||
void *state;
|
void *state;
|
||||||
} HnswAllocator;
|
} HnswAllocator;
|
||||||
|
|
||||||
|
typedef struct HnswTypeInfo
|
||||||
|
{
|
||||||
|
int maxDimensions;
|
||||||
|
Datum (*normalize) (PG_FUNCTION_ARGS);
|
||||||
|
void (*checkValue) (Pointer v);
|
||||||
|
} HnswTypeInfo;
|
||||||
|
|
||||||
|
typedef struct HnswSupport
|
||||||
|
{
|
||||||
|
FmgrInfo *procinfo;
|
||||||
|
FmgrInfo *normprocinfo;
|
||||||
|
Oid collation;
|
||||||
|
} HnswSupport;
|
||||||
|
|
||||||
|
typedef struct HnswQuery
|
||||||
|
{
|
||||||
|
Datum value;
|
||||||
|
} HnswQuery;
|
||||||
|
|
||||||
typedef struct HnswBuildState
|
typedef struct HnswBuildState
|
||||||
{
|
{
|
||||||
/* Info */
|
/* Info */
|
||||||
@@ -254,7 +272,7 @@ typedef struct HnswBuildState
|
|||||||
Relation index;
|
Relation index;
|
||||||
IndexInfo *indexInfo;
|
IndexInfo *indexInfo;
|
||||||
ForkNumber forkNum;
|
ForkNumber forkNum;
|
||||||
HnswType type;
|
const HnswTypeInfo *typeInfo;
|
||||||
|
|
||||||
/* Settings */
|
/* Settings */
|
||||||
int dimensions;
|
int dimensions;
|
||||||
@@ -266,10 +284,7 @@ typedef struct HnswBuildState
|
|||||||
double reltuples;
|
double reltuples;
|
||||||
|
|
||||||
/* Support functions */
|
/* Support functions */
|
||||||
FmgrInfo *procinfo;
|
HnswSupport support;
|
||||||
FmgrInfo *normprocinfo;
|
|
||||||
FmgrInfo *normalizeprocinfo;
|
|
||||||
Oid collation;
|
|
||||||
|
|
||||||
/* Variables */
|
/* Variables */
|
||||||
HnswGraph graphData;
|
HnswGraph graphData;
|
||||||
@@ -317,10 +332,10 @@ typedef struct HnswElementTupleData
|
|||||||
uint8 type;
|
uint8 type;
|
||||||
uint8 level;
|
uint8 level;
|
||||||
uint8 deleted;
|
uint8 deleted;
|
||||||
uint8 unused;
|
uint8 version;
|
||||||
ItemPointerData heaptids[HNSW_HEAPTIDS];
|
ItemPointerData heaptids[HNSW_HEAPTIDS];
|
||||||
ItemPointerData neighbortid;
|
ItemPointerData neighbortid;
|
||||||
uint16 unused2;
|
uint16 unused;
|
||||||
Vector data;
|
Vector data;
|
||||||
} HnswElementTupleData;
|
} HnswElementTupleData;
|
||||||
|
|
||||||
@@ -329,24 +344,41 @@ typedef HnswElementTupleData * HnswElementTuple;
|
|||||||
typedef struct HnswNeighborTupleData
|
typedef struct HnswNeighborTupleData
|
||||||
{
|
{
|
||||||
uint8 type;
|
uint8 type;
|
||||||
uint8 unused;
|
uint8 version;
|
||||||
uint16 count;
|
uint16 count;
|
||||||
ItemPointerData indextids[FLEXIBLE_ARRAY_MEMBER];
|
ItemPointerData indextids[FLEXIBLE_ARRAY_MEMBER];
|
||||||
} HnswNeighborTupleData;
|
} HnswNeighborTupleData;
|
||||||
|
|
||||||
typedef HnswNeighborTupleData * HnswNeighborTuple;
|
typedef HnswNeighborTupleData * HnswNeighborTuple;
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
struct pointerhash_hash *pointers;
|
||||||
|
struct offsethash_hash *offsets;
|
||||||
|
struct tidhash_hash *tids;
|
||||||
|
} visited_hash;
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
HnswElement element;
|
||||||
|
ItemPointerData indextid;
|
||||||
|
} HnswUnvisited;
|
||||||
|
|
||||||
typedef struct HnswScanOpaqueData
|
typedef struct HnswScanOpaqueData
|
||||||
{
|
{
|
||||||
|
const HnswTypeInfo *typeInfo;
|
||||||
bool first;
|
bool first;
|
||||||
List *w;
|
List *w;
|
||||||
|
visited_hash v;
|
||||||
|
pairingheap *discarded;
|
||||||
|
HnswQuery q;
|
||||||
|
int m;
|
||||||
|
int64 tuples;
|
||||||
|
double previousDistance;
|
||||||
MemoryContext tmpCtx;
|
MemoryContext tmpCtx;
|
||||||
|
|
||||||
/* Support functions */
|
/* Support functions */
|
||||||
FmgrInfo *procinfo;
|
HnswSupport support;
|
||||||
FmgrInfo *normprocinfo;
|
|
||||||
FmgrInfo *normalizeprocinfo;
|
|
||||||
Oid collation;
|
|
||||||
} HnswScanOpaqueData;
|
} HnswScanOpaqueData;
|
||||||
|
|
||||||
typedef HnswScanOpaqueData * HnswScanOpaque;
|
typedef HnswScanOpaqueData * HnswScanOpaque;
|
||||||
@@ -364,8 +396,7 @@ typedef struct HnswVacuumState
|
|||||||
int efConstruction;
|
int efConstruction;
|
||||||
|
|
||||||
/* Support functions */
|
/* Support functions */
|
||||||
FmgrInfo *procinfo;
|
HnswSupport support;
|
||||||
Oid collation;
|
|
||||||
|
|
||||||
/* Variables */
|
/* Variables */
|
||||||
struct tidhash_hash *deleted;
|
struct tidhash_hash *deleted;
|
||||||
@@ -381,33 +412,35 @@ typedef struct HnswVacuumState
|
|||||||
int HnswGetM(Relation index);
|
int HnswGetM(Relation index);
|
||||||
int HnswGetEfConstruction(Relation index);
|
int HnswGetEfConstruction(Relation index);
|
||||||
FmgrInfo *HnswOptionalProcInfo(Relation index, uint16 procnum);
|
FmgrInfo *HnswOptionalProcInfo(Relation index, uint16 procnum);
|
||||||
HnswType HnswGetType(Relation index);
|
void HnswInitSupport(HnswSupport * support, Relation index);
|
||||||
Datum HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum value);
|
Datum HnswNormValue(const HnswTypeInfo * typeInfo, Oid collation, Datum value);
|
||||||
bool HnswCheckNorm(FmgrInfo *procinfo, Oid collation, Datum value);
|
bool HnswCheckNorm(HnswSupport * support, Datum value);
|
||||||
void HnswCheckValue(Datum value, HnswType type);
|
|
||||||
Buffer HnswNewBuffer(Relation index, ForkNumber forkNum);
|
Buffer HnswNewBuffer(Relation index, ForkNumber forkNum);
|
||||||
void HnswInitPage(Buffer buf, Page page);
|
void HnswInitPage(Buffer buf, Page page);
|
||||||
void HnswInit(void);
|
void HnswInit(void);
|
||||||
List *HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, FmgrInfo *procinfo, Oid collation, int m, bool inserting, HnswElement skipElement);
|
List *HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation index, HnswSupport * support, int m, bool inserting, HnswElement skipElement, visited_hash * v, pairingheap **discarded, bool initVisited, int64 *tuples);
|
||||||
HnswElement HnswGetEntryPoint(Relation index);
|
HnswElement HnswGetEntryPoint(Relation index);
|
||||||
void HnswGetMetaPageInfo(Relation index, int *m, HnswElement * entryPoint);
|
void HnswGetMetaPageInfo(Relation index, int *m, HnswElement * entryPoint);
|
||||||
void *HnswAlloc(HnswAllocator * allocator, Size size);
|
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, HnswSupport * support, int m, int efConstruction, bool existing);
|
||||||
HnswCandidate *HnswEntryCandidate(char *base, HnswElement em, Datum q, Relation rel, FmgrInfo *procinfo, Oid collation, bool loadVec);
|
HnswSearchCandidate *HnswEntryCandidate(char *base, HnswElement em, HnswQuery * q, Relation rel, HnswSupport * support, bool loadVec);
|
||||||
void HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum, bool building);
|
void HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum, bool building);
|
||||||
void HnswSetNeighborTuple(char *base, HnswNeighborTuple ntup, HnswElement e, int m);
|
void HnswSetNeighborTuple(char *base, HnswNeighborTuple ntup, HnswElement e, int m);
|
||||||
void HnswAddHeapTid(HnswElement element, ItemPointer heaptid);
|
void HnswAddHeapTid(HnswElement element, ItemPointer heaptid);
|
||||||
|
HnswNeighborArray *HnswInitNeighborArray(int lm, HnswAllocator * allocator);
|
||||||
void HnswInitNeighbors(char *base, HnswElement element, int m, HnswAllocator * alloc);
|
void HnswInitNeighbors(char *base, HnswElement element, int m, HnswAllocator * alloc);
|
||||||
bool HnswInsertTupleOnDisk(Relation index, Datum value, Datum *values, bool *isnull, ItemPointer heap_tid, bool building);
|
bool HnswInsertTupleOnDisk(Relation index, HnswSupport * support, Datum value, ItemPointer heaptid, bool building);
|
||||||
void HnswUpdateNeighborsOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting, bool building);
|
void HnswUpdateNeighborsOnDisk(Relation index, HnswSupport * support, HnswElement e, int m, bool checkExisting, bool building);
|
||||||
void HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHeaptids, bool loadVec);
|
void HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHeaptids, bool loadVec);
|
||||||
void HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec);
|
void HnswLoadElement(HnswElement element, double *distance, HnswQuery * q, Relation index, HnswSupport * support, bool loadVec, double *maxDistance);
|
||||||
|
bool HnswFormIndexValue(Datum *out, Datum *values, bool *isnull, const HnswTypeInfo * typeInfo, HnswSupport * support);
|
||||||
void HnswSetElementTuple(char *base, HnswElementTuple etup, HnswElement element);
|
void HnswSetElementTuple(char *base, HnswElementTuple etup, HnswElement element);
|
||||||
void HnswUpdateConnection(char *base, HnswElement element, HnswCandidate * hc, int lm, int lc, int *updateIdx, Relation index, FmgrInfo *procinfo, Oid collation);
|
void HnswUpdateConnection(char *base, HnswNeighborArray * neighbors, HnswElement newElement, float distance, int lm, int *updateIdx, Relation index, HnswSupport * support);
|
||||||
void HnswLoadNeighbors(HnswElement element, Relation index, int m);
|
bool HnswLoadNeighborTids(HnswElement element, ItemPointerData *indextids, Relation index, int m, int lm, int lc);
|
||||||
void HnswInitLockTranche(void);
|
void HnswInitLockTranche(void);
|
||||||
|
const HnswTypeInfo *HnswGetTypeInfo(Relation index);
|
||||||
PGDLLEXPORT void HnswParallelBuildMain(dsm_segment *seg, shm_toc *toc);
|
PGDLLEXPORT void HnswParallelBuildMain(dsm_segment *seg, shm_toc *toc);
|
||||||
|
|
||||||
/* Index access methods */
|
/* Index access methods */
|
||||||
|
|||||||
126
src/hnswbuild.c
126
src/hnswbuild.c
@@ -60,12 +60,6 @@
|
|||||||
#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"
|
||||||
@@ -75,10 +69,6 @@
|
|||||||
#define PARALLEL_KEY_HNSW_AREA UINT64CONST(0xA000000000000002)
|
#define PARALLEL_KEY_HNSW_AREA UINT64CONST(0xA000000000000002)
|
||||||
#define PARALLEL_KEY_QUERY_TEXT UINT64CONST(0xA000000000000003)
|
#define PARALLEL_KEY_QUERY_TEXT UINT64CONST(0xA000000000000003)
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 130000
|
|
||||||
#define GENERATIONCHUNK_RAWSIZE (SIZEOF_SIZE_T + SIZEOF_VOID_P * 2)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the metapage
|
* Create the metapage
|
||||||
*/
|
*/
|
||||||
@@ -192,7 +182,9 @@ CreateGraphPages(HnswBuildState * buildstate)
|
|||||||
|
|
||||||
/* Initial size check */
|
/* Initial size check */
|
||||||
if (etupSize > HNSW_TUPLE_ALLOC_SIZE)
|
if (etupSize > HNSW_TUPLE_ALLOC_SIZE)
|
||||||
elog(ERROR, "index tuple too large");
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
||||||
|
errmsg("index tuple too large")));
|
||||||
|
|
||||||
HnswSetElementTuple(base, etup, element);
|
HnswSetElementTuple(base, etup, element);
|
||||||
|
|
||||||
@@ -374,12 +366,18 @@ AddElementInMemory(char *base, HnswGraph * graph, HnswElement element)
|
|||||||
* Update neighbors
|
* Update neighbors
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
UpdateNeighborsInMemory(char *base, FmgrInfo *procinfo, Oid collation, HnswElement e, int m)
|
UpdateNeighborsInMemory(char *base, HnswSupport * support, HnswElement e, int m)
|
||||||
{
|
{
|
||||||
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);
|
||||||
HnswNeighborArray *neighbors = HnswGetNeighbors(base, e, lc);
|
Size neighborsSize = HNSW_NEIGHBOR_ARRAY_SIZE(lm);
|
||||||
|
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++)
|
||||||
{
|
{
|
||||||
@@ -389,9 +387,8 @@ 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, HnswGetNeighbors(base, neighborElement, lc), e, hc->distance, lm, NULL, NULL, support);
|
||||||
LWLockRelease(&neighborElement->lock);
|
LWLockRelease(&neighborElement->lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -401,7 +398,7 @@ UpdateNeighborsInMemory(char *base, FmgrInfo *procinfo, Oid collation, HnswEleme
|
|||||||
* Update graph in memory
|
* Update graph in memory
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
UpdateGraphInMemory(FmgrInfo *procinfo, Oid collation, HnswElement element, int m, int efConstruction, HnswElement entryPoint, HnswBuildState * buildstate)
|
UpdateGraphInMemory(HnswSupport * support, HnswElement element, int m, int efConstruction, HnswElement entryPoint, HnswBuildState * buildstate)
|
||||||
{
|
{
|
||||||
HnswGraph *graph = buildstate->graph;
|
HnswGraph *graph = buildstate->graph;
|
||||||
char *base = buildstate->hnswarea;
|
char *base = buildstate->hnswarea;
|
||||||
@@ -414,7 +411,7 @@ UpdateGraphInMemory(FmgrInfo *procinfo, Oid collation, HnswElement element, int
|
|||||||
AddElementInMemory(base, graph, element);
|
AddElementInMemory(base, graph, element);
|
||||||
|
|
||||||
/* Update neighbors */
|
/* Update neighbors */
|
||||||
UpdateNeighborsInMemory(base, procinfo, collation, element, m);
|
UpdateNeighborsInMemory(base, support, element, m);
|
||||||
|
|
||||||
/* Update entry point if needed (already have lock) */
|
/* Update entry point if needed (already have lock) */
|
||||||
if (entryPoint == NULL || element->level > entryPoint->level)
|
if (entryPoint == NULL || element->level > entryPoint->level)
|
||||||
@@ -427,9 +424,8 @@ UpdateGraphInMemory(FmgrInfo *procinfo, Oid collation, HnswElement element, int
|
|||||||
static void
|
static void
|
||||||
InsertTupleInMemory(HnswBuildState * buildstate, HnswElement element)
|
InsertTupleInMemory(HnswBuildState * buildstate, HnswElement element)
|
||||||
{
|
{
|
||||||
FmgrInfo *procinfo = buildstate->procinfo;
|
|
||||||
Oid collation = buildstate->collation;
|
|
||||||
HnswGraph *graph = buildstate->graph;
|
HnswGraph *graph = buildstate->graph;
|
||||||
|
HnswSupport *support = &buildstate->support;
|
||||||
HnswElement entryPoint;
|
HnswElement entryPoint;
|
||||||
LWLock *entryLock = &graph->entryLock;
|
LWLock *entryLock = &graph->entryLock;
|
||||||
LWLock *entryWaitLock = &graph->entryWaitLock;
|
LWLock *entryWaitLock = &graph->entryWaitLock;
|
||||||
@@ -461,10 +457,10 @@ InsertTupleInMemory(HnswBuildState * buildstate, HnswElement element)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Find neighbors for element */
|
/* Find neighbors for element */
|
||||||
HnswFindElementNeighbors(base, element, entryPoint, NULL, procinfo, collation, m, efConstruction, false);
|
HnswFindElementNeighbors(base, element, entryPoint, NULL, support, m, efConstruction, false);
|
||||||
|
|
||||||
/* Update graph in memory */
|
/* Update graph in memory */
|
||||||
UpdateGraphInMemory(procinfo, collation, element, m, efConstruction, entryPoint, buildstate);
|
UpdateGraphInMemory(support, element, m, efConstruction, entryPoint, buildstate);
|
||||||
|
|
||||||
/* Release entry lock */
|
/* Release entry lock */
|
||||||
LWLockRelease(entryLock);
|
LWLockRelease(entryLock);
|
||||||
@@ -479,25 +475,16 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
|
|||||||
HnswGraph *graph = buildstate->graph;
|
HnswGraph *graph = buildstate->graph;
|
||||||
HnswElement element;
|
HnswElement element;
|
||||||
HnswAllocator *allocator = &buildstate->allocator;
|
HnswAllocator *allocator = &buildstate->allocator;
|
||||||
|
HnswSupport *support = &buildstate->support;
|
||||||
Size valueSize;
|
Size valueSize;
|
||||||
Pointer valuePtr;
|
Pointer valuePtr;
|
||||||
LWLock *flushLock = &graph->flushLock;
|
LWLock *flushLock = &graph->flushLock;
|
||||||
char *base = buildstate->hnswarea;
|
char *base = buildstate->hnswarea;
|
||||||
|
Datum value;
|
||||||
|
|
||||||
/* Detoast once for all calls */
|
/* Form index value */
|
||||||
Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
|
if (!HnswFormIndexValue(&value, values, isnull, buildstate->typeInfo, support))
|
||||||
|
return false;
|
||||||
/* Check value */
|
|
||||||
HnswCheckValue(value, buildstate->type);
|
|
||||||
|
|
||||||
/* Normalize if needed */
|
|
||||||
if (buildstate->normprocinfo != NULL)
|
|
||||||
{
|
|
||||||
if (!HnswCheckNorm(buildstate->normprocinfo, buildstate->collation, value))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
value = HnswNormValue(buildstate->normalizeprocinfo, buildstate->collation, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get datum size */
|
/* Get datum size */
|
||||||
valueSize = VARSIZE_ANY(DatumGetPointer(value));
|
valueSize = VARSIZE_ANY(DatumGetPointer(value));
|
||||||
@@ -510,7 +497,7 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
|
|||||||
{
|
{
|
||||||
LWLockRelease(flushLock);
|
LWLockRelease(flushLock);
|
||||||
|
|
||||||
return HnswInsertTupleOnDisk(index, value, values, isnull, heaptid, true);
|
return HnswInsertTupleOnDisk(index, support, value, heaptid, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -542,7 +529,7 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
|
|||||||
|
|
||||||
LWLockRelease(flushLock);
|
LWLockRelease(flushLock);
|
||||||
|
|
||||||
return HnswInsertTupleOnDisk(index, value, values, isnull, heaptid, true);
|
return HnswInsertTupleOnDisk(index, support, value, heaptid, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ok, we can proceed to allocate the element */
|
/* Ok, we can proceed to allocate the element */
|
||||||
@@ -576,17 +563,13 @@ 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, CALLBACK_ITEM_POINTER, Datum *values,
|
BuildCallback(Relation index, ItemPointer tid, 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;
|
||||||
@@ -612,7 +595,7 @@ BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
|||||||
* Initialize the graph
|
* Initialize the graph
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
InitGraph(HnswGraph * graph, char *base, long memoryTotal)
|
InitGraph(HnswGraph * graph, char *base, Size memoryTotal)
|
||||||
{
|
{
|
||||||
/* Initialize the lock tranche if needed */
|
/* Initialize the lock tranche if needed */
|
||||||
HnswInitLockTranche();
|
HnswInitLockTranche();
|
||||||
@@ -649,11 +632,7 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -671,64 +650,51 @@ HnswSharedMemoryAlloc(Size size, void *state)
|
|||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Get max dimensions
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
GetMaxDimensions(HnswType type)
|
|
||||||
{
|
|
||||||
int maxDimensions = HNSW_MAX_DIM;
|
|
||||||
|
|
||||||
if (type == HNSW_TYPE_HALFVEC)
|
|
||||||
maxDimensions *= 2;
|
|
||||||
else if (type == HNSW_TYPE_BIT)
|
|
||||||
maxDimensions *= 32;
|
|
||||||
else if (type == HNSW_TYPE_SPARSEVEC)
|
|
||||||
maxDimensions = INT_MAX;
|
|
||||||
|
|
||||||
return maxDimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the build state
|
* Initialize the build state
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, IndexInfo *indexInfo, ForkNumber forkNum)
|
InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, IndexInfo *indexInfo, ForkNumber forkNum)
|
||||||
{
|
{
|
||||||
int maxDimensions;
|
|
||||||
|
|
||||||
buildstate->heap = heap;
|
buildstate->heap = heap;
|
||||||
buildstate->index = index;
|
buildstate->index = index;
|
||||||
buildstate->indexInfo = indexInfo;
|
buildstate->indexInfo = indexInfo;
|
||||||
buildstate->forkNum = forkNum;
|
buildstate->forkNum = forkNum;
|
||||||
buildstate->type = HnswGetType(index);
|
buildstate->typeInfo = HnswGetTypeInfo(index);
|
||||||
|
|
||||||
buildstate->m = HnswGetM(index);
|
buildstate->m = HnswGetM(index);
|
||||||
buildstate->efConstruction = HnswGetEfConstruction(index);
|
buildstate->efConstruction = HnswGetEfConstruction(index);
|
||||||
buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod;
|
buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod;
|
||||||
|
|
||||||
maxDimensions = GetMaxDimensions(buildstate->type);
|
/* Disallow varbit since require fixed dimensions */
|
||||||
|
if (TupleDescAttr(index->rd_att, 0)->atttypid == VARBITOID)
|
||||||
|
ereport(ERROR,
|
||||||
|
(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)
|
||||||
elog(ERROR, "column does not have dimensions");
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("column does not have dimensions")));
|
||||||
|
|
||||||
if (buildstate->dimensions > maxDimensions)
|
if (buildstate->dimensions > buildstate->typeInfo->maxDimensions)
|
||||||
elog(ERROR, "column cannot have more than %d dimensions for hnsw index", maxDimensions);
|
ereport(ERROR,
|
||||||
|
(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)
|
||||||
elog(ERROR, "ef_construction must be greater than or equal to 2 * m");
|
ereport(ERROR,
|
||||||
|
(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;
|
||||||
|
|
||||||
/* Get support functions */
|
/* Get support functions */
|
||||||
buildstate->procinfo = index_getprocinfo(index, 1, HNSW_DISTANCE_PROC);
|
HnswInitSupport(&buildstate->support, index);
|
||||||
buildstate->normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC);
|
|
||||||
buildstate->normalizeprocinfo = HnswOptionalProcInfo(index, HNSW_NORMALIZE_PROC);
|
|
||||||
buildstate->collation = index->rd_indcollation[0];
|
|
||||||
|
|
||||||
InitGraph(&buildstate->graphData, NULL, maintenance_work_mem * 1024L);
|
InitGraph(&buildstate->graphData, NULL, (Size) maintenance_work_mem * 1024L);
|
||||||
buildstate->graph = &buildstate->graphData;
|
buildstate->graph = &buildstate->graphData;
|
||||||
buildstate->ml = HnswGetMl(buildstate->m);
|
buildstate->ml = HnswGetMl(buildstate->m);
|
||||||
buildstate->maxLevel = HnswGetMaxLevel(buildstate->m);
|
buildstate->maxLevel = HnswGetMaxLevel(buildstate->m);
|
||||||
@@ -1138,8 +1104,8 @@ BuildIndex(Relation heap, Relation index, IndexInfo *indexInfo,
|
|||||||
|
|
||||||
BuildGraph(buildstate, forkNum);
|
BuildGraph(buildstate, forkNum);
|
||||||
|
|
||||||
if (RelationNeedsWAL(index))
|
if (RelationNeedsWAL(index) || forkNum == INIT_FORKNUM)
|
||||||
log_newpage_range(index, forkNum, 0, RelationGetNumberOfBlocks(index), true);
|
log_newpage_range(index, forkNum, 0, RelationGetNumberOfBlocksInFork(index, forkNum), true);
|
||||||
|
|
||||||
FreeBuildState(buildstate);
|
FreeBuildState(buildstate);
|
||||||
}
|
}
|
||||||
|
|||||||
343
src/hnswinsert.c
343
src/hnswinsert.c
@@ -36,14 +36,15 @@ 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 ntupSize, Buffer *nbuf, Page *npage, OffsetNumber *freeOffno, OffsetNumber *freeNeighborOffno, BlockNumber *newInsertPage)
|
HnswFreeOffset(Relation index, Buffer buf, Page page, HnswElement element, Size etupSize, Size ntupSize, Buffer *nbuf, Page *npage, OffsetNumber *freeOffno, OffsetNumber *freeNeighborOffno, BlockNumber *newInsertPage, uint8 *tupleVersion)
|
||||||
{
|
{
|
||||||
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))
|
||||||
{
|
{
|
||||||
HnswElementTuple etup = (HnswElementTuple) PageGetItem(page, PageGetItemId(page, offno));
|
ItemId eitemid = PageGetItemId(page, offno);
|
||||||
|
HnswElementTuple etup = (HnswElementTuple) PageGetItem(page, eitemid);
|
||||||
|
|
||||||
/* Skip neighbor tuples */
|
/* Skip neighbor tuples */
|
||||||
if (!HnswIsElementTuple(etup))
|
if (!HnswIsElementTuple(etup))
|
||||||
@@ -54,7 +55,9 @@ 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 itemid;
|
ItemId nitemid;
|
||||||
|
Size pageFree;
|
||||||
|
Size npageFree;
|
||||||
|
|
||||||
if (!BlockNumberIsValid(*newInsertPage))
|
if (!BlockNumberIsValid(*newInsertPage))
|
||||||
*newInsertPage = elementPage;
|
*newInsertPage = elementPage;
|
||||||
@@ -73,13 +76,29 @@ HnswFreeOffset(Relation index, Buffer buf, Page page, HnswElement element, Size
|
|||||||
*npage = BufferGetPage(*nbuf);
|
*npage = BufferGetPage(*nbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
itemid = PageGetItemId(*npage, neighborOffno);
|
nitemid = PageGetItemId(*npage, neighborOffno);
|
||||||
|
|
||||||
/* Check for space on neighbor tuple page */
|
/* Ensure aligned for space check */
|
||||||
if (PageGetFreeSpace(*npage) + ItemIdGetLength(itemid) - sizeof(ItemIdData) >= ntupSize)
|
Assert(etupSize == MAXALIGN(etupSize));
|
||||||
|
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;
|
||||||
|
*tupleVersion = etup->version;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (*nbuf != buf)
|
else if (*nbuf != buf)
|
||||||
@@ -135,6 +154,7 @@ AddElementOnDisk(Relation index, HnswElement e, int m, BlockNumber insertPage, B
|
|||||||
OffsetNumber freeOffno = InvalidOffsetNumber;
|
OffsetNumber freeOffno = InvalidOffsetNumber;
|
||||||
OffsetNumber freeNeighborOffno = InvalidOffsetNumber;
|
OffsetNumber freeNeighborOffno = InvalidOffsetNumber;
|
||||||
BlockNumber newInsertPage = InvalidBlockNumber;
|
BlockNumber newInsertPage = InvalidBlockNumber;
|
||||||
|
uint8 tupleVersion;
|
||||||
char *base = NULL;
|
char *base = NULL;
|
||||||
|
|
||||||
/* Calculate sizes */
|
/* Calculate sizes */
|
||||||
@@ -184,7 +204,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, ntupSize, &nbuf, &npage, &freeOffno, &freeNeighborOffno, &newInsertPage))
|
if (HnswFreeOffset(index, buf, page, e, etupSize, ntupSize, &nbuf, &npage, &freeOffno, &freeNeighborOffno, &newInsertPage, &tupleVersion))
|
||||||
{
|
{
|
||||||
if (nbuf != buf)
|
if (nbuf != buf)
|
||||||
{
|
{
|
||||||
@@ -194,6 +214,10 @@ AddElementOnDisk(Relation index, HnswElement e, int m, BlockNumber insertPage, B
|
|||||||
npage = GenericXLogRegisterBuffer(state, nbuf, 0);
|
npage = GenericXLogRegisterBuffer(state, nbuf, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set tuple version */
|
||||||
|
etup->version = tupleVersion;
|
||||||
|
ntup->version = tupleVersion;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,6 +340,107 @@ AddElementOnDisk(Relation index, HnswElement e, int m, BlockNumber insertPage, B
|
|||||||
*updatedInsertPage = newInsertPage;
|
*updatedInsertPage = newInsertPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load neighbors
|
||||||
|
*/
|
||||||
|
static HnswNeighborArray *
|
||||||
|
HnswLoadNeighbors(HnswElement element, Relation index, int m, int lm, int lc)
|
||||||
|
{
|
||||||
|
char *base = NULL;
|
||||||
|
HnswNeighborArray *neighbors = HnswInitNeighborArray(lm, NULL);
|
||||||
|
ItemPointerData indextids[HNSW_MAX_M * 2];
|
||||||
|
|
||||||
|
if (!HnswLoadNeighborTids(element, indextids, index, m, lm, lc))
|
||||||
|
return neighbors;
|
||||||
|
|
||||||
|
for (int i = 0; i < lm; i++)
|
||||||
|
{
|
||||||
|
ItemPointer indextid = &indextids[i];
|
||||||
|
HnswElement e;
|
||||||
|
HnswCandidate *hc;
|
||||||
|
|
||||||
|
if (!ItemPointerIsValid(indextid))
|
||||||
|
break;
|
||||||
|
|
||||||
|
e = HnswInitElementFromBlock(ItemPointerGetBlockNumber(indextid), ItemPointerGetOffsetNumber(indextid));
|
||||||
|
hc = &neighbors->items[neighbors->length++];
|
||||||
|
HnswPtrStore(base, hc->element, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return neighbors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load elements for insert
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
LoadElementsForInsert(HnswNeighborArray * neighbors, HnswQuery * q, int *idx, Relation index, HnswSupport * support)
|
||||||
|
{
|
||||||
|
char *base = NULL;
|
||||||
|
|
||||||
|
for (int i = 0; i < neighbors->length; i++)
|
||||||
|
{
|
||||||
|
HnswCandidate *hc = &neighbors->items[i];
|
||||||
|
HnswElement element = HnswPtrAccess(base, hc->element);
|
||||||
|
double distance;
|
||||||
|
|
||||||
|
HnswLoadElement(element, &distance, q, index, support, true, NULL);
|
||||||
|
hc->distance = distance;
|
||||||
|
|
||||||
|
/* Prune element if being deleted */
|
||||||
|
if (element->heaptidsLength == 0)
|
||||||
|
{
|
||||||
|
*idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get update index
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
GetUpdateIndex(HnswElement element, HnswElement newElement, float distance, int m, int lm, int lc, Relation index, HnswSupport * support, MemoryContext updateCtx)
|
||||||
|
{
|
||||||
|
char *base = NULL;
|
||||||
|
int idx = -1;
|
||||||
|
HnswNeighborArray *neighbors;
|
||||||
|
MemoryContext oldCtx = MemoryContextSwitchTo(updateCtx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get latest neighbors since they may have changed. Do not lock yet since
|
||||||
|
* selecting neighbors can take time. Could use optimistic locking to
|
||||||
|
* retry if another update occurs before getting exclusive lock.
|
||||||
|
*/
|
||||||
|
neighbors = HnswLoadNeighbors(element, index, m, lm, lc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Could improve performance for vacuuming by checking neighbors against
|
||||||
|
* list of elements being deleted to find index. It's important to exclude
|
||||||
|
* already deleted elements for this since they can be replaced at any
|
||||||
|
* time.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (neighbors->length < lm)
|
||||||
|
idx = -2;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HnswQuery q;
|
||||||
|
|
||||||
|
q.value = HnswGetValue(base, element);
|
||||||
|
|
||||||
|
LoadElementsForInsert(neighbors, &q, &idx, index, support);
|
||||||
|
|
||||||
|
if (idx == -1)
|
||||||
|
HnswUpdateConnection(base, neighbors, newElement, distance, lm, &idx, index, support);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldCtx);
|
||||||
|
MemoryContextReset(updateCtx);
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if connection already exists
|
* Check if connection already exists
|
||||||
*/
|
*/
|
||||||
@@ -336,14 +461,94 @@ ConnectionExists(HnswElement e, HnswNeighborTuple ntup, int startIdx, int lm)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update neighbor
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
UpdateNeighborOnDisk(HnswElement element, HnswElement newElement, int idx, int m, int lm, int lc, Relation index, bool checkExisting, bool building)
|
||||||
|
{
|
||||||
|
Buffer buf;
|
||||||
|
Page page;
|
||||||
|
GenericXLogState *state;
|
||||||
|
HnswNeighborTuple ntup;
|
||||||
|
int startIdx;
|
||||||
|
OffsetNumber offno = element->neighborOffno;
|
||||||
|
|
||||||
|
/* Register page */
|
||||||
|
buf = ReadBuffer(index, element->neighborPage);
|
||||||
|
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
||||||
|
if (building)
|
||||||
|
{
|
||||||
|
state = NULL;
|
||||||
|
page = BufferGetPage(buf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state = GenericXLogStart(index);
|
||||||
|
page = GenericXLogRegisterBuffer(state, buf, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get tuple */
|
||||||
|
ntup = (HnswNeighborTuple) PageGetItem(page, PageGetItemId(page, offno));
|
||||||
|
|
||||||
|
/* Calculate index for update */
|
||||||
|
startIdx = (element->level - lc) * m;
|
||||||
|
|
||||||
|
/* Check for existing connection */
|
||||||
|
if (checkExisting && ConnectionExists(newElement, ntup, startIdx, lm))
|
||||||
|
idx = -1;
|
||||||
|
else if (idx == -2)
|
||||||
|
{
|
||||||
|
/* Find free offset if still exists */
|
||||||
|
/* TODO Retry updating connections if not */
|
||||||
|
for (int j = 0; j < lm; j++)
|
||||||
|
{
|
||||||
|
if (!ItemPointerIsValid(&ntup->indextids[startIdx + j]))
|
||||||
|
{
|
||||||
|
idx = startIdx + j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
idx += startIdx;
|
||||||
|
|
||||||
|
/* Make robust to issues */
|
||||||
|
if (idx >= 0 && idx < ntup->count)
|
||||||
|
{
|
||||||
|
ItemPointer indextid = &ntup->indextids[idx];
|
||||||
|
|
||||||
|
/* Update neighbor on the buffer */
|
||||||
|
ItemPointerSet(indextid, newElement->blkno, newElement->offno);
|
||||||
|
|
||||||
|
/* Commit */
|
||||||
|
if (building)
|
||||||
|
MarkBufferDirty(buf);
|
||||||
|
else
|
||||||
|
GenericXLogFinish(state);
|
||||||
|
}
|
||||||
|
else if (!building)
|
||||||
|
GenericXLogAbort(state);
|
||||||
|
|
||||||
|
UnlockReleaseBuffer(buf);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update neighbors
|
* Update neighbors
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
HnswUpdateNeighborsOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting, bool building)
|
HnswUpdateNeighborsOnDisk(Relation index, HnswSupport * support, HnswElement e, int m, bool checkExisting, bool building)
|
||||||
{
|
{
|
||||||
char *base = NULL;
|
char *base = NULL;
|
||||||
|
|
||||||
|
/* Use separate memory context to improve performance for larger vectors */
|
||||||
|
MemoryContext updateCtx = GenerationContextCreate(CurrentMemoryContext,
|
||||||
|
"Hnsw insert update context",
|
||||||
|
#if PG_VERSION_NUM >= 150000
|
||||||
|
128 * 1024, 128 * 1024,
|
||||||
|
#endif
|
||||||
|
128 * 1024);
|
||||||
|
|
||||||
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);
|
||||||
@@ -352,92 +557,20 @@ HnswUpdateNeighborsOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, Hns
|
|||||||
for (int i = 0; i < neighbors->length; i++)
|
for (int i = 0; i < neighbors->length; i++)
|
||||||
{
|
{
|
||||||
HnswCandidate *hc = &neighbors->items[i];
|
HnswCandidate *hc = &neighbors->items[i];
|
||||||
Buffer buf;
|
|
||||||
Page page;
|
|
||||||
GenericXLogState *state;
|
|
||||||
HnswNeighborTuple ntup;
|
|
||||||
int idx = -1;
|
|
||||||
int startIdx;
|
|
||||||
HnswElement neighborElement = HnswPtrAccess(base, hc->element);
|
HnswElement neighborElement = HnswPtrAccess(base, hc->element);
|
||||||
OffsetNumber offno = neighborElement->neighborOffno;
|
int idx;
|
||||||
|
|
||||||
/* Get latest neighbors since they may have changed */
|
idx = GetUpdateIndex(neighborElement, e, hc->distance, m, lm, lc, index, support, updateCtx);
|
||||||
/* Do not lock yet since selecting neighbors can take time */
|
|
||||||
HnswLoadNeighbors(neighborElement, index, m);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Could improve performance for vacuuming by checking neighbors
|
|
||||||
* against list of elements being deleted to find index. It's
|
|
||||||
* important to exclude already deleted elements for this since
|
|
||||||
* they can be replaced at any time.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Select neighbors */
|
|
||||||
HnswUpdateConnection(NULL, e, hc, lm, lc, &idx, index, procinfo, collation);
|
|
||||||
|
|
||||||
/* New element was not selected as a neighbor */
|
/* New element was not selected as a neighbor */
|
||||||
if (idx == -1)
|
if (idx == -1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Register page */
|
UpdateNeighborOnDisk(neighborElement, e, idx, m, lm, lc, index, checkExisting, building);
|
||||||
buf = ReadBuffer(index, neighborElement->neighborPage);
|
|
||||||
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
|
||||||
if (building)
|
|
||||||
{
|
|
||||||
state = NULL;
|
|
||||||
page = BufferGetPage(buf);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state = GenericXLogStart(index);
|
|
||||||
page = GenericXLogRegisterBuffer(state, buf, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get tuple */
|
|
||||||
ntup = (HnswNeighborTuple) PageGetItem(page, PageGetItemId(page, offno));
|
|
||||||
|
|
||||||
/* Calculate index for update */
|
|
||||||
startIdx = (neighborElement->level - lc) * m;
|
|
||||||
|
|
||||||
/* Check for existing connection */
|
|
||||||
if (checkExisting && ConnectionExists(e, ntup, startIdx, lm))
|
|
||||||
idx = -1;
|
|
||||||
else if (idx == -2)
|
|
||||||
{
|
|
||||||
/* Find free offset if still exists */
|
|
||||||
/* TODO Retry updating connections if not */
|
|
||||||
for (int j = 0; j < lm; j++)
|
|
||||||
{
|
|
||||||
if (!ItemPointerIsValid(&ntup->indextids[startIdx + j]))
|
|
||||||
{
|
|
||||||
idx = startIdx + j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
idx += startIdx;
|
|
||||||
|
|
||||||
/* Make robust to issues */
|
|
||||||
if (idx >= 0 && idx < ntup->count)
|
|
||||||
{
|
|
||||||
ItemPointer indextid = &ntup->indextids[idx];
|
|
||||||
|
|
||||||
/* Update neighbor on the buffer */
|
|
||||||
ItemPointerSet(indextid, e->blkno, e->offno);
|
|
||||||
|
|
||||||
/* Commit */
|
|
||||||
if (building)
|
|
||||||
MarkBufferDirty(buf);
|
|
||||||
else
|
|
||||||
GenericXLogFinish(state);
|
|
||||||
}
|
|
||||||
else if (!building)
|
|
||||||
GenericXLogAbort(state);
|
|
||||||
|
|
||||||
UnlockReleaseBuffer(buf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemoryContextDelete(updateCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -527,7 +660,7 @@ FindDuplicateOnDisk(Relation index, HnswElement element, bool building)
|
|||||||
* Update graph on disk
|
* Update graph on disk
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
UpdateGraphOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement element, int m, int efConstruction, HnswElement entryPoint, bool building)
|
UpdateGraphOnDisk(Relation index, HnswSupport * support, HnswElement element, int m, int efConstruction, HnswElement entryPoint, bool building)
|
||||||
{
|
{
|
||||||
BlockNumber newInsertPage = InvalidBlockNumber;
|
BlockNumber newInsertPage = InvalidBlockNumber;
|
||||||
|
|
||||||
@@ -543,7 +676,7 @@ UpdateGraphOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement
|
|||||||
HnswUpdateMetaPage(index, 0, NULL, newInsertPage, MAIN_FORKNUM, building);
|
HnswUpdateMetaPage(index, 0, NULL, newInsertPage, MAIN_FORKNUM, building);
|
||||||
|
|
||||||
/* Update neighbors */
|
/* Update neighbors */
|
||||||
HnswUpdateNeighborsOnDisk(index, procinfo, collation, element, m, false, building);
|
HnswUpdateNeighborsOnDisk(index, support, element, m, false, building);
|
||||||
|
|
||||||
/* Update entry point if needed */
|
/* Update entry point if needed */
|
||||||
if (entryPoint == NULL || element->level > entryPoint->level)
|
if (entryPoint == NULL || element->level > entryPoint->level)
|
||||||
@@ -554,14 +687,12 @@ UpdateGraphOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement
|
|||||||
* Insert a tuple into the index
|
* Insert a tuple into the index
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
HnswInsertTupleOnDisk(Relation index, Datum value, Datum *values, bool *isnull, ItemPointer heap_tid, bool building)
|
HnswInsertTupleOnDisk(Relation index, HnswSupport * support, Datum value, ItemPointer heaptid, bool building)
|
||||||
{
|
{
|
||||||
HnswElement entryPoint;
|
HnswElement entryPoint;
|
||||||
HnswElement element;
|
HnswElement element;
|
||||||
int m;
|
int m;
|
||||||
int efConstruction = HnswGetEfConstruction(index);
|
int efConstruction = HnswGetEfConstruction(index);
|
||||||
FmgrInfo *procinfo = index_getprocinfo(index, 1, HNSW_DISTANCE_PROC);
|
|
||||||
Oid collation = index->rd_indcollation[0];
|
|
||||||
LOCKMODE lockmode = ShareLock;
|
LOCKMODE lockmode = ShareLock;
|
||||||
char *base = NULL;
|
char *base = NULL;
|
||||||
|
|
||||||
@@ -576,7 +707,7 @@ HnswInsertTupleOnDisk(Relation index, Datum value, Datum *values, bool *isnull,
|
|||||||
HnswGetMetaPageInfo(index, &m, &entryPoint);
|
HnswGetMetaPageInfo(index, &m, &entryPoint);
|
||||||
|
|
||||||
/* Create an element */
|
/* Create an element */
|
||||||
element = HnswInitElement(base, heap_tid, m, HnswGetMl(m), HnswGetMaxLevel(m), NULL);
|
element = HnswInitElement(base, heaptid, m, HnswGetMl(m), HnswGetMaxLevel(m), NULL);
|
||||||
HnswPtrStore(base, element->value, DatumGetPointer(value));
|
HnswPtrStore(base, element->value, DatumGetPointer(value));
|
||||||
|
|
||||||
/* Prevent concurrent inserts when likely updating entry point */
|
/* Prevent concurrent inserts when likely updating entry point */
|
||||||
@@ -594,10 +725,10 @@ HnswInsertTupleOnDisk(Relation index, Datum value, Datum *values, bool *isnull,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Find neighbors for element */
|
/* Find neighbors for element */
|
||||||
HnswFindElementNeighbors(base, element, entryPoint, index, procinfo, collation, m, efConstruction, false);
|
HnswFindElementNeighbors(base, element, entryPoint, index, support, m, efConstruction, false);
|
||||||
|
|
||||||
/* Update graph on disk */
|
/* Update graph on disk */
|
||||||
UpdateGraphOnDisk(index, procinfo, collation, element, m, efConstruction, entryPoint, building);
|
UpdateGraphOnDisk(index, support, element, m, efConstruction, entryPoint, building);
|
||||||
|
|
||||||
/* Release lock */
|
/* Release lock */
|
||||||
UnlockPage(index, HNSW_UPDATE_LOCK, lockmode);
|
UnlockPage(index, HNSW_UPDATE_LOCK, lockmode);
|
||||||
@@ -609,29 +740,19 @@ HnswInsertTupleOnDisk(Relation index, Datum value, Datum *values, bool *isnull,
|
|||||||
* Insert a tuple into the index
|
* Insert a tuple into the index
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid)
|
HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid)
|
||||||
{
|
{
|
||||||
Datum value;
|
Datum value;
|
||||||
FmgrInfo *normprocinfo;
|
const HnswTypeInfo *typeInfo = HnswGetTypeInfo(index);
|
||||||
Oid collation = index->rd_indcollation[0];
|
HnswSupport support;
|
||||||
|
|
||||||
/* Detoast once for all calls */
|
HnswInitSupport(&support, index);
|
||||||
value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
|
|
||||||
|
|
||||||
/* Check value */
|
/* Form index value */
|
||||||
HnswCheckValue(value, HnswGetType(index));
|
if (!HnswFormIndexValue(&value, values, isnull, typeInfo, &support))
|
||||||
|
return;
|
||||||
|
|
||||||
/* Normalize if needed */
|
HnswInsertTupleOnDisk(index, &support, value, heaptid, false);
|
||||||
normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC);
|
|
||||||
if (normprocinfo != NULL)
|
|
||||||
{
|
|
||||||
if (!HnswCheckNorm(normprocinfo, collation, value))
|
|
||||||
return;
|
|
||||||
|
|
||||||
value = HnswNormValue(HnswOptionalProcInfo(index, HNSW_NORMALIZE_PROC), collation, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
HnswInsertTupleOnDisk(index, value, values, isnull, heap_tid, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
157
src/hnswscan.c
157
src/hnswscan.c
@@ -5,39 +5,74 @@
|
|||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
#include "storage/lmgr.h"
|
#include "storage/lmgr.h"
|
||||||
|
#include "utils/float.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Algorithm 5 from paper
|
* Algorithm 5 from paper
|
||||||
*/
|
*/
|
||||||
static List *
|
static List *
|
||||||
GetScanItems(IndexScanDesc scan, Datum q)
|
GetScanItems(IndexScanDesc scan, Datum value)
|
||||||
{
|
{
|
||||||
HnswScanOpaque so = (HnswScanOpaque) scan->opaque;
|
HnswScanOpaque so = (HnswScanOpaque) scan->opaque;
|
||||||
Relation index = scan->indexRelation;
|
Relation index = scan->indexRelation;
|
||||||
FmgrInfo *procinfo = so->procinfo;
|
HnswSupport *support = &so->support;
|
||||||
Oid collation = so->collation;
|
|
||||||
List *ep;
|
List *ep;
|
||||||
List *w;
|
List *w;
|
||||||
int m;
|
int m;
|
||||||
HnswElement entryPoint;
|
HnswElement entryPoint;
|
||||||
char *base = NULL;
|
char *base = NULL;
|
||||||
|
HnswQuery *q = &so->q;
|
||||||
|
|
||||||
/* Get m and entry point */
|
/* Get m and entry point */
|
||||||
HnswGetMetaPageInfo(index, &m, &entryPoint);
|
HnswGetMetaPageInfo(index, &m, &entryPoint);
|
||||||
|
|
||||||
|
q->value = value;
|
||||||
|
so->m = m;
|
||||||
|
|
||||||
if (entryPoint == NULL)
|
if (entryPoint == NULL)
|
||||||
return NIL;
|
return NIL;
|
||||||
|
|
||||||
ep = list_make1(HnswEntryCandidate(base, entryPoint, q, index, procinfo, collation, false));
|
ep = list_make1(HnswEntryCandidate(base, entryPoint, q, index, support, false));
|
||||||
|
|
||||||
for (int lc = entryPoint->level; lc >= 1; lc--)
|
for (int lc = entryPoint->level; lc >= 1; lc--)
|
||||||
{
|
{
|
||||||
w = HnswSearchLayer(base, q, ep, 1, lc, index, procinfo, collation, m, false, NULL);
|
w = HnswSearchLayer(base, q, ep, 1, lc, index, support, m, false, NULL, NULL, NULL, true, NULL);
|
||||||
ep = w;
|
ep = w;
|
||||||
}
|
}
|
||||||
|
|
||||||
return HnswSearchLayer(base, q, ep, hnsw_ef_search, 0, index, procinfo, collation, m, false, NULL);
|
return HnswSearchLayer(base, q, ep, hnsw_ef_search, 0, index, support, m, false, NULL, &so->v, hnsw_iterative_search != HNSW_ITERATIVE_SEARCH_OFF ? &so->discarded : NULL, true, &so->tuples);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resume scan at ground level with discarded candidates
|
||||||
|
*/
|
||||||
|
static List *
|
||||||
|
ResumeScanItems(IndexScanDesc scan)
|
||||||
|
{
|
||||||
|
HnswScanOpaque so = (HnswScanOpaque) scan->opaque;
|
||||||
|
Relation index = scan->indexRelation;
|
||||||
|
List *ep = NIL;
|
||||||
|
char *base = NULL;
|
||||||
|
int batch_size = hnsw_ef_search;
|
||||||
|
|
||||||
|
if (pairingheap_is_empty(so->discarded))
|
||||||
|
return NIL;
|
||||||
|
|
||||||
|
/* Get next batch of candidates */
|
||||||
|
for (int i = 0; i < batch_size; i++)
|
||||||
|
{
|
||||||
|
HnswSearchCandidate *sc;
|
||||||
|
|
||||||
|
if (pairingheap_is_empty(so->discarded))
|
||||||
|
break;
|
||||||
|
|
||||||
|
sc = HnswGetSearchCandidate(w_node, pairingheap_remove_first(so->discarded));
|
||||||
|
|
||||||
|
ep = lappend(ep, sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return HnswSearchLayer(base, &so->q, ep, batch_size, 0, index, &so->support, so->m, false, NULL, &so->v, &so->discarded, false, &so->tuples);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -59,9 +94,9 @@ GetScanValue(IndexScanDesc scan)
|
|||||||
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
|
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
|
||||||
Assert(!VARATT_IS_EXTENDED(DatumGetPointer(value)));
|
Assert(!VARATT_IS_EXTENDED(DatumGetPointer(value)));
|
||||||
|
|
||||||
/* Fine if normalization fails */
|
/* Normalize if needed */
|
||||||
if (so->normprocinfo != NULL)
|
if (so->support.normprocinfo != NULL)
|
||||||
value = HnswNormValue(so->normalizeprocinfo, so->collation, value);
|
value = HnswNormValue(so->typeInfo, so->support.collation, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
@@ -79,16 +114,16 @@ hnswbeginscan(Relation index, int nkeys, int norderbys)
|
|||||||
scan = RelationGetIndexScan(index, nkeys, norderbys);
|
scan = RelationGetIndexScan(index, nkeys, norderbys);
|
||||||
|
|
||||||
so = (HnswScanOpaque) palloc(sizeof(HnswScanOpaqueData));
|
so = (HnswScanOpaque) palloc(sizeof(HnswScanOpaqueData));
|
||||||
|
so->typeInfo = HnswGetTypeInfo(index);
|
||||||
so->first = true;
|
so->first = true;
|
||||||
|
so->v.tids = NULL;
|
||||||
|
so->discarded = NULL;
|
||||||
so->tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
so->tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
"Hnsw scan temporary context",
|
"Hnsw scan temporary context",
|
||||||
ALLOCSET_DEFAULT_SIZES);
|
ALLOCSET_DEFAULT_SIZES);
|
||||||
|
|
||||||
/* Set support functions */
|
/* Set support functions */
|
||||||
so->procinfo = index_getprocinfo(index, 1, HNSW_DISTANCE_PROC);
|
HnswInitSupport(&so->support, index);
|
||||||
so->normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC);
|
|
||||||
so->normalizeprocinfo = HnswOptionalProcInfo(index, HNSW_NORMALIZE_PROC);
|
|
||||||
so->collation = index->rd_indcollation[0];
|
|
||||||
|
|
||||||
scan->opaque = so;
|
scan->opaque = so;
|
||||||
|
|
||||||
@@ -103,7 +138,15 @@ hnswrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int no
|
|||||||
{
|
{
|
||||||
HnswScanOpaque so = (HnswScanOpaque) scan->opaque;
|
HnswScanOpaque so = (HnswScanOpaque) scan->opaque;
|
||||||
|
|
||||||
|
if (so->v.tids != NULL)
|
||||||
|
tidhash_reset(so->v.tids);
|
||||||
|
|
||||||
|
if (so->discarded != NULL)
|
||||||
|
pairingheap_reset(so->discarded);
|
||||||
|
|
||||||
so->first = true;
|
so->first = true;
|
||||||
|
so->tuples = 0;
|
||||||
|
so->previousDistance = -get_float8_infinity();
|
||||||
MemoryContextReset(so->tmpCtx);
|
MemoryContextReset(so->tmpCtx);
|
||||||
|
|
||||||
if (keys && scan->numberOfKeys > 0)
|
if (keys && scan->numberOfKeys > 0)
|
||||||
@@ -160,27 +203,105 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir)
|
|||||||
|
|
||||||
so->first = false;
|
so->first = false;
|
||||||
|
|
||||||
#if defined(HNSW_MEMORY) && PG_VERSION_NUM >= 130000
|
#if defined(HNSW_MEMORY)
|
||||||
elog(INFO, "memory: %zu MB", MemoryContextMemAllocated(so->tmpCtx, false) / (1024 * 1024));
|
elog(INFO, "memory: %zu KB", MemoryContextMemAllocated(so->tmpCtx, false) / 1024);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
while (list_length(so->w) > 0)
|
for (;;)
|
||||||
{
|
{
|
||||||
char *base = NULL;
|
char *base = NULL;
|
||||||
HnswCandidate *hc = llast(so->w);
|
HnswSearchCandidate *sc;
|
||||||
HnswElement element = HnswPtrAccess(base, hc->element);
|
HnswElement element;
|
||||||
ItemPointer heaptid;
|
ItemPointer heaptid;
|
||||||
|
|
||||||
|
if (list_length(so->w) == 0)
|
||||||
|
{
|
||||||
|
if (hnsw_iterative_search == HNSW_ITERATIVE_SEARCH_OFF)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Empty index */
|
||||||
|
if (so->discarded == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Reached max number of additional tuples */
|
||||||
|
if (hnsw_max_iterative_tuples != -1 && so->tuples >= hnsw_ef_search + hnsw_max_iterative_tuples)
|
||||||
|
{
|
||||||
|
if (pairingheap_is_empty(so->discarded))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Return remaining tuples */
|
||||||
|
so->w = lappend(so->w, HnswGetSearchCandidate(w_node, pairingheap_remove_first(so->discarded)));
|
||||||
|
}
|
||||||
|
/* Prevent scans from consuming too much memory */
|
||||||
|
else if (MemoryContextMemAllocated(so->tmpCtx, false) > (Size) work_mem * 1024L)
|
||||||
|
{
|
||||||
|
if (pairingheap_is_empty(so->discarded))
|
||||||
|
{
|
||||||
|
ereport(DEBUG1,
|
||||||
|
(errmsg("hnsw index scan exceeded work_mem after " INT64_FORMAT " tuples", so->tuples),
|
||||||
|
errhint("Increase work_mem to scan more tuples.")));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return remaining tuples */
|
||||||
|
so->w = lappend(so->w, HnswGetSearchCandidate(w_node, pairingheap_remove_first(so->discarded)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Locking ensures when neighbors are read, the elements they
|
||||||
|
* reference will not be deleted (and replaced) during the
|
||||||
|
* iteration.
|
||||||
|
*
|
||||||
|
* Elements loaded into memory on previous iterations may have
|
||||||
|
* been deleted (and replaced), so when reading neighbors, the
|
||||||
|
* element version must be checked.
|
||||||
|
*/
|
||||||
|
LockPage(scan->indexRelation, HNSW_SCAN_LOCK, ShareLock);
|
||||||
|
|
||||||
|
so->w = ResumeScanItems(scan);
|
||||||
|
|
||||||
|
UnlockPage(scan->indexRelation, HNSW_SCAN_LOCK, ShareLock);
|
||||||
|
|
||||||
|
#if defined(HNSW_MEMORY)
|
||||||
|
elog(INFO, "memory: %zu KB", MemoryContextMemAllocated(so->tmpCtx, false) / 1024);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list_length(so->w) == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sc = llast(so->w);
|
||||||
|
element = HnswPtrAccess(base, sc->element);
|
||||||
|
|
||||||
/* Move to next element if no valid heap TIDs */
|
/* Move to next element if no valid heap TIDs */
|
||||||
if (element->heaptidsLength == 0)
|
if (element->heaptidsLength == 0)
|
||||||
{
|
{
|
||||||
so->w = list_delete_last(so->w);
|
so->w = list_delete_last(so->w);
|
||||||
|
|
||||||
|
/* Mark memory as free for next iteration */
|
||||||
|
if (hnsw_iterative_search != HNSW_ITERATIVE_SEARCH_OFF)
|
||||||
|
{
|
||||||
|
pfree(element);
|
||||||
|
pfree(sc);
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
heaptid = &element->heaptids[--element->heaptidsLength];
|
heaptid = &element->heaptids[--element->heaptidsLength];
|
||||||
|
|
||||||
|
if (hnsw_iterative_search == HNSW_ITERATIVE_SEARCH_STRICT)
|
||||||
|
{
|
||||||
|
if (sc->distance < so->previousDistance)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
so->previousDistance = sc->distance;
|
||||||
|
}
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldCtx);
|
MemoryContextSwitchTo(oldCtx);
|
||||||
|
|
||||||
scan->xs_heaptid = *heaptid;
|
scan->xs_heaptid = *heaptid;
|
||||||
|
|||||||
795
src/hnswutils.c
795
src/hnswutils.c
File diff suppressed because it is too large
Load Diff
@@ -184,13 +184,12 @@ static void
|
|||||||
RepairGraphElement(HnswVacuumState * vacuumstate, HnswElement element, HnswElement entryPoint)
|
RepairGraphElement(HnswVacuumState * vacuumstate, HnswElement element, HnswElement entryPoint)
|
||||||
{
|
{
|
||||||
Relation index = vacuumstate->index;
|
Relation index = vacuumstate->index;
|
||||||
|
HnswSupport *support = &vacuumstate->support;
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
Page page;
|
Page page;
|
||||||
GenericXLogState *state;
|
GenericXLogState *state;
|
||||||
int m = vacuumstate->m;
|
int m = vacuumstate->m;
|
||||||
int efConstruction = vacuumstate->efConstruction;
|
int efConstruction = vacuumstate->efConstruction;
|
||||||
FmgrInfo *procinfo = vacuumstate->procinfo;
|
|
||||||
Oid collation = vacuumstate->collation;
|
|
||||||
BufferAccessStrategy bas = vacuumstate->bas;
|
BufferAccessStrategy bas = vacuumstate->bas;
|
||||||
HnswNeighborTuple ntup = vacuumstate->ntup;
|
HnswNeighborTuple ntup = vacuumstate->ntup;
|
||||||
Size ntupSize = HNSW_NEIGHBOR_TUPLE_SIZE(element->level, m);
|
Size ntupSize = HNSW_NEIGHBOR_TUPLE_SIZE(element->level, m);
|
||||||
@@ -205,7 +204,7 @@ RepairGraphElement(HnswVacuumState * vacuumstate, HnswElement element, HnswEleme
|
|||||||
element->heaptidsLength = 0;
|
element->heaptidsLength = 0;
|
||||||
|
|
||||||
/* Find neighbors for element, skipping itself */
|
/* Find neighbors for element, skipping itself */
|
||||||
HnswFindElementNeighbors(base, element, entryPoint, index, procinfo, collation, m, efConstruction, true);
|
HnswFindElementNeighbors(base, element, entryPoint, index, support, m, efConstruction, true);
|
||||||
|
|
||||||
/* Zero memory for each element */
|
/* Zero memory for each element */
|
||||||
MemSet(ntup, 0, HNSW_TUPLE_ALLOC_SIZE);
|
MemSet(ntup, 0, HNSW_TUPLE_ALLOC_SIZE);
|
||||||
@@ -229,7 +228,7 @@ RepairGraphElement(HnswVacuumState * vacuumstate, HnswElement element, HnswEleme
|
|||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
|
|
||||||
/* Update neighbors */
|
/* Update neighbors */
|
||||||
HnswUpdateNeighborsOnDisk(index, procinfo, collation, element, m, true, false);
|
HnswUpdateNeighborsOnDisk(index, support, element, m, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -239,6 +238,7 @@ static void
|
|||||||
RepairGraphEntryPoint(HnswVacuumState * vacuumstate)
|
RepairGraphEntryPoint(HnswVacuumState * vacuumstate)
|
||||||
{
|
{
|
||||||
Relation index = vacuumstate->index;
|
Relation index = vacuumstate->index;
|
||||||
|
HnswSupport *support = &vacuumstate->support;
|
||||||
HnswElement highestPoint = &vacuumstate->highestPoint;
|
HnswElement highestPoint = &vacuumstate->highestPoint;
|
||||||
HnswElement entryPoint;
|
HnswElement entryPoint;
|
||||||
MemoryContext oldCtx = MemoryContextSwitchTo(vacuumstate->tmpCtx);
|
MemoryContext oldCtx = MemoryContextSwitchTo(vacuumstate->tmpCtx);
|
||||||
@@ -256,7 +256,7 @@ RepairGraphEntryPoint(HnswVacuumState * vacuumstate)
|
|||||||
LockPage(index, HNSW_UPDATE_LOCK, ShareLock);
|
LockPage(index, HNSW_UPDATE_LOCK, ShareLock);
|
||||||
|
|
||||||
/* Load element */
|
/* Load element */
|
||||||
HnswLoadElement(highestPoint, NULL, NULL, index, vacuumstate->procinfo, vacuumstate->collation, true);
|
HnswLoadElement(highestPoint, NULL, NULL, index, support, true, NULL);
|
||||||
|
|
||||||
/* Repair if needed */
|
/* Repair if needed */
|
||||||
if (NeedsUpdated(vacuumstate, highestPoint))
|
if (NeedsUpdated(vacuumstate, highestPoint))
|
||||||
@@ -294,7 +294,7 @@ RepairGraphEntryPoint(HnswVacuumState * vacuumstate)
|
|||||||
* is outdated, this can remove connections at higher levels in
|
* is outdated, this can remove connections at higher levels in
|
||||||
* the graph until they are repaired, but this should be fine.
|
* the graph until they are repaired, but this should be fine.
|
||||||
*/
|
*/
|
||||||
HnswLoadElement(entryPoint, NULL, NULL, index, vacuumstate->procinfo, vacuumstate->collation, true);
|
HnswLoadElement(entryPoint, NULL, NULL, index, support, true, NULL);
|
||||||
|
|
||||||
if (NeedsUpdated(vacuumstate, entryPoint))
|
if (NeedsUpdated(vacuumstate, entryPoint))
|
||||||
{
|
{
|
||||||
@@ -527,6 +527,14 @@ MarkDeleted(HnswVacuumState * vacuumstate)
|
|||||||
for (int i = 0; i < ntup->count; i++)
|
for (int i = 0; i < ntup->count; i++)
|
||||||
ItemPointerSetInvalid(&ntup->indextids[i]);
|
ItemPointerSetInvalid(&ntup->indextids[i]);
|
||||||
|
|
||||||
|
/* Increment version */
|
||||||
|
/* This is used to avoid incorrect reads for iterative scans */
|
||||||
|
/* Reserve some bits for future use */
|
||||||
|
etup->version++;
|
||||||
|
if (etup->version > 15)
|
||||||
|
etup->version = 1;
|
||||||
|
ntup->version = etup->version;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We modified the tuples in place, no need to call
|
* We modified the tuples in place, no need to call
|
||||||
* PageIndexTupleOverwrite
|
* PageIndexTupleOverwrite
|
||||||
@@ -573,13 +581,13 @@ InitVacuumState(HnswVacuumState * vacuumstate, IndexVacuumInfo *info, IndexBulkD
|
|||||||
vacuumstate->callback_state = callback_state;
|
vacuumstate->callback_state = callback_state;
|
||||||
vacuumstate->efConstruction = HnswGetEfConstruction(index);
|
vacuumstate->efConstruction = HnswGetEfConstruction(index);
|
||||||
vacuumstate->bas = GetAccessStrategy(BAS_BULKREAD);
|
vacuumstate->bas = GetAccessStrategy(BAS_BULKREAD);
|
||||||
vacuumstate->procinfo = index_getprocinfo(index, 1, HNSW_DISTANCE_PROC);
|
|
||||||
vacuumstate->collation = index->rd_indcollation[0];
|
|
||||||
vacuumstate->ntup = palloc0(HNSW_TUPLE_ALLOC_SIZE);
|
vacuumstate->ntup = palloc0(HNSW_TUPLE_ALLOC_SIZE);
|
||||||
vacuumstate->tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
vacuumstate->tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
"Hnsw vacuum temporary context",
|
"Hnsw vacuum temporary context",
|
||||||
ALLOCSET_DEFAULT_SIZES);
|
ALLOCSET_DEFAULT_SIZES);
|
||||||
|
|
||||||
|
HnswInitSupport(&vacuumstate->support, index);
|
||||||
|
|
||||||
/* Get m from metapage */
|
/* Get m from metapage */
|
||||||
HnswGetMetaPageInfo(index, &vacuumstate->m, NULL);
|
HnswGetMetaPageInfo(index, &vacuumstate->m, NULL);
|
||||||
|
|
||||||
|
|||||||
118
src/ivfbuild.c
118
src/ivfbuild.c
@@ -26,12 +26,6 @@
|
|||||||
#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"
|
||||||
@@ -63,7 +57,7 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
|
|||||||
if (!IvfflatCheckNorm(buildstate->kmeansnormprocinfo, buildstate->collation, value))
|
if (!IvfflatCheckNorm(buildstate->kmeansnormprocinfo, buildstate->collation, value))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
value = IvfflatNormValue(buildstate->normalizeprocinfo, buildstate->collation, value);
|
value = IvfflatNormValue(buildstate->typeInfo, buildstate->collation, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (samples->length < targsamples)
|
if (samples->length < targsamples)
|
||||||
@@ -96,7 +90,7 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
|
|||||||
* Callback for sampling
|
* Callback for sampling
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
SampleCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
SampleCallback(Relation index, ItemPointer tid, Datum *values,
|
||||||
bool *isnull, bool tupleIsAlive, void *state)
|
bool *isnull, bool tupleIsAlive, void *state)
|
||||||
{
|
{
|
||||||
IvfflatBuildState *buildstate = (IvfflatBuildState *) state;
|
IvfflatBuildState *buildstate = (IvfflatBuildState *) state;
|
||||||
@@ -161,7 +155,7 @@ AddTupleToSort(Relation index, ItemPointer tid, Datum *values, IvfflatBuildState
|
|||||||
if (!IvfflatCheckNorm(buildstate->normprocinfo, buildstate->collation, value))
|
if (!IvfflatCheckNorm(buildstate->normprocinfo, buildstate->collation, value))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
value = IvfflatNormValue(buildstate->normalizeprocinfo, buildstate->collation, value);
|
value = IvfflatNormValue(buildstate->typeInfo, buildstate->collation, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find the list that minimizes the distance */
|
/* Find the list that minimizes the distance */
|
||||||
@@ -207,16 +201,12 @@ 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, CALLBACK_ITEM_POINTER, Datum *values,
|
BuildCallback(Relation index, ItemPointer tid, 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;
|
||||||
@@ -319,62 +309,36 @@ InsertTuples(Relation index, IvfflatBuildState * buildstate, ForkNumber forkNum)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Get max dimensions
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
GetMaxDimensions(IvfflatType type)
|
|
||||||
{
|
|
||||||
int maxDimensions = IVFFLAT_MAX_DIM;
|
|
||||||
|
|
||||||
if (type == IVFFLAT_TYPE_HALFVEC)
|
|
||||||
maxDimensions *= 2;
|
|
||||||
else if (type == IVFFLAT_TYPE_BIT)
|
|
||||||
maxDimensions *= 32;
|
|
||||||
|
|
||||||
return maxDimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get item size
|
|
||||||
*/
|
|
||||||
static Size
|
|
||||||
GetItemSize(IvfflatType type, int dimensions)
|
|
||||||
{
|
|
||||||
if (type == IVFFLAT_TYPE_VECTOR)
|
|
||||||
return VECTOR_SIZE(dimensions);
|
|
||||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
|
||||||
return HALFVEC_SIZE(dimensions);
|
|
||||||
else if (type == IVFFLAT_TYPE_BIT)
|
|
||||||
return VARBITTOTALLEN(dimensions);
|
|
||||||
else
|
|
||||||
elog(ERROR, "Unsupported type");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the build state
|
* Initialize the build state
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, IndexInfo *indexInfo)
|
InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, IndexInfo *indexInfo)
|
||||||
{
|
{
|
||||||
int maxDimensions;
|
|
||||||
|
|
||||||
buildstate->heap = heap;
|
buildstate->heap = heap;
|
||||||
buildstate->index = index;
|
buildstate->index = index;
|
||||||
buildstate->indexInfo = indexInfo;
|
buildstate->indexInfo = indexInfo;
|
||||||
buildstate->type = IvfflatGetType(index);
|
buildstate->typeInfo = IvfflatGetTypeInfo(index);
|
||||||
|
|
||||||
buildstate->lists = IvfflatGetLists(index);
|
buildstate->lists = IvfflatGetLists(index);
|
||||||
buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod;
|
buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod;
|
||||||
|
|
||||||
maxDimensions = GetMaxDimensions(buildstate->type);
|
/* Disallow varbit since require fixed dimensions */
|
||||||
|
if (TupleDescAttr(index->rd_att, 0)->atttypid == VARBITOID)
|
||||||
|
ereport(ERROR,
|
||||||
|
(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)
|
||||||
elog(ERROR, "column does not have dimensions");
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("column does not have dimensions")));
|
||||||
|
|
||||||
if (buildstate->dimensions > maxDimensions)
|
if (buildstate->dimensions > buildstate->typeInfo->maxDimensions)
|
||||||
elog(ERROR, "column cannot have more than %d dimensions for ivfflat index", maxDimensions);
|
ereport(ERROR,
|
||||||
|
(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;
|
||||||
@@ -383,12 +347,13 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
|
|||||||
buildstate->procinfo = index_getprocinfo(index, 1, IVFFLAT_DISTANCE_PROC);
|
buildstate->procinfo = index_getprocinfo(index, 1, IVFFLAT_DISTANCE_PROC);
|
||||||
buildstate->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
|
buildstate->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
|
||||||
buildstate->kmeansnormprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
buildstate->kmeansnormprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
||||||
buildstate->normalizeprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORMALIZE_PROC);
|
|
||||||
buildstate->collation = index->rd_indcollation[0];
|
buildstate->collation = index->rd_indcollation[0];
|
||||||
|
|
||||||
/* 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)
|
||||||
elog(ERROR, "dimensions must be greater than one for this opclass");
|
ereport(ERROR,
|
||||||
|
(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);
|
||||||
@@ -398,7 +363,7 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
|
|||||||
|
|
||||||
buildstate->slot = MakeSingleTupleTableSlot(buildstate->tupdesc, &TTSOpsVirtual);
|
buildstate->slot = MakeSingleTupleTableSlot(buildstate->tupdesc, &TTSOpsVirtual);
|
||||||
|
|
||||||
buildstate->centers = VectorArrayInit(buildstate->lists, buildstate->dimensions, GetItemSize(buildstate->type, buildstate->dimensions));
|
buildstate->centers = VectorArrayInit(buildstate->lists, buildstate->dimensions, buildstate->typeInfo->itemSize(buildstate->dimensions));
|
||||||
buildstate->listInfo = palloc(sizeof(ListInfo) * buildstate->lists);
|
buildstate->listInfo = palloc(sizeof(ListInfo) * buildstate->lists);
|
||||||
|
|
||||||
buildstate->tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
buildstate->tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
@@ -468,7 +433,7 @@ ComputeCenters(IvfflatBuildState * buildstate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate centers */
|
/* Calculate centers */
|
||||||
IvfflatBench("k-means", IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers, buildstate->type));
|
IvfflatBench("k-means", IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers, buildstate->typeInfo));
|
||||||
|
|
||||||
/* Free samples before we allocate more memory */
|
/* Free samples before we allocate more memory */
|
||||||
VectorArrayFree(buildstate->samples);
|
VectorArrayFree(buildstate->samples);
|
||||||
@@ -523,10 +488,13 @@ CreateListPages(Relation index, VectorArray centers, int dimensions,
|
|||||||
{
|
{
|
||||||
OffsetNumber offno;
|
OffsetNumber offno;
|
||||||
|
|
||||||
|
/* Zero memory for each list */
|
||||||
|
MemSet(list, 0, listSize);
|
||||||
|
|
||||||
/* Load list */
|
/* Load list */
|
||||||
list->startPage = InvalidBlockNumber;
|
list->startPage = InvalidBlockNumber;
|
||||||
list->insertPage = InvalidBlockNumber;
|
list->insertPage = InvalidBlockNumber;
|
||||||
memcpy(&list->center, VectorArrayGet(centers, i), centers->itemsize);
|
memcpy(&list->center, VectorArrayGet(centers, i), VARSIZE_ANY(VectorArrayGet(centers, i)));
|
||||||
|
|
||||||
/* Ensure free space */
|
/* Ensure free space */
|
||||||
if (PageGetFreeSpace(page) < listSize)
|
if (PageGetFreeSpace(page) < listSize)
|
||||||
@@ -592,6 +560,20 @@ 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
|
||||||
*/
|
*/
|
||||||
@@ -639,12 +621,6 @@ 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;
|
||||||
@@ -657,7 +633,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 = tuplesort_begin_heap(buildstate.tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, sortmem, coordinate, false);
|
ivfspool->sortstate = InitBuildSortState(buildstate.tupdesc, sortmem, coordinate);
|
||||||
buildstate.sortstate = ivfspool->sortstate;
|
buildstate.sortstate = ivfspool->sortstate;
|
||||||
scan = table_beginscan_parallel(ivfspool->heap,
|
scan = table_beginscan_parallel(ivfspool->heap,
|
||||||
ParallelTableScanFromIvfflatShared(ivfshared));
|
ParallelTableScanFromIvfflatShared(ivfshared));
|
||||||
@@ -954,12 +930,6 @@ 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 */
|
||||||
@@ -980,7 +950,7 @@ AssignTuples(IvfflatBuildState * buildstate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Begin serial/leader tuplesort */
|
/* Begin serial/leader tuplesort */
|
||||||
buildstate->sortstate = tuplesort_begin_heap(buildstate->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, maintenance_work_mem, coordinate, false);
|
buildstate->sortstate = InitBuildSortState(buildstate->tupdesc, maintenance_work_mem, coordinate);
|
||||||
|
|
||||||
/* Add tuples to sort */
|
/* Add tuples to sort */
|
||||||
if (buildstate->heap != NULL)
|
if (buildstate->heap != NULL)
|
||||||
@@ -1036,6 +1006,10 @@ 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,6 +7,7 @@
|
|||||||
#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"
|
||||||
@@ -26,11 +27,7 @@ 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
|
IVFFLAT_DEFAULT_LISTS, IVFFLAT_MIN_LISTS, IVFFLAT_MAX_LISTS, AccessExclusiveLock);
|
||||||
#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,
|
||||||
@@ -72,14 +69,16 @@ ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|||||||
GenericCosts costs;
|
GenericCosts costs;
|
||||||
int lists;
|
int lists;
|
||||||
double ratio;
|
double ratio;
|
||||||
|
double sequentialRatio = 0.5;
|
||||||
|
double startupPages;
|
||||||
double spc_seq_page_cost;
|
double spc_seq_page_cost;
|
||||||
Relation index;
|
Relation index;
|
||||||
|
|
||||||
/* Never use index without order */
|
/* Never use index without order */
|
||||||
if (path->indexorderbys == NULL)
|
if (path->indexorderbys == NULL)
|
||||||
{
|
{
|
||||||
*indexStartupCost = DBL_MAX;
|
*indexStartupCost = get_float8_infinity();
|
||||||
*indexTotalCost = DBL_MAX;
|
*indexTotalCost = get_float8_infinity();
|
||||||
*indexSelectivity = 0;
|
*indexSelectivity = 0;
|
||||||
*indexCorrelation = 0;
|
*indexCorrelation = 0;
|
||||||
*indexPages = 0;
|
*indexPages = 0;
|
||||||
@@ -88,6 +87,8 @@ ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|||||||
|
|
||||||
MemSet(&costs, 0, sizeof(costs));
|
MemSet(&costs, 0, sizeof(costs));
|
||||||
|
|
||||||
|
genericcostestimate(root, path, loop_count, &costs);
|
||||||
|
|
||||||
index = index_open(path->indexinfo->indexoid, NoLock);
|
index = index_open(path->indexinfo->indexoid, NoLock);
|
||||||
IvfflatGetMetaPageInfo(index, &lists, NULL);
|
IvfflatGetMetaPageInfo(index, &lists, NULL);
|
||||||
index_close(index, NoLock);
|
index_close(index, NoLock);
|
||||||
@@ -97,41 +98,26 @@ ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|||||||
if (ratio > 1.0)
|
if (ratio > 1.0)
|
||||||
ratio = 1.0;
|
ratio = 1.0;
|
||||||
|
|
||||||
/*
|
|
||||||
* This gives us the subset of tuples to visit. This value is passed into
|
|
||||||
* the generic cost estimator to determine the number of pages to visit
|
|
||||||
* during the index scan.
|
|
||||||
*/
|
|
||||||
costs.numIndexTuples = path->indexinfo->tuples * ratio;
|
|
||||||
|
|
||||||
genericcostestimate(root, path, loop_count, &costs);
|
|
||||||
|
|
||||||
get_tablespace_page_costs(path->indexinfo->reltablespace, NULL, &spc_seq_page_cost);
|
get_tablespace_page_costs(path->indexinfo->reltablespace, NULL, &spc_seq_page_cost);
|
||||||
|
|
||||||
|
/* Change some page cost from random to sequential */
|
||||||
|
costs.indexTotalCost -= sequentialRatio * costs.numIndexPages * (costs.spc_random_page_cost - spc_seq_page_cost);
|
||||||
|
|
||||||
|
/* Startup cost is cost before returning the first row */
|
||||||
|
costs.indexStartupCost = costs.indexTotalCost * ratio;
|
||||||
|
|
||||||
/* Adjust cost if needed since TOAST not included in seq scan cost */
|
/* Adjust cost if needed since TOAST not included in seq scan cost */
|
||||||
if (costs.numIndexPages > path->indexinfo->rel->pages && ratio < 0.5)
|
startupPages = costs.numIndexPages * ratio;
|
||||||
|
if (startupPages > path->indexinfo->rel->pages && ratio < 0.5)
|
||||||
{
|
{
|
||||||
/* Change all page cost from random to sequential */
|
/* Change rest of page cost from random to sequential */
|
||||||
costs.indexTotalCost -= costs.numIndexPages * (costs.spc_random_page_cost - spc_seq_page_cost);
|
costs.indexStartupCost -= (1 - sequentialRatio) * startupPages * (costs.spc_random_page_cost - spc_seq_page_cost);
|
||||||
|
|
||||||
/* Remove cost of extra pages */
|
/* Remove cost of extra pages */
|
||||||
costs.indexTotalCost -= (costs.numIndexPages - path->indexinfo->rel->pages) * spc_seq_page_cost;
|
costs.indexStartupCost -= (startupPages - path->indexinfo->rel->pages) * spc_seq_page_cost;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Change some page cost from random to sequential */
|
|
||||||
costs.indexTotalCost -= 0.5 * costs.numIndexPages * (costs.spc_random_page_cost - spc_seq_page_cost);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
*indexStartupCost = costs.indexStartupCost;
|
||||||
* If the list selectivity is lower than what is returned from the generic
|
|
||||||
* cost estimator, use that.
|
|
||||||
*/
|
|
||||||
if (ratio < costs.indexSelectivity)
|
|
||||||
costs.indexSelectivity = ratio;
|
|
||||||
|
|
||||||
/* Use total cost since most work happens before first tuple is returned */
|
|
||||||
*indexStartupCost = costs.indexTotalCost;
|
|
||||||
*indexTotalCost = costs.indexTotalCost;
|
*indexTotalCost = costs.indexTotalCost;
|
||||||
*indexSelectivity = costs.indexSelectivity;
|
*indexSelectivity = costs.indexSelectivity;
|
||||||
*indexCorrelation = costs.indexCorrelation;
|
*indexCorrelation = costs.indexCorrelation;
|
||||||
@@ -148,23 +134,10 @@ 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -181,17 +154,15 @@ ivfflatvalidate(Oid opclassoid)
|
|||||||
*
|
*
|
||||||
* See https://www.postgresql.org/docs/current/index-api.html
|
* See https://www.postgresql.org/docs/current/index-api.html
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(ivfflathandler);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(ivfflathandler);
|
||||||
Datum
|
Datum
|
||||||
ivfflathandler(PG_FUNCTION_ARGS)
|
ivfflathandler(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
|
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
|
||||||
|
|
||||||
amroutine->amstrategies = 0;
|
amroutine->amstrategies = 0;
|
||||||
amroutine->amsupport = 6;
|
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 */
|
||||||
@@ -204,17 +175,24 @@ ivfflathandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amclusterable = false;
|
amroutine->amclusterable = false;
|
||||||
amroutine->ampredlocks = false;
|
amroutine->ampredlocks = false;
|
||||||
amroutine->amcanparallel = false;
|
amroutine->amcanparallel = false;
|
||||||
amroutine->amcaninclude = false;
|
#if PG_VERSION_NUM >= 170000
|
||||||
#if PG_VERSION_NUM >= 130000
|
amroutine->amcanbuildparallel = true;
|
||||||
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
|
|
||||||
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
|
|
||||||
#endif
|
#endif
|
||||||
|
amroutine->amcaninclude = false;
|
||||||
|
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
|
||||||
|
#if PG_VERSION_NUM >= 160000
|
||||||
|
amroutine->amsummarizing = false;
|
||||||
|
#endif
|
||||||
|
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
|
||||||
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 */
|
||||||
|
|||||||
@@ -28,8 +28,7 @@
|
|||||||
#define IVFFLAT_NORM_PROC 2
|
#define IVFFLAT_NORM_PROC 2
|
||||||
#define IVFFLAT_KMEANS_DISTANCE_PROC 3
|
#define IVFFLAT_KMEANS_DISTANCE_PROC 3
|
||||||
#define IVFFLAT_KMEANS_NORM_PROC 4
|
#define IVFFLAT_KMEANS_NORM_PROC 4
|
||||||
#define IVFFLAT_NORMALIZE_PROC 5
|
#define IVFFLAT_TYPE_INFO_PROC 5
|
||||||
#define IVFFLAT_TYPE_SUPPORT_PROC 6
|
|
||||||
|
|
||||||
#define IVFFLAT_VERSION 1
|
#define IVFFLAT_VERSION 1
|
||||||
#define IVFFLAT_MAGIC_NUMBER 0x14FF1A7
|
#define IVFFLAT_MAGIC_NUMBER 0x14FF1A7
|
||||||
@@ -45,14 +44,6 @@
|
|||||||
#define IVFFLAT_MAX_LISTS 32768
|
#define IVFFLAT_MAX_LISTS 32768
|
||||||
#define IVFFLAT_DEFAULT_PROBES 1
|
#define IVFFLAT_DEFAULT_PROBES 1
|
||||||
|
|
||||||
typedef enum IvfflatType
|
|
||||||
{
|
|
||||||
IVFFLAT_TYPE_VECTOR,
|
|
||||||
IVFFLAT_TYPE_HALFVEC,
|
|
||||||
IVFFLAT_TYPE_BIT,
|
|
||||||
IVFFLAT_TYPE_UNSUPPORTED
|
|
||||||
} IvfflatType;
|
|
||||||
|
|
||||||
/* Build phases */
|
/* Build phases */
|
||||||
/* PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE is 1 */
|
/* PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE is 1 */
|
||||||
#define PROGRESS_IVFFLAT_PHASE_KMEANS 2
|
#define PROGRESS_IVFFLAT_PHASE_KMEANS 2
|
||||||
@@ -158,13 +149,22 @@ typedef struct IvfflatLeader
|
|||||||
char *ivfcenters;
|
char *ivfcenters;
|
||||||
} IvfflatLeader;
|
} IvfflatLeader;
|
||||||
|
|
||||||
|
typedef struct IvfflatTypeInfo
|
||||||
|
{
|
||||||
|
int maxDimensions;
|
||||||
|
Datum (*normalize) (PG_FUNCTION_ARGS);
|
||||||
|
Size (*itemSize) (int dimensions);
|
||||||
|
void (*updateCenter) (Pointer v, int dimensions, float *x);
|
||||||
|
void (*sumCenter) (Pointer v, float *x);
|
||||||
|
} IvfflatTypeInfo;
|
||||||
|
|
||||||
typedef struct IvfflatBuildState
|
typedef struct IvfflatBuildState
|
||||||
{
|
{
|
||||||
/* Info */
|
/* Info */
|
||||||
Relation heap;
|
Relation heap;
|
||||||
Relation index;
|
Relation index;
|
||||||
IndexInfo *indexInfo;
|
IndexInfo *indexInfo;
|
||||||
IvfflatType type;
|
const IvfflatTypeInfo *typeInfo;
|
||||||
|
|
||||||
/* Settings */
|
/* Settings */
|
||||||
int dimensions;
|
int dimensions;
|
||||||
@@ -178,7 +178,6 @@ typedef struct IvfflatBuildState
|
|||||||
FmgrInfo *procinfo;
|
FmgrInfo *procinfo;
|
||||||
FmgrInfo *normprocinfo;
|
FmgrInfo *normprocinfo;
|
||||||
FmgrInfo *kmeansnormprocinfo;
|
FmgrInfo *kmeansnormprocinfo;
|
||||||
FmgrInfo *normalizeprocinfo;
|
|
||||||
Oid collation;
|
Oid collation;
|
||||||
|
|
||||||
/* Variables */
|
/* Variables */
|
||||||
@@ -246,6 +245,7 @@ typedef struct IvfflatScanList
|
|||||||
|
|
||||||
typedef struct IvfflatScanOpaqueData
|
typedef struct IvfflatScanOpaqueData
|
||||||
{
|
{
|
||||||
|
const IvfflatTypeInfo *typeInfo;
|
||||||
int probes;
|
int probes;
|
||||||
int dimensions;
|
int dimensions;
|
||||||
bool first;
|
bool first;
|
||||||
@@ -253,13 +253,13 @@ typedef struct IvfflatScanOpaqueData
|
|||||||
/* Sorting */
|
/* Sorting */
|
||||||
Tuplesortstate *sortstate;
|
Tuplesortstate *sortstate;
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *vslot;
|
||||||
bool isnull;
|
TupleTableSlot *mslot;
|
||||||
|
BufferAccessStrategy bas;
|
||||||
|
|
||||||
/* Support functions */
|
/* Support functions */
|
||||||
FmgrInfo *procinfo;
|
FmgrInfo *procinfo;
|
||||||
FmgrInfo *normprocinfo;
|
FmgrInfo *normprocinfo;
|
||||||
FmgrInfo *normalizeprocinfo;
|
|
||||||
Oid collation;
|
Oid collation;
|
||||||
Datum (*distfunc) (FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2);
|
Datum (*distfunc) (FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2);
|
||||||
|
|
||||||
@@ -271,17 +271,27 @@ typedef struct IvfflatScanOpaqueData
|
|||||||
typedef IvfflatScanOpaqueData * IvfflatScanOpaque;
|
typedef IvfflatScanOpaqueData * IvfflatScanOpaque;
|
||||||
|
|
||||||
#define VECTOR_ARRAY_SIZE(_length, _size) (sizeof(VectorArrayData) + (_length) * MAXALIGN(_size))
|
#define VECTOR_ARRAY_SIZE(_length, _size) (sizeof(VectorArrayData) + (_length) * MAXALIGN(_size))
|
||||||
#define VECTOR_ARRAY_OFFSET(_arr, _offset) ((char*) (_arr)->items + (_offset) * (_arr)->itemsize)
|
|
||||||
#define VectorArrayGet(_arr, _offset) VECTOR_ARRAY_OFFSET(_arr, _offset)
|
/* Use functions instead of macros to avoid double evaluation */
|
||||||
#define VectorArraySet(_arr, _offset, _val) memcpy(VECTOR_ARRAY_OFFSET(_arr, _offset), _val, (_arr)->itemsize)
|
|
||||||
|
static inline Pointer
|
||||||
|
VectorArrayGet(VectorArray arr, int offset)
|
||||||
|
{
|
||||||
|
return ((char *) arr->items) + (offset * arr->itemsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
VectorArraySet(VectorArray arr, int offset, Pointer val)
|
||||||
|
{
|
||||||
|
memcpy(VectorArrayGet(arr, offset), val, VARSIZE_ANY(val));
|
||||||
|
}
|
||||||
|
|
||||||
/* Methods */
|
/* Methods */
|
||||||
VectorArray VectorArrayInit(int maxlen, int dimensions, Size itemsize);
|
VectorArray VectorArrayInit(int maxlen, int dimensions, Size itemsize);
|
||||||
void VectorArrayFree(VectorArray arr);
|
void VectorArrayFree(VectorArray arr);
|
||||||
void IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatType type);
|
void IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers, const IvfflatTypeInfo * typeInfo);
|
||||||
FmgrInfo *IvfflatOptionalProcInfo(Relation index, uint16 procnum);
|
FmgrInfo *IvfflatOptionalProcInfo(Relation index, uint16 procnum);
|
||||||
IvfflatType IvfflatGetType(Relation index);
|
Datum IvfflatNormValue(const IvfflatTypeInfo * typeInfo, Oid collation, Datum value);
|
||||||
Datum IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum value);
|
|
||||||
bool IvfflatCheckNorm(FmgrInfo *procinfo, Oid collation, Datum value);
|
bool IvfflatCheckNorm(FmgrInfo *procinfo, Oid collation, Datum value);
|
||||||
int IvfflatGetLists(Relation index);
|
int IvfflatGetLists(Relation index);
|
||||||
void IvfflatGetMetaPageInfo(Relation index, int *lists, int *dimensions);
|
void IvfflatGetMetaPageInfo(Relation index, int *lists, int *dimensions);
|
||||||
@@ -292,6 +302,7 @@ Buffer IvfflatNewBuffer(Relation index, ForkNumber forkNum);
|
|||||||
void IvfflatInitPage(Buffer buf, Page page);
|
void IvfflatInitPage(Buffer buf, Page page);
|
||||||
void IvfflatInitRegisterPage(Relation index, Buffer *buf, Page *page, GenericXLogState **state);
|
void IvfflatInitRegisterPage(Relation index, Buffer *buf, Page *page, GenericXLogState **state);
|
||||||
void IvfflatInit(void);
|
void IvfflatInit(void);
|
||||||
|
const IvfflatTypeInfo *IvfflatGetTypeInfo(Relation index);
|
||||||
PGDLLEXPORT void IvfflatParallelBuildMain(dsm_segment *seg, shm_toc *toc);
|
PGDLLEXPORT void IvfflatParallelBuildMain(dsm_segment *seg, shm_toc *toc);
|
||||||
|
|
||||||
/* Index access methods */
|
/* Index access methods */
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ FindInsertPage(Relation index, Datum *values, BlockNumber *insertPage, ListInfo
|
|||||||
static void
|
static void
|
||||||
InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel)
|
InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel)
|
||||||
{
|
{
|
||||||
|
const IvfflatTypeInfo *typeInfo = IvfflatGetTypeInfo(index);
|
||||||
IndexTuple itup;
|
IndexTuple itup;
|
||||||
Datum value;
|
Datum value;
|
||||||
FmgrInfo *normprocinfo;
|
FmgrInfo *normprocinfo;
|
||||||
@@ -90,9 +91,12 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, R
|
|||||||
if (!IvfflatCheckNorm(normprocinfo, collation, value))
|
if (!IvfflatCheckNorm(normprocinfo, collation, value))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
value = IvfflatNormValue(IvfflatOptionalProcInfo(index, IVFFLAT_NORMALIZE_PROC), 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));
|
||||||
|
|||||||
424
src/ivfkmeans.c
424
src/ivfkmeans.c
@@ -92,7 +92,7 @@ InitCenters(Relation index, VectorArray samples, VectorArray centers, float *low
|
|||||||
* Norm centers
|
* Norm centers
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
NormCenters(FmgrInfo *normalizeprocinfo, Oid collation, VectorArray centers)
|
NormCenters(const IvfflatTypeInfo * typeInfo, Oid collation, VectorArray centers)
|
||||||
{
|
{
|
||||||
MemoryContext normCtx = AllocSetContextCreate(CurrentMemoryContext,
|
MemoryContext normCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
"Ivfflat norm temporary context",
|
"Ivfflat norm temporary context",
|
||||||
@@ -102,7 +102,7 @@ NormCenters(FmgrInfo *normalizeprocinfo, Oid collation, VectorArray centers)
|
|||||||
for (int j = 0; j < centers->length; j++)
|
for (int j = 0; j < centers->length; j++)
|
||||||
{
|
{
|
||||||
Datum center = PointerGetDatum(VectorArrayGet(centers, j));
|
Datum center = PointerGetDatum(VectorArrayGet(centers, j));
|
||||||
Datum newCenter = IvfflatNormValue(normalizeprocinfo, collation, center);
|
Datum newCenter = IvfflatNormValue(typeInfo, collation, center);
|
||||||
Size size = VARSIZE_ANY(DatumGetPointer(newCenter));
|
Size size = VARSIZE_ANY(DatumGetPointer(newCenter));
|
||||||
|
|
||||||
if (size > centers->itemsize)
|
if (size > centers->itemsize)
|
||||||
@@ -117,120 +117,31 @@ NormCenters(FmgrInfo *normalizeprocinfo, Oid collation, VectorArray centers)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compare vectors
|
* Quick approach if we have no data
|
||||||
*/
|
|
||||||
static int
|
|
||||||
CompareVectors(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
return vector_cmp_internal((Vector *) a, (Vector *) b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compare half vectors
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
CompareHalfVectors(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
return halfvec_cmp_internal((HalfVector *) a, (HalfVector *) b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compare bit vectors
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
CompareBitVectors(const void *a, const void *b)
|
|
||||||
{
|
|
||||||
return DirectFunctionCall2(bitcmp, VarBitPGetDatum((VarBit *) a), VarBitPGetDatum((VarBit *) b));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sort vector array
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
SortVectorArray(VectorArray arr, IvfflatType type)
|
RandomCenters(Relation index, VectorArray centers, const IvfflatTypeInfo * typeInfo)
|
||||||
{
|
|
||||||
if (type == IVFFLAT_TYPE_VECTOR)
|
|
||||||
qsort(arr->items, arr->length, arr->itemsize, CompareVectors);
|
|
||||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
|
||||||
qsort(arr->items, arr->length, arr->itemsize, CompareHalfVectors);
|
|
||||||
else if (type == IVFFLAT_TYPE_BIT)
|
|
||||||
qsort(arr->items, arr->length, arr->itemsize, CompareBitVectors);
|
|
||||||
else
|
|
||||||
elog(ERROR, "Unsupported type");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Quick approach if we have little data
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
QuickCenters(Relation index, VectorArray samples, VectorArray centers, IvfflatType type)
|
|
||||||
{
|
{
|
||||||
int dimensions = centers->dim;
|
int dimensions = centers->dim;
|
||||||
Oid collation = index->rd_indcollation[0];
|
|
||||||
FmgrInfo *normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
FmgrInfo *normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
||||||
FmgrInfo *normalizeprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORMALIZE_PROC);
|
Oid collation = index->rd_indcollation[0];
|
||||||
|
float *x = (float *) palloc(sizeof(float) * dimensions);
|
||||||
|
|
||||||
/* Copy existing vectors while avoiding duplicates */
|
/* Fill with random data */
|
||||||
if (samples->length > 0)
|
|
||||||
{
|
|
||||||
SortVectorArray(samples, type);
|
|
||||||
|
|
||||||
for (int i = 0; i < samples->length; i++)
|
|
||||||
{
|
|
||||||
Datum vec = PointerGetDatum(VectorArrayGet(samples, i));
|
|
||||||
|
|
||||||
if (i == 0 || !datumIsEqual(vec, PointerGetDatum(VectorArrayGet(samples, i - 1)), false, -1))
|
|
||||||
{
|
|
||||||
VectorArraySet(centers, centers->length, DatumGetPointer(vec));
|
|
||||||
centers->length++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill remaining with random data */
|
|
||||||
while (centers->length < centers->maxlen)
|
while (centers->length < centers->maxlen)
|
||||||
{
|
{
|
||||||
Datum center = PointerGetDatum(VectorArrayGet(centers, centers->length));
|
Pointer center = VectorArrayGet(centers, centers->length);
|
||||||
|
|
||||||
if (type == IVFFLAT_TYPE_VECTOR)
|
for (int i = 0; i < dimensions; i++)
|
||||||
{
|
x[i] = (float) RandomDouble();
|
||||||
Vector *vec = DatumGetVector(center);
|
|
||||||
|
|
||||||
SET_VARSIZE(vec, VECTOR_SIZE(dimensions));
|
typeInfo->updateCenter(center, dimensions, x);
|
||||||
vec->dim = dimensions;
|
|
||||||
|
|
||||||
for (int j = 0; j < dimensions; j++)
|
|
||||||
vec->x[j] = RandomDouble();
|
|
||||||
}
|
|
||||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
|
||||||
{
|
|
||||||
HalfVector *vec = DatumGetHalfVector(center);
|
|
||||||
|
|
||||||
SET_VARSIZE(vec, HALFVEC_SIZE(dimensions));
|
|
||||||
vec->dim = dimensions;
|
|
||||||
|
|
||||||
for (int j = 0; j < dimensions; j++)
|
|
||||||
vec->x[j] = Float4ToHalfUnchecked((float) RandomDouble());
|
|
||||||
}
|
|
||||||
else if (type == IVFFLAT_TYPE_BIT)
|
|
||||||
{
|
|
||||||
VarBit *vec = DatumGetVarBitP(center);
|
|
||||||
|
|
||||||
SET_VARSIZE(vec, VARBITTOTALLEN(dimensions));
|
|
||||||
VARBITLEN(vec) = dimensions;
|
|
||||||
|
|
||||||
for (int j = 0; j < dimensions; j++)
|
|
||||||
VARBITS(vec)[j / dimensions] |= (RandomDouble() > 0.5 ? 1 : 0) << (7 - (j % 8));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
elog(ERROR, "Unsupported type");
|
|
||||||
|
|
||||||
centers->length++;
|
centers->length++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fine if existing vectors are normalized twice */
|
|
||||||
if (normprocinfo != NULL)
|
if (normprocinfo != NULL)
|
||||||
NormCenters(normalizeprocinfo, collation, centers);
|
NormCenters(typeInfo, collation, centers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef IVFFLAT_MEMORY
|
#ifdef IVFFLAT_MEMORY
|
||||||
@@ -240,12 +151,8 @@ QuickCenters(Relation index, VectorArray samples, VectorArray centers, IvfflatTy
|
|||||||
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
|
||||||
@@ -254,82 +161,27 @@ ShowMemoryUsage(MemoryContext context, Size estimatedSize)
|
|||||||
* Sum centers
|
* Sum centers
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
SumCenters(VectorArray samples, VectorArray aggCenters, int *closestCenters, IvfflatType type)
|
SumCenters(VectorArray samples, float *agg, int *closestCenters, const IvfflatTypeInfo * typeInfo)
|
||||||
{
|
{
|
||||||
int dimensions = aggCenters->dim;
|
for (int j = 0; j < samples->length; j++)
|
||||||
int numSamples = samples->length;
|
|
||||||
|
|
||||||
if (type == IVFFLAT_TYPE_VECTOR)
|
|
||||||
{
|
{
|
||||||
for (int j = 0; j < numSamples; j++)
|
float *x = agg + ((int64) closestCenters[j] * samples->dim);
|
||||||
{
|
|
||||||
Vector *aggCenter = (Vector *) VectorArrayGet(aggCenters, closestCenters[j]);
|
|
||||||
Vector *vec = (Vector *) VectorArrayGet(samples, j);
|
|
||||||
|
|
||||||
for (int k = 0; k < dimensions; k++)
|
typeInfo->sumCenter(VectorArrayGet(samples, j), x);
|
||||||
aggCenter->x[k] += vec->x[k];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < numSamples; j++)
|
|
||||||
{
|
|
||||||
Vector *aggCenter = (Vector *) VectorArrayGet(aggCenters, closestCenters[j]);
|
|
||||||
HalfVector *vec = (HalfVector *) VectorArrayGet(samples, j);
|
|
||||||
|
|
||||||
for (int k = 0; k < dimensions; k++)
|
|
||||||
aggCenter->x[k] += HalfToFloat4(vec->x[k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type == IVFFLAT_TYPE_BIT)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < numSamples; j++)
|
|
||||||
{
|
|
||||||
Vector *aggCenter = (Vector *) VectorArrayGet(aggCenters, closestCenters[j]);
|
|
||||||
VarBit *vec = (VarBit *) VectorArrayGet(samples, j);
|
|
||||||
|
|
||||||
for (int k = 0; k < dimensions; k++)
|
|
||||||
aggCenter->x[k] += (float) (((VARBITS(vec)[k / 8]) >> (7 - (k % 8))) & 0x01);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
elog(ERROR, "Unsupported type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set new centers
|
* Update centers
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
SetNewCenters(VectorArray aggCenters, VectorArray newCenters, IvfflatType type)
|
UpdateCenters(float *agg, VectorArray centers, const IvfflatTypeInfo * typeInfo)
|
||||||
{
|
{
|
||||||
int dimensions = aggCenters->dim;
|
for (int j = 0; j < centers->length; j++)
|
||||||
int numCenters = aggCenters->maxlen;
|
|
||||||
|
|
||||||
if (type == IVFFLAT_TYPE_HALFVEC)
|
|
||||||
{
|
{
|
||||||
for (int j = 0; j < numCenters; j++)
|
float *x = agg + ((int64) j * centers->dim);
|
||||||
{
|
|
||||||
Vector *aggCenter = (Vector *) VectorArrayGet(aggCenters, j);
|
|
||||||
HalfVector *newCenter = (HalfVector *) VectorArrayGet(newCenters, j);
|
|
||||||
|
|
||||||
for (int k = 0; k < dimensions; k++)
|
typeInfo->updateCenter(VectorArrayGet(centers, j), centers->dim, x);
|
||||||
newCenter->x[k] = Float4ToHalfUnchecked(aggCenter->x[k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type == IVFFLAT_TYPE_BIT)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < numCenters; j++)
|
|
||||||
{
|
|
||||||
Vector *aggCenter = (Vector *) VectorArrayGet(aggCenters, j);
|
|
||||||
VarBit *newCenter = (VarBit *) VectorArrayGet(newCenters, j);
|
|
||||||
unsigned char *nx = VARBITS(newCenter);
|
|
||||||
|
|
||||||
for (uint32 k = 0; k < VARBITBYTES(newCenter); k++)
|
|
||||||
nx[k] = 0;
|
|
||||||
|
|
||||||
for (int k = 0; k < dimensions; k++)
|
|
||||||
nx[k / 8] |= (aggCenter->x[k] > 0.5) << (7 - (k % 8));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,25 +189,25 @@ SetNewCenters(VectorArray aggCenters, VectorArray newCenters, IvfflatType type)
|
|||||||
* Compute new centers
|
* Compute new centers
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ComputeNewCenters(VectorArray samples, VectorArray aggCenters, VectorArray newCenters, int *centerCounts, int *closestCenters, FmgrInfo *normprocinfo, FmgrInfo *normalizeprocinfo, Oid collation, IvfflatType type)
|
ComputeNewCenters(VectorArray samples, float *agg, VectorArray newCenters, int *centerCounts, int *closestCenters, FmgrInfo *normprocinfo, Oid collation, const IvfflatTypeInfo * typeInfo)
|
||||||
{
|
{
|
||||||
int dimensions = aggCenters->dim;
|
int dimensions = newCenters->dim;
|
||||||
int numCenters = aggCenters->maxlen;
|
int numCenters = newCenters->length;
|
||||||
int numSamples = samples->length;
|
int numSamples = samples->length;
|
||||||
|
|
||||||
/* Reset sum and count */
|
/* Reset sum and count */
|
||||||
for (int j = 0; j < numCenters; j++)
|
for (int j = 0; j < numCenters; j++)
|
||||||
{
|
{
|
||||||
Vector *vec = (Vector *) VectorArrayGet(aggCenters, j);
|
float *x = agg + ((int64) j * dimensions);
|
||||||
|
|
||||||
for (int k = 0; k < dimensions; k++)
|
for (int k = 0; k < dimensions; k++)
|
||||||
vec->x[k] = 0.0;
|
x[k] = 0.0;
|
||||||
|
|
||||||
centerCounts[j] = 0;
|
centerCounts[j] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Increment sum of closest center */
|
/* Increment sum of closest center */
|
||||||
SumCenters(samples, aggCenters, closestCenters, type);
|
SumCenters(samples, agg, closestCenters, typeInfo);
|
||||||
|
|
||||||
/* Increment count of closest center */
|
/* Increment count of closest center */
|
||||||
for (int j = 0; j < numSamples; j++)
|
for (int j = 0; j < numSamples; j++)
|
||||||
@@ -364,7 +216,7 @@ ComputeNewCenters(VectorArray samples, VectorArray aggCenters, VectorArray newCe
|
|||||||
/* Divide sum by count */
|
/* Divide sum by count */
|
||||||
for (int j = 0; j < numCenters; j++)
|
for (int j = 0; j < numCenters; j++)
|
||||||
{
|
{
|
||||||
Vector *vec = (Vector *) VectorArrayGet(aggCenters, j);
|
float *x = agg + ((int64) j * dimensions);
|
||||||
|
|
||||||
if (centerCounts[j] > 0)
|
if (centerCounts[j] > 0)
|
||||||
{
|
{
|
||||||
@@ -372,27 +224,27 @@ ComputeNewCenters(VectorArray samples, VectorArray aggCenters, VectorArray newCe
|
|||||||
/* TODO Update bounds */
|
/* TODO Update bounds */
|
||||||
for (int k = 0; k < dimensions; k++)
|
for (int k = 0; k < dimensions; k++)
|
||||||
{
|
{
|
||||||
if (isinf(vec->x[k]))
|
if (isinf(x[k]))
|
||||||
vec->x[k] = vec->x[k] > 0 ? FLT_MAX : -FLT_MAX;
|
x[k] = x[k] > 0 ? FLT_MAX : -FLT_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int k = 0; k < dimensions; k++)
|
for (int k = 0; k < dimensions; k++)
|
||||||
vec->x[k] /= centerCounts[j];
|
x[k] /= centerCounts[j];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* TODO Handle empty centers properly */
|
/* TODO Handle empty centers properly */
|
||||||
for (int k = 0; k < dimensions; k++)
|
for (int k = 0; k < dimensions; k++)
|
||||||
vec->x[k] = RandomDouble();
|
x[k] = RandomDouble();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set new centers if different from agg centers */
|
/* Set new centers */
|
||||||
SetNewCenters(aggCenters, newCenters, type);
|
UpdateCenters(agg, newCenters, typeInfo);
|
||||||
|
|
||||||
/* Normalize if needed */
|
/* Normalize if needed */
|
||||||
if (normprocinfo != NULL)
|
if (normprocinfo != NULL)
|
||||||
NormCenters(normalizeprocinfo, collation, newCenters);
|
NormCenters(typeInfo, collation, newCenters);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -404,17 +256,16 @@ ComputeNewCenters(VectorArray samples, VectorArray aggCenters, VectorArray newCe
|
|||||||
* https://www.aaai.org/Papers/ICML/2003/ICML03-022.pdf
|
* https://www.aaai.org/Papers/ICML/2003/ICML03-022.pdf
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatType type)
|
ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, const IvfflatTypeInfo * typeInfo)
|
||||||
{
|
{
|
||||||
FmgrInfo *procinfo;
|
FmgrInfo *procinfo;
|
||||||
FmgrInfo *normprocinfo;
|
FmgrInfo *normprocinfo;
|
||||||
FmgrInfo *normalizeprocinfo;
|
|
||||||
Oid collation;
|
Oid collation;
|
||||||
int dimensions = centers->dim;
|
int dimensions = centers->dim;
|
||||||
int numCenters = centers->maxlen;
|
int numCenters = centers->maxlen;
|
||||||
int numSamples = samples->length;
|
int numSamples = samples->length;
|
||||||
VectorArray newCenters;
|
VectorArray newCenters;
|
||||||
VectorArray aggCenters;
|
float *agg;
|
||||||
int *centerCounts;
|
int *centerCounts;
|
||||||
int *closestCenters;
|
int *closestCenters;
|
||||||
float *lowerBound;
|
float *lowerBound;
|
||||||
@@ -422,14 +273,12 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
|||||||
float *s;
|
float *s;
|
||||||
float *halfcdist;
|
float *halfcdist;
|
||||||
float *newcdist;
|
float *newcdist;
|
||||||
MemoryContext kmeansCtx;
|
|
||||||
MemoryContext oldCtx;
|
|
||||||
|
|
||||||
/* Calculate allocation sizes */
|
/* Calculate allocation sizes */
|
||||||
Size samplesSize = VECTOR_ARRAY_SIZE(samples->maxlen, samples->itemsize);
|
Size samplesSize = VECTOR_ARRAY_SIZE(samples->maxlen, samples->itemsize);
|
||||||
Size centersSize = VECTOR_ARRAY_SIZE(centers->maxlen, centers->itemsize);
|
Size centersSize = VECTOR_ARRAY_SIZE(centers->maxlen, centers->itemsize);
|
||||||
Size newCentersSize = VECTOR_ARRAY_SIZE(numCenters, centers->itemsize);
|
Size newCentersSize = VECTOR_ARRAY_SIZE(numCenters, centers->itemsize);
|
||||||
Size aggCentersSize = type == IVFFLAT_TYPE_VECTOR ? 0 : VECTOR_ARRAY_SIZE(numCenters, VECTOR_SIZE(dimensions));
|
Size aggSize = sizeof(float) * (int64) numCenters * dimensions;
|
||||||
Size centerCountsSize = sizeof(int) * numCenters;
|
Size centerCountsSize = sizeof(int) * numCenters;
|
||||||
Size closestCentersSize = sizeof(int) * numSamples;
|
Size closestCentersSize = sizeof(int) * numSamples;
|
||||||
Size lowerBoundSize = sizeof(float) * numSamples * numCenters;
|
Size lowerBoundSize = sizeof(float) * numSamples * numCenters;
|
||||||
@@ -439,7 +288,7 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
|||||||
Size newcdistSize = sizeof(float) * numCenters;
|
Size newcdistSize = sizeof(float) * numCenters;
|
||||||
|
|
||||||
/* Calculate total size */
|
/* Calculate total size */
|
||||||
Size totalSize = samplesSize + centersSize + newCentersSize + aggCentersSize + centerCountsSize + closestCentersSize + lowerBoundSize + upperBoundSize + sSize + halfcdistSize + newcdistSize;
|
Size totalSize = samplesSize + centersSize + newCentersSize + aggSize + centerCountsSize + closestCentersSize + lowerBoundSize + upperBoundSize + sSize + halfcdistSize + newcdistSize;
|
||||||
|
|
||||||
/* Check memory requirements */
|
/* Check memory requirements */
|
||||||
/* Add one to error message to ceil */
|
/* Add one to error message to ceil */
|
||||||
@@ -456,17 +305,11 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
|||||||
/* Set support functions */
|
/* Set support functions */
|
||||||
procinfo = index_getprocinfo(index, 1, IVFFLAT_KMEANS_DISTANCE_PROC);
|
procinfo = index_getprocinfo(index, 1, IVFFLAT_KMEANS_DISTANCE_PROC);
|
||||||
normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
||||||
normalizeprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORMALIZE_PROC);
|
|
||||||
collation = index->rd_indcollation[0];
|
collation = index->rd_indcollation[0];
|
||||||
|
|
||||||
/* Use memory context */
|
|
||||||
kmeansCtx = AllocSetContextCreate(CurrentMemoryContext,
|
|
||||||
"Ivfflat kmeans temporary context",
|
|
||||||
ALLOCSET_DEFAULT_SIZES);
|
|
||||||
oldCtx = MemoryContextSwitchTo(kmeansCtx);
|
|
||||||
|
|
||||||
/* Allocate space */
|
/* Allocate space */
|
||||||
/* Use float instead of double to save memory */
|
/* Use float instead of double to save memory */
|
||||||
|
agg = palloc(aggSize);
|
||||||
centerCounts = palloc(centerCountsSize);
|
centerCounts = palloc(centerCountsSize);
|
||||||
closestCenters = palloc(closestCentersSize);
|
closestCenters = palloc(closestCentersSize);
|
||||||
lowerBound = palloc_extended(lowerBoundSize, MCXT_ALLOC_HUGE);
|
lowerBound = palloc_extended(lowerBoundSize, MCXT_ALLOC_HUGE);
|
||||||
@@ -475,53 +318,12 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
|||||||
halfcdist = palloc_extended(halfcdistSize, MCXT_ALLOC_HUGE);
|
halfcdist = palloc_extended(halfcdistSize, MCXT_ALLOC_HUGE);
|
||||||
newcdist = palloc(newcdistSize);
|
newcdist = palloc(newcdistSize);
|
||||||
|
|
||||||
aggCenters = VectorArrayInit(numCenters, dimensions, VECTOR_SIZE(dimensions));
|
/* Initialize new centers */
|
||||||
aggCenters->length = numCenters;
|
newCenters = VectorArrayInit(numCenters, dimensions, centers->itemsize);
|
||||||
|
newCenters->length = numCenters;
|
||||||
for (int j = 0; j < numCenters; j++)
|
|
||||||
{
|
|
||||||
Vector *vec = (Vector *) VectorArrayGet(aggCenters, j);
|
|
||||||
|
|
||||||
SET_VARSIZE(vec, VECTOR_SIZE(dimensions));
|
|
||||||
vec->dim = dimensions;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == IVFFLAT_TYPE_VECTOR)
|
|
||||||
{
|
|
||||||
/* Use same centers to save memory */
|
|
||||||
newCenters = aggCenters;
|
|
||||||
}
|
|
||||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
|
||||||
{
|
|
||||||
newCenters = VectorArrayInit(numCenters, dimensions, centers->itemsize);
|
|
||||||
newCenters->length = numCenters;
|
|
||||||
|
|
||||||
for (int j = 0; j < numCenters; j++)
|
|
||||||
{
|
|
||||||
HalfVector *vec = (HalfVector *) VectorArrayGet(newCenters, j);
|
|
||||||
|
|
||||||
SET_VARSIZE(vec, HALFVEC_SIZE(dimensions));
|
|
||||||
vec->dim = dimensions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type == IVFFLAT_TYPE_BIT)
|
|
||||||
{
|
|
||||||
newCenters = VectorArrayInit(numCenters, dimensions, centers->itemsize);
|
|
||||||
newCenters->length = numCenters;
|
|
||||||
|
|
||||||
for (int j = 0; j < numCenters; j++)
|
|
||||||
{
|
|
||||||
VarBit *vec = (VarBit *) VectorArrayGet(newCenters, j);
|
|
||||||
|
|
||||||
SET_VARSIZE(vec, VARBITTOTALLEN(dimensions));
|
|
||||||
VARBITLEN(vec) = dimensions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
elog(ERROR, "Unsupported type");
|
|
||||||
|
|
||||||
#ifdef IVFFLAT_MEMORY
|
#ifdef IVFFLAT_MEMORY
|
||||||
ShowMemoryUsage(oldCtx, totalSize);
|
ShowMemoryUsage(MemoryContextGetParent(CurrentMemoryContext), totalSize);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Pick initial centers */
|
/* Pick initial centers */
|
||||||
@@ -658,7 +460,7 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Step 4: For each center c, let m(c) be mean of all points assigned */
|
/* Step 4: For each center c, let m(c) be mean of all points assigned */
|
||||||
ComputeNewCenters(samples, aggCenters, newCenters, centerCounts, closestCenters, normprocinfo, normalizeprocinfo, collation, type);
|
ComputeNewCenters(samples, agg, newCenters, centerCounts, closestCenters, normprocinfo, collation, typeInfo);
|
||||||
|
|
||||||
/* Step 5 */
|
/* Step 5 */
|
||||||
for (int j = 0; j < numCenters; j++)
|
for (int j = 0; j < numCenters; j++)
|
||||||
@@ -689,82 +491,68 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatTyp
|
|||||||
if (changes == 0 && iteration != 0)
|
if (changes == 0 && iteration != 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldCtx);
|
/*
|
||||||
MemoryContextDelete(kmeansCtx);
|
* Ensure no NaN or infinite values
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
CheckElements(VectorArray centers, const IvfflatTypeInfo * typeInfo)
|
||||||
|
{
|
||||||
|
float *scratch = palloc(sizeof(float) * centers->dim);
|
||||||
|
|
||||||
|
for (int i = 0; i < centers->length; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < centers->dim; j++)
|
||||||
|
scratch[j] = 0;
|
||||||
|
|
||||||
|
/* /fp:fast may not propagate NaN with MSVC, but that's alright */
|
||||||
|
typeInfo->sumCenter(VectorArrayGet(centers, i), scratch);
|
||||||
|
|
||||||
|
for (int j = 0; j < centers->dim; j++)
|
||||||
|
{
|
||||||
|
if (isnan(scratch[j]))
|
||||||
|
elog(ERROR, "NaN detected. Please report a bug.");
|
||||||
|
|
||||||
|
if (isinf(scratch[j]))
|
||||||
|
elog(ERROR, "Infinite value detected. Please report a bug.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure no zero vectors for cosine distance
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
CheckNorms(VectorArray centers, Relation index)
|
||||||
|
{
|
||||||
|
/* Check NORM_PROC instead of KMEANS_NORM_PROC */
|
||||||
|
FmgrInfo *normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
|
||||||
|
Oid collation = index->rd_indcollation[0];
|
||||||
|
|
||||||
|
if (normprocinfo == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < centers->length; i++)
|
||||||
|
{
|
||||||
|
double norm = DatumGetFloat8(FunctionCall1Coll(normprocinfo, collation, PointerGetDatum(VectorArrayGet(centers, i))));
|
||||||
|
|
||||||
|
if (norm == 0)
|
||||||
|
elog(ERROR, "Zero norm detected. Please report a bug.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Detect issues with centers
|
* Detect issues with centers
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
CheckCenters(Relation index, VectorArray centers, IvfflatType type)
|
CheckCenters(Relation index, VectorArray centers, const IvfflatTypeInfo * typeInfo)
|
||||||
{
|
{
|
||||||
FmgrInfo *normprocinfo;
|
|
||||||
|
|
||||||
if (centers->length != centers->maxlen)
|
if (centers->length != centers->maxlen)
|
||||||
elog(ERROR, "Not enough centers. Please report a bug.");
|
elog(ERROR, "Not enough centers. Please report a bug.");
|
||||||
|
|
||||||
/* Ensure no NaN or infinite values */
|
CheckElements(centers, typeInfo);
|
||||||
for (int i = 0; i < centers->length; i++)
|
CheckNorms(centers, index);
|
||||||
{
|
|
||||||
if (type == IVFFLAT_TYPE_VECTOR)
|
|
||||||
{
|
|
||||||
Vector *vec = (Vector *) VectorArrayGet(centers, i);
|
|
||||||
|
|
||||||
for (int j = 0; j < vec->dim; j++)
|
|
||||||
{
|
|
||||||
if (isnan(vec->x[j]))
|
|
||||||
elog(ERROR, "NaN detected. Please report a bug.");
|
|
||||||
|
|
||||||
if (isinf(vec->x[j]))
|
|
||||||
elog(ERROR, "Infinite value detected. Please report a bug.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type == IVFFLAT_TYPE_HALFVEC)
|
|
||||||
{
|
|
||||||
HalfVector *vec = (HalfVector *) VectorArrayGet(centers, i);
|
|
||||||
|
|
||||||
for (int j = 0; j < vec->dim; j++)
|
|
||||||
{
|
|
||||||
if (HalfIsNan(vec->x[j]))
|
|
||||||
elog(ERROR, "NaN detected. Please report a bug.");
|
|
||||||
|
|
||||||
if (HalfIsInf(vec->x[j]))
|
|
||||||
elog(ERROR, "Infinite value detected. Please report a bug.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (type != IVFFLAT_TYPE_BIT)
|
|
||||||
elog(ERROR, "Unsupported type");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type != IVFFLAT_TYPE_BIT)
|
|
||||||
{
|
|
||||||
/* Ensure no duplicate centers */
|
|
||||||
SortVectorArray(centers, type);
|
|
||||||
|
|
||||||
for (int i = 1; i < centers->length; i++)
|
|
||||||
{
|
|
||||||
if (datumIsEqual(PointerGetDatum(VectorArrayGet(centers, i)), PointerGetDatum(VectorArrayGet(centers, i - 1)), false, -1))
|
|
||||||
elog(ERROR, "Duplicate centers detected. Please report a bug.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure no zero vectors for cosine distance */
|
|
||||||
/* Check NORM_PROC instead of KMEANS_NORM_PROC */
|
|
||||||
normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
|
|
||||||
if (normprocinfo != NULL)
|
|
||||||
{
|
|
||||||
Oid collation = index->rd_indcollation[0];
|
|
||||||
|
|
||||||
for (int i = 0; i < centers->length; i++)
|
|
||||||
{
|
|
||||||
double norm = DatumGetFloat8(FunctionCall1Coll(normprocinfo, collation, PointerGetDatum(VectorArrayGet(centers, i))));
|
|
||||||
|
|
||||||
if (norm == 0)
|
|
||||||
elog(ERROR, "Zero norm detected. Please report a bug.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -772,12 +560,20 @@ CheckCenters(Relation index, VectorArray centers, IvfflatType type)
|
|||||||
* We use spherical k-means for inner product and cosine
|
* We use spherical k-means for inner product and cosine
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers, IvfflatType type)
|
IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers, const IvfflatTypeInfo * typeInfo)
|
||||||
{
|
{
|
||||||
if (samples->length <= centers->maxlen)
|
MemoryContext kmeansCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
QuickCenters(index, samples, centers, type);
|
"Ivfflat kmeans temporary context",
|
||||||
else
|
ALLOCSET_DEFAULT_SIZES);
|
||||||
ElkanKmeans(index, samples, centers, type);
|
MemoryContext oldCtx = MemoryContextSwitchTo(kmeansCtx);
|
||||||
|
|
||||||
CheckCenters(index, centers, type);
|
if (samples->length == 0)
|
||||||
|
RandomCenters(index, centers, typeInfo);
|
||||||
|
else
|
||||||
|
ElkanKmeans(index, samples, centers, typeInfo);
|
||||||
|
|
||||||
|
CheckCenters(index, centers, typeInfo);
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldCtx);
|
||||||
|
MemoryContextDelete(kmeansCtx);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,16 +11,23 @@
|
|||||||
#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 (((const IvfflatScanList *) a)->distance > ((const IvfflatScanList *) b)->distance)
|
if (GetScanListConst(a)->distance > GetScanListConst(b)->distance)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (((const IvfflatScanList *) a)->distance < ((const IvfflatScanList *) b)->distance)
|
if (GetScanListConst(a)->distance < GetScanListConst(b)->distance)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -72,14 +79,14 @@ GetScanLists(IndexScanDesc scan, Datum value)
|
|||||||
|
|
||||||
/* Calculate max distance */
|
/* Calculate max distance */
|
||||||
if (listCount == so->probes)
|
if (listCount == so->probes)
|
||||||
maxDistance = ((IvfflatScanList *) pairingheap_first(so->listQueue))->distance;
|
maxDistance = GetScanList(pairingheap_first(so->listQueue))->distance;
|
||||||
}
|
}
|
||||||
else if (distance < maxDistance)
|
else if (distance < maxDistance)
|
||||||
{
|
{
|
||||||
IvfflatScanList *scanlist;
|
IvfflatScanList *scanlist;
|
||||||
|
|
||||||
/* Remove */
|
/* Remove */
|
||||||
scanlist = (IvfflatScanList *) pairingheap_remove_first(so->listQueue);
|
scanlist = GetScanList(pairingheap_remove_first(so->listQueue));
|
||||||
|
|
||||||
/* Reuse */
|
/* Reuse */
|
||||||
scanlist->startPage = list->startPage;
|
scanlist->startPage = list->startPage;
|
||||||
@@ -87,7 +94,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 = ((IvfflatScanList *) pairingheap_first(so->listQueue))->distance;
|
maxDistance = GetScanList(pairingheap_first(so->listQueue))->distance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,19 +113,12 @@ 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 = MakeSingleTupleTableSlot(so->tupdesc, &TTSOpsVirtual);
|
TupleTableSlot *slot = so->vslot;
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 = ((IvfflatScanList *) pairingheap_remove_first(so->listQueue))->startPage;
|
BlockNumber searchPage = GetScanList(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, bas);
|
buf = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM, searchPage, RBM_NORMAL, so->bas);
|
||||||
LockBuffer(buf, BUFFER_LOCK_SHARE);
|
LockBuffer(buf, BUFFER_LOCK_SHARE);
|
||||||
page = BufferGetPage(buf);
|
page = BufferGetPage(buf);
|
||||||
maxoffno = PageGetMaxOffsetNumber(page);
|
maxoffno = PageGetMaxOffsetNumber(page);
|
||||||
@@ -166,8 +166,6 @@ 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"),
|
||||||
@@ -209,14 +207,28 @@ GetScanValue(IndexScanDesc scan)
|
|||||||
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
|
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
|
||||||
Assert(!VARATT_IS_EXTENDED(DatumGetPointer(value)));
|
Assert(!VARATT_IS_EXTENDED(DatumGetPointer(value)));
|
||||||
|
|
||||||
/* Check normprocinfo since normalizeprocinfo not set for vector */
|
/* Normalize if needed */
|
||||||
if (so->normprocinfo != NULL)
|
if (so->normprocinfo != NULL)
|
||||||
value = IvfflatNormValue(so->normalizeprocinfo, so->collation, value);
|
value = IvfflatNormValue(so->typeInfo, so->collation, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
*/
|
*/
|
||||||
@@ -227,10 +239,6 @@ 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);
|
||||||
@@ -242,6 +250,7 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
|
|||||||
probes = lists;
|
probes = lists;
|
||||||
|
|
||||||
so = (IvfflatScanOpaque) palloc(offsetof(IvfflatScanOpaqueData, lists) + probes * sizeof(IvfflatScanList));
|
so = (IvfflatScanOpaque) palloc(offsetof(IvfflatScanOpaqueData, lists) + probes * sizeof(IvfflatScanList));
|
||||||
|
so->typeInfo = IvfflatGetTypeInfo(index);
|
||||||
so->first = true;
|
so->first = true;
|
||||||
so->probes = probes;
|
so->probes = probes;
|
||||||
so->dimensions = dimensions;
|
so->dimensions = dimensions;
|
||||||
@@ -249,7 +258,6 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
|
|||||||
/* Set support functions */
|
/* Set support functions */
|
||||||
so->procinfo = index_getprocinfo(index, 1, IVFFLAT_DISTANCE_PROC);
|
so->procinfo = index_getprocinfo(index, 1, IVFFLAT_DISTANCE_PROC);
|
||||||
so->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
|
so->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
|
||||||
so->normalizeprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORMALIZE_PROC);
|
|
||||||
so->collation = index->rd_indcollation[0];
|
so->collation = index->rd_indcollation[0];
|
||||||
|
|
||||||
/* Create tuple description for sorting */
|
/* Create tuple description for sorting */
|
||||||
@@ -258,9 +266,18 @@ 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 = tuplesort_begin_heap(so->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, work_mem, NULL, false);
|
so->sortstate = InitScanSortState(so->tupdesc);
|
||||||
|
|
||||||
so->slot = MakeSingleTupleTableSlot(so->tupdesc, &TTSOpsMinimalTuple);
|
/* Need separate slots for puttuple and gettuple */
|
||||||
|
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);
|
||||||
|
|
||||||
@@ -277,10 +294,8 @@ 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);
|
||||||
@@ -327,14 +342,19 @@ 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->slot, NULL))
|
if (tuplesort_gettupleslot(so->sortstate, true, false, so->mslot, NULL))
|
||||||
{
|
{
|
||||||
ItemPointer heaptid = (ItemPointer) DatumGetPointer(slot_getattr(so->slot, 2, &so->isnull));
|
bool 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;
|
||||||
@@ -355,6 +375,10 @@ 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;
|
||||||
|
|||||||
187
src/ivfutils.c
187
src/ivfutils.c
@@ -1,8 +1,11 @@
|
|||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "access/generic_xlog.h"
|
#include "access/generic_xlog.h"
|
||||||
|
#include "bitvec.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
|
#include "halfutils.h"
|
||||||
|
#include "halfvec.h"
|
||||||
#include "ivfflat.h"
|
#include "ivfflat.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
|
|
||||||
@@ -61,37 +64,13 @@ IvfflatOptionalProcInfo(Relation index, uint16 procnum)
|
|||||||
return index_getprocinfo(index, 1, procnum);
|
return index_getprocinfo(index, 1, procnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Get type
|
|
||||||
*/
|
|
||||||
IvfflatType
|
|
||||||
IvfflatGetType(Relation index)
|
|
||||||
{
|
|
||||||
FmgrInfo *procinfo = IvfflatOptionalProcInfo(index, IVFFLAT_TYPE_SUPPORT_PROC);
|
|
||||||
Oid typid = TupleDescAttr(index->rd_att, 0)->atttypid;
|
|
||||||
IvfflatType result;
|
|
||||||
|
|
||||||
if (procinfo == NULL)
|
|
||||||
return IVFFLAT_TYPE_VECTOR;
|
|
||||||
|
|
||||||
result = (IvfflatType) DatumGetInt32(FunctionCall1(procinfo, ObjectIdGetDatum(typid)));
|
|
||||||
|
|
||||||
if (result == IVFFLAT_TYPE_UNSUPPORTED)
|
|
||||||
elog(ERROR, "type not supported for ivfflat index");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Normalize value
|
* Normalize value
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum value)
|
IvfflatNormValue(const IvfflatTypeInfo * typeInfo, Oid collation, Datum value)
|
||||||
{
|
{
|
||||||
if (procinfo == NULL)
|
return DirectFunctionCall1Coll(typeInfo->normalize, collation, value);
|
||||||
return DirectFunctionCall1(l2_normalize, value);
|
|
||||||
|
|
||||||
return FunctionCall1Coll(procinfo, collation, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -191,7 +170,11 @@ IvfflatGetMetaPageInfo(Relation index, int *lists, int *dimensions)
|
|||||||
page = BufferGetPage(buf);
|
page = BufferGetPage(buf);
|
||||||
metap = IvfflatPageGetMeta(page);
|
metap = IvfflatPageGetMeta(page);
|
||||||
|
|
||||||
*lists = metap->lists;
|
if (unlikely(metap->magicNumber != IVFFLAT_MAGIC_NUMBER))
|
||||||
|
elog(ERROR, "ivfflat index is not valid");
|
||||||
|
|
||||||
|
if (lists != NULL)
|
||||||
|
*lists = metap->lists;
|
||||||
|
|
||||||
if (dimensions != NULL)
|
if (dimensions != NULL)
|
||||||
*dimensions = metap->dimensions;
|
*dimensions = metap->dimensions;
|
||||||
@@ -246,21 +229,145 @@ IvfflatUpdateList(Relation index, ListInfo listInfo,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_ivfflat_support);
|
PGDLLEXPORT Datum l2_normalize(PG_FUNCTION_ARGS);
|
||||||
Datum
|
PGDLLEXPORT Datum halfvec_l2_normalize(PG_FUNCTION_ARGS);
|
||||||
halfvec_ivfflat_support(PG_FUNCTION_ARGS)
|
PGDLLEXPORT Datum sparsevec_l2_normalize(PG_FUNCTION_ARGS);
|
||||||
{
|
|
||||||
PG_RETURN_INT32(IVFFLAT_TYPE_HALFVEC);
|
|
||||||
};
|
|
||||||
|
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(bit_ivfflat_support);
|
static Size
|
||||||
Datum
|
VectorItemSize(int dimensions)
|
||||||
bit_ivfflat_support(PG_FUNCTION_ARGS)
|
|
||||||
{
|
{
|
||||||
Oid typid = PG_GETARG_OID(0);
|
return VECTOR_SIZE(dimensions);
|
||||||
|
}
|
||||||
|
|
||||||
if (typid == BITOID)
|
static Size
|
||||||
PG_RETURN_INT32(IVFFLAT_TYPE_BIT);
|
HalfvecItemSize(int dimensions)
|
||||||
|
{
|
||||||
|
return HALFVEC_SIZE(dimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Size
|
||||||
|
BitItemSize(int dimensions)
|
||||||
|
{
|
||||||
|
return VARBITTOTALLEN(dimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
VectorUpdateCenter(Pointer v, int dimensions, float *x)
|
||||||
|
{
|
||||||
|
Vector *vec = (Vector *) v;
|
||||||
|
|
||||||
|
SET_VARSIZE(vec, VECTOR_SIZE(dimensions));
|
||||||
|
vec->dim = dimensions;
|
||||||
|
|
||||||
|
for (int k = 0; k < dimensions; k++)
|
||||||
|
vec->x[k] = x[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
HalfvecUpdateCenter(Pointer v, int dimensions, float *x)
|
||||||
|
{
|
||||||
|
HalfVector *vec = (HalfVector *) v;
|
||||||
|
|
||||||
|
SET_VARSIZE(vec, HALFVEC_SIZE(dimensions));
|
||||||
|
vec->dim = dimensions;
|
||||||
|
|
||||||
|
for (int k = 0; k < dimensions; k++)
|
||||||
|
vec->x[k] = Float4ToHalfUnchecked(x[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
BitUpdateCenter(Pointer v, int dimensions, float *x)
|
||||||
|
{
|
||||||
|
VarBit *vec = (VarBit *) v;
|
||||||
|
unsigned char *nx = VARBITS(vec);
|
||||||
|
|
||||||
|
SET_VARSIZE(vec, VARBITTOTALLEN(dimensions));
|
||||||
|
VARBITLEN(vec) = dimensions;
|
||||||
|
|
||||||
|
for (uint32 k = 0; k < VARBITBYTES(vec); k++)
|
||||||
|
nx[k] = 0;
|
||||||
|
|
||||||
|
for (int k = 0; k < dimensions; k++)
|
||||||
|
nx[k / 8] |= (x[k] > 0.5 ? 1 : 0) << (7 - (k % 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
VectorSumCenter(Pointer v, float *x)
|
||||||
|
{
|
||||||
|
Vector *vec = (Vector *) v;
|
||||||
|
|
||||||
|
for (int k = 0; k < vec->dim; k++)
|
||||||
|
x[k] += vec->x[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
HalfvecSumCenter(Pointer v, float *x)
|
||||||
|
{
|
||||||
|
HalfVector *vec = (HalfVector *) v;
|
||||||
|
|
||||||
|
for (int k = 0; k < vec->dim; k++)
|
||||||
|
x[k] += HalfToFloat4(vec->x[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
BitSumCenter(Pointer v, float *x)
|
||||||
|
{
|
||||||
|
VarBit *vec = (VarBit *) v;
|
||||||
|
|
||||||
|
for (int k = 0; k < VARBITLEN(vec); k++)
|
||||||
|
x[k] += (float) (((VARBITS(vec)[k / 8]) >> (7 - (k % 8))) & 0x01);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get type info
|
||||||
|
*/
|
||||||
|
const IvfflatTypeInfo *
|
||||||
|
IvfflatGetTypeInfo(Relation index)
|
||||||
|
{
|
||||||
|
FmgrInfo *procinfo = IvfflatOptionalProcInfo(index, IVFFLAT_TYPE_INFO_PROC);
|
||||||
|
|
||||||
|
if (procinfo == NULL)
|
||||||
|
{
|
||||||
|
static const IvfflatTypeInfo typeInfo = {
|
||||||
|
.maxDimensions = IVFFLAT_MAX_DIM,
|
||||||
|
.normalize = l2_normalize,
|
||||||
|
.itemSize = VectorItemSize,
|
||||||
|
.updateCenter = VectorUpdateCenter,
|
||||||
|
.sumCenter = VectorSumCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
return (&typeInfo);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
PG_RETURN_INT32(IVFFLAT_TYPE_UNSUPPORTED);
|
return (const IvfflatTypeInfo *) DatumGetPointer(FunctionCall0Coll(procinfo, InvalidOid));
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(ivfflat_halfvec_support);
|
||||||
|
Datum
|
||||||
|
ivfflat_halfvec_support(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
static const IvfflatTypeInfo typeInfo = {
|
||||||
|
.maxDimensions = IVFFLAT_MAX_DIM * 2,
|
||||||
|
.normalize = halfvec_l2_normalize,
|
||||||
|
.itemSize = HalfvecItemSize,
|
||||||
|
.updateCenter = HalfvecUpdateCenter,
|
||||||
|
.sumCenter = HalfvecSumCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(&typeInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(ivfflat_bit_support);
|
||||||
|
Datum
|
||||||
|
ivfflat_bit_support(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
static const IvfflatTypeInfo typeInfo = {
|
||||||
|
.maxDimensions = IVFFLAT_MAX_DIM * 32,
|
||||||
|
.normalize = NULL,
|
||||||
|
.itemSize = BitItemSize,
|
||||||
|
.updateCenter = BitUpdateCenter,
|
||||||
|
.sumCenter = BitSumCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(&typeInfo);
|
||||||
};
|
};
|
||||||
|
|||||||
249
src/sparsevec.c
249
src/sparsevec.c
@@ -3,6 +3,8 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
|
#include "common/string.h"
|
||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
#include "halfutils.h"
|
#include "halfutils.h"
|
||||||
#include "halfvec.h"
|
#include "halfvec.h"
|
||||||
@@ -10,6 +12,7 @@
|
|||||||
#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
|
||||||
@@ -97,27 +100,24 @@ CheckIndex(int32 *indices, int i, int dim)
|
|||||||
{
|
{
|
||||||
int32 index = indices[i];
|
int32 index = indices[i];
|
||||||
|
|
||||||
if (index < 1)
|
if (index < 0 || index >= dim)
|
||||||
|
{
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||||
errmsg("index must be greater than zero")));
|
errmsg("sparsevec index out of bounds")));
|
||||||
|
}
|
||||||
if (index > dim)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
|
||||||
errmsg("index must be less than or equal to dimensions")));
|
|
||||||
|
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
{
|
{
|
||||||
if (index < indices[i - 1])
|
if (index < indices[i - 1])
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||||
errmsg("indexes must be in ascending order")));
|
errmsg("sparsevec indices must be in ascending order")));
|
||||||
|
|
||||||
if (index == indices[i - 1])
|
if (index == indices[i - 1])
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||||
errmsg("indexes must not contain duplicates")));
|
errmsg("sparsevec indices must not contain duplicates")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,13 +190,13 @@ CompareIndices(const void *a, const void *b)
|
|||||||
/*
|
/*
|
||||||
* Convert textual representation to internal representation
|
* Convert textual representation to internal representation
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_in);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_in);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_in(PG_FUNCTION_ARGS)
|
sparsevec_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
char *lit = PG_GETARG_CSTRING(0);
|
char *lit = PG_GETARG_CSTRING(0);
|
||||||
int32 typmod = PG_GETARG_INT32(2);
|
int32 typmod = PG_GETARG_INT32(2);
|
||||||
int dim;
|
long dim;
|
||||||
char *pt = lit;
|
char *pt = lit;
|
||||||
char *stringEnd;
|
char *stringEnd;
|
||||||
SparseVector *result;
|
SparseVector *result;
|
||||||
@@ -246,7 +246,6 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
|||||||
long index;
|
long index;
|
||||||
float value;
|
float value;
|
||||||
|
|
||||||
/* TODO Better error */
|
|
||||||
if (nnz == maxNnz)
|
if (nnz == maxNnz)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||||
@@ -262,7 +261,6 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
|||||||
errmsg("invalid input syntax for type sparsevec: \"%s\"", lit)));
|
errmsg("invalid input syntax for type sparsevec: \"%s\"", lit)));
|
||||||
|
|
||||||
/* Use similar logic as int2vectorin */
|
/* Use similar logic as int2vectorin */
|
||||||
errno = 0;
|
|
||||||
index = strtol(pt, &stringEnd, 10);
|
index = strtol(pt, &stringEnd, 10);
|
||||||
|
|
||||||
if (stringEnd == pt)
|
if (stringEnd == pt)
|
||||||
@@ -270,10 +268,11 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
|||||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||||
errmsg("invalid input syntax for type sparsevec: \"%s\"", lit)));
|
errmsg("invalid input syntax for type sparsevec: \"%s\"", lit)));
|
||||||
|
|
||||||
if (errno == ERANGE || index < 1 || index > INT_MAX)
|
/* Keep in int range for correct error message later */
|
||||||
ereport(ERROR,
|
if (index > INT_MAX)
|
||||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
index = INT_MAX;
|
||||||
errmsg("index \"%ld\" is out of range for type sparsevec", index)));
|
else if (index < INT_MIN + 1)
|
||||||
|
index = INT_MIN + 1;
|
||||||
|
|
||||||
pt = stringEnd;
|
pt = stringEnd;
|
||||||
|
|
||||||
@@ -312,7 +311,8 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
|||||||
/* Do not store zero values */
|
/* Do not store zero values */
|
||||||
if (value != 0)
|
if (value != 0)
|
||||||
{
|
{
|
||||||
elements[nnz].index = index;
|
/* Convert 1-based numbering (SQL) to 0-based (C) */
|
||||||
|
elements[nnz].index = index - 1;
|
||||||
elements[nnz].value = value;
|
elements[nnz].value = value;
|
||||||
nnz++;
|
nnz++;
|
||||||
}
|
}
|
||||||
@@ -351,7 +351,6 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
|||||||
pt++;
|
pt++;
|
||||||
|
|
||||||
/* Use similar logic as int2vectorin */
|
/* Use similar logic as int2vectorin */
|
||||||
errno = 0;
|
|
||||||
dim = strtol(pt, &stringEnd, 10);
|
dim = strtol(pt, &stringEnd, 10);
|
||||||
|
|
||||||
if (stringEnd == pt)
|
if (stringEnd == pt)
|
||||||
@@ -359,6 +358,12 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
|||||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||||
errmsg("invalid input syntax for type sparsevec: \"%s\"", lit)));
|
errmsg("invalid input syntax for type sparsevec: \"%s\"", lit)));
|
||||||
|
|
||||||
|
/* Keep in int range for correct error message later */
|
||||||
|
if (dim > INT_MAX)
|
||||||
|
dim = INT_MAX;
|
||||||
|
else if (dim < INT_MIN)
|
||||||
|
dim = INT_MIN;
|
||||||
|
|
||||||
pt = stringEnd;
|
pt = stringEnd;
|
||||||
|
|
||||||
/* Only whitespace is allowed after the closing brace */
|
/* Only whitespace is allowed after the closing brace */
|
||||||
@@ -406,7 +411,7 @@ sparsevec_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to textual representation
|
* Convert internal representation to textual representation
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_out);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_out);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_out(PG_FUNCTION_ARGS)
|
sparsevec_out(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -441,7 +446,8 @@ sparsevec_out(PG_FUNCTION_ARGS)
|
|||||||
if (i > 0)
|
if (i > 0)
|
||||||
AppendChar(ptr, ',');
|
AppendChar(ptr, ',');
|
||||||
|
|
||||||
AppendInt(ptr, sparsevec->indices[i]);
|
/* Convert 0-based numbering (C) to 1-based (SQL) */
|
||||||
|
AppendInt(ptr, sparsevec->indices[i] + 1);
|
||||||
AppendChar(ptr, ':');
|
AppendChar(ptr, ':');
|
||||||
AppendFloat(ptr, values[i]);
|
AppendFloat(ptr, values[i]);
|
||||||
}
|
}
|
||||||
@@ -458,7 +464,7 @@ sparsevec_out(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert type modifier
|
* Convert type modifier
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_typmod_in);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_typmod_in);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_typmod_in(PG_FUNCTION_ARGS)
|
sparsevec_typmod_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -489,7 +495,7 @@ sparsevec_typmod_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert external binary representation to internal representation
|
* Convert external binary representation to internal representation
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_recv);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_recv);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_recv(PG_FUNCTION_ARGS)
|
sparsevec_recv(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -517,6 +523,7 @@ sparsevec_recv(PG_FUNCTION_ARGS)
|
|||||||
result = InitSparseVector(dim, nnz);
|
result = InitSparseVector(dim, nnz);
|
||||||
values = SPARSEVEC_VALUES(result);
|
values = SPARSEVEC_VALUES(result);
|
||||||
|
|
||||||
|
/* Binary representation uses zero-based numbering for indices */
|
||||||
for (int i = 0; i < nnz; i++)
|
for (int i = 0; i < nnz; i++)
|
||||||
{
|
{
|
||||||
result->indices[i] = pq_getmsgint(buf, sizeof(int32));
|
result->indices[i] = pq_getmsgint(buf, sizeof(int32));
|
||||||
@@ -527,6 +534,7 @@ sparsevec_recv(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
values[i] = pq_getmsgfloat4(buf);
|
values[i] = pq_getmsgfloat4(buf);
|
||||||
CheckElement(values[i]);
|
CheckElement(values[i]);
|
||||||
|
|
||||||
if (values[i] == 0)
|
if (values[i] == 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATA_EXCEPTION),
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||||
@@ -539,7 +547,7 @@ sparsevec_recv(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to the external binary representation
|
* Convert internal representation to the external binary representation
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_send);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_send);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_send(PG_FUNCTION_ARGS)
|
sparsevec_send(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -551,8 +559,11 @@ sparsevec_send(PG_FUNCTION_ARGS)
|
|||||||
pq_sendint(&buf, svec->dim, sizeof(int32));
|
pq_sendint(&buf, svec->dim, sizeof(int32));
|
||||||
pq_sendint(&buf, svec->nnz, sizeof(int32));
|
pq_sendint(&buf, svec->nnz, sizeof(int32));
|
||||||
pq_sendint(&buf, svec->unused, sizeof(int32));
|
pq_sendint(&buf, svec->unused, sizeof(int32));
|
||||||
|
|
||||||
|
/* Binary representation uses zero-based numbering for indices */
|
||||||
for (int i = 0; i < svec->nnz; i++)
|
for (int i = 0; i < svec->nnz; i++)
|
||||||
pq_sendint(&buf, svec->indices[i], sizeof(int32));
|
pq_sendint(&buf, svec->indices[i], sizeof(int32));
|
||||||
|
|
||||||
for (int i = 0; i < svec->nnz; i++)
|
for (int i = 0; i < svec->nnz; i++)
|
||||||
pq_sendfloat4(&buf, values[i]);
|
pq_sendfloat4(&buf, values[i]);
|
||||||
|
|
||||||
@@ -563,7 +574,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
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec);
|
||||||
Datum
|
Datum
|
||||||
sparsevec(PG_FUNCTION_ARGS)
|
sparsevec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -578,7 +589,7 @@ sparsevec(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert dense vector to sparse vector
|
* Convert dense vector to sparse vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_to_sparsevec);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_to_sparsevec);
|
||||||
Datum
|
Datum
|
||||||
vector_to_sparsevec(PG_FUNCTION_ARGS)
|
vector_to_sparsevec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -609,7 +620,7 @@ vector_to_sparsevec(PG_FUNCTION_ARGS)
|
|||||||
if (j >= result->nnz)
|
if (j >= result->nnz)
|
||||||
elog(ERROR, "safety check failed");
|
elog(ERROR, "safety check failed");
|
||||||
|
|
||||||
result->indices[j] = i + 1;
|
result->indices[j] = i;
|
||||||
values[j] = vec->x[i];
|
values[j] = vec->x[i];
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
@@ -621,7 +632,7 @@ vector_to_sparsevec(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert half vector to sparse vector
|
* Convert half vector to sparse vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_to_sparsevec);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_to_sparsevec);
|
||||||
Datum
|
Datum
|
||||||
halfvec_to_sparsevec(PG_FUNCTION_ARGS)
|
halfvec_to_sparsevec(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -652,7 +663,7 @@ halfvec_to_sparsevec(PG_FUNCTION_ARGS)
|
|||||||
if (j >= result->nnz)
|
if (j >= result->nnz)
|
||||||
elog(ERROR, "safety check failed");
|
elog(ERROR, "safety check failed");
|
||||||
|
|
||||||
result->indices[j] = i + 1;
|
result->indices[j] = i;
|
||||||
values[j] = HalfToFloat4(vec->x[i]);
|
values[j] = HalfToFloat4(vec->x[i]);
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
@@ -661,6 +672,137 @@ 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
|
||||||
*/
|
*/
|
||||||
@@ -683,7 +825,7 @@ SparsevecL2SquaredDistance(SparseVector * a, SparseVector * b)
|
|||||||
|
|
||||||
if (ai == bi)
|
if (ai == bi)
|
||||||
{
|
{
|
||||||
double diff = ax[i] - bx[j];
|
float diff = ax[i] - bx[j];
|
||||||
|
|
||||||
distance += diff * diff;
|
distance += diff * diff;
|
||||||
}
|
}
|
||||||
@@ -712,7 +854,7 @@ SparsevecL2SquaredDistance(SparseVector * a, SparseVector * b)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 distance between sparse vectors
|
* Get the L2 distance between sparse vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l2_distance);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_l2_distance(PG_FUNCTION_ARGS)
|
sparsevec_l2_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -721,14 +863,14 @@ sparsevec_l2_distance(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
CheckDims(a, b);
|
CheckDims(a, b);
|
||||||
|
|
||||||
PG_RETURN_FLOAT8(sqrt(SparsevecL2SquaredDistance(a, b)));
|
PG_RETURN_FLOAT8(sqrt((double) SparsevecL2SquaredDistance(a, b)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_squared_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l2_squared_distance);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_l2_squared_distance(PG_FUNCTION_ARGS)
|
sparsevec_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -737,7 +879,7 @@ sparsevec_l2_squared_distance(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
CheckDims(a, b);
|
CheckDims(a, b);
|
||||||
|
|
||||||
PG_RETURN_FLOAT8(SparsevecL2SquaredDistance(a, b));
|
PG_RETURN_FLOAT8((double) SparsevecL2SquaredDistance(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -779,7 +921,7 @@ SparsevecInnerProduct(SparseVector * a, SparseVector * b)
|
|||||||
/*
|
/*
|
||||||
* Get the inner product of two sparse vectors
|
* Get the inner product of two sparse vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_inner_product);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_inner_product);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_inner_product(PG_FUNCTION_ARGS)
|
sparsevec_inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -788,13 +930,13 @@ sparsevec_inner_product(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
CheckDims(a, b);
|
CheckDims(a, b);
|
||||||
|
|
||||||
PG_RETURN_FLOAT8(SparsevecInnerProduct(a, b));
|
PG_RETURN_FLOAT8((double) SparsevecInnerProduct(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the negative inner product of two sparse vectors
|
* Get the negative inner product of two sparse vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_negative_inner_product);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_negative_inner_product);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_negative_inner_product(PG_FUNCTION_ARGS)
|
sparsevec_negative_inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -803,13 +945,13 @@ sparsevec_negative_inner_product(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
CheckDims(a, b);
|
CheckDims(a, b);
|
||||||
|
|
||||||
PG_RETURN_FLOAT8(-SparsevecInnerProduct(a, b));
|
PG_RETURN_FLOAT8((double) -SparsevecInnerProduct(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the cosine distance between two sparse vectors
|
* Get the cosine distance between two sparse vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_cosine_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_cosine_distance);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_cosine_distance(PG_FUNCTION_ARGS)
|
sparsevec_cosine_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -854,7 +996,7 @@ sparsevec_cosine_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L1 distance between two sparse vectors
|
* Get the L1 distance between two sparse vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l1_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l1_distance);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_l1_distance(PG_FUNCTION_ARGS)
|
sparsevec_l1_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -862,7 +1004,7 @@ sparsevec_l1_distance(PG_FUNCTION_ARGS)
|
|||||||
SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
|
SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
|
||||||
float *ax = SPARSEVEC_VALUES(a);
|
float *ax = SPARSEVEC_VALUES(a);
|
||||||
float *bx = SPARSEVEC_VALUES(b);
|
float *bx = SPARSEVEC_VALUES(b);
|
||||||
double distance = 0.0;
|
float distance = 0.0;
|
||||||
int bpos = 0;
|
int bpos = 0;
|
||||||
|
|
||||||
CheckDims(a, b);
|
CheckDims(a, b);
|
||||||
@@ -897,13 +1039,13 @@ sparsevec_l1_distance(PG_FUNCTION_ARGS)
|
|||||||
for (int j = bpos; j < b->nnz; j++)
|
for (int j = bpos; j < b->nnz; j++)
|
||||||
distance += fabsf(bx[j]);
|
distance += fabsf(bx[j]);
|
||||||
|
|
||||||
PG_RETURN_FLOAT8(distance);
|
PG_RETURN_FLOAT8((double) distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the L2 norm of a sparse vector
|
* Get the L2 norm of a sparse vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_norm);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l2_norm);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_l2_norm(PG_FUNCTION_ARGS)
|
sparsevec_l2_norm(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -921,7 +1063,7 @@ sparsevec_l2_norm(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Normalize a sparse vector with the L2 norm
|
* Normalize a sparse vector with the L2 norm
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_l2_normalize);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_l2_normalize);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_l2_normalize(PG_FUNCTION_ARGS)
|
sparsevec_l2_normalize(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1013,11 +1155,10 @@ sparsevec_cmp_internal(SparseVector * a, SparseVector * b)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check <= dim since indices start at 1 */
|
if (a->nnz < b->nnz && b->indices[nnz] < a->dim)
|
||||||
if (a->nnz < b->nnz && b->indices[nnz] <= a->dim)
|
|
||||||
return bx[nnz] < 0 ? 1 : -1;
|
return bx[nnz] < 0 ? 1 : -1;
|
||||||
|
|
||||||
if (a->nnz > b->nnz && a->indices[nnz] <= b->dim)
|
if (a->nnz > b->nnz && a->indices[nnz] < b->dim)
|
||||||
return ax[nnz] < 0 ? -1 : 1;
|
return ax[nnz] < 0 ? -1 : 1;
|
||||||
|
|
||||||
if (a->dim < b->dim)
|
if (a->dim < b->dim)
|
||||||
@@ -1032,7 +1173,7 @@ sparsevec_cmp_internal(SparseVector * a, SparseVector * b)
|
|||||||
/*
|
/*
|
||||||
* Less than
|
* Less than
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_lt);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_lt);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_lt(PG_FUNCTION_ARGS)
|
sparsevec_lt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1045,7 +1186,7 @@ sparsevec_lt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Less than or equal
|
* Less than or equal
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_le);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_le);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_le(PG_FUNCTION_ARGS)
|
sparsevec_le(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1058,7 +1199,7 @@ sparsevec_le(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Equal
|
* Equal
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_eq);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_eq);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_eq(PG_FUNCTION_ARGS)
|
sparsevec_eq(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1071,7 +1212,7 @@ sparsevec_eq(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Not equal
|
* Not equal
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_ne);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_ne);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_ne(PG_FUNCTION_ARGS)
|
sparsevec_ne(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1084,7 +1225,7 @@ sparsevec_ne(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than or equal
|
* Greater than or equal
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_ge);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_ge);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_ge(PG_FUNCTION_ARGS)
|
sparsevec_ge(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1097,7 +1238,7 @@ sparsevec_ge(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than
|
* Greater than
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_gt);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_gt);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_gt(PG_FUNCTION_ARGS)
|
sparsevec_gt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1110,7 +1251,7 @@ sparsevec_gt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Compare sparse vectors
|
* Compare sparse vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_cmp);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_cmp);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_cmp(PG_FUNCTION_ARGS)
|
sparsevec_cmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,27 +1,40 @@
|
|||||||
#ifndef SPARSEVEC_H
|
#ifndef SPARSEVEC_H
|
||||||
#define SPARSEVEC_H
|
#define SPARSEVEC_H
|
||||||
|
|
||||||
#include "fmgr.h"
|
#define SPARSEVEC_MAX_DIM 1000000000
|
||||||
|
|
||||||
#define SPARSEVEC_MAX_DIM 100000
|
|
||||||
#define SPARSEVEC_MAX_NNZ 16000
|
#define SPARSEVEC_MAX_NNZ 16000
|
||||||
|
|
||||||
/* Ensure values are aligned */
|
|
||||||
#define SPARSEVEC_SIZE(_nnz) (offsetof(SparseVector, indices) + MAXALIGN((_nnz) * sizeof(int32)) + (_nnz * sizeof(float)))
|
|
||||||
#define SPARSEVEC_VALUES(x) ((float *) (((char *) (x)) + offsetof(SparseVector, indices) + MAXALIGN((x)->nnz * sizeof(int32))))
|
|
||||||
#define DatumGetSparseVector(x) ((SparseVector *) PG_DETOAST_DATUM(x))
|
#define DatumGetSparseVector(x) ((SparseVector *) PG_DETOAST_DATUM(x))
|
||||||
#define PG_GETARG_SPARSEVEC_P(x) DatumGetSparseVector(PG_GETARG_DATUM(x))
|
#define PG_GETARG_SPARSEVEC_P(x) DatumGetSparseVector(PG_GETARG_DATUM(x))
|
||||||
#define PG_RETURN_SPARSEVEC_P(x) PG_RETURN_POINTER(x)
|
#define PG_RETURN_SPARSEVEC_P(x) PG_RETURN_POINTER(x)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Indices use 0-based numbering for the on-disk (and binary) format (consistent with C)
|
||||||
|
* and are always sorted. Values come after indices.
|
||||||
|
*/
|
||||||
typedef struct SparseVector
|
typedef struct SparseVector
|
||||||
{
|
{
|
||||||
int32 vl_len_; /* varlena header (do not touch directly!) */
|
int32 vl_len_; /* varlena header (do not touch directly!) */
|
||||||
int32 dim; /* number of dimensions */
|
int32 dim; /* number of dimensions */
|
||||||
int32 nnz;
|
int32 nnz; /* number of non-zero elements */
|
||||||
int32 unused;
|
int32 unused; /* reserved for future use, always zero */
|
||||||
int32 indices[FLEXIBLE_ARRAY_MEMBER];
|
int32 indices[FLEXIBLE_ARRAY_MEMBER];
|
||||||
} SparseVector;
|
} SparseVector;
|
||||||
|
|
||||||
|
/* Use functions instead of macros to avoid double evaluation */
|
||||||
|
|
||||||
|
static inline Size
|
||||||
|
SPARSEVEC_SIZE(int nnz)
|
||||||
|
{
|
||||||
|
return offsetof(SparseVector, indices) + (nnz * sizeof(int32)) + (nnz * sizeof(float));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float *
|
||||||
|
SPARSEVEC_VALUES(SparseVector * x)
|
||||||
|
{
|
||||||
|
return (float *) (((char *) x) + offsetof(SparseVector, indices) + (x->nnz * sizeof(int32)));
|
||||||
|
}
|
||||||
|
|
||||||
SparseVector *InitSparseVector(int dim, int nnz);
|
SparseVector *InitSparseVector(int dim, int nnz);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
123
src/vector.c
123
src/vector.c
@@ -26,11 +26,6 @@
|
|||||||
#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))
|
||||||
|
|
||||||
@@ -160,28 +155,10 @@ CheckStateArray(ArrayType *statearray, const char *caller)
|
|||||||
return (float8 *) ARR_DATA_PTR(statearray);
|
return (float8 *) ARR_DATA_PTR(statearray);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 120003
|
|
||||||
static pg_noinline void
|
|
||||||
float_overflow_error(void)
|
|
||||||
{
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("value out of range: overflow")));
|
|
||||||
}
|
|
||||||
|
|
||||||
static pg_noinline void
|
|
||||||
float_underflow_error(void)
|
|
||||||
{
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("value out of range: underflow")));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert textual representation to internal representation
|
* Convert textual representation to internal representation
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_in);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_in);
|
||||||
Datum
|
Datum
|
||||||
vector_in(PG_FUNCTION_ARGS)
|
vector_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -294,7 +271,7 @@ vector_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to textual representation
|
* Convert internal representation to textual representation
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_out);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_out);
|
||||||
Datum
|
Datum
|
||||||
vector_out(PG_FUNCTION_ARGS)
|
vector_out(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -348,7 +325,7 @@ PrintVector(char *msg, Vector * vector)
|
|||||||
/*
|
/*
|
||||||
* Convert type modifier
|
* Convert type modifier
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_typmod_in);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_typmod_in);
|
||||||
Datum
|
Datum
|
||||||
vector_typmod_in(PG_FUNCTION_ARGS)
|
vector_typmod_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -379,7 +356,7 @@ vector_typmod_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert external binary representation to internal representation
|
* Convert external binary representation to internal representation
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_recv);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_recv);
|
||||||
Datum
|
Datum
|
||||||
vector_recv(PG_FUNCTION_ARGS)
|
vector_recv(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -413,7 +390,7 @@ vector_recv(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to the external binary representation
|
* Convert internal representation to the external binary representation
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_send);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_send);
|
||||||
Datum
|
Datum
|
||||||
vector_send(PG_FUNCTION_ARGS)
|
vector_send(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -433,7 +410,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
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector);
|
||||||
Datum
|
Datum
|
||||||
vector(PG_FUNCTION_ARGS)
|
vector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -448,7 +425,7 @@ vector(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert array to vector
|
* Convert array to vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(array_to_vector);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(array_to_vector);
|
||||||
Datum
|
Datum
|
||||||
array_to_vector(PG_FUNCTION_ARGS)
|
array_to_vector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -522,7 +499,7 @@ array_to_vector(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert vector to float4[]
|
* Convert vector to float4[]
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_to_float4);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_to_float4);
|
||||||
Datum
|
Datum
|
||||||
vector_to_float4(PG_FUNCTION_ARGS)
|
vector_to_float4(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -546,7 +523,7 @@ vector_to_float4(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert half vector to vector
|
* Convert half vector to vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(halfvec_to_vector);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(halfvec_to_vector);
|
||||||
Datum
|
Datum
|
||||||
halfvec_to_vector(PG_FUNCTION_ARGS)
|
halfvec_to_vector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -584,7 +561,7 @@ VectorL2SquaredDistance(int dim, float *ax, float *bx)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 distance between vectors
|
* Get the L2 distance between vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(l2_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(l2_distance);
|
||||||
Datum
|
Datum
|
||||||
l2_distance(PG_FUNCTION_ARGS)
|
l2_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -600,7 +577,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
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_l2_squared_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_l2_squared_distance);
|
||||||
Datum
|
Datum
|
||||||
vector_l2_squared_distance(PG_FUNCTION_ARGS)
|
vector_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -627,7 +604,7 @@ VectorInnerProduct(int dim, float *ax, float *bx)
|
|||||||
/*
|
/*
|
||||||
* Get the inner product of two vectors
|
* Get the inner product of two vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(inner_product);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(inner_product);
|
||||||
Datum
|
Datum
|
||||||
inner_product(PG_FUNCTION_ARGS)
|
inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -642,7 +619,7 @@ inner_product(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the negative inner product of two vectors
|
* Get the negative inner product of two vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_negative_inner_product);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_negative_inner_product);
|
||||||
Datum
|
Datum
|
||||||
vector_negative_inner_product(PG_FUNCTION_ARGS)
|
vector_negative_inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -676,7 +653,7 @@ VectorCosineSimilarity(int dim, float *ax, float *bx)
|
|||||||
/*
|
/*
|
||||||
* Get the cosine distance between two vectors
|
* Get the cosine distance between two vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(cosine_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(cosine_distance);
|
||||||
Datum
|
Datum
|
||||||
cosine_distance(PG_FUNCTION_ARGS)
|
cosine_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -708,7 +685,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)
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_spherical_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_spherical_distance);
|
||||||
Datum
|
Datum
|
||||||
vector_spherical_distance(PG_FUNCTION_ARGS)
|
vector_spherical_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -745,7 +722,7 @@ VectorL1Distance(int dim, float *ax, float *bx)
|
|||||||
/*
|
/*
|
||||||
* Get the L1 distance between two vectors
|
* Get the L1 distance between two vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(l1_distance);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(l1_distance);
|
||||||
Datum
|
Datum
|
||||||
l1_distance(PG_FUNCTION_ARGS)
|
l1_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -760,7 +737,7 @@ l1_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the dimensions of a vector
|
* Get the dimensions of a vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_dims);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_dims);
|
||||||
Datum
|
Datum
|
||||||
vector_dims(PG_FUNCTION_ARGS)
|
vector_dims(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -772,7 +749,7 @@ vector_dims(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 norm of a vector
|
* Get the L2 norm of a vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_norm);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_norm);
|
||||||
Datum
|
Datum
|
||||||
vector_norm(PG_FUNCTION_ARGS)
|
vector_norm(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -790,7 +767,7 @@ vector_norm(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Normalize a vector with the L2 norm
|
* Normalize a vector with the L2 norm
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(l2_normalize);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(l2_normalize);
|
||||||
Datum
|
Datum
|
||||||
l2_normalize(PG_FUNCTION_ARGS)
|
l2_normalize(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -829,7 +806,7 @@ l2_normalize(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Add vectors
|
* Add vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_add);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_add);
|
||||||
Datum
|
Datum
|
||||||
vector_add(PG_FUNCTION_ARGS)
|
vector_add(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -862,7 +839,7 @@ vector_add(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Subtract vectors
|
* Subtract vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_sub);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_sub);
|
||||||
Datum
|
Datum
|
||||||
vector_sub(PG_FUNCTION_ARGS)
|
vector_sub(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -895,7 +872,7 @@ vector_sub(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Multiply vectors
|
* Multiply vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_mul);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_mul);
|
||||||
Datum
|
Datum
|
||||||
vector_mul(PG_FUNCTION_ARGS)
|
vector_mul(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -931,7 +908,7 @@ vector_mul(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Concatenate vectors
|
* Concatenate vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_concat);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_concat);
|
||||||
Datum
|
Datum
|
||||||
vector_concat(PG_FUNCTION_ARGS)
|
vector_concat(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -955,7 +932,7 @@ vector_concat(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Quantize a vector
|
* Quantize a vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(binary_quantize);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(binary_quantize);
|
||||||
Datum
|
Datum
|
||||||
binary_quantize(PG_FUNCTION_ARGS)
|
binary_quantize(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -973,24 +950,39 @@ binary_quantize(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get a subvector
|
* Get a subvector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(subvector);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(subvector);
|
||||||
Datum
|
Datum
|
||||||
subvector(PG_FUNCTION_ARGS)
|
subvector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
int32 start = PG_GETARG_INT32(1);
|
int32 start = PG_GETARG_INT32(1);
|
||||||
int32 count = PG_GETARG_INT32(2);
|
int32 count = PG_GETARG_INT32(2);
|
||||||
int32 end = start + count;
|
int32 end;
|
||||||
float *ax = a->x;
|
float *ax = a->x;
|
||||||
Vector *result;
|
Vector *result;
|
||||||
int dim;
|
int dim;
|
||||||
|
|
||||||
|
if (count < 1)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||||
|
errmsg("vector must have at least 1 dimension")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if (start + count > a->dim), avoiding integer overflow. a->dim
|
||||||
|
* and count are both positive, so a->dim - count won't overflow.
|
||||||
|
*/
|
||||||
|
if (start > a->dim - count)
|
||||||
|
end = a->dim + 1;
|
||||||
|
else
|
||||||
|
end = start + count;
|
||||||
|
|
||||||
/* Indexing starts at 1, like substring */
|
/* Indexing starts at 1, like substring */
|
||||||
if (start < 1)
|
if (start < 1)
|
||||||
start = 1;
|
start = 1;
|
||||||
|
else if (start > a->dim)
|
||||||
if (end > a->dim)
|
ereport(ERROR,
|
||||||
end = a->dim + 1;
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||||
|
errmsg("vector must have at least 1 dimension")));
|
||||||
|
|
||||||
dim = end - start;
|
dim = end - start;
|
||||||
CheckDim(dim);
|
CheckDim(dim);
|
||||||
@@ -1032,7 +1024,7 @@ vector_cmp_internal(Vector * a, Vector * b)
|
|||||||
/*
|
/*
|
||||||
* Less than
|
* Less than
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_lt);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_lt);
|
||||||
Datum
|
Datum
|
||||||
vector_lt(PG_FUNCTION_ARGS)
|
vector_lt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1045,7 +1037,7 @@ vector_lt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Less than or equal
|
* Less than or equal
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_le);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_le);
|
||||||
Datum
|
Datum
|
||||||
vector_le(PG_FUNCTION_ARGS)
|
vector_le(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1058,7 +1050,7 @@ vector_le(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Equal
|
* Equal
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_eq);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_eq);
|
||||||
Datum
|
Datum
|
||||||
vector_eq(PG_FUNCTION_ARGS)
|
vector_eq(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1071,7 +1063,7 @@ vector_eq(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Not equal
|
* Not equal
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ne);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_ne);
|
||||||
Datum
|
Datum
|
||||||
vector_ne(PG_FUNCTION_ARGS)
|
vector_ne(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1084,7 +1076,7 @@ vector_ne(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than or equal
|
* Greater than or equal
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ge);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_ge);
|
||||||
Datum
|
Datum
|
||||||
vector_ge(PG_FUNCTION_ARGS)
|
vector_ge(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1097,7 +1089,7 @@ vector_ge(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than
|
* Greater than
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_gt);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_gt);
|
||||||
Datum
|
Datum
|
||||||
vector_gt(PG_FUNCTION_ARGS)
|
vector_gt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1110,7 +1102,7 @@ vector_gt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Compare vectors
|
* Compare vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_cmp);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_cmp);
|
||||||
Datum
|
Datum
|
||||||
vector_cmp(PG_FUNCTION_ARGS)
|
vector_cmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1123,7 +1115,7 @@ vector_cmp(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Accumulate vectors
|
* Accumulate vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_accum);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_accum);
|
||||||
Datum
|
Datum
|
||||||
vector_accum(PG_FUNCTION_ARGS)
|
vector_accum(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1182,12 +1174,13 @@ vector_accum(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Combine vectors or half vectors
|
* Combine vectors or half vectors (also used for halfvec_combine)
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_combine);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_combine);
|
||||||
Datum
|
Datum
|
||||||
vector_combine(PG_FUNCTION_ARGS)
|
vector_combine(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
/* Must also update parameters of halfvec_combine if modifying */
|
||||||
ArrayType *statearray1 = PG_GETARG_ARRAYTYPE_P(0);
|
ArrayType *statearray1 = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
ArrayType *statearray2 = PG_GETARG_ARRAYTYPE_P(1);
|
ArrayType *statearray2 = PG_GETARG_ARRAYTYPE_P(1);
|
||||||
float8 *statevalues1;
|
float8 *statevalues1;
|
||||||
@@ -1254,7 +1247,7 @@ vector_combine(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Average vectors
|
* Average vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_avg);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(vector_avg);
|
||||||
Datum
|
Datum
|
||||||
vector_avg(PG_FUNCTION_ARGS)
|
vector_avg(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1288,7 +1281,7 @@ vector_avg(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert sparse vector to dense vector
|
* Convert sparse vector to dense vector
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_to_vector);
|
FUNCTION_PREFIX PG_FUNCTION_INFO_V1(sparsevec_to_vector);
|
||||||
Datum
|
Datum
|
||||||
sparsevec_to_vector(PG_FUNCTION_ARGS)
|
sparsevec_to_vector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -1303,7 +1296,7 @@ sparsevec_to_vector(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
result = InitVector(dim);
|
result = InitVector(dim);
|
||||||
for (int i = 0; i < svec->nnz; i++)
|
for (int i = 0; i < svec->nnz; i++)
|
||||||
result->x[svec->indices[i] - 1] = values[i];
|
result->x[svec->indices[i]] = values[i];
|
||||||
|
|
||||||
PG_RETURN_POINTER(result);
|
PG_RETURN_POINTER(result);
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/vector.h
12
src/vector.h
@@ -1,8 +1,6 @@
|
|||||||
#ifndef VECTOR_H
|
#ifndef VECTOR_H
|
||||||
#define VECTOR_H
|
#define VECTOR_H
|
||||||
|
|
||||||
#include "fmgr.h"
|
|
||||||
|
|
||||||
#define VECTOR_MAX_DIM 16000
|
#define VECTOR_MAX_DIM 16000
|
||||||
|
|
||||||
#define VECTOR_SIZE(_dim) (offsetof(Vector, x) + sizeof(float)*(_dim))
|
#define VECTOR_SIZE(_dim) (offsetof(Vector, x) + sizeof(float)*(_dim))
|
||||||
@@ -14,13 +12,19 @@ typedef struct Vector
|
|||||||
{
|
{
|
||||||
int32 vl_len_; /* varlena header (do not touch directly!) */
|
int32 vl_len_; /* varlena header (do not touch directly!) */
|
||||||
int16 dim; /* number of dimensions */
|
int16 dim; /* number of dimensions */
|
||||||
int16 unused;
|
int16 unused; /* reserved for future use, always zero */
|
||||||
float x[FLEXIBLE_ARRAY_MEMBER];
|
float x[FLEXIBLE_ARRAY_MEMBER];
|
||||||
} Vector;
|
} Vector;
|
||||||
|
|
||||||
Vector *InitVector(int dim);
|
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);
|
||||||
PGDLLEXPORT Datum l2_normalize(PG_FUNCTION_ARGS);
|
|
||||||
|
/* TODO Move to better place */
|
||||||
|
#if PG_VERSION_NUM >= 160000
|
||||||
|
#define FUNCTION_PREFIX
|
||||||
|
#else
|
||||||
|
#define FUNCTION_PREFIX PGDLLEXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
61
test/expected/btree.out
Normal file
61
test/expected/btree.out
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
SET enable_seqscan = off;
|
||||||
|
-- vector
|
||||||
|
CREATE TABLE t (val vector(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t (val);
|
||||||
|
SELECT * FROM t WHERE val = '[1,2,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM t ORDER BY val;
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[0,0,0]
|
||||||
|
[1,1,1]
|
||||||
|
[1,2,3]
|
||||||
|
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- halfvec
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t (val);
|
||||||
|
SELECT * FROM t WHERE val = '[1,2,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM t ORDER BY val;
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[0,0,0]
|
||||||
|
[1,1,1]
|
||||||
|
[1,2,3]
|
||||||
|
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- sparsevec
|
||||||
|
CREATE TABLE t (val sparsevec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||||
|
CREATE INDEX ON t (val);
|
||||||
|
SELECT * FROM t WHERE val = '{1:1,2:2,3:3}/3';
|
||||||
|
val
|
||||||
|
-----------------
|
||||||
|
{1:1,2:2,3:3}/3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM t ORDER BY val;
|
||||||
|
val
|
||||||
|
-----------------
|
||||||
|
{}/3
|
||||||
|
{1:1,2:1,3:1}/3
|
||||||
|
{1:1,2:2,3:3}/3
|
||||||
|
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val halfvec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t (val);
|
|
||||||
SELECT * FROM t WHERE val = '[1,2,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT * FROM t ORDER BY val;
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[0,0,0]
|
|
||||||
[1,1,1]
|
|
||||||
[1,2,3]
|
|
||||||
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val sparsevec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
|
||||||
CREATE INDEX ON t (val);
|
|
||||||
SELECT * FROM t WHERE val = '{1:1,2:2,3:3}/3';
|
|
||||||
val
|
|
||||||
-----------------
|
|
||||||
{1:1,2:2,3:3}/3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT * FROM t ORDER BY val;
|
|
||||||
val
|
|
||||||
-----------------
|
|
||||||
{}/3
|
|
||||||
{1:1,2:1,3:1}/3
|
|
||||||
{1:1,2:2,3:3}/3
|
|
||||||
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val vector(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t (val);
|
|
||||||
SELECT * FROM t WHERE val = '[1,2,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT * FROM t ORDER BY val;
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[0,0,0]
|
|
||||||
[1,1,1]
|
|
||||||
[1,2,3]
|
|
||||||
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -208,6 +208,62 @@ 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,15 +1,50 @@
|
|||||||
CREATE TABLE t (val vector(3), val2 halfvec(3), val3 sparsevec(3));
|
-- vector
|
||||||
INSERT INTO t (val, val2, val3) VALUES ('[0,0,0]', '[0,0,0]', '{}/3'), ('[1,2,3]', '[1,2,3]', '{1:1,2:2,3:3}/3'), ('[1,1,1]', '[1,1,1]', '{1:1,2:1,3:1}/3'), (NULL, NULL, NULL);
|
CREATE TABLE t (val vector(3));
|
||||||
CREATE TABLE t2 (val vector(3), val2 halfvec(3), val3 sparsevec(3));
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
\copy t TO 'results/data.bin' WITH (FORMAT binary)
|
CREATE TABLE t2 (val vector(3));
|
||||||
\copy t2 FROM 'results/data.bin' WITH (FORMAT binary)
|
\copy t TO 'results/vector.bin' WITH (FORMAT binary)
|
||||||
|
\copy t2 FROM 'results/vector.bin' WITH (FORMAT binary)
|
||||||
SELECT * FROM t2 ORDER BY val;
|
SELECT * FROM t2 ORDER BY val;
|
||||||
val | val2 | val3
|
val
|
||||||
---------+---------+-----------------
|
---------
|
||||||
[0,0,0] | [0,0,0] | {}/3
|
[0,0,0]
|
||||||
[1,1,1] | [1,1,1] | {1:1,2:1,3:1}/3
|
[1,1,1]
|
||||||
[1,2,3] | [1,2,3] | {1:1,2:2,3:3}/3
|
[1,2,3]
|
||||||
| |
|
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
DROP TABLE t2;
|
||||||
|
-- halfvec
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE TABLE t2 (val halfvec(3));
|
||||||
|
\copy t TO 'results/halfvec.bin' WITH (FORMAT binary)
|
||||||
|
\copy t2 FROM 'results/halfvec.bin' WITH (FORMAT binary)
|
||||||
|
SELECT * FROM t2 ORDER BY val;
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[0,0,0]
|
||||||
|
[1,1,1]
|
||||||
|
[1,2,3]
|
||||||
|
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
DROP TABLE t2;
|
||||||
|
-- sparsevec
|
||||||
|
CREATE TABLE t (val sparsevec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||||
|
CREATE TABLE t2 (val sparsevec(3));
|
||||||
|
\copy t TO 'results/sparsevec.bin' WITH (FORMAT binary)
|
||||||
|
\copy t2 FROM 'results/sparsevec.bin' WITH (FORMAT binary)
|
||||||
|
SELECT * FROM t2 ORDER BY val;
|
||||||
|
val
|
||||||
|
-----------------
|
||||||
|
{}/3
|
||||||
|
{1:1,2:1,3:1}/3
|
||||||
|
{1:1,2:2,3:3}/3
|
||||||
|
|
||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
DROP TABLE t;
|
DROP TABLE t;
|
||||||
|
|||||||
@@ -1,3 +1,167 @@
|
|||||||
|
SELECT '[1,2,3]'::halfvec;
|
||||||
|
halfvec
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[-1,-2,-3]'::halfvec;
|
||||||
|
halfvec
|
||||||
|
------------
|
||||||
|
[-1,-2,-3]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[1.,2.,3.]'::halfvec;
|
||||||
|
halfvec
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT ' [ 1, 2 , 3 ] '::halfvec;
|
||||||
|
halfvec
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[1.23456]'::halfvec;
|
||||||
|
halfvec
|
||||||
|
------------
|
||||||
|
[1.234375]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[hello,1]'::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: "[hello,1]"
|
||||||
|
LINE 1: SELECT '[hello,1]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[NaN,1]'::halfvec;
|
||||||
|
ERROR: NaN not allowed in halfvec
|
||||||
|
LINE 1: SELECT '[NaN,1]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[Infinity,1]'::halfvec;
|
||||||
|
ERROR: infinite value not allowed in halfvec
|
||||||
|
LINE 1: SELECT '[Infinity,1]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[-Infinity,1]'::halfvec;
|
||||||
|
ERROR: infinite value not allowed in halfvec
|
||||||
|
LINE 1: SELECT '[-Infinity,1]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[65519,-65519]'::halfvec;
|
||||||
|
halfvec
|
||||||
|
----------------
|
||||||
|
[65504,-65504]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[65520,-65520]'::halfvec;
|
||||||
|
ERROR: "65520" is out of range for type halfvec
|
||||||
|
LINE 1: SELECT '[65520,-65520]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[1e-8,-1e-8]'::halfvec;
|
||||||
|
halfvec
|
||||||
|
---------
|
||||||
|
[0,-0]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[4e38,1]'::halfvec;
|
||||||
|
ERROR: "4e38" is out of range for type halfvec
|
||||||
|
LINE 1: SELECT '[4e38,1]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[1e-46,1]'::halfvec;
|
||||||
|
halfvec
|
||||||
|
---------
|
||||||
|
[0,1]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[1,2,3'::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: "[1,2,3"
|
||||||
|
LINE 1: SELECT '[1,2,3'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[1,2,3]9'::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: "[1,2,3]9"
|
||||||
|
LINE 1: SELECT '[1,2,3]9'::halfvec;
|
||||||
|
^
|
||||||
|
DETAIL: Junk after closing right brace.
|
||||||
|
SELECT '1,2,3'::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: "1,2,3"
|
||||||
|
LINE 1: SELECT '1,2,3'::halfvec;
|
||||||
|
^
|
||||||
|
DETAIL: Vector contents must start with "[".
|
||||||
|
SELECT ''::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: ""
|
||||||
|
LINE 1: SELECT ''::halfvec;
|
||||||
|
^
|
||||||
|
DETAIL: Vector contents must start with "[".
|
||||||
|
SELECT '['::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: "["
|
||||||
|
LINE 1: SELECT '['::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[ '::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: "[ "
|
||||||
|
LINE 1: SELECT '[ '::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[,'::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: "[,"
|
||||||
|
LINE 1: SELECT '[,'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[]'::halfvec;
|
||||||
|
ERROR: halfvec must have at least 1 dimension
|
||||||
|
LINE 1: SELECT '[]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[ ]'::halfvec;
|
||||||
|
ERROR: halfvec must have at least 1 dimension
|
||||||
|
LINE 1: SELECT '[ ]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[,]'::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: "[,]"
|
||||||
|
LINE 1: SELECT '[,]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[1,]'::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: "[1,]"
|
||||||
|
LINE 1: SELECT '[1,]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[1a]'::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: "[1a]"
|
||||||
|
LINE 1: SELECT '[1a]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[1,,3]'::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: "[1,,3]"
|
||||||
|
LINE 1: SELECT '[1,,3]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[1, ,3]'::halfvec;
|
||||||
|
ERROR: invalid input syntax for type halfvec: "[1, ,3]"
|
||||||
|
LINE 1: SELECT '[1, ,3]'::halfvec;
|
||||||
|
^
|
||||||
|
SELECT '[1,2,3]'::halfvec(3);
|
||||||
|
halfvec
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[1,2,3]'::halfvec(2);
|
||||||
|
ERROR: expected 2 dimensions, not 3
|
||||||
|
SELECT '[1,2,3]'::halfvec(3, 2);
|
||||||
|
ERROR: invalid type modifier
|
||||||
|
LINE 1: SELECT '[1,2,3]'::halfvec(3, 2);
|
||||||
|
^
|
||||||
|
SELECT '[1,2,3]'::halfvec('a');
|
||||||
|
ERROR: invalid input syntax for type integer: "a"
|
||||||
|
LINE 1: SELECT '[1,2,3]'::halfvec('a');
|
||||||
|
^
|
||||||
|
SELECT '[1,2,3]'::halfvec(0);
|
||||||
|
ERROR: dimensions for type halfvec must be at least 1
|
||||||
|
LINE 1: SELECT '[1,2,3]'::halfvec(0);
|
||||||
|
^
|
||||||
|
SELECT '[1,2,3]'::halfvec(16001);
|
||||||
|
ERROR: dimensions for type halfvec cannot exceed 16000
|
||||||
|
LINE 1: SELECT '[1,2,3]'::halfvec(16001);
|
||||||
|
^
|
||||||
|
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::halfvec[]);
|
||||||
|
unnest
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
[4,5,6]
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT '{"[1,2,3]"}'::halfvec(2)[];
|
||||||
|
ERROR: expected 2 dimensions, not 3
|
||||||
SELECT '[1,2,3]'::halfvec + '[4,5,6]';
|
SELECT '[1,2,3]'::halfvec + '[4,5,6]';
|
||||||
?column?
|
?column?
|
||||||
----------
|
----------
|
||||||
@@ -176,6 +340,18 @@ SELECT l2_norm('[0,1]'::halfvec);
|
|||||||
1
|
1
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_norm('[0,0]'::halfvec);
|
||||||
|
l2_norm
|
||||||
|
---------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_norm('[2]'::halfvec);
|
||||||
|
l2_norm
|
||||||
|
---------
|
||||||
|
2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT l2_distance('[0,0]'::halfvec, '[3,4]');
|
SELECT l2_distance('[0,0]'::halfvec, '[3,4]');
|
||||||
l2_distance
|
l2_distance
|
||||||
-------------
|
-------------
|
||||||
@@ -394,6 +570,20 @@ SELECT subvector('[1,2,3,4,5]'::halfvec, 3, -1);
|
|||||||
ERROR: halfvec must have at least 1 dimension
|
ERROR: halfvec must have at least 1 dimension
|
||||||
SELECT subvector('[1,2,3,4,5]'::halfvec, -1, 2);
|
SELECT subvector('[1,2,3,4,5]'::halfvec, -1, 2);
|
||||||
ERROR: halfvec must have at least 1 dimension
|
ERROR: halfvec must have at least 1 dimension
|
||||||
|
SELECT subvector('[1,2,3,4,5]'::halfvec, 2147483647, 10);
|
||||||
|
ERROR: halfvec must have at least 1 dimension
|
||||||
|
SELECT subvector('[1,2,3,4,5]'::halfvec, 3, 2147483647);
|
||||||
|
subvector
|
||||||
|
-----------
|
||||||
|
[3,4,5]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT subvector('[1,2,3,4,5]'::halfvec, -2147483644, 2147483647);
|
||||||
|
subvector
|
||||||
|
-----------
|
||||||
|
[1,2]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]']) v;
|
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]']) v;
|
||||||
avg
|
avg
|
||||||
-----------
|
-----------
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
SELECT '[1,2,3]'::halfvec;
|
|
||||||
halfvec
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[-1,-2,-3]'::halfvec;
|
|
||||||
halfvec
|
|
||||||
------------
|
|
||||||
[-1,-2,-3]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[1.,2.,3.]'::halfvec;
|
|
||||||
halfvec
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT ' [ 1, 2 , 3 ] '::halfvec;
|
|
||||||
halfvec
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[1.23456]'::halfvec;
|
|
||||||
halfvec
|
|
||||||
------------
|
|
||||||
[1.234375]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[hello,1]'::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: "[hello,1]"
|
|
||||||
LINE 1: SELECT '[hello,1]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[NaN,1]'::halfvec;
|
|
||||||
ERROR: NaN not allowed in halfvec
|
|
||||||
LINE 1: SELECT '[NaN,1]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[Infinity,1]'::halfvec;
|
|
||||||
ERROR: infinite value not allowed in halfvec
|
|
||||||
LINE 1: SELECT '[Infinity,1]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[-Infinity,1]'::halfvec;
|
|
||||||
ERROR: infinite value not allowed in halfvec
|
|
||||||
LINE 1: SELECT '[-Infinity,1]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[65519,-65519]'::halfvec;
|
|
||||||
halfvec
|
|
||||||
----------------
|
|
||||||
[65504,-65504]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[65520,-65520]'::halfvec;
|
|
||||||
ERROR: "65520" is out of range for type halfvec
|
|
||||||
LINE 1: SELECT '[65520,-65520]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[1e-8,-1e-8]'::halfvec;
|
|
||||||
halfvec
|
|
||||||
---------
|
|
||||||
[0,-0]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[4e38,1]'::halfvec;
|
|
||||||
ERROR: "4e38" is out of range for type halfvec
|
|
||||||
LINE 1: SELECT '[4e38,1]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[1e-46,1]'::halfvec;
|
|
||||||
halfvec
|
|
||||||
---------
|
|
||||||
[0,1]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[1,2,3'::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: "[1,2,3"
|
|
||||||
LINE 1: SELECT '[1,2,3'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[1,2,3]9'::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: "[1,2,3]9"
|
|
||||||
LINE 1: SELECT '[1,2,3]9'::halfvec;
|
|
||||||
^
|
|
||||||
DETAIL: Junk after closing right brace.
|
|
||||||
SELECT '1,2,3'::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: "1,2,3"
|
|
||||||
LINE 1: SELECT '1,2,3'::halfvec;
|
|
||||||
^
|
|
||||||
DETAIL: Vector contents must start with "[".
|
|
||||||
SELECT ''::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: ""
|
|
||||||
LINE 1: SELECT ''::halfvec;
|
|
||||||
^
|
|
||||||
DETAIL: Vector contents must start with "[".
|
|
||||||
SELECT '['::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: "["
|
|
||||||
LINE 1: SELECT '['::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[ '::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: "[ "
|
|
||||||
LINE 1: SELECT '[ '::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[,'::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: "[,"
|
|
||||||
LINE 1: SELECT '[,'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[]'::halfvec;
|
|
||||||
ERROR: halfvec must have at least 1 dimension
|
|
||||||
LINE 1: SELECT '[]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[ ]'::halfvec;
|
|
||||||
ERROR: halfvec must have at least 1 dimension
|
|
||||||
LINE 1: SELECT '[ ]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[,]'::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: "[,]"
|
|
||||||
LINE 1: SELECT '[,]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[1,]'::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: "[1,]"
|
|
||||||
LINE 1: SELECT '[1,]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[1a]'::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: "[1a]"
|
|
||||||
LINE 1: SELECT '[1a]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[1,,3]'::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: "[1,,3]"
|
|
||||||
LINE 1: SELECT '[1,,3]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[1, ,3]'::halfvec;
|
|
||||||
ERROR: invalid input syntax for type halfvec: "[1, ,3]"
|
|
||||||
LINE 1: SELECT '[1, ,3]'::halfvec;
|
|
||||||
^
|
|
||||||
SELECT '[1,2,3]'::halfvec(3);
|
|
||||||
halfvec
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[1,2,3]'::halfvec(2);
|
|
||||||
ERROR: expected 2 dimensions, not 3
|
|
||||||
SELECT '[1,2,3]'::halfvec(3, 2);
|
|
||||||
ERROR: invalid type modifier
|
|
||||||
LINE 1: SELECT '[1,2,3]'::halfvec(3, 2);
|
|
||||||
^
|
|
||||||
SELECT '[1,2,3]'::halfvec('a');
|
|
||||||
ERROR: invalid input syntax for type integer: "a"
|
|
||||||
LINE 1: SELECT '[1,2,3]'::halfvec('a');
|
|
||||||
^
|
|
||||||
SELECT '[1,2,3]'::halfvec(0);
|
|
||||||
ERROR: dimensions for type halfvec must be at least 1
|
|
||||||
LINE 1: SELECT '[1,2,3]'::halfvec(0);
|
|
||||||
^
|
|
||||||
SELECT '[1,2,3]'::halfvec(16001);
|
|
||||||
ERROR: dimensions for type halfvec cannot exceed 16000
|
|
||||||
LINE 1: SELECT '[1,2,3]'::halfvec(16001);
|
|
||||||
^
|
|
||||||
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::halfvec[]);
|
|
||||||
unnest
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
[4,5,6]
|
|
||||||
(2 rows)
|
|
||||||
|
|
||||||
SELECT '{"[1,2,3]"}'::halfvec(2)[];
|
|
||||||
ERROR: expected 2 dimensions, not 3
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
|
-- hamming
|
||||||
CREATE TABLE t (val bit(3));
|
CREATE TABLE t (val bit(3));
|
||||||
INSERT INTO t (val) VALUES (B'000'), (B'100'), (B'111'), (NULL);
|
INSERT INTO t (val) VALUES (B'000'), (B'100'), (B'111'), (NULL);
|
||||||
CREATE INDEX ON t USING hnsw (val bit_hamming_ops);
|
CREATE INDEX ON t USING hnsw (val bit_hamming_ops);
|
||||||
@@ -19,7 +20,28 @@ SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <~> (SELECT NULL::bit)) t2;
|
|||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
DROP TABLE t;
|
DROP TABLE t;
|
||||||
-- TODO move
|
-- jaccard
|
||||||
|
CREATE TABLE t (val bit(4));
|
||||||
|
INSERT INTO t (val) VALUES (B'0000'), (B'1100'), (B'1111'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val bit_jaccard_ops);
|
||||||
|
INSERT INTO t (val) VALUES (B'1110');
|
||||||
|
SELECT * FROM t ORDER BY val <%> B'1111';
|
||||||
|
val
|
||||||
|
------
|
||||||
|
1111
|
||||||
|
1110
|
||||||
|
1100
|
||||||
|
0000
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <%> (SELECT NULL::bit)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- varbit
|
||||||
CREATE TABLE t (val varbit(3));
|
CREATE TABLE t (val varbit(3));
|
||||||
CREATE INDEX ON t USING hnsw (val bit_hamming_ops);
|
CREATE INDEX ON t USING hnsw (val bit_hamming_ops);
|
||||||
ERROR: type not supported for hnsw index
|
ERROR: type not supported for hnsw index
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val bit(4));
|
|
||||||
INSERT INTO t (val) VALUES (B'0000'), (B'1100'), (B'1111'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val bit_jaccard_ops);
|
|
||||||
INSERT INTO t (val) VALUES (B'1110');
|
|
||||||
SELECT * FROM t ORDER BY val <%> B'1111';
|
|
||||||
val
|
|
||||||
------
|
|
||||||
1111
|
|
||||||
1110
|
|
||||||
1100
|
|
||||||
0000
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <%> (SELECT NULL::bit)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
102
test/expected/hnsw_halfvec.out
Normal file
102
test/expected/hnsw_halfvec.out
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
SET enable_seqscan = off;
|
||||||
|
-- L2
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val halfvec_l2_ops);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
[1,2,4]
|
||||||
|
[1,1,1]
|
||||||
|
[0,0,0]
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::halfvec)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM t;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
TRUNCATE t;
|
||||||
|
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||||
|
val
|
||||||
|
-----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- inner product
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val halfvec_ip_ops);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,4]
|
||||||
|
[1,2,3]
|
||||||
|
[1,1,1]
|
||||||
|
[0,0,0]
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::halfvec)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- cosine
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val halfvec_cosine_ops);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,1,1]
|
||||||
|
[1,2,3]
|
||||||
|
[1,2,4]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::halfvec)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- L1
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val halfvec_l1_ops);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <+> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
[1,2,4]
|
||||||
|
[1,1,1]
|
||||||
|
[0,0,0]
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::halfvec)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val halfvec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val halfvec_cosine_ops);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,1,1]
|
|
||||||
[1,2,3]
|
|
||||||
[1,2,4]
|
|
||||||
(3 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::halfvec)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val halfvec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val halfvec_ip_ops);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,4]
|
|
||||||
[1,2,3]
|
|
||||||
[1,1,1]
|
|
||||||
[0,0,0]
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::halfvec)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val halfvec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val halfvec_l1_ops);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <+> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
[1,2,4]
|
|
||||||
[1,1,1]
|
|
||||||
[0,0,0]
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::halfvec)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val halfvec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val halfvec_l2_ops);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
[1,2,4]
|
|
||||||
[1,1,1]
|
|
||||||
[0,0,0]
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::halfvec)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM t;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
TRUNCATE t;
|
|
||||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
|
||||||
val
|
|
||||||
-----
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
CREATE TABLE t (val vector(3));
|
|
||||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (m = 1);
|
|
||||||
ERROR: value 1 out of bounds for option "m"
|
|
||||||
DETAIL: Valid values are between "2" and "100".
|
|
||||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (m = 101);
|
|
||||||
ERROR: value 101 out of bounds for option "m"
|
|
||||||
DETAIL: Valid values are between "2" and "100".
|
|
||||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (ef_construction = 3);
|
|
||||||
ERROR: value 3 out of bounds for option "ef_construction"
|
|
||||||
DETAIL: Valid values are between "4" and "1000".
|
|
||||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (ef_construction = 1001);
|
|
||||||
ERROR: value 1001 out of bounds for option "ef_construction"
|
|
||||||
DETAIL: Valid values are between "4" and "1000".
|
|
||||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (m = 16, ef_construction = 31);
|
|
||||||
ERROR: ef_construction must be greater than or equal to 2 * m
|
|
||||||
SHOW hnsw.ef_search;
|
|
||||||
hnsw.ef_search
|
|
||||||
----------------
|
|
||||||
40
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SET hnsw.ef_search = 0;
|
|
||||||
ERROR: 0 is outside the valid range for parameter "hnsw.ef_search" (1 .. 1000)
|
|
||||||
SET hnsw.ef_search = 1001;
|
|
||||||
ERROR: 1001 is outside the valid range for parameter "hnsw.ef_search" (1 .. 1000)
|
|
||||||
DROP TABLE t;
|
|
||||||
112
test/expected/hnsw_sparsevec.out
Normal file
112
test/expected/hnsw_sparsevec.out
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
SET enable_seqscan = off;
|
||||||
|
-- L2
|
||||||
|
CREATE TABLE t (val sparsevec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
|
||||||
|
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
||||||
|
SELECT * FROM t ORDER BY val <-> '{1:3,2:3,3:3}/3';
|
||||||
|
val
|
||||||
|
-----------------
|
||||||
|
{1:1,2:2,3:3}/3
|
||||||
|
{1:1,2:2,3:4}/3
|
||||||
|
{1:1,2:1,3:1}/3
|
||||||
|
{}/3
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::sparsevec)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM t;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
TRUNCATE t;
|
||||||
|
SELECT * FROM t ORDER BY val <-> '{1:3,2:3,3:3}/3';
|
||||||
|
val
|
||||||
|
-----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- inner product
|
||||||
|
CREATE TABLE t (val sparsevec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val sparsevec_ip_ops);
|
||||||
|
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
||||||
|
SELECT * FROM t ORDER BY val <#> '{1:3,2:3,3:3}/3';
|
||||||
|
val
|
||||||
|
-----------------
|
||||||
|
{1:1,2:2,3:4}/3
|
||||||
|
{1:1,2:2,3:3}/3
|
||||||
|
{1:1,2:1,3:1}/3
|
||||||
|
{}/3
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::sparsevec)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- cosine
|
||||||
|
CREATE TABLE t (val sparsevec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val sparsevec_cosine_ops);
|
||||||
|
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
||||||
|
SELECT * FROM t ORDER BY val <=> '{1:3,2:3,3:3}/3';
|
||||||
|
val
|
||||||
|
-----------------
|
||||||
|
{1:1,2:1,3:1}/3
|
||||||
|
{1:1,2:2,3:3}/3
|
||||||
|
{1:1,2:2,3:4}/3
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '{}/3') t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::sparsevec)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- L1
|
||||||
|
CREATE TABLE t (val sparsevec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val sparsevec_l1_ops);
|
||||||
|
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
||||||
|
SELECT * FROM t ORDER BY val <+> '{1:3,2:3,3:3}/3';
|
||||||
|
val
|
||||||
|
-----------------
|
||||||
|
{1:1,2:2,3:3}/3
|
||||||
|
{1:1,2:2,3:4}/3
|
||||||
|
{1:1,2:1,3:1}/3
|
||||||
|
{}/3
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::sparsevec)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- non-zero elements
|
||||||
|
CREATE TABLE t (val sparsevec(1001));
|
||||||
|
INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec);
|
||||||
|
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
|
||||||
|
ERROR: sparsevec cannot have more than 1000 non-zero elements for hnsw index
|
||||||
|
TRUNCATE t;
|
||||||
|
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
|
||||||
|
INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec);
|
||||||
|
ERROR: sparsevec cannot have more than 1000 non-zero elements for hnsw index
|
||||||
|
DROP TABLE t;
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val sparsevec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val sparsevec_cosine_ops);
|
|
||||||
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
|
||||||
SELECT * FROM t ORDER BY val <=> '{1:3,2:3,3:3}/3';
|
|
||||||
val
|
|
||||||
-----------------
|
|
||||||
{1:1,2:1,3:1}/3
|
|
||||||
{1:1,2:2,3:3}/3
|
|
||||||
{1:1,2:2,3:4}/3
|
|
||||||
(3 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '{}/3') t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::sparsevec)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val sparsevec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val sparsevec_ip_ops);
|
|
||||||
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
|
||||||
SELECT * FROM t ORDER BY val <#> '{1:3,2:3,3:3}/3';
|
|
||||||
val
|
|
||||||
-----------------
|
|
||||||
{1:1,2:2,3:4}/3
|
|
||||||
{1:1,2:2,3:3}/3
|
|
||||||
{1:1,2:1,3:1}/3
|
|
||||||
{}/3
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::sparsevec)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val sparsevec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val sparsevec_l1_ops);
|
|
||||||
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
|
||||||
SELECT * FROM t ORDER BY val <+> '{1:3,2:3,3:3}/3';
|
|
||||||
val
|
|
||||||
-----------------
|
|
||||||
{1:1,2:2,3:3}/3
|
|
||||||
{1:1,2:2,3:4}/3
|
|
||||||
{1:1,2:1,3:1}/3
|
|
||||||
{}/3
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::sparsevec)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val sparsevec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
|
|
||||||
INSERT INTO t (val) VALUES ('{1:1,2:2,3:4}/3');
|
|
||||||
SELECT * FROM t ORDER BY val <-> '{1:3,2:3,3:3}/3';
|
|
||||||
val
|
|
||||||
-----------------
|
|
||||||
{1:1,2:2,3:3}/3
|
|
||||||
{1:1,2:2,3:4}/3
|
|
||||||
{1:1,2:1,3:1}/3
|
|
||||||
{}/3
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::sparsevec)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM t;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
TRUNCATE t;
|
|
||||||
SELECT * FROM t ORDER BY val <-> '{1:3,2:3,3:3}/3';
|
|
||||||
val
|
|
||||||
-----
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
-- TODO move
|
|
||||||
CREATE TABLE t (val sparsevec(1001));
|
|
||||||
INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec);
|
|
||||||
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
|
|
||||||
ERROR: sparsevec cannot have more than 1000 non-zero elements for hnsw index
|
|
||||||
TRUNCATE t;
|
|
||||||
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
|
|
||||||
INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec);
|
|
||||||
ERROR: sparsevec cannot have more than 1000 non-zero elements for hnsw index
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE UNLOGGED TABLE t (val vector(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops);
|
|
||||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
[1,1,1]
|
|
||||||
[0,0,0]
|
|
||||||
(3 rows)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
142
test/expected/hnsw_vector.out
Normal file
142
test/expected/hnsw_vector.out
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
SET enable_seqscan = off;
|
||||||
|
-- L2
|
||||||
|
CREATE TABLE t (val vector(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val vector_l2_ops);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
[1,2,4]
|
||||||
|
[1,1,1]
|
||||||
|
[0,0,0]
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::vector)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM t;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
TRUNCATE t;
|
||||||
|
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||||
|
val
|
||||||
|
-----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- inner product
|
||||||
|
CREATE TABLE t (val vector(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val vector_ip_ops);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,4]
|
||||||
|
[1,2,3]
|
||||||
|
[1,1,1]
|
||||||
|
[0,0,0]
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::vector)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- cosine
|
||||||
|
CREATE TABLE t (val vector(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val vector_cosine_ops);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,1,1]
|
||||||
|
[1,2,3]
|
||||||
|
[1,2,4]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::vector)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- L1
|
||||||
|
CREATE TABLE t (val vector(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val vector_l1_ops);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <+> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
[1,2,4]
|
||||||
|
[1,1,1]
|
||||||
|
[0,0,0]
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::vector)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- unlogged
|
||||||
|
CREATE UNLOGGED TABLE t (val vector(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val vector_l2_ops);
|
||||||
|
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
[1,1,1]
|
||||||
|
[0,0,0]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- options
|
||||||
|
CREATE TABLE t (val vector(3));
|
||||||
|
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (m = 1);
|
||||||
|
ERROR: value 1 out of bounds for option "m"
|
||||||
|
DETAIL: Valid values are between "2" and "100".
|
||||||
|
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (m = 101);
|
||||||
|
ERROR: value 101 out of bounds for option "m"
|
||||||
|
DETAIL: Valid values are between "2" and "100".
|
||||||
|
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (ef_construction = 3);
|
||||||
|
ERROR: value 3 out of bounds for option "ef_construction"
|
||||||
|
DETAIL: Valid values are between "4" and "1000".
|
||||||
|
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (ef_construction = 1001);
|
||||||
|
ERROR: value 1001 out of bounds for option "ef_construction"
|
||||||
|
DETAIL: Valid values are between "4" and "1000".
|
||||||
|
CREATE INDEX ON t USING hnsw (val vector_l2_ops) WITH (m = 16, ef_construction = 31);
|
||||||
|
ERROR: ef_construction must be greater than or equal to 2 * m
|
||||||
|
SHOW hnsw.ef_search;
|
||||||
|
hnsw.ef_search
|
||||||
|
----------------
|
||||||
|
40
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SET hnsw.ef_search = 0;
|
||||||
|
ERROR: 0 is outside the valid range for parameter "hnsw.ef_search" (1 .. 1000)
|
||||||
|
SET hnsw.ef_search = 1001;
|
||||||
|
ERROR: 1001 is outside the valid range for parameter "hnsw.ef_search" (1 .. 1000)
|
||||||
|
DROP TABLE t;
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val vector(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val vector_cosine_ops);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,1,1]
|
|
||||||
[1,2,3]
|
|
||||||
[1,2,4]
|
|
||||||
(3 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::vector)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val vector(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val vector_ip_ops);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,4]
|
|
||||||
[1,2,3]
|
|
||||||
[1,1,1]
|
|
||||||
[0,0,0]
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::vector)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val vector(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val vector_l1_ops);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <+> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
[1,2,4]
|
|
||||||
[1,1,1]
|
|
||||||
[0,0,0]
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::vector)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val vector(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val vector_l2_ops);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
[1,2,4]
|
|
||||||
[1,1,1]
|
|
||||||
[0,0,0]
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::vector)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM t;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
TRUNCATE t;
|
|
||||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
|
||||||
val
|
|
||||||
-----
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
|
-- hamming
|
||||||
CREATE TABLE t (val bit(3));
|
CREATE TABLE t (val bit(3));
|
||||||
INSERT INTO t (val) VALUES (B'000'), (B'100'), (B'111'), (NULL);
|
INSERT INTO t (val) VALUES (B'000'), (B'100'), (B'111'), (NULL);
|
||||||
CREATE INDEX ON t USING ivfflat (val bit_hamming_ops) WITH (lists = 1);
|
CREATE INDEX ON t USING ivfflat (val bit_hamming_ops) WITH (lists = 1);
|
||||||
@@ -19,7 +20,7 @@ SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <~> (SELECT NULL::bit)) t2;
|
|||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
DROP TABLE t;
|
DROP TABLE t;
|
||||||
-- TODO move
|
-- varbit
|
||||||
CREATE TABLE t (val varbit(3));
|
CREATE TABLE t (val varbit(3));
|
||||||
CREATE INDEX ON t USING ivfflat (val bit_hamming_ops) WITH (lists = 1);
|
CREATE INDEX ON t USING ivfflat (val bit_hamming_ops) WITH (lists = 1);
|
||||||
ERROR: type not supported for ivfflat index
|
ERROR: type not supported for ivfflat index
|
||||||
84
test/expected/ivfflat_halfvec.out
Normal file
84
test/expected/ivfflat_halfvec.out
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
SET enable_seqscan = off;
|
||||||
|
-- L2
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING ivfflat (val halfvec_l2_ops) WITH (lists = 1);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
[1,2,4]
|
||||||
|
[1,1,1]
|
||||||
|
[0,0,0]
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::halfvec)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM t;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
TRUNCATE t;
|
||||||
|
NOTICE: ivfflat index created with little data
|
||||||
|
DETAIL: This will cause low recall.
|
||||||
|
HINT: Drop the index until the table has more data.
|
||||||
|
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||||
|
val
|
||||||
|
-----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- inner product
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING ivfflat (val halfvec_ip_ops) WITH (lists = 1);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,4]
|
||||||
|
[1,2,3]
|
||||||
|
[1,1,1]
|
||||||
|
[0,0,0]
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::halfvec)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- cosine
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING ivfflat (val halfvec_cosine_ops) WITH (lists = 1);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,1,1]
|
||||||
|
[1,2,3]
|
||||||
|
[1,2,4]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::halfvec)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val halfvec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING ivfflat (val halfvec_cosine_ops) WITH (lists = 1);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,1,1]
|
|
||||||
[1,2,3]
|
|
||||||
[1,2,4]
|
|
||||||
(3 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::halfvec)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val halfvec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING ivfflat (val halfvec_ip_ops) WITH (lists = 1);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,4]
|
|
||||||
[1,2,3]
|
|
||||||
[1,1,1]
|
|
||||||
[0,0,0]
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::halfvec)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val halfvec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING ivfflat (val halfvec_l2_ops) WITH (lists = 1);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
[1,2,4]
|
|
||||||
[1,1,1]
|
|
||||||
[0,0,0]
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::halfvec)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM t;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
TRUNCATE t;
|
|
||||||
NOTICE: ivfflat index created with little data
|
|
||||||
DETAIL: This will cause low recall.
|
|
||||||
HINT: Drop the index until the table has more data.
|
|
||||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
|
||||||
val
|
|
||||||
-----
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
CREATE TABLE t (val vector(3));
|
|
||||||
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 0);
|
|
||||||
ERROR: value 0 out of bounds for option "lists"
|
|
||||||
DETAIL: Valid values are between "1" and "32768".
|
|
||||||
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 32769);
|
|
||||||
ERROR: value 32769 out of bounds for option "lists"
|
|
||||||
DETAIL: Valid values are between "1" and "32768".
|
|
||||||
SHOW ivfflat.probes;
|
|
||||||
ivfflat.probes
|
|
||||||
----------------
|
|
||||||
1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE UNLOGGED TABLE t (val vector(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 1);
|
|
||||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
[1,1,1]
|
|
||||||
[0,0,0]
|
|
||||||
(3 rows)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
112
test/expected/ivfflat_vector.out
Normal file
112
test/expected/ivfflat_vector.out
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
SET enable_seqscan = off;
|
||||||
|
-- L2
|
||||||
|
CREATE TABLE t (val vector(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 1);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
[1,2,4]
|
||||||
|
[1,1,1]
|
||||||
|
[0,0,0]
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::vector)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM t;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
TRUNCATE t;
|
||||||
|
NOTICE: ivfflat index created with little data
|
||||||
|
DETAIL: This will cause low recall.
|
||||||
|
HINT: Drop the index until the table has more data.
|
||||||
|
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||||
|
val
|
||||||
|
-----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- inner product
|
||||||
|
CREATE TABLE t (val vector(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING ivfflat (val vector_ip_ops) WITH (lists = 1);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,4]
|
||||||
|
[1,2,3]
|
||||||
|
[1,1,1]
|
||||||
|
[0,0,0]
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::vector)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- cosine
|
||||||
|
CREATE TABLE t (val vector(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING ivfflat (val vector_cosine_ops) WITH (lists = 1);
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,1,1]
|
||||||
|
[1,2,3]
|
||||||
|
[1,2,4]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::vector)) t2;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- unlogged
|
||||||
|
CREATE UNLOGGED TABLE t (val vector(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 1);
|
||||||
|
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||||
|
val
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
[1,1,1]
|
||||||
|
[0,0,0]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
-- options
|
||||||
|
CREATE TABLE t (val vector(3));
|
||||||
|
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 0);
|
||||||
|
ERROR: value 0 out of bounds for option "lists"
|
||||||
|
DETAIL: Valid values are between "1" and "32768".
|
||||||
|
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 32769);
|
||||||
|
ERROR: value 32769 out of bounds for option "lists"
|
||||||
|
DETAIL: Valid values are between "1" and "32768".
|
||||||
|
SHOW ivfflat.probes;
|
||||||
|
ivfflat.probes
|
||||||
|
----------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val vector(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING ivfflat (val vector_cosine_ops) WITH (lists = 1);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,1,1]
|
|
||||||
[1,2,3]
|
|
||||||
[1,2,4]
|
|
||||||
(3 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::vector)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val vector(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING ivfflat (val vector_ip_ops) WITH (lists = 1);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,4]
|
|
||||||
[1,2,3]
|
|
||||||
[1,1,1]
|
|
||||||
[0,0,0]
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::vector)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
CREATE TABLE t (val vector(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 1);
|
|
||||||
INSERT INTO t (val) VALUES ('[1,2,4]');
|
|
||||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
|
||||||
val
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
[1,2,4]
|
|
||||||
[1,1,1]
|
|
||||||
[0,0,0]
|
|
||||||
(4 rows)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::vector)) t2;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
4
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT COUNT(*) FROM t;
|
|
||||||
count
|
|
||||||
-------
|
|
||||||
5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
TRUNCATE t;
|
|
||||||
NOTICE: ivfflat index created with little data
|
|
||||||
DETAIL: This will cause low recall.
|
|
||||||
HINT: Drop the index until the table has more data.
|
|
||||||
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
|
||||||
val
|
|
||||||
-----
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
653
test/expected/sparsevec.out
Normal file
653
test/expected/sparsevec.out
Normal file
@@ -0,0 +1,653 @@
|
|||||||
|
SELECT '{1:1.5,3:3.5}/5'::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
-----------------
|
||||||
|
{1:1.5,3:3.5}/5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:-2,3:-4}/5'::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
---------------
|
||||||
|
{1:-2,3:-4}/5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:2.,3:4.}/5'::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
-------------
|
||||||
|
{1:2,3:4}/5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT ' { 1 : 1.5 , 3 : 3.5 } / 5 '::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
-----------------
|
||||||
|
{1:1.5,3:3.5}/5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1.23456}/1'::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
---------------
|
||||||
|
{1:1.23456}/1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:hello,2:1}/2'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{1:hello,2:1}/2"
|
||||||
|
LINE 1: SELECT '{1:hello,2:1}/2'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1:NaN,2:1}/2'::sparsevec;
|
||||||
|
ERROR: NaN not allowed in sparsevec
|
||||||
|
LINE 1: SELECT '{1:NaN,2:1}/2'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1:Infinity,2:1}/2'::sparsevec;
|
||||||
|
ERROR: infinite value not allowed in sparsevec
|
||||||
|
LINE 1: SELECT '{1:Infinity,2:1}/2'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1:-Infinity,2:1}/2'::sparsevec;
|
||||||
|
ERROR: infinite value not allowed in sparsevec
|
||||||
|
LINE 1: SELECT '{1:-Infinity,2:1}/2'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1:1.5e38,2:-1.5e38}/2'::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
--------------------------
|
||||||
|
{1:1.5e+38,2:-1.5e+38}/2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1.5e+38,2:-1.5e+38}/2'::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
--------------------------
|
||||||
|
{1:1.5e+38,2:-1.5e+38}/2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1.5e-38,2:-1.5e-38}/2'::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
--------------------------
|
||||||
|
{1:1.5e-38,2:-1.5e-38}/2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:4e38,2:1}/2'::sparsevec;
|
||||||
|
ERROR: "4e38" is out of range for type sparsevec
|
||||||
|
LINE 1: SELECT '{1:4e38,2:1}/2'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1:-4e38,2:1}/2'::sparsevec;
|
||||||
|
ERROR: "-4e38" is out of range for type sparsevec
|
||||||
|
LINE 1: SELECT '{1:-4e38,2:1}/2'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1:1e-46,2:1}/2'::sparsevec;
|
||||||
|
ERROR: "1e-46" is out of range for type sparsevec
|
||||||
|
LINE 1: SELECT '{1:1e-46,2:1}/2'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1:-1e-46,2:1}/2'::sparsevec;
|
||||||
|
ERROR: "-1e-46" is out of range for type sparsevec
|
||||||
|
LINE 1: SELECT '{1:-1e-46,2:1}/2'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT ''::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: ""
|
||||||
|
LINE 1: SELECT ''::sparsevec;
|
||||||
|
^
|
||||||
|
DETAIL: Vector contents must start with "{".
|
||||||
|
SELECT '{'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{"
|
||||||
|
LINE 1: SELECT '{'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{ '::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{ "
|
||||||
|
LINE 1: SELECT '{ '::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{:'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{:"
|
||||||
|
LINE 1: SELECT '{:'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{,'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{,"
|
||||||
|
LINE 1: SELECT '{,'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{}'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{}"
|
||||||
|
LINE 1: SELECT '{}'::sparsevec;
|
||||||
|
^
|
||||||
|
DETAIL: Unexpected end of input.
|
||||||
|
SELECT '{}/'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{}/"
|
||||||
|
LINE 1: SELECT '{}/'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{}/1'::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
-----------
|
||||||
|
{}/1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{}/1a'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{}/1a"
|
||||||
|
LINE 1: SELECT '{}/1a'::sparsevec;
|
||||||
|
^
|
||||||
|
DETAIL: Junk after closing.
|
||||||
|
SELECT '{ }/1'::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
-----------
|
||||||
|
{}/1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{:}/1'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{:}/1"
|
||||||
|
LINE 1: SELECT '{:}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{,}/1'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{,}/1"
|
||||||
|
LINE 1: SELECT '{,}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1,}/1'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{1,}/1"
|
||||||
|
LINE 1: SELECT '{1,}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{:1}/1'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{:1}/1"
|
||||||
|
LINE 1: SELECT '{:1}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1:}/1'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{1:}/1"
|
||||||
|
LINE 1: SELECT '{1:}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1a:1}/1'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{1a:1}/1"
|
||||||
|
LINE 1: SELECT '{1a:1}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1:1a}/1'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{1:1a}/1"
|
||||||
|
LINE 1: SELECT '{1:1a}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1:1,}/1'::sparsevec;
|
||||||
|
ERROR: invalid input syntax for type sparsevec: "{1:1,}/1"
|
||||||
|
LINE 1: SELECT '{1:1,}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1:0,2:1,3:0}/3'::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
-----------
|
||||||
|
{2:1}/3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{2:1,1:1}/2'::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
-------------
|
||||||
|
{1:1,2:1}/2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,1:1}/2'::sparsevec;
|
||||||
|
ERROR: sparsevec indices must not contain duplicates
|
||||||
|
LINE 1: SELECT '{1:1,1:1}/2'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{1:1,2:1,1:1}/2'::sparsevec;
|
||||||
|
ERROR: sparsevec indices must not contain duplicates
|
||||||
|
LINE 1: SELECT '{1:1,2:1,1:1}/2'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{}/5'::sparsevec;
|
||||||
|
sparsevec
|
||||||
|
-----------
|
||||||
|
{}/5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{}/-1'::sparsevec;
|
||||||
|
ERROR: sparsevec must have at least 1 dimension
|
||||||
|
LINE 1: SELECT '{}/-1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{}/1000000001'::sparsevec;
|
||||||
|
ERROR: sparsevec cannot have more than 1000000000 dimensions
|
||||||
|
LINE 1: SELECT '{}/1000000001'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{}/2147483648'::sparsevec;
|
||||||
|
ERROR: sparsevec cannot have more than 1000000000 dimensions
|
||||||
|
LINE 1: SELECT '{}/2147483648'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{}/-2147483649'::sparsevec;
|
||||||
|
ERROR: sparsevec must have at least 1 dimension
|
||||||
|
LINE 1: SELECT '{}/-2147483649'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{}/9223372036854775808'::sparsevec;
|
||||||
|
ERROR: sparsevec cannot have more than 1000000000 dimensions
|
||||||
|
LINE 1: SELECT '{}/9223372036854775808'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{}/-9223372036854775809'::sparsevec;
|
||||||
|
ERROR: sparsevec must have at least 1 dimension
|
||||||
|
LINE 1: SELECT '{}/-9223372036854775809'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{2147483647:1}/1'::sparsevec;
|
||||||
|
ERROR: sparsevec index out of bounds
|
||||||
|
LINE 1: SELECT '{2147483647:1}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{2147483648:1}/1'::sparsevec;
|
||||||
|
ERROR: sparsevec index out of bounds
|
||||||
|
LINE 1: SELECT '{2147483648:1}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{-2147483648:1}/1'::sparsevec;
|
||||||
|
ERROR: sparsevec index out of bounds
|
||||||
|
LINE 1: SELECT '{-2147483648:1}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{-2147483649:1}/1'::sparsevec;
|
||||||
|
ERROR: sparsevec index out of bounds
|
||||||
|
LINE 1: SELECT '{-2147483649:1}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{0:1}/1'::sparsevec;
|
||||||
|
ERROR: sparsevec index out of bounds
|
||||||
|
LINE 1: SELECT '{0:1}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{2:1}/1'::sparsevec;
|
||||||
|
ERROR: sparsevec index out of bounds
|
||||||
|
LINE 1: SELECT '{2:1}/1'::sparsevec;
|
||||||
|
^
|
||||||
|
SELECT '{}/3'::sparsevec(3);
|
||||||
|
sparsevec
|
||||||
|
-----------
|
||||||
|
{}/3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{}/3'::sparsevec(2);
|
||||||
|
ERROR: expected 2 dimensions, not 3
|
||||||
|
SELECT '{}/3'::sparsevec(3, 2);
|
||||||
|
ERROR: invalid type modifier
|
||||||
|
LINE 1: SELECT '{}/3'::sparsevec(3, 2);
|
||||||
|
^
|
||||||
|
SELECT '{}/3'::sparsevec('a');
|
||||||
|
ERROR: invalid input syntax for type integer: "a"
|
||||||
|
LINE 1: SELECT '{}/3'::sparsevec('a');
|
||||||
|
^
|
||||||
|
SELECT '{}/3'::sparsevec(0);
|
||||||
|
ERROR: dimensions for type sparsevec must be at least 1
|
||||||
|
LINE 1: SELECT '{}/3'::sparsevec(0);
|
||||||
|
^
|
||||||
|
SELECT '{}/3'::sparsevec(1000000001);
|
||||||
|
ERROR: dimensions for type sparsevec cannot exceed 1000000000
|
||||||
|
LINE 1: SELECT '{}/3'::sparsevec(1000000001);
|
||||||
|
^
|
||||||
|
SELECT '{1:1,2:2,3:3}/3'::sparsevec < '{1:1,2:2,3:3}/3';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2,3:3}/3'::sparsevec < '{1:1,2:2}/2';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2,3:3}/3'::sparsevec <= '{1:1,2:2,3:3}/3';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2,3:3}/3'::sparsevec <= '{1:1,2:2}/2';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2,3:3}/3'::sparsevec = '{1:1,2:2,3:3}/3';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2,3:3}/3'::sparsevec = '{1:1,2:2}/2';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2,3:3}/3'::sparsevec != '{1:1,2:2,3:3}/3';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2,3:3}/3'::sparsevec != '{1:1,2:2}/2';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2,3:3}/3'::sparsevec >= '{1:1,2:2,3:3}/3';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2,3:3}/3'::sparsevec >= '{1:1,2:2}/2';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2,3:3}/3'::sparsevec > '{1:1,2:2,3:3}/3';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2,3:3}/3'::sparsevec > '{1:1,2:2}/2';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{1:1,2:2,3:3}/3');
|
||||||
|
sparsevec_cmp
|
||||||
|
---------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{}/3');
|
||||||
|
sparsevec_cmp
|
||||||
|
---------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT sparsevec_cmp('{}/3', '{1:1,2:2,3:3}/3');
|
||||||
|
sparsevec_cmp
|
||||||
|
---------------
|
||||||
|
-1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT sparsevec_cmp('{1:1,2:2}/2', '{1:1,2:2,3:3}/3');
|
||||||
|
sparsevec_cmp
|
||||||
|
---------------
|
||||||
|
-1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{1:1,2:2}/2');
|
||||||
|
sparsevec_cmp
|
||||||
|
---------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT sparsevec_cmp('{1:1,2:2}/2', '{1:2,2:3,3:4}/3');
|
||||||
|
sparsevec_cmp
|
||||||
|
---------------
|
||||||
|
-1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT sparsevec_cmp('{1:2,2:3}/2', '{1:1,2:2,3:3}/3');
|
||||||
|
sparsevec_cmp
|
||||||
|
---------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT round(l2_norm('{1:1,2:1}/2'::sparsevec)::numeric, 5);
|
||||||
|
round
|
||||||
|
---------
|
||||||
|
1.41421
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_norm('{1:3,2:4}/2'::sparsevec);
|
||||||
|
l2_norm
|
||||||
|
---------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_norm('{2:1}/2'::sparsevec);
|
||||||
|
l2_norm
|
||||||
|
---------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_norm('{1:3e37,2:4e37}/2'::sparsevec)::real;
|
||||||
|
l2_norm
|
||||||
|
---------
|
||||||
|
5e+37
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_norm('{}/2'::sparsevec);
|
||||||
|
l2_norm
|
||||||
|
---------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_norm('{1:2}/1'::sparsevec);
|
||||||
|
l2_norm
|
||||||
|
---------
|
||||||
|
2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_distance('{}/2'::sparsevec, '{1:3,2:4}/2');
|
||||||
|
l2_distance
|
||||||
|
-------------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_distance('{1:3}/2'::sparsevec, '{2:4}/2');
|
||||||
|
l2_distance
|
||||||
|
-------------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_distance('{2:4}/2'::sparsevec, '{1:3}/2');
|
||||||
|
l2_distance
|
||||||
|
-------------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_distance('{1:3,2:4}/2'::sparsevec, '{}/2');
|
||||||
|
l2_distance
|
||||||
|
-------------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_distance('{}/2'::sparsevec, '{2:1}/2');
|
||||||
|
l2_distance
|
||||||
|
-------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{}/2'::sparsevec <-> '{1:3,2:4}/2';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2');
|
||||||
|
inner_product
|
||||||
|
---------------
|
||||||
|
10
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
|
||||||
|
ERROR: different sparsevec dimensions 2 and 1
|
||||||
|
SELECT inner_product('{1:1,3:3}/4'::sparsevec, '{2:2,4:4}/4');
|
||||||
|
inner_product
|
||||||
|
---------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT inner_product('{2:2,4:4}/4'::sparsevec, '{1:1,3:3}/4');
|
||||||
|
inner_product
|
||||||
|
---------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT inner_product('{1:1,3:3,5:5}/5'::sparsevec, '{2:4,3:6,4:8}/5');
|
||||||
|
inner_product
|
||||||
|
---------------
|
||||||
|
18
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT inner_product('{1:1}/2'::sparsevec, '{}/2');
|
||||||
|
inner_product
|
||||||
|
---------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT inner_product('{}/2'::sparsevec, '{1:1}/2');
|
||||||
|
inner_product
|
||||||
|
---------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT inner_product('{1:3e38}/1'::sparsevec, '{1:3e38}/1');
|
||||||
|
inner_product
|
||||||
|
---------------
|
||||||
|
Infinity
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT inner_product('{1:1,3:3,5:5}/5'::sparsevec, '{2:4,3:6,4:8}/5');
|
||||||
|
inner_product
|
||||||
|
---------------
|
||||||
|
18
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2}/2'::sparsevec <#> '{1:3,2:4}/2';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
-11
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2');
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{}/2');
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
NaN
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:1,2:1}/2');
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1:1}/2'::sparsevec, '{2:2}/2');
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:-1,2:-1}/2');
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1:2}/2'::sparsevec, '{2:2}/2');
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{2:2}/2'::sparsevec, '{1:2}/2');
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
|
||||||
|
ERROR: different sparsevec dimensions 2 and 1
|
||||||
|
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:1.1,2:1.1}/2');
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:-1.1,2:-1.1}/2');
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1:3e38}/1'::sparsevec, '{1:3e38}/1');
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
NaN
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{}/1'::sparsevec, '{}/1');
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
NaN
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1:1,2:2}/2'::sparsevec <=> '{1:2,2:4}/2';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l1_distance('{}/2'::sparsevec, '{1:3,2:4}/2');
|
||||||
|
l1_distance
|
||||||
|
-------------
|
||||||
|
7
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l1_distance('{}/2'::sparsevec, '{2:1}/2');
|
||||||
|
l1_distance
|
||||||
|
-------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l1_distance('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
|
||||||
|
ERROR: different sparsevec dimensions 2 and 1
|
||||||
|
SELECT l1_distance('{1:3e38}/1'::sparsevec, '{1:-3e38}/1');
|
||||||
|
l1_distance
|
||||||
|
-------------
|
||||||
|
Infinity
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l1_distance('{1:1,3:3,5:5,7:7}/8'::sparsevec, '{2:2,4:4,6:6,8:8}/8');
|
||||||
|
l1_distance
|
||||||
|
-------------
|
||||||
|
36
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l1_distance('{1:1,3:3,5:5,7:7,9:9}/9'::sparsevec, '{2:2,4:4,6:6,8:8}/9');
|
||||||
|
l1_distance
|
||||||
|
-------------
|
||||||
|
45
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{}/2'::sparsevec <+> '{1:3,2:4}/2';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
7
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_normalize('{1:3,2:4}/2'::sparsevec);
|
||||||
|
l2_normalize
|
||||||
|
-----------------
|
||||||
|
{1:0.6,2:0.8}/2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_normalize('{1:3}/2'::sparsevec);
|
||||||
|
l2_normalize
|
||||||
|
--------------
|
||||||
|
{1:1}/2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_normalize('{2:0.1}/2'::sparsevec);
|
||||||
|
l2_normalize
|
||||||
|
--------------
|
||||||
|
{2:1}/2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_normalize('{}/2'::sparsevec);
|
||||||
|
l2_normalize
|
||||||
|
--------------
|
||||||
|
{}/2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_normalize('{1:3e38}/1'::sparsevec);
|
||||||
|
l2_normalize
|
||||||
|
--------------
|
||||||
|
{1:1}/1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_normalize('{1:3e38,2:1e-37}/2'::sparsevec);
|
||||||
|
l2_normalize
|
||||||
|
--------------
|
||||||
|
{1:1}/2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_normalize('{2:3e37,4:3e-37,6:4e37,8:4e-37}/9'::sparsevec);
|
||||||
|
l2_normalize
|
||||||
|
-----------------
|
||||||
|
{2:0.6,6:0.8}/9
|
||||||
|
(1 row)
|
||||||
|
|
||||||
@@ -1,336 +0,0 @@
|
|||||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec < '{1:1,2:2,3:3}/3';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
f
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec < '{1:1,2:2}/2';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
f
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec <= '{1:1,2:2,3:3}/3';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
t
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec <= '{1:1,2:2}/2';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
f
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec = '{1:1,2:2,3:3}/3';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
t
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec = '{1:1,2:2}/2';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
f
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec != '{1:1,2:2,3:3}/3';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
f
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec != '{1:1,2:2}/2';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
t
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec >= '{1:1,2:2,3:3}/3';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
t
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec >= '{1:1,2:2}/2';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
t
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec > '{1:1,2:2,3:3}/3';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
f
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2,3:3}/3'::sparsevec > '{1:1,2:2}/2';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
t
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{1:1,2:2,3:3}/3');
|
|
||||||
sparsevec_cmp
|
|
||||||
---------------
|
|
||||||
0
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{}/3');
|
|
||||||
sparsevec_cmp
|
|
||||||
---------------
|
|
||||||
1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT sparsevec_cmp('{}/3', '{1:1,2:2,3:3}/3');
|
|
||||||
sparsevec_cmp
|
|
||||||
---------------
|
|
||||||
-1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT sparsevec_cmp('{1:1,2:2}/2', '{1:1,2:2,3:3}/3');
|
|
||||||
sparsevec_cmp
|
|
||||||
---------------
|
|
||||||
-1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT sparsevec_cmp('{1:1,2:2,3:3}/3', '{1:1,2:2}/2');
|
|
||||||
sparsevec_cmp
|
|
||||||
---------------
|
|
||||||
1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT sparsevec_cmp('{1:1,2:2}/2', '{1:2,2:3,3:4}/3');
|
|
||||||
sparsevec_cmp
|
|
||||||
---------------
|
|
||||||
-1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT sparsevec_cmp('{1:2,2:3}/2', '{1:1,2:2,3:3}/3');
|
|
||||||
sparsevec_cmp
|
|
||||||
---------------
|
|
||||||
1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT round(l2_norm('{1:1,2:1}/2'::sparsevec)::numeric, 5);
|
|
||||||
round
|
|
||||||
---------
|
|
||||||
1.41421
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_norm('{1:3,2:4}/2'::sparsevec);
|
|
||||||
l2_norm
|
|
||||||
---------
|
|
||||||
5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_norm('{2:1}/2'::sparsevec);
|
|
||||||
l2_norm
|
|
||||||
---------
|
|
||||||
1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_norm('{1:3e37,2:4e37}/2'::sparsevec)::real;
|
|
||||||
l2_norm
|
|
||||||
---------
|
|
||||||
5e+37
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_distance('{}/2'::sparsevec, '{1:3,2:4}/2');
|
|
||||||
l2_distance
|
|
||||||
-------------
|
|
||||||
5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_distance('{1:3}/2'::sparsevec, '{2:4}/2');
|
|
||||||
l2_distance
|
|
||||||
-------------
|
|
||||||
5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_distance('{2:4}/2'::sparsevec, '{1:3}/2');
|
|
||||||
l2_distance
|
|
||||||
-------------
|
|
||||||
5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_distance('{}/2'::sparsevec, '{2:1}/2');
|
|
||||||
l2_distance
|
|
||||||
-------------
|
|
||||||
1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{}/2'::sparsevec <-> '{1:3,2:4}/2';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2');
|
|
||||||
inner_product
|
|
||||||
---------------
|
|
||||||
10
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT inner_product('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
|
|
||||||
ERROR: different sparsevec dimensions 2 and 1
|
|
||||||
SELECT inner_product('{1:3e38}/1'::sparsevec, '{1:3e38}/1');
|
|
||||||
inner_product
|
|
||||||
---------------
|
|
||||||
Infinity
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT inner_product('{1:1,3:3,5:5}/5'::sparsevec, '{2:4,3:6,4:8}/5');
|
|
||||||
inner_product
|
|
||||||
---------------
|
|
||||||
18
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2}/2'::sparsevec <#> '{1:3,2:4}/2';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
-11
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{1:2,2:4}/2');
|
|
||||||
cosine_distance
|
|
||||||
-----------------
|
|
||||||
0
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{}/2');
|
|
||||||
cosine_distance
|
|
||||||
-----------------
|
|
||||||
NaN
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:1,2:1}/2');
|
|
||||||
cosine_distance
|
|
||||||
-----------------
|
|
||||||
0
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT cosine_distance('{1:1}/2'::sparsevec, '{2:2}/2');
|
|
||||||
cosine_distance
|
|
||||||
-----------------
|
|
||||||
1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:-1,2:-1}/2');
|
|
||||||
cosine_distance
|
|
||||||
-----------------
|
|
||||||
2
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT cosine_distance('{1:2}/2'::sparsevec, '{2:2}/2');
|
|
||||||
cosine_distance
|
|
||||||
-----------------
|
|
||||||
1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT cosine_distance('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
|
|
||||||
ERROR: different sparsevec dimensions 2 and 1
|
|
||||||
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:1.1,2:1.1}/2');
|
|
||||||
cosine_distance
|
|
||||||
-----------------
|
|
||||||
0
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT cosine_distance('{1:1,2:1}/2'::sparsevec, '{1:-1.1,2:-1.1}/2');
|
|
||||||
cosine_distance
|
|
||||||
-----------------
|
|
||||||
2
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT cosine_distance('{1:3e38}/1'::sparsevec, '{1:3e38}/1');
|
|
||||||
cosine_distance
|
|
||||||
-----------------
|
|
||||||
NaN
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT cosine_distance('{}/1'::sparsevec, '{}/1');
|
|
||||||
cosine_distance
|
|
||||||
-----------------
|
|
||||||
NaN
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,2:2}/2'::sparsevec <=> '{1:2,2:4}/2';
|
|
||||||
?column?
|
|
||||||
----------
|
|
||||||
0
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l1_distance('{}/2'::sparsevec, '{1:3,2:4}/2');
|
|
||||||
l1_distance
|
|
||||||
-------------
|
|
||||||
7
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l1_distance('{}/2'::sparsevec, '{2:1}/2');
|
|
||||||
l1_distance
|
|
||||||
-------------
|
|
||||||
1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l1_distance('{1:1,2:2}/2'::sparsevec, '{1:3}/1');
|
|
||||||
ERROR: different sparsevec dimensions 2 and 1
|
|
||||||
SELECT l1_distance('{1:3e38}/1'::sparsevec, '{1:-3e38}/1');
|
|
||||||
l1_distance
|
|
||||||
-------------
|
|
||||||
Infinity
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l1_distance('{1:1,3:3,5:5,7:7}/8'::sparsevec, '{2:2,4:4,6:6,8:8}/8');
|
|
||||||
l1_distance
|
|
||||||
-------------
|
|
||||||
36
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l1_distance('{1:1,3:3,5:5,7:7,9:9}/9'::sparsevec, '{2:2,4:4,6:6,8:8}/9');
|
|
||||||
l1_distance
|
|
||||||
-------------
|
|
||||||
45
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_normalize('{1:3,2:4}/2'::sparsevec);
|
|
||||||
l2_normalize
|
|
||||||
-----------------
|
|
||||||
{1:0.6,2:0.8}/2
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_normalize('{1:3}/2'::sparsevec);
|
|
||||||
l2_normalize
|
|
||||||
--------------
|
|
||||||
{1:1}/2
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_normalize('{2:0.1}/2'::sparsevec);
|
|
||||||
l2_normalize
|
|
||||||
--------------
|
|
||||||
{2:1}/2
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_normalize('{}/2'::sparsevec);
|
|
||||||
l2_normalize
|
|
||||||
--------------
|
|
||||||
{}/2
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_normalize('{1:3e38}/1'::sparsevec);
|
|
||||||
l2_normalize
|
|
||||||
--------------
|
|
||||||
{1:1}/1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_normalize('{1:3e38,2:1e-37}/2'::sparsevec);
|
|
||||||
l2_normalize
|
|
||||||
--------------
|
|
||||||
{1:1}/2
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT l2_normalize('{2:3e37,4:3e-37,6:4e37,8:4e-37}/9'::sparsevec);
|
|
||||||
l2_normalize
|
|
||||||
-----------------
|
|
||||||
{2:0.6,6:0.8}/9
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
SELECT '{1:1.5,3:3.5}/5'::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
-----------------
|
|
||||||
{1:1.5,3:3.5}/5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:-2,3:-4}/5'::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
---------------
|
|
||||||
{1:-2,3:-4}/5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:2.,3:4.}/5'::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
-------------
|
|
||||||
{1:2,3:4}/5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT ' { 1 : 1.5 , 3 : 3.5 } / 5 '::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
-----------------
|
|
||||||
{1:1.5,3:3.5}/5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1.23456}/1'::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
---------------
|
|
||||||
{1:1.23456}/1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:hello,2:1}/2'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{1:hello,2:1}/2"
|
|
||||||
LINE 1: SELECT '{1:hello,2:1}/2'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1:NaN,2:1}/2'::sparsevec;
|
|
||||||
ERROR: NaN not allowed in sparsevec
|
|
||||||
LINE 1: SELECT '{1:NaN,2:1}/2'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1:Infinity,2:1}/2'::sparsevec;
|
|
||||||
ERROR: infinite value not allowed in sparsevec
|
|
||||||
LINE 1: SELECT '{1:Infinity,2:1}/2'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1:-Infinity,2:1}/2'::sparsevec;
|
|
||||||
ERROR: infinite value not allowed in sparsevec
|
|
||||||
LINE 1: SELECT '{1:-Infinity,2:1}/2'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1:1.5e38,2:-1.5e38}/2'::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
--------------------------
|
|
||||||
{1:1.5e+38,2:-1.5e+38}/2
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1.5e+38,2:-1.5e+38}/2'::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
--------------------------
|
|
||||||
{1:1.5e+38,2:-1.5e+38}/2
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1.5e-38,2:-1.5e-38}/2'::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
--------------------------
|
|
||||||
{1:1.5e-38,2:-1.5e-38}/2
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:4e38,2:1}/2'::sparsevec;
|
|
||||||
ERROR: "4e38" is out of range for type sparsevec
|
|
||||||
LINE 1: SELECT '{1:4e38,2:1}/2'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1:-4e38,2:1}/2'::sparsevec;
|
|
||||||
ERROR: "-4e38" is out of range for type sparsevec
|
|
||||||
LINE 1: SELECT '{1:-4e38,2:1}/2'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1:1e-46,2:1}/2'::sparsevec;
|
|
||||||
ERROR: "1e-46" is out of range for type sparsevec
|
|
||||||
LINE 1: SELECT '{1:1e-46,2:1}/2'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1:-1e-46,2:1}/2'::sparsevec;
|
|
||||||
ERROR: "-1e-46" is out of range for type sparsevec
|
|
||||||
LINE 1: SELECT '{1:-1e-46,2:1}/2'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT ''::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: ""
|
|
||||||
LINE 1: SELECT ''::sparsevec;
|
|
||||||
^
|
|
||||||
DETAIL: Vector contents must start with "{".
|
|
||||||
SELECT '{'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{"
|
|
||||||
LINE 1: SELECT '{'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{ '::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{ "
|
|
||||||
LINE 1: SELECT '{ '::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{:'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{:"
|
|
||||||
LINE 1: SELECT '{:'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{,'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{,"
|
|
||||||
LINE 1: SELECT '{,'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{}'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{}"
|
|
||||||
LINE 1: SELECT '{}'::sparsevec;
|
|
||||||
^
|
|
||||||
DETAIL: Unexpected end of input.
|
|
||||||
SELECT '{}/'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{}/"
|
|
||||||
LINE 1: SELECT '{}/'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{}/1'::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
-----------
|
|
||||||
{}/1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{}/1a'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{}/1a"
|
|
||||||
LINE 1: SELECT '{}/1a'::sparsevec;
|
|
||||||
^
|
|
||||||
DETAIL: Junk after closing.
|
|
||||||
SELECT '{ }/1'::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
-----------
|
|
||||||
{}/1
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{:}/1'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{:}/1"
|
|
||||||
LINE 1: SELECT '{:}/1'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{,}/1'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{,}/1"
|
|
||||||
LINE 1: SELECT '{,}/1'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1,}/1'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{1,}/1"
|
|
||||||
LINE 1: SELECT '{1,}/1'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{:1}/1'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{:1}/1"
|
|
||||||
LINE 1: SELECT '{:1}/1'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1:}/1'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{1:}/1"
|
|
||||||
LINE 1: SELECT '{1:}/1'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1a:1}/1'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{1a:1}/1"
|
|
||||||
LINE 1: SELECT '{1a:1}/1'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1:1a}/1'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{1:1a}/1"
|
|
||||||
LINE 1: SELECT '{1:1a}/1'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1:1,}/1'::sparsevec;
|
|
||||||
ERROR: invalid input syntax for type sparsevec: "{1:1,}/1"
|
|
||||||
LINE 1: SELECT '{1:1,}/1'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1:0,2:1,3:0}/3'::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
-----------
|
|
||||||
{2:1}/3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{2:1,1:1}/2'::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
-------------
|
|
||||||
{1:1,2:1}/2
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{1:1,1:1}/2'::sparsevec;
|
|
||||||
ERROR: indexes must not contain duplicates
|
|
||||||
LINE 1: SELECT '{1:1,1:1}/2'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{1:1,2:1,1:1}/2'::sparsevec;
|
|
||||||
ERROR: indexes must not contain duplicates
|
|
||||||
LINE 1: SELECT '{1:1,2:1,1:1}/2'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{}/5'::sparsevec;
|
|
||||||
sparsevec
|
|
||||||
-----------
|
|
||||||
{}/5
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{}/-1'::sparsevec;
|
|
||||||
ERROR: sparsevec must have at least 1 dimension
|
|
||||||
LINE 1: SELECT '{}/-1'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{}/100001'::sparsevec;
|
|
||||||
ERROR: sparsevec cannot have more than 100000 dimensions
|
|
||||||
LINE 1: SELECT '{}/100001'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{0:1}/1'::sparsevec;
|
|
||||||
ERROR: index "0" is out of range for type sparsevec
|
|
||||||
LINE 1: SELECT '{0:1}/1'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{2:1}/1'::sparsevec;
|
|
||||||
ERROR: index must be less than or equal to dimensions
|
|
||||||
LINE 1: SELECT '{2:1}/1'::sparsevec;
|
|
||||||
^
|
|
||||||
SELECT '{}/3'::sparsevec(3);
|
|
||||||
sparsevec
|
|
||||||
-----------
|
|
||||||
{}/3
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '{}/3'::sparsevec(2);
|
|
||||||
ERROR: expected 2 dimensions, not 3
|
|
||||||
SELECT '{}/3'::sparsevec(3, 2);
|
|
||||||
ERROR: invalid type modifier
|
|
||||||
LINE 1: SELECT '{}/3'::sparsevec(3, 2);
|
|
||||||
^
|
|
||||||
SELECT '{}/3'::sparsevec('a');
|
|
||||||
ERROR: invalid input syntax for type integer: "a"
|
|
||||||
LINE 1: SELECT '{}/3'::sparsevec('a');
|
|
||||||
^
|
|
||||||
SELECT '{}/3'::sparsevec(0);
|
|
||||||
ERROR: dimensions for type sparsevec must be at least 1
|
|
||||||
LINE 1: SELECT '{}/3'::sparsevec(0);
|
|
||||||
^
|
|
||||||
SELECT '{}/3'::sparsevec(100001);
|
|
||||||
ERROR: dimensions for type sparsevec cannot exceed 100000
|
|
||||||
LINE 1: SELECT '{}/3'::sparsevec(100001);
|
|
||||||
^
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
SELECT '[1,2,3]'::vector;
|
|
||||||
vector
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[-1,-2,-3]'::vector;
|
|
||||||
vector
|
|
||||||
------------
|
|
||||||
[-1,-2,-3]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[1.,2.,3.]'::vector;
|
|
||||||
vector
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT ' [ 1, 2 , 3 ] '::vector;
|
|
||||||
vector
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[1.23456]'::vector;
|
|
||||||
vector
|
|
||||||
-----------
|
|
||||||
[1.23456]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[hello,1]'::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: "[hello,1]"
|
|
||||||
LINE 1: SELECT '[hello,1]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[NaN,1]'::vector;
|
|
||||||
ERROR: NaN not allowed in vector
|
|
||||||
LINE 1: SELECT '[NaN,1]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[Infinity,1]'::vector;
|
|
||||||
ERROR: infinite value not allowed in vector
|
|
||||||
LINE 1: SELECT '[Infinity,1]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[-Infinity,1]'::vector;
|
|
||||||
ERROR: infinite value not allowed in vector
|
|
||||||
LINE 1: SELECT '[-Infinity,1]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[1.5e38,-1.5e38]'::vector;
|
|
||||||
vector
|
|
||||||
--------------------
|
|
||||||
[1.5e+38,-1.5e+38]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[1.5e+38,-1.5e+38]'::vector;
|
|
||||||
vector
|
|
||||||
--------------------
|
|
||||||
[1.5e+38,-1.5e+38]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[1.5e-38,-1.5e-38]'::vector;
|
|
||||||
vector
|
|
||||||
--------------------
|
|
||||||
[1.5e-38,-1.5e-38]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[4e38,1]'::vector;
|
|
||||||
ERROR: "4e38" is out of range for type vector
|
|
||||||
LINE 1: SELECT '[4e38,1]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[-4e38,1]'::vector;
|
|
||||||
ERROR: "-4e38" is out of range for type vector
|
|
||||||
LINE 1: SELECT '[-4e38,1]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[1e-46,1]'::vector;
|
|
||||||
vector
|
|
||||||
--------
|
|
||||||
[0,1]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[-1e-46,1]'::vector;
|
|
||||||
vector
|
|
||||||
--------
|
|
||||||
[-0,1]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[1,2,3'::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: "[1,2,3"
|
|
||||||
LINE 1: SELECT '[1,2,3'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[1,2,3]9'::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: "[1,2,3]9"
|
|
||||||
LINE 1: SELECT '[1,2,3]9'::vector;
|
|
||||||
^
|
|
||||||
DETAIL: Junk after closing right brace.
|
|
||||||
SELECT '1,2,3'::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: "1,2,3"
|
|
||||||
LINE 1: SELECT '1,2,3'::vector;
|
|
||||||
^
|
|
||||||
DETAIL: Vector contents must start with "[".
|
|
||||||
SELECT ''::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: ""
|
|
||||||
LINE 1: SELECT ''::vector;
|
|
||||||
^
|
|
||||||
DETAIL: Vector contents must start with "[".
|
|
||||||
SELECT '['::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: "["
|
|
||||||
LINE 1: SELECT '['::vector;
|
|
||||||
^
|
|
||||||
SELECT '[ '::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: "[ "
|
|
||||||
LINE 1: SELECT '[ '::vector;
|
|
||||||
^
|
|
||||||
SELECT '[,'::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: "[,"
|
|
||||||
LINE 1: SELECT '[,'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[]'::vector;
|
|
||||||
ERROR: vector must have at least 1 dimension
|
|
||||||
LINE 1: SELECT '[]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[ ]'::vector;
|
|
||||||
ERROR: vector must have at least 1 dimension
|
|
||||||
LINE 1: SELECT '[ ]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[,]'::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: "[,]"
|
|
||||||
LINE 1: SELECT '[,]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[1,]'::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: "[1,]"
|
|
||||||
LINE 1: SELECT '[1,]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[1a]'::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: "[1a]"
|
|
||||||
LINE 1: SELECT '[1a]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[1,,3]'::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: "[1,,3]"
|
|
||||||
LINE 1: SELECT '[1,,3]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[1, ,3]'::vector;
|
|
||||||
ERROR: invalid input syntax for type vector: "[1, ,3]"
|
|
||||||
LINE 1: SELECT '[1, ,3]'::vector;
|
|
||||||
^
|
|
||||||
SELECT '[1,2,3]'::vector(3);
|
|
||||||
vector
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT '[1,2,3]'::vector(2);
|
|
||||||
ERROR: expected 2 dimensions, not 3
|
|
||||||
SELECT '[1,2,3]'::vector(3, 2);
|
|
||||||
ERROR: invalid type modifier
|
|
||||||
LINE 1: SELECT '[1,2,3]'::vector(3, 2);
|
|
||||||
^
|
|
||||||
SELECT '[1,2,3]'::vector('a');
|
|
||||||
ERROR: invalid input syntax for type integer: "a"
|
|
||||||
LINE 1: SELECT '[1,2,3]'::vector('a');
|
|
||||||
^
|
|
||||||
SELECT '[1,2,3]'::vector(0);
|
|
||||||
ERROR: dimensions for type vector must be at least 1
|
|
||||||
LINE 1: SELECT '[1,2,3]'::vector(0);
|
|
||||||
^
|
|
||||||
SELECT '[1,2,3]'::vector(16001);
|
|
||||||
ERROR: dimensions for type vector cannot exceed 16000
|
|
||||||
LINE 1: SELECT '[1,2,3]'::vector(16001);
|
|
||||||
^
|
|
||||||
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::vector[]);
|
|
||||||
unnest
|
|
||||||
---------
|
|
||||||
[1,2,3]
|
|
||||||
[4,5,6]
|
|
||||||
(2 rows)
|
|
||||||
|
|
||||||
SELECT '{"[1,2,3]"}'::vector(2)[];
|
|
||||||
ERROR: expected 2 dimensions, not 3
|
|
||||||
@@ -1,3 +1,179 @@
|
|||||||
|
SELECT '[1,2,3]'::vector;
|
||||||
|
vector
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[-1,-2,-3]'::vector;
|
||||||
|
vector
|
||||||
|
------------
|
||||||
|
[-1,-2,-3]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[1.,2.,3.]'::vector;
|
||||||
|
vector
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT ' [ 1, 2 , 3 ] '::vector;
|
||||||
|
vector
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[1.23456]'::vector;
|
||||||
|
vector
|
||||||
|
-----------
|
||||||
|
[1.23456]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[hello,1]'::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: "[hello,1]"
|
||||||
|
LINE 1: SELECT '[hello,1]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[NaN,1]'::vector;
|
||||||
|
ERROR: NaN not allowed in vector
|
||||||
|
LINE 1: SELECT '[NaN,1]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[Infinity,1]'::vector;
|
||||||
|
ERROR: infinite value not allowed in vector
|
||||||
|
LINE 1: SELECT '[Infinity,1]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[-Infinity,1]'::vector;
|
||||||
|
ERROR: infinite value not allowed in vector
|
||||||
|
LINE 1: SELECT '[-Infinity,1]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[1.5e38,-1.5e38]'::vector;
|
||||||
|
vector
|
||||||
|
--------------------
|
||||||
|
[1.5e+38,-1.5e+38]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[1.5e+38,-1.5e+38]'::vector;
|
||||||
|
vector
|
||||||
|
--------------------
|
||||||
|
[1.5e+38,-1.5e+38]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[1.5e-38,-1.5e-38]'::vector;
|
||||||
|
vector
|
||||||
|
--------------------
|
||||||
|
[1.5e-38,-1.5e-38]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[4e38,1]'::vector;
|
||||||
|
ERROR: "4e38" is out of range for type vector
|
||||||
|
LINE 1: SELECT '[4e38,1]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[-4e38,1]'::vector;
|
||||||
|
ERROR: "-4e38" is out of range for type vector
|
||||||
|
LINE 1: SELECT '[-4e38,1]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[1e-46,1]'::vector;
|
||||||
|
vector
|
||||||
|
--------
|
||||||
|
[0,1]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[-1e-46,1]'::vector;
|
||||||
|
vector
|
||||||
|
--------
|
||||||
|
[-0,1]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[1,2,3'::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: "[1,2,3"
|
||||||
|
LINE 1: SELECT '[1,2,3'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[1,2,3]9'::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: "[1,2,3]9"
|
||||||
|
LINE 1: SELECT '[1,2,3]9'::vector;
|
||||||
|
^
|
||||||
|
DETAIL: Junk after closing right brace.
|
||||||
|
SELECT '1,2,3'::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: "1,2,3"
|
||||||
|
LINE 1: SELECT '1,2,3'::vector;
|
||||||
|
^
|
||||||
|
DETAIL: Vector contents must start with "[".
|
||||||
|
SELECT ''::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: ""
|
||||||
|
LINE 1: SELECT ''::vector;
|
||||||
|
^
|
||||||
|
DETAIL: Vector contents must start with "[".
|
||||||
|
SELECT '['::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: "["
|
||||||
|
LINE 1: SELECT '['::vector;
|
||||||
|
^
|
||||||
|
SELECT '[ '::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: "[ "
|
||||||
|
LINE 1: SELECT '[ '::vector;
|
||||||
|
^
|
||||||
|
SELECT '[,'::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: "[,"
|
||||||
|
LINE 1: SELECT '[,'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[]'::vector;
|
||||||
|
ERROR: vector must have at least 1 dimension
|
||||||
|
LINE 1: SELECT '[]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[ ]'::vector;
|
||||||
|
ERROR: vector must have at least 1 dimension
|
||||||
|
LINE 1: SELECT '[ ]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[,]'::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: "[,]"
|
||||||
|
LINE 1: SELECT '[,]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[1,]'::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: "[1,]"
|
||||||
|
LINE 1: SELECT '[1,]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[1a]'::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: "[1a]"
|
||||||
|
LINE 1: SELECT '[1a]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[1,,3]'::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: "[1,,3]"
|
||||||
|
LINE 1: SELECT '[1,,3]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[1, ,3]'::vector;
|
||||||
|
ERROR: invalid input syntax for type vector: "[1, ,3]"
|
||||||
|
LINE 1: SELECT '[1, ,3]'::vector;
|
||||||
|
^
|
||||||
|
SELECT '[1,2,3]'::vector(3);
|
||||||
|
vector
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[1,2,3]'::vector(2);
|
||||||
|
ERROR: expected 2 dimensions, not 3
|
||||||
|
SELECT '[1,2,3]'::vector(3, 2);
|
||||||
|
ERROR: invalid type modifier
|
||||||
|
LINE 1: SELECT '[1,2,3]'::vector(3, 2);
|
||||||
|
^
|
||||||
|
SELECT '[1,2,3]'::vector('a');
|
||||||
|
ERROR: invalid input syntax for type integer: "a"
|
||||||
|
LINE 1: SELECT '[1,2,3]'::vector('a');
|
||||||
|
^
|
||||||
|
SELECT '[1,2,3]'::vector(0);
|
||||||
|
ERROR: dimensions for type vector must be at least 1
|
||||||
|
LINE 1: SELECT '[1,2,3]'::vector(0);
|
||||||
|
^
|
||||||
|
SELECT '[1,2,3]'::vector(16001);
|
||||||
|
ERROR: dimensions for type vector cannot exceed 16000
|
||||||
|
LINE 1: SELECT '[1,2,3]'::vector(16001);
|
||||||
|
^
|
||||||
|
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::vector[]);
|
||||||
|
unnest
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
[4,5,6]
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT '{"[1,2,3]"}'::vector(2)[];
|
||||||
|
ERROR: expected 2 dimensions, not 3
|
||||||
SELECT '[1,2,3]'::vector + '[4,5,6]';
|
SELECT '[1,2,3]'::vector + '[4,5,6]';
|
||||||
?column?
|
?column?
|
||||||
----------
|
----------
|
||||||
@@ -182,6 +358,18 @@ SELECT vector_norm('[3e37,4e37]')::real;
|
|||||||
5e+37
|
5e+37
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
SELECT vector_norm('[0,0]');
|
||||||
|
vector_norm
|
||||||
|
-------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT vector_norm('[2]');
|
||||||
|
vector_norm
|
||||||
|
-------------
|
||||||
|
2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT l2_distance('[0,0]'::vector, '[3,4]');
|
SELECT l2_distance('[0,0]'::vector, '[3,4]');
|
||||||
l2_distance
|
l2_distance
|
||||||
-------------
|
-------------
|
||||||
@@ -418,6 +606,20 @@ SELECT subvector('[1,2,3,4,5]'::vector, 3, -1);
|
|||||||
ERROR: vector must have at least 1 dimension
|
ERROR: vector must have at least 1 dimension
|
||||||
SELECT subvector('[1,2,3,4,5]'::vector, -1, 2);
|
SELECT subvector('[1,2,3,4,5]'::vector, -1, 2);
|
||||||
ERROR: vector must have at least 1 dimension
|
ERROR: vector must have at least 1 dimension
|
||||||
|
SELECT subvector('[1,2,3,4,5]'::vector, 2147483647, 10);
|
||||||
|
ERROR: vector must have at least 1 dimension
|
||||||
|
SELECT subvector('[1,2,3,4,5]'::vector, 3, 2147483647);
|
||||||
|
subvector
|
||||||
|
-----------
|
||||||
|
[3,4,5]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT subvector('[1,2,3,4,5]'::vector, -2147483644, 2147483647);
|
||||||
|
subvector
|
||||||
|
-----------
|
||||||
|
[1,2]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::vector, '[3,5,7]']) v;
|
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::vector, '[3,5,7]']) v;
|
||||||
avg
|
avg
|
||||||
-----------
|
-----------
|
||||||
11
test/perl/PostgreSQL/Test/Cluster.pm
Normal file
11
test/perl/PostgreSQL/Test/Cluster.pm
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package PostgreSQL::Test::Cluster;
|
||||||
|
|
||||||
|
use PostgresNode;
|
||||||
|
|
||||||
|
sub new
|
||||||
|
{
|
||||||
|
my ($class, $name) = @_;
|
||||||
|
return get_new_node($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
5
test/perl/PostgreSQL/Test/Utils.pm
Normal file
5
test/perl/PostgreSQL/Test/Utils.pm
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package PostgreSQL::Test::Utils;
|
||||||
|
|
||||||
|
use TestLib;
|
||||||
|
|
||||||
|
1;
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
use PostgreSQL::Test::Cluster;
|
|
||||||
|
|
||||||
sub get_new_node
|
|
||||||
{
|
|
||||||
return PostgreSQL::Test::Cluster->new(@_);
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
use PostgreSQL::Test::Utils;
|
|
||||||
|
|
||||||
1;
|
|
||||||
34
test/sql/btree.sql
Normal file
34
test/sql/btree.sql
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
SET enable_seqscan = off;
|
||||||
|
|
||||||
|
-- vector
|
||||||
|
|
||||||
|
CREATE TABLE t (val vector(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t (val);
|
||||||
|
|
||||||
|
SELECT * FROM t WHERE val = '[1,2,3]';
|
||||||
|
SELECT * FROM t ORDER BY val;
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
|
||||||
|
-- halfvec
|
||||||
|
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t (val);
|
||||||
|
|
||||||
|
SELECT * FROM t WHERE val = '[1,2,3]';
|
||||||
|
SELECT * FROM t ORDER BY val;
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
|
||||||
|
-- sparsevec
|
||||||
|
|
||||||
|
CREATE TABLE t (val sparsevec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||||
|
CREATE INDEX ON t (val);
|
||||||
|
|
||||||
|
SELECT * FROM t WHERE val = '{1:1,2:2,3:3}/3';
|
||||||
|
SELECT * FROM t ORDER BY val;
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
|
|
||||||
CREATE TABLE t (val halfvec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t (val);
|
|
||||||
|
|
||||||
SELECT * FROM t WHERE val = '[1,2,3]';
|
|
||||||
SELECT * FROM t ORDER BY val;
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
|
|
||||||
CREATE TABLE t (val sparsevec(3));
|
|
||||||
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
|
||||||
CREATE INDEX ON t (val);
|
|
||||||
|
|
||||||
SELECT * FROM t WHERE val = '{1:1,2:2,3:3}/3';
|
|
||||||
SELECT * FROM t ORDER BY val;
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
|
|
||||||
CREATE TABLE t (val vector(3));
|
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
|
||||||
CREATE INDEX ON t (val);
|
|
||||||
|
|
||||||
SELECT * FROM t WHERE val = '[1,2,3]';
|
|
||||||
SELECT * FROM t ORDER BY val;
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
@@ -58,6 +58,22 @@ 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;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,42 @@
|
|||||||
CREATE TABLE t (val vector(3), val2 halfvec(3), val3 sparsevec(3));
|
-- vector
|
||||||
INSERT INTO t (val, val2, val3) VALUES ('[0,0,0]', '[0,0,0]', '{}/3'), ('[1,2,3]', '[1,2,3]', '{1:1,2:2,3:3}/3'), ('[1,1,1]', '[1,1,1]', '{1:1,2:1,3:1}/3'), (NULL, NULL, NULL);
|
|
||||||
|
|
||||||
CREATE TABLE t2 (val vector(3), val2 halfvec(3), val3 sparsevec(3));
|
CREATE TABLE t (val vector(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
|
||||||
\copy t TO 'results/data.bin' WITH (FORMAT binary)
|
CREATE TABLE t2 (val vector(3));
|
||||||
\copy t2 FROM 'results/data.bin' WITH (FORMAT binary)
|
|
||||||
|
\copy t TO 'results/vector.bin' WITH (FORMAT binary)
|
||||||
|
\copy t2 FROM 'results/vector.bin' WITH (FORMAT binary)
|
||||||
|
|
||||||
|
SELECT * FROM t2 ORDER BY val;
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
DROP TABLE t2;
|
||||||
|
|
||||||
|
-- halfvec
|
||||||
|
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
|
||||||
|
CREATE TABLE t2 (val halfvec(3));
|
||||||
|
|
||||||
|
\copy t TO 'results/halfvec.bin' WITH (FORMAT binary)
|
||||||
|
\copy t2 FROM 'results/halfvec.bin' WITH (FORMAT binary)
|
||||||
|
|
||||||
|
SELECT * FROM t2 ORDER BY val;
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
DROP TABLE t2;
|
||||||
|
|
||||||
|
-- sparsevec
|
||||||
|
|
||||||
|
CREATE TABLE t (val sparsevec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('{}/3'), ('{1:1,2:2,3:3}/3'), ('{1:1,2:1,3:1}/3'), (NULL);
|
||||||
|
|
||||||
|
CREATE TABLE t2 (val sparsevec(3));
|
||||||
|
|
||||||
|
\copy t TO 'results/sparsevec.bin' WITH (FORMAT binary)
|
||||||
|
\copy t2 FROM 'results/sparsevec.bin' WITH (FORMAT binary)
|
||||||
|
|
||||||
SELECT * FROM t2 ORDER BY val;
|
SELECT * FROM t2 ORDER BY val;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,42 @@
|
|||||||
|
SELECT '[1,2,3]'::halfvec;
|
||||||
|
SELECT '[-1,-2,-3]'::halfvec;
|
||||||
|
SELECT '[1.,2.,3.]'::halfvec;
|
||||||
|
SELECT ' [ 1, 2 , 3 ] '::halfvec;
|
||||||
|
SELECT '[1.23456]'::halfvec;
|
||||||
|
SELECT '[hello,1]'::halfvec;
|
||||||
|
SELECT '[NaN,1]'::halfvec;
|
||||||
|
SELECT '[Infinity,1]'::halfvec;
|
||||||
|
SELECT '[-Infinity,1]'::halfvec;
|
||||||
|
SELECT '[65519,-65519]'::halfvec;
|
||||||
|
SELECT '[65520,-65520]'::halfvec;
|
||||||
|
SELECT '[1e-8,-1e-8]'::halfvec;
|
||||||
|
SELECT '[4e38,1]'::halfvec;
|
||||||
|
SELECT '[1e-46,1]'::halfvec;
|
||||||
|
SELECT '[1,2,3'::halfvec;
|
||||||
|
SELECT '[1,2,3]9'::halfvec;
|
||||||
|
SELECT '1,2,3'::halfvec;
|
||||||
|
SELECT ''::halfvec;
|
||||||
|
SELECT '['::halfvec;
|
||||||
|
SELECT '[ '::halfvec;
|
||||||
|
SELECT '[,'::halfvec;
|
||||||
|
SELECT '[]'::halfvec;
|
||||||
|
SELECT '[ ]'::halfvec;
|
||||||
|
SELECT '[,]'::halfvec;
|
||||||
|
SELECT '[1,]'::halfvec;
|
||||||
|
SELECT '[1a]'::halfvec;
|
||||||
|
SELECT '[1,,3]'::halfvec;
|
||||||
|
SELECT '[1, ,3]'::halfvec;
|
||||||
|
|
||||||
|
SELECT '[1,2,3]'::halfvec(3);
|
||||||
|
SELECT '[1,2,3]'::halfvec(2);
|
||||||
|
SELECT '[1,2,3]'::halfvec(3, 2);
|
||||||
|
SELECT '[1,2,3]'::halfvec('a');
|
||||||
|
SELECT '[1,2,3]'::halfvec(0);
|
||||||
|
SELECT '[1,2,3]'::halfvec(16001);
|
||||||
|
|
||||||
|
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::halfvec[]);
|
||||||
|
SELECT '{"[1,2,3]"}'::halfvec(2)[];
|
||||||
|
|
||||||
SELECT '[1,2,3]'::halfvec + '[4,5,6]';
|
SELECT '[1,2,3]'::halfvec + '[4,5,6]';
|
||||||
SELECT '[65519]'::halfvec + '[65519]';
|
SELECT '[65519]'::halfvec + '[65519]';
|
||||||
SELECT '[1,2]'::halfvec + '[3]';
|
SELECT '[1,2]'::halfvec + '[3]';
|
||||||
@@ -40,6 +79,8 @@ SELECT vector_dims('[1,2,3]'::halfvec);
|
|||||||
SELECT round(l2_norm('[1,1]'::halfvec)::numeric, 5);
|
SELECT round(l2_norm('[1,1]'::halfvec)::numeric, 5);
|
||||||
SELECT l2_norm('[3,4]'::halfvec);
|
SELECT l2_norm('[3,4]'::halfvec);
|
||||||
SELECT l2_norm('[0,1]'::halfvec);
|
SELECT l2_norm('[0,1]'::halfvec);
|
||||||
|
SELECT l2_norm('[0,0]'::halfvec);
|
||||||
|
SELECT l2_norm('[2]'::halfvec);
|
||||||
|
|
||||||
SELECT l2_distance('[0,0]'::halfvec, '[3,4]');
|
SELECT l2_distance('[0,0]'::halfvec, '[3,4]');
|
||||||
SELECT l2_distance('[0,0]'::halfvec, '[0,1]');
|
SELECT l2_distance('[0,0]'::halfvec, '[0,1]');
|
||||||
@@ -88,6 +129,9 @@ SELECT subvector('[1,2,3,4,5]'::halfvec, 3, 9);
|
|||||||
SELECT subvector('[1,2,3,4,5]'::halfvec, 1, 0);
|
SELECT subvector('[1,2,3,4,5]'::halfvec, 1, 0);
|
||||||
SELECT subvector('[1,2,3,4,5]'::halfvec, 3, -1);
|
SELECT subvector('[1,2,3,4,5]'::halfvec, 3, -1);
|
||||||
SELECT subvector('[1,2,3,4,5]'::halfvec, -1, 2);
|
SELECT subvector('[1,2,3,4,5]'::halfvec, -1, 2);
|
||||||
|
SELECT subvector('[1,2,3,4,5]'::halfvec, 2147483647, 10);
|
||||||
|
SELECT subvector('[1,2,3,4,5]'::halfvec, 3, 2147483647);
|
||||||
|
SELECT subvector('[1,2,3,4,5]'::halfvec, -2147483644, 2147483647);
|
||||||
|
|
||||||
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]']) v;
|
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]']) v;
|
||||||
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]', NULL]) v;
|
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::halfvec, '[3,5,7]', NULL]) v;
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
SELECT '[1,2,3]'::halfvec;
|
|
||||||
SELECT '[-1,-2,-3]'::halfvec;
|
|
||||||
SELECT '[1.,2.,3.]'::halfvec;
|
|
||||||
SELECT ' [ 1, 2 , 3 ] '::halfvec;
|
|
||||||
SELECT '[1.23456]'::halfvec;
|
|
||||||
SELECT '[hello,1]'::halfvec;
|
|
||||||
SELECT '[NaN,1]'::halfvec;
|
|
||||||
SELECT '[Infinity,1]'::halfvec;
|
|
||||||
SELECT '[-Infinity,1]'::halfvec;
|
|
||||||
SELECT '[65519,-65519]'::halfvec;
|
|
||||||
SELECT '[65520,-65520]'::halfvec;
|
|
||||||
SELECT '[1e-8,-1e-8]'::halfvec;
|
|
||||||
SELECT '[4e38,1]'::halfvec;
|
|
||||||
SELECT '[1e-46,1]'::halfvec;
|
|
||||||
SELECT '[1,2,3'::halfvec;
|
|
||||||
SELECT '[1,2,3]9'::halfvec;
|
|
||||||
SELECT '1,2,3'::halfvec;
|
|
||||||
SELECT ''::halfvec;
|
|
||||||
SELECT '['::halfvec;
|
|
||||||
SELECT '[ '::halfvec;
|
|
||||||
SELECT '[,'::halfvec;
|
|
||||||
SELECT '[]'::halfvec;
|
|
||||||
SELECT '[ ]'::halfvec;
|
|
||||||
SELECT '[,]'::halfvec;
|
|
||||||
SELECT '[1,]'::halfvec;
|
|
||||||
SELECT '[1a]'::halfvec;
|
|
||||||
SELECT '[1,,3]'::halfvec;
|
|
||||||
SELECT '[1, ,3]'::halfvec;
|
|
||||||
|
|
||||||
SELECT '[1,2,3]'::halfvec(3);
|
|
||||||
SELECT '[1,2,3]'::halfvec(2);
|
|
||||||
SELECT '[1,2,3]'::halfvec(3, 2);
|
|
||||||
SELECT '[1,2,3]'::halfvec('a');
|
|
||||||
SELECT '[1,2,3]'::halfvec(0);
|
|
||||||
SELECT '[1,2,3]'::halfvec(16001);
|
|
||||||
|
|
||||||
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::halfvec[]);
|
|
||||||
SELECT '{"[1,2,3]"}'::halfvec(2)[];
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
|
|
||||||
|
-- hamming
|
||||||
|
|
||||||
CREATE TABLE t (val bit(3));
|
CREATE TABLE t (val bit(3));
|
||||||
INSERT INTO t (val) VALUES (B'000'), (B'100'), (B'111'), (NULL);
|
INSERT INTO t (val) VALUES (B'000'), (B'100'), (B'111'), (NULL);
|
||||||
CREATE INDEX ON t USING hnsw (val bit_hamming_ops);
|
CREATE INDEX ON t USING hnsw (val bit_hamming_ops);
|
||||||
@@ -11,7 +13,21 @@ SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <~> (SELECT NULL::bit)) t2;
|
|||||||
|
|
||||||
DROP TABLE t;
|
DROP TABLE t;
|
||||||
|
|
||||||
-- TODO move
|
-- jaccard
|
||||||
|
|
||||||
|
CREATE TABLE t (val bit(4));
|
||||||
|
INSERT INTO t (val) VALUES (B'0000'), (B'1100'), (B'1111'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val bit_jaccard_ops);
|
||||||
|
|
||||||
|
INSERT INTO t (val) VALUES (B'1110');
|
||||||
|
|
||||||
|
SELECT * FROM t ORDER BY val <%> B'1111';
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <%> (SELECT NULL::bit)) t2;
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
|
||||||
|
-- varbit
|
||||||
|
|
||||||
CREATE TABLE t (val varbit(3));
|
CREATE TABLE t (val varbit(3));
|
||||||
CREATE INDEX ON t USING hnsw (val bit_hamming_ops);
|
CREATE INDEX ON t USING hnsw (val bit_hamming_ops);
|
||||||
CREATE INDEX ON t USING hnsw ((val::bit(3)) bit_hamming_ops);
|
CREATE INDEX ON t USING hnsw ((val::bit(3)) bit_hamming_ops);
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
SET enable_seqscan = off;
|
|
||||||
|
|
||||||
CREATE TABLE t (val bit(4));
|
|
||||||
INSERT INTO t (val) VALUES (B'0000'), (B'1100'), (B'1111'), (NULL);
|
|
||||||
CREATE INDEX ON t USING hnsw (val bit_jaccard_ops);
|
|
||||||
|
|
||||||
INSERT INTO t (val) VALUES (B'1110');
|
|
||||||
|
|
||||||
SELECT * FROM t ORDER BY val <%> B'1111';
|
|
||||||
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <%> (SELECT NULL::bit)) t2;
|
|
||||||
|
|
||||||
DROP TABLE t;
|
|
||||||
58
test/sql/hnsw_halfvec.sql
Normal file
58
test/sql/hnsw_halfvec.sql
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
SET enable_seqscan = off;
|
||||||
|
|
||||||
|
-- L2
|
||||||
|
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val halfvec_l2_ops);
|
||||||
|
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
|
||||||
|
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::halfvec)) t2;
|
||||||
|
SELECT COUNT(*) FROM t;
|
||||||
|
|
||||||
|
TRUNCATE t;
|
||||||
|
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
|
||||||
|
-- inner product
|
||||||
|
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val halfvec_ip_ops);
|
||||||
|
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
|
||||||
|
SELECT * FROM t ORDER BY val <#> '[3,3,3]';
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::halfvec)) t2;
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
|
||||||
|
-- cosine
|
||||||
|
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val halfvec_cosine_ops);
|
||||||
|
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
|
||||||
|
SELECT * FROM t ORDER BY val <=> '[3,3,3]';
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '[0,0,0]') t2;
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::halfvec)) t2;
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
|
|
||||||
|
-- L1
|
||||||
|
|
||||||
|
CREATE TABLE t (val halfvec(3));
|
||||||
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
CREATE INDEX ON t USING hnsw (val halfvec_l1_ops);
|
||||||
|
|
||||||
|
INSERT INTO t (val) VALUES ('[1,2,4]');
|
||||||
|
|
||||||
|
SELECT * FROM t ORDER BY val <+> '[3,3,3]';
|
||||||
|
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <+> (SELECT NULL::halfvec)) t2;
|
||||||
|
|
||||||
|
DROP TABLE t;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user