mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-23 04:20:56 +08:00
Compare commits
82 Commits
v0.3.2
...
random_vec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7a0abe6ad | ||
|
|
9b13db5c5c | ||
|
|
ca9d4ed82a | ||
|
|
b1ffe2d4d3 | ||
|
|
246f8814bc | ||
|
|
a5d8bb2688 | ||
|
|
95e496ba00 | ||
|
|
6bfc2cb9a1 | ||
|
|
25d6747721 | ||
|
|
fce99e0472 | ||
|
|
ec415bf5c4 | ||
|
|
0111ffffbd | ||
|
|
7d1398d1a7 | ||
|
|
b6f1a82774 | ||
|
|
7b0b6a7875 | ||
|
|
42d0cf1a25 | ||
|
|
d164fd35a8 | ||
|
|
13e0fa24fe | ||
|
|
7c65fd13c1 | ||
|
|
9616a611c3 | ||
|
|
474933ae39 | ||
|
|
b1237c7c4e | ||
|
|
66108cb406 | ||
|
|
8c3eb51ddc | ||
|
|
915cf35e27 | ||
|
|
ec800ef903 | ||
|
|
4a2f20f5fc | ||
|
|
e2b103a343 | ||
|
|
5d82d7bf99 | ||
|
|
7c74d8ab75 | ||
|
|
8dbd39f641 | ||
|
|
d4681dd898 | ||
|
|
14984a08ea | ||
|
|
30e07a6943 | ||
|
|
be26cf2f82 | ||
|
|
f205312430 | ||
|
|
085b75b727 | ||
|
|
3d8543f9ff | ||
|
|
8cbf0254bb | ||
|
|
e09f93cba7 | ||
|
|
b400ac0f36 | ||
|
|
aa62b42a9a | ||
|
|
46b8294d5f | ||
|
|
989b4c4208 | ||
|
|
fa491cd906 | ||
|
|
00b767b107 | ||
|
|
1823024d9c | ||
|
|
563f888ef1 | ||
|
|
141fea8d08 | ||
|
|
83407a9e23 | ||
|
|
0b1d36bb33 | ||
|
|
6d4ac36d50 | ||
|
|
e2f33dfdba | ||
|
|
3f3ba5b8e7 | ||
|
|
0f69cc789a | ||
|
|
b09e14ce14 | ||
|
|
1b5cb17f22 | ||
|
|
7314d8b05a | ||
|
|
0a46adb112 | ||
|
|
8c81213592 | ||
|
|
f77b044303 | ||
|
|
fab7b50c3a | ||
|
|
9e2e726010 | ||
|
|
2621f9f947 | ||
|
|
4fbe55f97e | ||
|
|
9c9489e888 | ||
|
|
d376011087 | ||
|
|
5107e057ad | ||
|
|
1c1812aeac | ||
|
|
da12d9d8b0 | ||
|
|
de26e1e78a | ||
|
|
8691d2ad53 | ||
|
|
573200c188 | ||
|
|
cd7cab804f | ||
|
|
84a8bdd661 | ||
|
|
7a13a6cd3c | ||
|
|
35d5cb9f06 | ||
|
|
bc6b9e4bba | ||
|
|
d605948be4 | ||
|
|
37a784d3f6 | ||
|
|
c74bb5495b | ||
|
|
cc35af160e |
@@ -1,6 +1,6 @@
|
|||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*.{c,h,pl,pm}]
|
[*.{c,h,pl,pm,sql}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = tab
|
indent_size = tab
|
||||||
tab_width = 4
|
tab_width = 4
|
||||||
|
|||||||
67
.github/workflows/build.yml
vendored
67
.github/workflows/build.yml
vendored
@@ -1,39 +1,72 @@
|
|||||||
name: build
|
name: build
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
ubuntu:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest]
|
|
||||||
postgres: [15, 14, 13, 12, 11, 10]
|
|
||||||
include:
|
include:
|
||||||
- os: macos-latest
|
- postgres: 15
|
||||||
postgres: 14
|
os: ubuntu-22.04
|
||||||
|
- postgres: 14
|
||||||
|
os: ubuntu-22.04
|
||||||
|
- postgres: 13
|
||||||
|
os: ubuntu-20.04
|
||||||
|
- postgres: 12
|
||||||
|
os: ubuntu-20.04
|
||||||
|
- postgres: 11
|
||||||
|
os: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ankane/setup-postgres@v1
|
- uses: ankane/setup-postgres@v1
|
||||||
with:
|
with:
|
||||||
postgres-version: ${{ matrix.postgres }}
|
postgres-version: ${{ matrix.postgres }}
|
||||||
dev-files: true
|
dev-files: true
|
||||||
- if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
|
||||||
run: sudo apt-get update && sudo apt-get install libipc-run-perl
|
|
||||||
- run: make
|
- run: make
|
||||||
- if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
- run: |
|
||||||
run: |
|
|
||||||
export PG_CONFIG=`which pg_config`
|
export PG_CONFIG=`which pg_config`
|
||||||
sudo --preserve-env=PG_CONFIG make install
|
sudo --preserve-env=PG_CONFIG make install
|
||||||
- if: ${{ startsWith(matrix.os, 'macos') }}
|
|
||||||
run: make install
|
|
||||||
- run: make installcheck
|
- run: make installcheck
|
||||||
- if: ${{ failure() }}
|
- if: ${{ failure() }}
|
||||||
run: cat regression.diffs
|
run: cat regression.diffs
|
||||||
- if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
- run: |
|
||||||
run: make prove_installcheck
|
sudo apt-get update
|
||||||
- if: ${{ startsWith(matrix.os, 'macos') }}
|
sudo apt-get install libipc-run-perl
|
||||||
run: |
|
- run: make prove_installcheck
|
||||||
brew install cpanm && cpanm IPC::Run
|
mac:
|
||||||
|
runs-on: macos-latest
|
||||||
|
if: ${{ !startsWith(github.ref_name, 'windows') }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ankane/setup-postgres@v1
|
||||||
|
with:
|
||||||
|
postgres-version: 14
|
||||||
|
- run: make
|
||||||
|
- run: make install
|
||||||
|
- run: make installcheck
|
||||||
|
- if: ${{ failure() }}
|
||||||
|
run: cat regression.diffs
|
||||||
|
- run: |
|
||||||
|
brew install cpanm
|
||||||
|
cpanm --notest IPC::Run
|
||||||
wget -q https://github.com/postgres/postgres/archive/refs/tags/REL_14_5.tar.gz
|
wget -q https://github.com/postgres/postgres/archive/refs/tags/REL_14_5.tar.gz
|
||||||
tar xf REL_14_5.tar.gz
|
tar xf REL_14_5.tar.gz
|
||||||
make prove_installcheck PROVE=prove PROVE_FLAGS="-I ./postgres-REL_14_5/src/test/perl" PERL5LIB="/Users/runner/perl5/lib/perl5"
|
- run: make prove_installcheck PROVE_FLAGS="-I ./postgres-REL_14_5/src/test/perl" PERL5LIB="/Users/runner/perl5/lib/perl5"
|
||||||
|
windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
if: ${{ !startsWith(github.ref_name, 'mac') }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- 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
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -6,3 +6,7 @@ regression.*
|
|||||||
*.o
|
*.o
|
||||||
*.so
|
*.so
|
||||||
*.bc
|
*.bc
|
||||||
|
*.dll
|
||||||
|
*.obj
|
||||||
|
*.lib
|
||||||
|
*.exp
|
||||||
|
|||||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,3 +1,20 @@
|
|||||||
|
## 0.4.1 (unreleased)
|
||||||
|
|
||||||
|
- Added `random_vector` function
|
||||||
|
|
||||||
|
## 0.4.0 (2023-01-11)
|
||||||
|
|
||||||
|
If upgrading with Postgres < 13, see [this note](https://github.com/pgvector/pgvector#040).
|
||||||
|
|
||||||
|
- Changed text representation for vector elements to match `real`
|
||||||
|
- Changed storage for vector from `plain` to `extended`
|
||||||
|
- Increased max dimensions for vector from 1024 to 16000
|
||||||
|
- Increased max dimensions for index from 1024 to 2000
|
||||||
|
- Improved accuracy of text parsing for certain inputs
|
||||||
|
- Added `avg` aggregate for vector
|
||||||
|
- Added experimental support for Windows
|
||||||
|
- Dropped support for Postgres 10
|
||||||
|
|
||||||
## 0.3.2 (2022-11-22)
|
## 0.3.2 (2022-11-22)
|
||||||
|
|
||||||
- Fixed `invalid memory alloc request size` error
|
- Fixed `invalid memory alloc request size` error
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
|
Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
|
||||||
|
|
||||||
Portions Copyright (c) 1994, The Regents of the University of California
|
Portions Copyright (c) 1994, The Regents of the University of California
|
||||||
|
|
||||||
|
|||||||
@@ -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.3.2",
|
"version": "0.4.0",
|
||||||
"maintainer": [
|
"maintainer": [
|
||||||
"Andrew Kane <andrew@ankane.org>"
|
"Andrew Kane <andrew@ankane.org>"
|
||||||
],
|
],
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
"prereqs": {
|
"prereqs": {
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"requires": {
|
"requires": {
|
||||||
"PostgreSQL": "10.0.0"
|
"PostgreSQL": "11.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
"vector": {
|
"vector": {
|
||||||
"file": "sql/vector.sql",
|
"file": "sql/vector.sql",
|
||||||
"docfile": "README.md",
|
"docfile": "README.md",
|
||||||
"version": "0.3.2",
|
"version": "0.4.0",
|
||||||
"abstract": "Open-source vector similarity search for Postgres"
|
"abstract": "Open-source vector similarity search for Postgres"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
9
Makefile
9
Makefile
@@ -1,5 +1,5 @@
|
|||||||
EXTENSION = vector
|
EXTENSION = vector
|
||||||
EXTVERSION = 0.3.2
|
EXTVERSION = 0.4.0
|
||||||
|
|
||||||
MODULE_big = vector
|
MODULE_big = vector
|
||||||
DATA = $(wildcard sql/*--*.sql)
|
DATA = $(wildcard sql/*--*.sql)
|
||||||
@@ -7,7 +7,7 @@ OBJS = src/ivfbuild.o src/ivfflat.o src/ivfinsert.o src/ivfkmeans.o src/ivfscan.
|
|||||||
|
|
||||||
TESTS = $(wildcard test/sql/*.sql)
|
TESTS = $(wildcard test/sql/*.sql)
|
||||||
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
|
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
|
||||||
REGRESS_OPTS = --inputdir=test
|
REGRESS_OPTS = --inputdir=test --load-extension=vector
|
||||||
|
|
||||||
OPTFLAGS = -march=native
|
OPTFLAGS = -march=native
|
||||||
|
|
||||||
@@ -40,6 +40,11 @@ PG_CONFIG ?= pg_config
|
|||||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
include $(PGXS)
|
include $(PGXS)
|
||||||
|
|
||||||
|
# for Mac
|
||||||
|
ifeq ($(PROVE),)
|
||||||
|
PROVE = prove
|
||||||
|
endif
|
||||||
|
|
||||||
# for Postgres 15
|
# for Postgres 15
|
||||||
PROVE_FLAGS += -I ./test/perl
|
PROVE_FLAGS += -I ./test/perl
|
||||||
|
|
||||||
|
|||||||
70
Makefile.win
Normal file
70
Makefile.win
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
EXTENSION = vector
|
||||||
|
EXTVERSION = 0.4.0
|
||||||
|
|
||||||
|
OBJS = src\ivfbuild.obj src\ivfflat.obj src\ivfinsert.obj src\ivfkmeans.obj src\ivfscan.obj src\ivfutils.obj src\ivfvacuum.obj src\vector.obj
|
||||||
|
|
||||||
|
REGRESS = btree cast copy functions input ivfflat_cosine ivfflat_ip ivfflat_l2 ivfflat_options ivfflat_unlogged
|
||||||
|
REGRESS_OPTS = --inputdir=test --load-extension=vector
|
||||||
|
|
||||||
|
# For /arch flags
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/build/reference/arch-minimum-cpu-architecture
|
||||||
|
OPTFLAGS =
|
||||||
|
|
||||||
|
# For auto-vectorization:
|
||||||
|
# - MSVC (needs /O2 /fp:fast) - https://learn.microsoft.com/en-us/cpp/parallel/auto-parallelization-and-auto-vectorization?#auto-vectorizer
|
||||||
|
PG_CFLAGS = $(PG_CFLAGS) $(OPTFLAGS) /O2 /fp:fast
|
||||||
|
|
||||||
|
# Debug MSVC auto-vectorization
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/tool-errors/vectorizer-and-parallelizer-messages
|
||||||
|
# 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
|
||||||
|
!ifndef PGROOT
|
||||||
|
!error PGROOT is not set
|
||||||
|
!endif
|
||||||
|
BINDIR = $(PGROOT)\bin
|
||||||
|
INCLUDEDIR = $(PGROOT)\include
|
||||||
|
INCLUDEDIR_SERVER = $(PGROOT)\include\server
|
||||||
|
LIBDIR = $(PGROOT)\lib
|
||||||
|
PKGLIBDIR = $(PGROOT)\lib
|
||||||
|
SHAREDIR = $(PGROOT)\share
|
||||||
|
|
||||||
|
CFLAGS = /nologo /I"$(INCLUDEDIR_SERVER)\port\win32_msvc" /I"$(INCLUDEDIR_SERVER)\port\win32" /I"$(INCLUDEDIR_SERVER)" /I"$(INCLUDEDIR)"
|
||||||
|
|
||||||
|
CFLAGS = $(CFLAGS) $(PG_CFLAGS)
|
||||||
|
|
||||||
|
SHLIB = $(EXTENSION).dll
|
||||||
|
|
||||||
|
LIBS = "$(LIBDIR)\postgres.lib"
|
||||||
|
|
||||||
|
.c.obj:
|
||||||
|
$(CC) $(CFLAGS) /c $< /Fo$@
|
||||||
|
|
||||||
|
$(SHLIB): $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(OBJS) $(LIBS) /link /DLL /OUT:$(SHLIB)
|
||||||
|
|
||||||
|
all: $(SHLIB)
|
||||||
|
|
||||||
|
install:
|
||||||
|
copy $(SHLIB) "$(PKGLIBDIR)"
|
||||||
|
copy $(EXTENSION).control "$(SHAREDIR)\extension"
|
||||||
|
copy sql\$(EXTENSION)--*.sql "$(SHAREDIR)\extension"
|
||||||
|
|
||||||
|
installcheck:
|
||||||
|
"$(BINDIR)\pg_regress" --bindir="$(BINDIR)" $(REGRESS_OPTS) $(REGRESS)
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
del /f "$(PKGLIBDIR)\$(SHLIB)"
|
||||||
|
del /f "$(SHAREDIR)\extension\$(EXTENSION).control"
|
||||||
|
del /f "$(SHAREDIR)\extension\vector--*.sql"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
del /f $(SHLIB) $(EXTENSION).lib $(EXTENSION).exp
|
||||||
|
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
|
||||||
85
README.md
85
README.md
@@ -14,10 +14,10 @@ Supports L2 distance, inner product, and cosine distance
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Compile and install the extension (supports Postgres 10+)
|
Compile and install the extension (supports Postgres 11+)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone --branch v0.3.2 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.4.0 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
make
|
make
|
||||||
make install # may need sudo
|
make install # may need sudo
|
||||||
@@ -29,7 +29,7 @@ Then load it in databases where you want to use it
|
|||||||
CREATE EXTENSION vector;
|
CREATE EXTENSION vector;
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also install it with [Docker](#docker), [Homebrew](#homebrew), or [PGXN](#pgxn)
|
You can also install it with [Docker](#docker), [Homebrew](#homebrew), [PGXN](#pgxn), or [conda-forge](#conda-forge)
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ Cosine distance
|
|||||||
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops);
|
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops);
|
||||||
```
|
```
|
||||||
|
|
||||||
Indexes should be created after the table has some data for optimal clustering. Also, unlike typical indexes which only affect performance, you may see different results for queries after adding an approximate index.
|
Indexes should be created after the table has some data for optimal clustering. Also, unlike typical indexes which only affect performance, you may see different results for queries after adding an approximate index. Vectors with up to 2,000 dimensions can be indexed.
|
||||||
|
|
||||||
### Index Options
|
### Index Options
|
||||||
|
|
||||||
@@ -119,10 +119,9 @@ SELECT phase, tuples_done, tuples_total FROM pg_stat_progress_create_index;
|
|||||||
The phases are:
|
The phases are:
|
||||||
|
|
||||||
1. `initializing`
|
1. `initializing`
|
||||||
2. `sampling table`
|
2. `performing k-means`
|
||||||
3. `performing k-means`
|
3. `sorting tuples`
|
||||||
4. `sorting tuples`
|
4. `loading tuples`
|
||||||
5. `loading tuples`
|
|
||||||
|
|
||||||
Note: `tuples_done` and `tuples_total` are only populated during the `loading tuples` phase
|
Note: `tuples_done` and `tuples_total` are only populated during the `loading tuples` phase
|
||||||
|
|
||||||
@@ -164,7 +163,7 @@ CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 1000
|
|||||||
|
|
||||||
### Vector Type
|
### Vector Type
|
||||||
|
|
||||||
Each vector takes `4 * dimensions + 8` bytes of storage. Each element is a float, and all elements must be finite (no `NaN`, `Infinity` or `-Infinity`). Vectors can have up to 1024 dimensions.
|
Each vector takes `4 * dimensions + 8` bytes of storage. Each element is a single precision floating-point number (like the `real` type in Postgres), and all elements must be finite (no `NaN`, `Infinity` or `-Infinity`). Vectors can have up to 16,000 dimensions.
|
||||||
|
|
||||||
### Vector Operators
|
### Vector Operators
|
||||||
|
|
||||||
@@ -180,25 +179,31 @@ Operator | Description
|
|||||||
|
|
||||||
Function | Description
|
Function | Description
|
||||||
--- | ---
|
--- | ---
|
||||||
cosine_distance(vector, vector) | cosine distance
|
cosine_distance(vector, vector) → double precision | cosine distance
|
||||||
inner_product(vector, vector) | inner product
|
inner_product(vector, vector) → double precision | inner product
|
||||||
l2_distance(vector, vector) | Euclidean distance
|
l2_distance(vector, vector) → double precision | Euclidean distance
|
||||||
vector_dims(vector) | number of dimensions
|
vector_dims(vector) → integer | number of dimensions
|
||||||
vector_norm(vector) | Euclidean norm
|
vector_norm(vector) → double precision | Euclidean norm
|
||||||
|
random_vector(integer) → vector | random vector [unreleased]
|
||||||
|
|
||||||
|
### Aggregate Functions
|
||||||
|
|
||||||
|
Function | Description
|
||||||
|
--- | ---
|
||||||
|
avg(vector) → vector | arithmetic mean
|
||||||
|
|
||||||
## Libraries
|
## Libraries
|
||||||
|
|
||||||
Libraries that use pgvector:
|
Language | Libraries
|
||||||
|
--- | ---
|
||||||
- [pgvector-python](https://github.com/pgvector/pgvector-python) (Python)
|
Python | [pgvector-python](https://github.com/pgvector/pgvector-python)
|
||||||
- [Neighbor](https://github.com/ankane/neighbor) (Ruby)
|
Ruby | [Neighbor](https://github.com/ankane/neighbor), [pgvector-ruby](https://github.com/pgvector/pgvector-ruby)
|
||||||
- [pgvector-ruby](https://github.com/pgvector/pgvector-ruby) (Ruby)
|
Node | [pgvector-node](https://github.com/pgvector/pgvector-node)
|
||||||
- [pgvector-node](https://github.com/pgvector/pgvector-node) (Node.js)
|
Go | [pgvector-go](https://github.com/pgvector/pgvector-go)
|
||||||
- [pgvector-go](https://github.com/pgvector/pgvector-go) (Go)
|
PHP | [pgvector-php](https://github.com/pgvector/pgvector-php)
|
||||||
- [pgvector-php](https://github.com/pgvector/pgvector-php) (PHP)
|
Rust | [pgvector-rust](https://github.com/pgvector/pgvector-rust)
|
||||||
- [pgvector-rust](https://github.com/pgvector/pgvector-rust) (Rust)
|
C++ | [pgvector-cpp](https://github.com/pgvector/pgvector-cpp)
|
||||||
- [pgvector-cpp](https://github.com/pgvector/pgvector-cpp) (C++)
|
Elixir | [pgvector-elixir](https://github.com/pgvector/pgvector-elixir)
|
||||||
- [pgvector-elixir](https://github.com/pgvector/pgvector-elixir) (Elixir)
|
|
||||||
|
|
||||||
## Frequently Asked Questions
|
## Frequently Asked Questions
|
||||||
|
|
||||||
@@ -210,12 +215,12 @@ A non-partitioned table has a limit of 32 TB by default in Postgres. A partition
|
|||||||
|
|
||||||
Yes, pgvector uses the write-ahead log (WAL), which allows for replication and point-in-time recovery.
|
Yes, pgvector uses the write-ahead log (WAL), which allows for replication and point-in-time recovery.
|
||||||
|
|
||||||
#### What if my data has more than 1024 dimensions?
|
#### What if I want to index vectors with more than 2,000 dimensions?
|
||||||
|
|
||||||
Two things you can try are:
|
Two things you can try are:
|
||||||
|
|
||||||
1. use dimensionality reduction
|
1. use dimensionality reduction
|
||||||
2. compile Postgres with a larger block size (`./configure --with-blocksize=32`) and edit the limit in `src/vector.h`
|
2. compile Postgres with a larger block size (`./configure --with-blocksize=32`) and edit the limit in `src/ivfflat.h`
|
||||||
|
|
||||||
## Additional Installation Methods
|
## Additional Installation Methods
|
||||||
|
|
||||||
@@ -232,7 +237,7 @@ This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres).
|
|||||||
You can also build the image manually
|
You can also build the image manually
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone --branch v0.3.2 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.4.0 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
docker build -t pgvector .
|
docker build -t pgvector .
|
||||||
```
|
```
|
||||||
@@ -253,14 +258,24 @@ Install from the [PostgreSQL Extension Network](https://pgxn.org/dist/vector) wi
|
|||||||
pgxn install vector
|
pgxn install vector
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### conda-forge
|
||||||
|
|
||||||
|
Install from [conda-forge](https://anaconda.org/conda-forge/pgvector) with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
conda install -c conda-forge pgvector
|
||||||
|
```
|
||||||
|
|
||||||
|
This method is [community-maintained](https://github.com/conda-forge/pgvector-feedstock) by [@mmcauliffe](https://github.com/mmcauliffe)
|
||||||
|
|
||||||
## Hosted Postgres
|
## Hosted Postgres
|
||||||
|
|
||||||
Some Postgres providers only support specific extensions. To request a new extension:
|
Some Postgres providers only support specific extensions. To request a new extension:
|
||||||
|
|
||||||
- Amazon RDS - follow the instructions on [this page](https://aws.amazon.com/rds/postgresql/faqs/)
|
- Amazon RDS - follow the instructions on [this page](https://aws.amazon.com/rds/postgresql/faqs/)
|
||||||
- Google Cloud SQL - follow the instructions on [this page](https://cloud.google.com/sql/docs/postgres/extensions#requesting-support-for-a-new-extension)
|
- Google Cloud SQL - vote or comment on [this page](https://issuetracker.google.com/issues/265172065)
|
||||||
- DigitalOcean Managed Databases - vote or comment on [this page](https://ideas.digitalocean.com/app-framework-services/p/pgvector-extension-for-postgresql)
|
- DigitalOcean Managed Databases - vote or comment on [this page](https://ideas.digitalocean.com/app-framework-services/p/pgvector-extension-for-postgresql)
|
||||||
- Azure Database for PostgreSQL - follow the instructions on [this page](https://docs.microsoft.com/en-us/azure/postgresql/concepts-extensions#next-steps)
|
- Azure Database for PostgreSQL - vote or comment on [this page](https://feedback.azure.com/d365community/idea/7b423322-6189-ed11-a81b-000d3ae49307)
|
||||||
|
|
||||||
## Upgrading
|
## Upgrading
|
||||||
|
|
||||||
@@ -272,6 +287,16 @@ ALTER EXTENSION vector UPDATE;
|
|||||||
|
|
||||||
## Upgrade Notes
|
## Upgrade Notes
|
||||||
|
|
||||||
|
### 0.4.0
|
||||||
|
|
||||||
|
If upgrading with Postgres < 13, remove this line from `sql/vector--0.3.2--0.4.0.sql`:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
ALTER TYPE vector SET (STORAGE = extended);
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run `make install` and `ALTER EXTENSION vector UPDATE;`.
|
||||||
|
|
||||||
### 0.3.1
|
### 0.3.1
|
||||||
|
|
||||||
If upgrading from 0.2.7 or 0.3.0, recreate all `ivfflat` indexes after upgrading to ensure all data is indexed.
|
If upgrading from 0.2.7 or 0.3.0, recreate all `ivfflat` indexes after upgrading to ensure all data is indexed.
|
||||||
|
|||||||
23
sql/vector--0.3.2--0.4.0.sql
Normal file
23
sql/vector--0.3.2--0.4.0.sql
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION vector UPDATE TO '0.4.0'" to load this file. \quit
|
||||||
|
|
||||||
|
-- remove this single line for Postgres < 13
|
||||||
|
ALTER TYPE vector SET (STORAGE = extended);
|
||||||
|
|
||||||
|
CREATE FUNCTION vector_accum(double precision[], vector) RETURNS double precision[]
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION vector_avg(double precision[]) RETURNS vector
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION vector_combine(double precision[], double precision[]) RETURNS double precision[]
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE AGGREGATE avg(vector) (
|
||||||
|
SFUNC = vector_accum,
|
||||||
|
STYPE = double precision[],
|
||||||
|
FINALFUNC = vector_avg,
|
||||||
|
COMBINEFUNC = vector_combine,
|
||||||
|
INITCOND = '{0}',
|
||||||
|
PARALLEL = SAFE
|
||||||
|
);
|
||||||
5
sql/vector--0.4.0--0.4.1.sql
Normal file
5
sql/vector--0.4.0--0.4.1.sql
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION vector UPDATE TO '0.4.1'" to load this file. \quit
|
||||||
|
|
||||||
|
CREATE FUNCTION random_vector(integer) RETURNS vector
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C VOLATILE STRICT PARALLEL SAFE;
|
||||||
@@ -25,7 +25,8 @@ CREATE TYPE vector (
|
|||||||
OUTPUT = vector_out,
|
OUTPUT = vector_out,
|
||||||
TYPMOD_IN = vector_typmod_in,
|
TYPMOD_IN = vector_typmod_in,
|
||||||
RECEIVE = vector_recv,
|
RECEIVE = vector_recv,
|
||||||
SEND = vector_send
|
SEND = vector_send,
|
||||||
|
STORAGE = extended
|
||||||
);
|
);
|
||||||
|
|
||||||
-- functions
|
-- functions
|
||||||
@@ -51,6 +52,9 @@ CREATE FUNCTION vector_add(vector, vector) RETURNS vector
|
|||||||
CREATE FUNCTION vector_sub(vector, vector) RETURNS vector
|
CREATE FUNCTION vector_sub(vector, vector) RETURNS vector
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION random_vector(integer) RETURNS vector
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C VOLATILE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
-- private functions
|
-- private functions
|
||||||
|
|
||||||
CREATE FUNCTION vector_lt(vector, vector) RETURNS bool
|
CREATE FUNCTION vector_lt(vector, vector) RETURNS bool
|
||||||
@@ -83,6 +87,26 @@ CREATE FUNCTION vector_negative_inner_product(vector, vector) RETURNS float8
|
|||||||
CREATE FUNCTION vector_spherical_distance(vector, vector) RETURNS float8
|
CREATE FUNCTION vector_spherical_distance(vector, vector) RETURNS float8
|
||||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION vector_accum(double precision[], vector) RETURNS double precision[]
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION vector_avg(double precision[]) RETURNS vector
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION vector_combine(double precision[], double precision[]) RETURNS double precision[]
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
-- aggregates
|
||||||
|
|
||||||
|
CREATE AGGREGATE avg(vector) (
|
||||||
|
SFUNC = vector_accum,
|
||||||
|
STYPE = double precision[],
|
||||||
|
FINALFUNC = vector_avg,
|
||||||
|
COMBINEFUNC = vector_combine,
|
||||||
|
INITCOND = '{0}',
|
||||||
|
PARALLEL = SAFE
|
||||||
|
);
|
||||||
|
|
||||||
-- cast functions
|
-- cast functions
|
||||||
|
|
||||||
CREATE FUNCTION vector(vector, integer, boolean) RETURNS vector
|
CREATE FUNCTION vector(vector, integer, boolean) RETURNS vector
|
||||||
|
|||||||
129
src/ivfbuild.c
129
src/ivfbuild.c
@@ -6,6 +6,7 @@
|
|||||||
#include "ivfflat.h"
|
#include "ivfflat.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
|
#include "utils/memutils.h"
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 140000
|
#if PG_VERSION_NUM >= 140000
|
||||||
#include "utils/backend_progress.h"
|
#include "utils/backend_progress.h"
|
||||||
@@ -22,13 +23,8 @@
|
|||||||
#define PROGRESS_CREATEIDX_TUPLES_DONE 0
|
#define PROGRESS_CREATEIDX_TUPLES_DONE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 110000
|
|
||||||
#include "catalog/pg_operator_d.h"
|
#include "catalog/pg_operator_d.h"
|
||||||
#include "catalog/pg_type_d.h"
|
#include "catalog/pg_type_d.h"
|
||||||
#else
|
|
||||||
#include "catalog/pg_operator.h"
|
|
||||||
#include "catalog/pg_type.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 130000
|
#if PG_VERSION_NUM >= 130000
|
||||||
#define CALLBACK_ITEM_POINTER ItemPointer tid
|
#define CALLBACK_ITEM_POINTER ItemPointer tid
|
||||||
@@ -43,20 +39,16 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callback for sampling
|
* Add sample
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
SampleCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
AddSample(Datum *values, IvfflatBuildState * buildstate)
|
||||||
bool *isnull, bool tupleIsAlive, void *state)
|
|
||||||
{
|
{
|
||||||
IvfflatBuildState *buildstate = (IvfflatBuildState *) state;
|
|
||||||
VectorArray samples = buildstate->samples;
|
VectorArray samples = buildstate->samples;
|
||||||
int targsamples = samples->maxlen;
|
int targsamples = samples->maxlen;
|
||||||
Datum value = values[0];
|
|
||||||
|
|
||||||
/* Skip nulls */
|
/* Detoast once for all calls */
|
||||||
if (isnull[0])
|
Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Normalize with KMEANS_NORM_PROC since spherical distance function
|
* Normalize with KMEANS_NORM_PROC since spherical distance function
|
||||||
@@ -94,6 +86,31 @@ SampleCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback for sampling
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
SampleCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
||||||
|
bool *isnull, bool tupleIsAlive, void *state)
|
||||||
|
{
|
||||||
|
IvfflatBuildState *buildstate = (IvfflatBuildState *) state;
|
||||||
|
MemoryContext oldCtx;
|
||||||
|
|
||||||
|
/* Skip nulls */
|
||||||
|
if (isnull[0])
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Use memory context since detoast can allocate */
|
||||||
|
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
|
||||||
|
|
||||||
|
/* Add sample */
|
||||||
|
AddSample(values, state);
|
||||||
|
|
||||||
|
/* Reset memory context */
|
||||||
|
MemoryContextSwitchTo(oldCtx);
|
||||||
|
MemoryContextReset(buildstate->tmpCtx);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sample rows with same logic as ANALYZE
|
* Sample rows with same logic as ANALYZE
|
||||||
*/
|
*/
|
||||||
@@ -103,11 +120,9 @@ SampleRows(IvfflatBuildState * buildstate)
|
|||||||
int targsamples = buildstate->samples->maxlen;
|
int targsamples = buildstate->samples->maxlen;
|
||||||
BlockNumber totalblocks = RelationGetNumberOfBlocks(buildstate->heap);
|
BlockNumber totalblocks = RelationGetNumberOfBlocks(buildstate->heap);
|
||||||
|
|
||||||
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_SAMPLE);
|
|
||||||
|
|
||||||
buildstate->rowstoskip = -1;
|
buildstate->rowstoskip = -1;
|
||||||
|
|
||||||
BlockSampler_Init(&buildstate->bs, totalblocks, targsamples, random());
|
BlockSampler_Init(&buildstate->bs, totalblocks, targsamples, RandomInt());
|
||||||
|
|
||||||
reservoir_init_selection_state(&buildstate->rstate, targsamples);
|
reservoir_init_selection_state(&buildstate->rstate, targsamples);
|
||||||
while (BlockSampler_HasMore(&buildstate->bs))
|
while (BlockSampler_HasMore(&buildstate->bs))
|
||||||
@@ -117,38 +132,28 @@ SampleRows(IvfflatBuildState * buildstate)
|
|||||||
#if PG_VERSION_NUM >= 120000
|
#if PG_VERSION_NUM >= 120000
|
||||||
table_index_build_range_scan(buildstate->heap, buildstate->index, buildstate->indexInfo,
|
table_index_build_range_scan(buildstate->heap, buildstate->index, buildstate->indexInfo,
|
||||||
false, true, false, targblock, 1, SampleCallback, (void *) buildstate, NULL);
|
false, true, false, targblock, 1, SampleCallback, (void *) buildstate, NULL);
|
||||||
#elif PG_VERSION_NUM >= 110000
|
|
||||||
IndexBuildHeapRangeScan(buildstate->heap, buildstate->index, buildstate->indexInfo,
|
|
||||||
false, true, targblock, 1, SampleCallback, (void *) buildstate, NULL);
|
|
||||||
#else
|
#else
|
||||||
IndexBuildHeapRangeScan(buildstate->heap, buildstate->index, buildstate->indexInfo,
|
IndexBuildHeapRangeScan(buildstate->heap, buildstate->index, buildstate->indexInfo,
|
||||||
false, true, targblock, 1, SampleCallback, (void *) buildstate);
|
false, true, targblock, 1, SampleCallback, (void *) buildstate, NULL);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callback for table_index_build_scan
|
* Add tuple to sort
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
AddTupleToSort(Relation index, ItemPointer tid, Datum *values, IvfflatBuildState * buildstate)
|
||||||
bool *isnull, bool tupleIsAlive, void *state)
|
|
||||||
{
|
{
|
||||||
IvfflatBuildState *buildstate = (IvfflatBuildState *) state;
|
|
||||||
double distance;
|
double distance;
|
||||||
double minDistance = DBL_MAX;
|
double minDistance = DBL_MAX;
|
||||||
int closestCenter = -1;
|
int closestCenter = -1;
|
||||||
VectorArray centers = buildstate->centers;
|
VectorArray centers = buildstate->centers;
|
||||||
TupleTableSlot *slot = buildstate->slot;
|
TupleTableSlot *slot = buildstate->slot;
|
||||||
Datum value = values[0];
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 130000
|
/* Detoast once for all calls */
|
||||||
ItemPointer tid = &hup->t_self;
|
Datum value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
|
||||||
#endif
|
|
||||||
|
|
||||||
if (isnull[0])
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Normalize if needed */
|
/* Normalize if needed */
|
||||||
if (buildstate->normprocinfo != NULL)
|
if (buildstate->normprocinfo != NULL)
|
||||||
@@ -196,6 +201,35 @@ BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
|||||||
buildstate->indtuples++;
|
buildstate->indtuples++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback for table_index_build_scan
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
||||||
|
bool *isnull, bool tupleIsAlive, void *state)
|
||||||
|
{
|
||||||
|
IvfflatBuildState *buildstate = (IvfflatBuildState *) state;
|
||||||
|
MemoryContext oldCtx;
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 130000
|
||||||
|
ItemPointer tid = &hup->t_self;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Skip nulls */
|
||||||
|
if (isnull[0])
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Use memory context since detoast can allocate */
|
||||||
|
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
|
||||||
|
|
||||||
|
/* Add tuple to sort */
|
||||||
|
AddTupleToSort(index, tid, values, buildstate);
|
||||||
|
|
||||||
|
/* Reset memory context */
|
||||||
|
MemoryContextSwitchTo(oldCtx);
|
||||||
|
MemoryContextReset(buildstate->tmpCtx);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get index tuple from sort state
|
* Get index tuple from sort state
|
||||||
*/
|
*/
|
||||||
@@ -205,11 +239,7 @@ GetNextTuple(Tuplesortstate *sortstate, TupleDesc tupdesc, TupleTableSlot *slot,
|
|||||||
Datum value;
|
Datum value;
|
||||||
bool isnull;
|
bool isnull;
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 100000
|
|
||||||
if (tuplesort_gettupleslot(sortstate, true, false, slot, NULL))
|
if (tuplesort_gettupleslot(sortstate, true, false, slot, NULL))
|
||||||
#else
|
|
||||||
if (tuplesort_gettupleslot(sortstate, true, slot, NULL))
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
*list = DatumGetInt32(slot_getattr(slot, 1, &isnull));
|
*list = DatumGetInt32(slot_getattr(slot, 1, &isnull));
|
||||||
value = slot_getattr(slot, 3, &isnull);
|
value = slot_getattr(slot, 3, &isnull);
|
||||||
@@ -308,6 +338,9 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
|
|||||||
if (buildstate->dimensions < 0)
|
if (buildstate->dimensions < 0)
|
||||||
elog(ERROR, "column does not have dimensions");
|
elog(ERROR, "column does not have dimensions");
|
||||||
|
|
||||||
|
if (buildstate->dimensions > IVFFLAT_MAX_DIM)
|
||||||
|
elog(ERROR, "column cannot have more than %d dimensions for ivfflat index", IVFFLAT_MAX_DIM);
|
||||||
|
|
||||||
buildstate->reltuples = 0;
|
buildstate->reltuples = 0;
|
||||||
buildstate->indtuples = 0;
|
buildstate->indtuples = 0;
|
||||||
|
|
||||||
@@ -331,11 +364,7 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
|
|||||||
#endif
|
#endif
|
||||||
TupleDescInitEntry(buildstate->tupdesc, (AttrNumber) 1, "list", INT4OID, -1, 0);
|
TupleDescInitEntry(buildstate->tupdesc, (AttrNumber) 1, "list", INT4OID, -1, 0);
|
||||||
TupleDescInitEntry(buildstate->tupdesc, (AttrNumber) 2, "tid", TIDOID, -1, 0);
|
TupleDescInitEntry(buildstate->tupdesc, (AttrNumber) 2, "tid", TIDOID, -1, 0);
|
||||||
#if PG_VERSION_NUM >= 110000
|
|
||||||
TupleDescInitEntry(buildstate->tupdesc, (AttrNumber) 3, "vector", RelationGetDescr(index)->attrs[0].atttypid, -1, 0);
|
TupleDescInitEntry(buildstate->tupdesc, (AttrNumber) 3, "vector", RelationGetDescr(index)->attrs[0].atttypid, -1, 0);
|
||||||
#else
|
|
||||||
TupleDescInitEntry(buildstate->tupdesc, (AttrNumber) 3, "vector", RelationGetDescr(index)->attrs[0]->atttypid, -1, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 120000
|
#if PG_VERSION_NUM >= 120000
|
||||||
buildstate->slot = MakeSingleTupleTableSlot(buildstate->tupdesc, &TTSOpsVirtual);
|
buildstate->slot = MakeSingleTupleTableSlot(buildstate->tupdesc, &TTSOpsVirtual);
|
||||||
@@ -349,6 +378,10 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
|
|||||||
/* Reuse for each tuple */
|
/* Reuse for each tuple */
|
||||||
buildstate->normvec = InitVector(buildstate->dimensions);
|
buildstate->normvec = InitVector(buildstate->dimensions);
|
||||||
|
|
||||||
|
buildstate->tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
|
"Ivfflat build temporary context",
|
||||||
|
ALLOCSET_DEFAULT_SIZES);
|
||||||
|
|
||||||
#ifdef IVFFLAT_KMEANS_DEBUG
|
#ifdef IVFFLAT_KMEANS_DEBUG
|
||||||
buildstate->inertia = 0;
|
buildstate->inertia = 0;
|
||||||
buildstate->listSums = palloc0(sizeof(double) * buildstate->lists);
|
buildstate->listSums = palloc0(sizeof(double) * buildstate->lists);
|
||||||
@@ -362,7 +395,7 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
|
|||||||
static void
|
static void
|
||||||
FreeBuildState(IvfflatBuildState * buildstate)
|
FreeBuildState(IvfflatBuildState * buildstate)
|
||||||
{
|
{
|
||||||
pfree(buildstate->centers);
|
VectorArrayFree(buildstate->centers);
|
||||||
pfree(buildstate->listInfo);
|
pfree(buildstate->listInfo);
|
||||||
pfree(buildstate->normvec);
|
pfree(buildstate->normvec);
|
||||||
|
|
||||||
@@ -370,6 +403,8 @@ FreeBuildState(IvfflatBuildState * buildstate)
|
|||||||
pfree(buildstate->listSums);
|
pfree(buildstate->listSums);
|
||||||
pfree(buildstate->listCounts);
|
pfree(buildstate->listCounts);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
MemoryContextDelete(buildstate->tmpCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -380,6 +415,8 @@ ComputeCenters(IvfflatBuildState * buildstate)
|
|||||||
{
|
{
|
||||||
int numSamples;
|
int numSamples;
|
||||||
|
|
||||||
|
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_KMEANS);
|
||||||
|
|
||||||
/* Target 50 samples per list, with at least 10000 samples */
|
/* Target 50 samples per list, with at least 10000 samples */
|
||||||
/* The number of samples has a large effect on index build time */
|
/* The number of samples has a large effect on index build time */
|
||||||
numSamples = buildstate->lists * 50;
|
numSamples = buildstate->lists * 50;
|
||||||
@@ -397,11 +434,10 @@ ComputeCenters(IvfflatBuildState * buildstate)
|
|||||||
SampleRows(buildstate);
|
SampleRows(buildstate);
|
||||||
|
|
||||||
/* Calculate centers */
|
/* Calculate centers */
|
||||||
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_KMEANS);
|
|
||||||
IvfflatBench("k-means", IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers));
|
IvfflatBench("k-means", IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers));
|
||||||
|
|
||||||
/* Free samples before we allocate more memory */
|
/* Free samples before we allocate more memory */
|
||||||
pfree(buildstate->samples);
|
VectorArrayFree(buildstate->samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -535,11 +571,7 @@ CreateEntryPages(IvfflatBuildState * buildstate, ForkNumber forkNum)
|
|||||||
|
|
||||||
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_SORT);
|
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_SORT);
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 110000
|
|
||||||
buildstate->sortstate = tuplesort_begin_heap(buildstate->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, maintenance_work_mem, NULL, false);
|
buildstate->sortstate = tuplesort_begin_heap(buildstate->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, maintenance_work_mem, NULL, false);
|
||||||
#else
|
|
||||||
buildstate->sortstate = tuplesort_begin_heap(buildstate->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, maintenance_work_mem, false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Add tuples to sort */
|
/* Add tuples to sort */
|
||||||
if (buildstate->heap != NULL)
|
if (buildstate->heap != NULL)
|
||||||
@@ -547,12 +579,9 @@ CreateEntryPages(IvfflatBuildState * buildstate, ForkNumber forkNum)
|
|||||||
#if PG_VERSION_NUM >= 120000
|
#if PG_VERSION_NUM >= 120000
|
||||||
buildstate->reltuples = table_index_build_scan(buildstate->heap, buildstate->index, buildstate->indexInfo,
|
buildstate->reltuples = table_index_build_scan(buildstate->heap, buildstate->index, buildstate->indexInfo,
|
||||||
true, true, BuildCallback, (void *) buildstate, NULL);
|
true, true, BuildCallback, (void *) buildstate, NULL);
|
||||||
#elif PG_VERSION_NUM >= 110000
|
|
||||||
buildstate->reltuples = IndexBuildHeapScan(buildstate->heap, buildstate->index, buildstate->indexInfo,
|
|
||||||
true, BuildCallback, (void *) buildstate, NULL);
|
|
||||||
#else
|
#else
|
||||||
buildstate->reltuples = IndexBuildHeapScan(buildstate->heap, buildstate->index, buildstate->indexInfo,
|
buildstate->reltuples = IndexBuildHeapScan(buildstate->heap, buildstate->index, buildstate->indexInfo,
|
||||||
true, BuildCallback, (void *) buildstate);
|
true, BuildCallback, (void *) buildstate, NULL);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,8 +45,6 @@ ivfflatbuildphasename(int64 phasenum)
|
|||||||
{
|
{
|
||||||
case PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE:
|
case PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE:
|
||||||
return "initializing";
|
return "initializing";
|
||||||
case PROGRESS_IVFFLAT_PHASE_SAMPLE:
|
|
||||||
return "sampling table";
|
|
||||||
case PROGRESS_IVFFLAT_PHASE_KMEANS:
|
case PROGRESS_IVFFLAT_PHASE_KMEANS:
|
||||||
return "performing k-means";
|
return "performing k-means";
|
||||||
case PROGRESS_IVFFLAT_PHASE_SORT:
|
case PROGRESS_IVFFLAT_PHASE_SORT:
|
||||||
@@ -66,9 +64,7 @@ static void
|
|||||||
ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
||||||
Cost *indexStartupCost, Cost *indexTotalCost,
|
Cost *indexStartupCost, Cost *indexTotalCost,
|
||||||
Selectivity *indexSelectivity, double *indexCorrelation
|
Selectivity *indexSelectivity, double *indexCorrelation
|
||||||
#if PG_VERSION_NUM >= 100000
|
|
||||||
,double *indexPages
|
,double *indexPages
|
||||||
#endif
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
GenericCosts costs;
|
GenericCosts costs;
|
||||||
@@ -86,9 +82,7 @@ ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|||||||
*indexTotalCost = DBL_MAX;
|
*indexTotalCost = DBL_MAX;
|
||||||
*indexSelectivity = 0;
|
*indexSelectivity = 0;
|
||||||
*indexCorrelation = 0;
|
*indexCorrelation = 0;
|
||||||
#if PG_VERSION_NUM >= 100000
|
|
||||||
*indexPages = 0;
|
*indexPages = 0;
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,9 +110,7 @@ ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
|||||||
*indexTotalCost = costs.indexTotalCost;
|
*indexTotalCost = costs.indexTotalCost;
|
||||||
*indexSelectivity = costs.indexSelectivity;
|
*indexSelectivity = costs.indexSelectivity;
|
||||||
*indexCorrelation = costs.indexCorrelation;
|
*indexCorrelation = costs.indexCorrelation;
|
||||||
#if PG_VERSION_NUM >= 100000
|
|
||||||
*indexPages = costs.numIndexPages;
|
*indexPages = costs.numIndexPages;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -164,7 +156,7 @@ ivfflatvalidate(Oid opclassoid)
|
|||||||
*
|
*
|
||||||
* See https://www.postgresql.org/docs/current/index-api.html
|
* See https://www.postgresql.org/docs/current/index-api.html
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(ivfflathandler);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(ivfflathandler);
|
||||||
Datum
|
Datum
|
||||||
ivfflathandler(PG_FUNCTION_ARGS)
|
ivfflathandler(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -186,12 +178,8 @@ ivfflathandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amstorage = false;
|
amroutine->amstorage = false;
|
||||||
amroutine->amclusterable = false;
|
amroutine->amclusterable = false;
|
||||||
amroutine->ampredlocks = false;
|
amroutine->ampredlocks = false;
|
||||||
#if PG_VERSION_NUM >= 100000
|
|
||||||
amroutine->amcanparallel = false;
|
amroutine->amcanparallel = false;
|
||||||
#endif
|
|
||||||
#if PG_VERSION_NUM >= 110000
|
|
||||||
amroutine->amcaninclude = false;
|
amroutine->amcaninclude = false;
|
||||||
#endif
|
|
||||||
#if PG_VERSION_NUM >= 130000
|
#if PG_VERSION_NUM >= 130000
|
||||||
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
|
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
|
||||||
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
|
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
|
||||||
@@ -224,11 +212,9 @@ ivfflathandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amrestrpos = NULL;
|
amroutine->amrestrpos = NULL;
|
||||||
|
|
||||||
/* Interface functions to support parallel index scans */
|
/* Interface functions to support parallel index scans */
|
||||||
#if PG_VERSION_NUM >= 100000
|
|
||||||
amroutine->amestimateparallelscan = NULL;
|
amroutine->amestimateparallelscan = NULL;
|
||||||
amroutine->aminitparallelscan = NULL;
|
amroutine->aminitparallelscan = NULL;
|
||||||
amroutine->amparallelrescan = NULL;
|
amroutine->amparallelrescan = NULL;
|
||||||
#endif
|
|
||||||
|
|
||||||
PG_RETURN_POINTER(amroutine);
|
PG_RETURN_POINTER(amroutine);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 110000
|
||||||
|
#error "Requires PostgreSQL 11+"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "access/generic_xlog.h"
|
#include "access/generic_xlog.h"
|
||||||
#include "access/reloptions.h"
|
#include "access/reloptions.h"
|
||||||
#include "nodes/execnodes.h"
|
#include "nodes/execnodes.h"
|
||||||
@@ -14,9 +18,7 @@
|
|||||||
#include "portability/instr_time.h"
|
#include "portability/instr_time.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 100000
|
#define IVFFLAT_MAX_DIM 2000
|
||||||
#error "Requires PostgreSQL 10+"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Support functions */
|
/* Support functions */
|
||||||
#define IVFFLAT_DISTANCE_PROC 1
|
#define IVFFLAT_DISTANCE_PROC 1
|
||||||
@@ -37,10 +39,9 @@
|
|||||||
|
|
||||||
/* Build phases */
|
/* Build phases */
|
||||||
/* PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE is 1 */
|
/* PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE is 1 */
|
||||||
#define PROGRESS_IVFFLAT_PHASE_SAMPLE 2
|
#define PROGRESS_IVFFLAT_PHASE_KMEANS 2
|
||||||
#define PROGRESS_IVFFLAT_PHASE_KMEANS 3
|
#define PROGRESS_IVFFLAT_PHASE_SORT 3
|
||||||
#define PROGRESS_IVFFLAT_PHASE_SORT 4
|
#define PROGRESS_IVFFLAT_PHASE_LOAD 4
|
||||||
#define PROGRESS_IVFFLAT_PHASE_LOAD 5
|
|
||||||
|
|
||||||
#define IVFFLAT_LIST_SIZE(_dim) (offsetof(IvfflatListData, center) + VECTOR_SIZE(_dim))
|
#define IVFFLAT_LIST_SIZE(_dim) (offsetof(IvfflatListData, center) + VECTOR_SIZE(_dim))
|
||||||
|
|
||||||
@@ -65,12 +66,15 @@
|
|||||||
/* Variables */
|
/* Variables */
|
||||||
extern int ivfflat_probes;
|
extern int ivfflat_probes;
|
||||||
|
|
||||||
|
/* Exported functions */
|
||||||
|
PGDLLEXPORT void _PG_init(void);
|
||||||
|
|
||||||
typedef struct VectorArrayData
|
typedef struct VectorArrayData
|
||||||
{
|
{
|
||||||
int length;
|
int length;
|
||||||
int maxlen;
|
int maxlen;
|
||||||
int dim;
|
int dim;
|
||||||
Vector items[FLEXIBLE_ARRAY_MEMBER];
|
Vector *items;
|
||||||
} VectorArrayData;
|
} VectorArrayData;
|
||||||
|
|
||||||
typedef VectorArrayData * VectorArray;
|
typedef VectorArrayData * VectorArray;
|
||||||
@@ -130,6 +134,9 @@ typedef struct IvfflatBuildState
|
|||||||
Tuplesortstate *sortstate;
|
Tuplesortstate *sortstate;
|
||||||
TupleDesc tupdesc;
|
TupleDesc tupdesc;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
|
|
||||||
|
/* Memory */
|
||||||
|
MemoryContext tmpCtx;
|
||||||
} IvfflatBuildState;
|
} IvfflatBuildState;
|
||||||
|
|
||||||
typedef struct IvfflatMetaPageData
|
typedef struct IvfflatMetaPageData
|
||||||
@@ -191,14 +198,14 @@ typedef struct IvfflatScanOpaqueData
|
|||||||
|
|
||||||
typedef IvfflatScanOpaqueData * IvfflatScanOpaque;
|
typedef IvfflatScanOpaqueData * IvfflatScanOpaque;
|
||||||
|
|
||||||
#define VECTOR_ARRAY_SIZE(_length, _dim) (offsetof(VectorArrayData, items) + _length * VECTOR_SIZE(_dim))
|
#define VECTOR_ARRAY_SIZE(_length, _dim) (sizeof(VectorArrayData) + (_length) * VECTOR_SIZE(_dim))
|
||||||
#define VECTOR_ARRAY_OFFSET(_arr, _offset) ((char*) _arr + offsetof(VectorArrayData, items) + (_offset) * VECTOR_SIZE(_arr->dim))
|
#define VECTOR_ARRAY_OFFSET(_arr, _offset) ((char*) (_arr)->items + (_offset) * VECTOR_SIZE((_arr)->dim))
|
||||||
#define VectorArrayGet(_arr, _offset) ((Vector *) VECTOR_ARRAY_OFFSET(_arr, _offset))
|
#define VectorArrayGet(_arr, _offset) ((Vector *) VECTOR_ARRAY_OFFSET(_arr, _offset))
|
||||||
#define VectorArraySet(_arr, _offset, _val) (memcpy(VECTOR_ARRAY_OFFSET(_arr, _offset), _val, VECTOR_SIZE(_arr->dim)))
|
#define VectorArraySet(_arr, _offset, _val) memcpy(VECTOR_ARRAY_OFFSET(_arr, _offset), _val, VECTOR_SIZE((_arr)->dim))
|
||||||
|
|
||||||
/* Methods */
|
/* Methods */
|
||||||
void _PG_init(void);
|
|
||||||
VectorArray VectorArrayInit(int maxlen, int dimensions);
|
VectorArray VectorArrayInit(int maxlen, int dimensions);
|
||||||
|
void VectorArrayFree(VectorArray arr);
|
||||||
void PrintVectorArray(char *msg, VectorArray arr);
|
void PrintVectorArray(char *msg, VectorArray arr);
|
||||||
void IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers);
|
void IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers);
|
||||||
FmgrInfo *IvfflatOptionalProcInfo(Relation rel, uint16 procnum);
|
FmgrInfo *IvfflatOptionalProcInfo(Relation rel, uint16 procnum);
|
||||||
@@ -218,9 +225,7 @@ bool ivfflatinsert(Relation index, Datum *values, bool *isnull, ItemPointer hea
|
|||||||
#if PG_VERSION_NUM >= 140000
|
#if PG_VERSION_NUM >= 140000
|
||||||
,bool indexUnchanged
|
,bool indexUnchanged
|
||||||
#endif
|
#endif
|
||||||
#if PG_VERSION_NUM >= 100000
|
|
||||||
,IndexInfo *indexInfo
|
,IndexInfo *indexInfo
|
||||||
#endif
|
|
||||||
);
|
);
|
||||||
IndexBulkDeleteResult *ivfflatbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state);
|
IndexBulkDeleteResult *ivfflatbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state);
|
||||||
IndexBulkDeleteResult *ivfflatvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats);
|
IndexBulkDeleteResult *ivfflatvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "ivfflat.h"
|
#include "ivfflat.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
|
#include "utils/memutils.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the list that minimizes the distance function
|
* Find the list that minimizes the distance function
|
||||||
@@ -57,8 +58,11 @@ FindInsertPage(Relation rel, Datum *values, BlockNumber *insertPage, ListInfo *
|
|||||||
* Insert a tuple into the index
|
* Insert a tuple into the index
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
InsertTuple(Relation rel, IndexTuple itup, Relation heapRel, Datum *values)
|
InsertTuple(Relation rel, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel)
|
||||||
{
|
{
|
||||||
|
IndexTuple itup;
|
||||||
|
Datum value;
|
||||||
|
FmgrInfo *normprocinfo;
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
Page page;
|
Page page;
|
||||||
GenericXLogState *state;
|
GenericXLogState *state;
|
||||||
@@ -67,11 +71,27 @@ InsertTuple(Relation rel, IndexTuple itup, Relation heapRel, Datum *values)
|
|||||||
ListInfo listInfo;
|
ListInfo listInfo;
|
||||||
BlockNumber originalInsertPage;
|
BlockNumber originalInsertPage;
|
||||||
|
|
||||||
|
/* Detoast once for all calls */
|
||||||
|
value = PointerGetDatum(PG_DETOAST_DATUM(values[0]));
|
||||||
|
|
||||||
|
/* Normalize if needed */
|
||||||
|
normprocinfo = IvfflatOptionalProcInfo(rel, IVFFLAT_NORM_PROC);
|
||||||
|
if (normprocinfo != NULL)
|
||||||
|
{
|
||||||
|
if (!IvfflatNormValue(normprocinfo, rel->rd_indcollation[0], &value, NULL))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Find the insert page - sets the page and list info */
|
/* Find the insert page - sets the page and list info */
|
||||||
FindInsertPage(rel, values, &insertPage, &listInfo);
|
FindInsertPage(rel, values, &insertPage, &listInfo);
|
||||||
Assert(BlockNumberIsValid(insertPage));
|
Assert(BlockNumberIsValid(insertPage));
|
||||||
originalInsertPage = insertPage;
|
originalInsertPage = insertPage;
|
||||||
|
|
||||||
|
/* Form tuple */
|
||||||
|
itup = index_form_tuple(RelationGetDescr(rel), &value, isnull);
|
||||||
|
itup->t_tid = *heap_tid;
|
||||||
|
|
||||||
|
/* Get tuple size */
|
||||||
itemsz = MAXALIGN(IndexTupleSize(itup));
|
itemsz = MAXALIGN(IndexTupleSize(itup));
|
||||||
Assert(itemsz <= BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(IvfflatPageOpaqueData)));
|
Assert(itemsz <= BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(IvfflatPageOpaqueData)));
|
||||||
|
|
||||||
@@ -130,9 +150,14 @@ InsertTuple(Relation rel, IndexTuple itup, Relation heapRel, Datum *values)
|
|||||||
/* Unlock extend relation lock as early as possible */
|
/* Unlock extend relation lock as early as possible */
|
||||||
UnlockReleaseBuffer(metabuf);
|
UnlockReleaseBuffer(metabuf);
|
||||||
|
|
||||||
/* Unlock rest */
|
/* Unlock previous buffer */
|
||||||
UnlockReleaseBuffer(newbuf);
|
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
|
|
||||||
|
/* Prepare new buffer */
|
||||||
|
state = GenericXLogStart(rel);
|
||||||
|
buf = newbuf;
|
||||||
|
page = GenericXLogRegisterBuffer(state, buf, 0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,36 +181,31 @@ ivfflatinsert(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid,
|
|||||||
#if PG_VERSION_NUM >= 140000
|
#if PG_VERSION_NUM >= 140000
|
||||||
,bool indexUnchanged
|
,bool indexUnchanged
|
||||||
#endif
|
#endif
|
||||||
#if PG_VERSION_NUM >= 100000
|
|
||||||
,IndexInfo *indexInfo
|
,IndexInfo *indexInfo
|
||||||
#endif
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
IndexTuple itup;
|
MemoryContext oldCtx;
|
||||||
Datum value;
|
MemoryContext insertCtx;
|
||||||
FmgrInfo *normprocinfo;
|
|
||||||
|
|
||||||
|
/* Skip nulls */
|
||||||
if (isnull[0])
|
if (isnull[0])
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
value = values[0];
|
/*
|
||||||
|
* Use memory context since detoast, IvfflatNormValue, and
|
||||||
|
* index_form_tuple can allocate
|
||||||
|
*/
|
||||||
|
insertCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
|
"Ivfflat insert temporary context",
|
||||||
|
ALLOCSET_DEFAULT_SIZES);
|
||||||
|
oldCtx = MemoryContextSwitchTo(insertCtx);
|
||||||
|
|
||||||
/* Normalize if needed */
|
/* Insert tuple */
|
||||||
normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
|
InsertTuple(index, values, isnull, heap_tid, heap);
|
||||||
if (normprocinfo != NULL)
|
|
||||||
{
|
|
||||||
if (!IvfflatNormValue(normprocinfo, index->rd_indcollation[0], &value, NULL))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
itup = index_form_tuple(RelationGetDescr(index), &value, isnull);
|
/* Delete memory context */
|
||||||
itup->t_tid = *heap_tid;
|
MemoryContextSwitchTo(oldCtx);
|
||||||
InsertTuple(index, itup, heap, &value);
|
MemoryContextDelete(insertCtx);
|
||||||
pfree(itup);
|
|
||||||
|
|
||||||
/* Clean up if we allocated a new value */
|
|
||||||
if (value != values[0])
|
|
||||||
pfree(DatumGetPointer(value));
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,6 @@
|
|||||||
#include "ivfflat.h"
|
#include "ivfflat.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 150000
|
|
||||||
#include "common/pg_prng.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 150000
|
|
||||||
#define RandomDouble() pg_prng_double(&pg_global_prng_state)
|
|
||||||
#else
|
|
||||||
#define RandomDouble() (((double) random()) / MAX_RANDOM_VALUE)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize with kmeans++
|
* Initialize with kmeans++
|
||||||
*
|
*
|
||||||
@@ -39,7 +29,7 @@ InitCenters(Relation index, VectorArray samples, VectorArray centers, float *low
|
|||||||
collation = index->rd_indcollation[0];
|
collation = index->rd_indcollation[0];
|
||||||
|
|
||||||
/* Choose an initial center uniformly at random */
|
/* Choose an initial center uniformly at random */
|
||||||
VectorArraySet(centers, 0, VectorArrayGet(samples, random() % samples->length));
|
VectorArraySet(centers, 0, VectorArrayGet(samples, RandomInt() % samples->length));
|
||||||
centers->length++;
|
centers->length++;
|
||||||
|
|
||||||
for (j = 0; j < numSamples; j++)
|
for (j = 0; j < numSamples; j++)
|
||||||
@@ -453,7 +443,7 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pfree(newCenters);
|
VectorArrayFree(newCenters);
|
||||||
pfree(centerCounts);
|
pfree(centerCounts);
|
||||||
pfree(closestCenters);
|
pfree(closestCenters);
|
||||||
pfree(lowerBound);
|
pfree(lowerBound);
|
||||||
|
|||||||
@@ -7,13 +7,8 @@
|
|||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 110000
|
|
||||||
#include "catalog/pg_operator_d.h"
|
#include "catalog/pg_operator_d.h"
|
||||||
#include "catalog/pg_type_d.h"
|
#include "catalog/pg_type_d.h"
|
||||||
#else
|
|
||||||
#include "catalog/pg_operator.h"
|
|
||||||
#include "catalog/pg_type.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compare list distances
|
* Compare list distances
|
||||||
@@ -216,11 +211,7 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
|
|||||||
TupleDescInitEntry(so->tupdesc, (AttrNumber) 3, "indexblkno", INT4OID, -1, 0);
|
TupleDescInitEntry(so->tupdesc, (AttrNumber) 3, "indexblkno", INT4OID, -1, 0);
|
||||||
|
|
||||||
/* Prep sort */
|
/* Prep sort */
|
||||||
#if PG_VERSION_NUM >= 110000
|
|
||||||
so->sortstate = tuplesort_begin_heap(so->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, work_mem, NULL, false);
|
so->sortstate = tuplesort_begin_heap(so->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, work_mem, NULL, false);
|
||||||
#else
|
|
||||||
so->sortstate = tuplesort_begin_heap(so->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, work_mem, false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 120000
|
#if PG_VERSION_NUM >= 120000
|
||||||
so->slot = MakeSingleTupleTableSlot(so->tupdesc, &TTSOpsMinimalTuple);
|
so->slot = MakeSingleTupleTableSlot(so->tupdesc, &TTSOpsMinimalTuple);
|
||||||
@@ -286,6 +277,10 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
|
|||||||
|
|
||||||
value = scan->orderByData->sk_argument;
|
value = scan->orderByData->sk_argument;
|
||||||
|
|
||||||
|
/* Value should not be compressed or toasted */
|
||||||
|
Assert(!VARATT_IS_COMPRESSED(DatumGetPointer(value)));
|
||||||
|
Assert(!VARATT_IS_EXTENDED(DatumGetPointer(value)));
|
||||||
|
|
||||||
if (so->normprocinfo != NULL)
|
if (so->normprocinfo != NULL)
|
||||||
{
|
{
|
||||||
/* No items will match if normalization fails */
|
/* No items will match if normalization fails */
|
||||||
@@ -302,11 +297,7 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
|
|||||||
pfree(DatumGetPointer(value));
|
pfree(DatumGetPointer(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 100000
|
|
||||||
if (tuplesort_gettupleslot(so->sortstate, true, false, so->slot, NULL))
|
if (tuplesort_gettupleslot(so->sortstate, true, false, so->slot, NULL))
|
||||||
#else
|
|
||||||
if (tuplesort_gettupleslot(so->sortstate, true, so->slot, NULL))
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
ItemPointer tid = (ItemPointer) DatumGetPointer(slot_getattr(so->slot, 2, &so->isnull));
|
ItemPointer tid = (ItemPointer) DatumGetPointer(slot_getattr(so->slot, 2, &so->isnull));
|
||||||
BlockNumber indexblkno = DatumGetInt32(slot_getattr(so->slot, 3, &so->isnull));
|
BlockNumber indexblkno = DatumGetInt32(slot_getattr(so->slot, 3, &so->isnull));
|
||||||
|
|||||||
@@ -10,14 +10,25 @@
|
|||||||
VectorArray
|
VectorArray
|
||||||
VectorArrayInit(int maxlen, int dimensions)
|
VectorArrayInit(int maxlen, int dimensions)
|
||||||
{
|
{
|
||||||
VectorArray res = palloc_extended(VECTOR_ARRAY_SIZE(maxlen, dimensions), MCXT_ALLOC_ZERO | MCXT_ALLOC_HUGE);
|
VectorArray res = palloc(sizeof(VectorArrayData));
|
||||||
|
|
||||||
res->length = 0;
|
res->length = 0;
|
||||||
res->maxlen = maxlen;
|
res->maxlen = maxlen;
|
||||||
res->dim = dimensions;
|
res->dim = dimensions;
|
||||||
|
res->items = palloc_extended(maxlen * VECTOR_SIZE(dimensions), MCXT_ALLOC_ZERO | MCXT_ALLOC_HUGE);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free a vector array
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
VectorArrayFree(VectorArray arr)
|
||||||
|
{
|
||||||
|
pfree(arr->items);
|
||||||
|
pfree(arr);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Print vector array - useful for debugging
|
* Print vector array - useful for debugging
|
||||||
*/
|
*/
|
||||||
@@ -75,7 +86,7 @@ IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * resul
|
|||||||
|
|
||||||
if (norm > 0)
|
if (norm > 0)
|
||||||
{
|
{
|
||||||
v = (Vector *) DatumGetPointer(*value);
|
v = DatumGetVector(*value);
|
||||||
|
|
||||||
if (result == NULL)
|
if (result == NULL)
|
||||||
result = InitVector(v->dim);
|
result = InitVector(v->dim);
|
||||||
|
|||||||
363
src/vector.c
363
src/vector.c
@@ -13,13 +13,20 @@
|
|||||||
#include "utils/numeric.h"
|
#include "utils/numeric.h"
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 120000
|
#if PG_VERSION_NUM >= 120000
|
||||||
|
#include "common/shortest_dec.h"
|
||||||
#include "utils/float.h"
|
#include "utils/float.h"
|
||||||
|
#else
|
||||||
|
#include <float.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 130000
|
#if PG_VERSION_NUM < 130000
|
||||||
|
#define TYPALIGN_DOUBLE 'd'
|
||||||
#define TYPALIGN_INT 'i'
|
#define TYPALIGN_INT 'i'
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define STATE_DIMS(x) (ARR_DIMS(x)[0] - 1)
|
||||||
|
#define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1))
|
||||||
|
|
||||||
PG_MODULE_MAGIC;
|
PG_MODULE_MAGIC;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -79,6 +86,30 @@ CheckElement(float value)
|
|||||||
errmsg("infinite value not allowed in vector")));
|
errmsg("infinite value not allowed in vector")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check state array
|
||||||
|
*/
|
||||||
|
static float8 *
|
||||||
|
CheckStateArray(ArrayType *statearray, const char *caller)
|
||||||
|
{
|
||||||
|
if (ARR_NDIM(statearray) != 1 ||
|
||||||
|
ARR_DIMS(statearray)[0] < 1 ||
|
||||||
|
ARR_HASNULL(statearray) ||
|
||||||
|
ARR_ELEMTYPE(statearray) != FLOAT8OID)
|
||||||
|
elog(ERROR, "%s: expected state array", caller);
|
||||||
|
return (float8 *) ARR_DATA_PTR(statearray);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 120000
|
||||||
|
static pg_noinline void
|
||||||
|
float_overflow_error(void)
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("value out of range: overflow")));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Print vector - useful for debugging
|
* Print vector - useful for debugging
|
||||||
*/
|
*/
|
||||||
@@ -106,14 +137,14 @@ PrintVector(char *msg, Vector * vector)
|
|||||||
/*
|
/*
|
||||||
* Convert textual representation to internal representation
|
* Convert textual representation to internal representation
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_in);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_in);
|
||||||
Datum
|
Datum
|
||||||
vector_in(PG_FUNCTION_ARGS)
|
vector_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
char *str = PG_GETARG_CSTRING(0);
|
char *str = PG_GETARG_CSTRING(0);
|
||||||
int32 typmod = PG_GETARG_INT32(2);
|
int32 typmod = PG_GETARG_INT32(2);
|
||||||
int i;
|
int i;
|
||||||
double x[VECTOR_MAX_DIM];
|
float x[VECTOR_MAX_DIM];
|
||||||
int dim = 0;
|
int dim = 0;
|
||||||
char *pt;
|
char *pt;
|
||||||
char *stringEnd;
|
char *stringEnd;
|
||||||
@@ -136,7 +167,8 @@ vector_in(PG_FUNCTION_ARGS)
|
|||||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
||||||
errmsg("vector cannot have more than %d dimensions", VECTOR_MAX_DIM)));
|
errmsg("vector cannot have more than %d dimensions", VECTOR_MAX_DIM)));
|
||||||
|
|
||||||
x[dim] = strtod(pt, &stringEnd);
|
/* Use strtof like float4in to avoid a double-rounding problem */
|
||||||
|
x[dim] = strtof(pt, &stringEnd);
|
||||||
CheckElement(x[dim]);
|
CheckElement(x[dim]);
|
||||||
dim++;
|
dim++;
|
||||||
|
|
||||||
@@ -182,35 +214,68 @@ vector_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to textual representation
|
* Convert internal representation to textual representation
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_out);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_out);
|
||||||
Datum
|
Datum
|
||||||
vector_out(PG_FUNCTION_ARGS)
|
vector_out(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *vector = PG_GETARG_VECTOR_P(0);
|
Vector *vector = PG_GETARG_VECTOR_P(0);
|
||||||
StringInfoData buf;
|
|
||||||
int dim = vector->dim;
|
int dim = vector->dim;
|
||||||
|
char *buf;
|
||||||
|
char *ptr;
|
||||||
int i;
|
int i;
|
||||||
|
int n;
|
||||||
|
|
||||||
initStringInfo(&buf);
|
#if PG_VERSION_NUM < 120000
|
||||||
|
int ndig = FLT_DIG + extra_float_digits;
|
||||||
|
|
||||||
appendStringInfoChar(&buf, '[');
|
if (ndig < 1)
|
||||||
|
ndig = 1;
|
||||||
|
|
||||||
|
#define FLOAT_SHORTEST_DECIMAL_LEN (ndig + 10)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Need:
|
||||||
|
*
|
||||||
|
* dim * (FLOAT_SHORTEST_DECIMAL_LEN - 1) bytes for
|
||||||
|
* float_to_shortest_decimal_bufn
|
||||||
|
*
|
||||||
|
* dim - 1 bytes for separator
|
||||||
|
*
|
||||||
|
* 3 bytes for [, ], and \0
|
||||||
|
*/
|
||||||
|
buf = (char *) palloc(FLOAT_SHORTEST_DECIMAL_LEN * dim + 2);
|
||||||
|
ptr = buf;
|
||||||
|
|
||||||
|
*ptr = '[';
|
||||||
|
ptr++;
|
||||||
for (i = 0; i < dim; i++)
|
for (i = 0; i < dim; i++)
|
||||||
{
|
{
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
appendStringInfoString(&buf, ",");
|
{
|
||||||
|
*ptr = ',';
|
||||||
appendStringInfoString(&buf, float8out_internal(vector->x[i]));
|
ptr++;
|
||||||
}
|
}
|
||||||
appendStringInfoChar(&buf, ']');
|
|
||||||
|
#if PG_VERSION_NUM >= 120000
|
||||||
|
n = float_to_shortest_decimal_bufn(vector->x[i], ptr);
|
||||||
|
#else
|
||||||
|
n = sprintf(ptr, "%.*g", ndig, vector->x[i]);
|
||||||
|
#endif
|
||||||
|
ptr += n;
|
||||||
|
}
|
||||||
|
*ptr = ']';
|
||||||
|
ptr++;
|
||||||
|
*ptr = '\0';
|
||||||
|
|
||||||
PG_FREE_IF_COPY(vector, 0);
|
PG_FREE_IF_COPY(vector, 0);
|
||||||
PG_RETURN_CSTRING(buf.data);
|
PG_RETURN_CSTRING(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert type modifier
|
* Convert type modifier
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_typmod_in);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_typmod_in);
|
||||||
Datum
|
Datum
|
||||||
vector_typmod_in(PG_FUNCTION_ARGS)
|
vector_typmod_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -241,7 +306,7 @@ vector_typmod_in(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert external binary representation to internal representation
|
* Convert external binary representation to internal representation
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_recv);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_recv);
|
||||||
Datum
|
Datum
|
||||||
vector_recv(PG_FUNCTION_ARGS)
|
vector_recv(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -273,7 +338,7 @@ vector_recv(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert internal representation to the external binary representation
|
* Convert internal representation to the external binary representation
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_send);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_send);
|
||||||
Datum
|
Datum
|
||||||
vector_send(PG_FUNCTION_ARGS)
|
vector_send(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -293,7 +358,7 @@ vector_send(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert vector to vector
|
* Convert vector to vector
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector);
|
||||||
Datum
|
Datum
|
||||||
vector(PG_FUNCTION_ARGS)
|
vector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -308,7 +373,7 @@ vector(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert array to vector
|
* Convert array to vector
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(array_to_vector);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(array_to_vector);
|
||||||
Datum
|
Datum
|
||||||
array_to_vector(PG_FUNCTION_ARGS)
|
array_to_vector(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -366,7 +431,7 @@ array_to_vector(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Convert vector to float4[]
|
* Convert vector to float4[]
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_to_float4);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_to_float4);
|
||||||
Datum
|
Datum
|
||||||
vector_to_float4(PG_FUNCTION_ARGS)
|
vector_to_float4(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -389,12 +454,14 @@ vector_to_float4(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 distance between vectors
|
* Get the L2 distance between vectors
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(l2_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(l2_distance);
|
||||||
Datum
|
Datum
|
||||||
l2_distance(PG_FUNCTION_ARGS)
|
l2_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
float *ax = a->x;
|
||||||
|
float *bx = b->x;
|
||||||
double distance = 0.0;
|
double distance = 0.0;
|
||||||
double diff;
|
double diff;
|
||||||
|
|
||||||
@@ -402,7 +469,7 @@ l2_distance(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
for (int i = 0; i < a->dim; i++)
|
for (int i = 0; i < a->dim; i++)
|
||||||
{
|
{
|
||||||
diff = a->x[i] - b->x[i];
|
diff = ax[i] - bx[i];
|
||||||
distance += diff * diff;
|
distance += diff * diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,12 +480,14 @@ 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
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_l2_squared_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_l2_squared_distance);
|
||||||
Datum
|
Datum
|
||||||
vector_l2_squared_distance(PG_FUNCTION_ARGS)
|
vector_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
float *ax = a->x;
|
||||||
|
float *bx = b->x;
|
||||||
double distance = 0.0;
|
double distance = 0.0;
|
||||||
double diff;
|
double diff;
|
||||||
|
|
||||||
@@ -426,7 +495,7 @@ vector_l2_squared_distance(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
for (int i = 0; i < a->dim; i++)
|
for (int i = 0; i < a->dim; i++)
|
||||||
{
|
{
|
||||||
diff = a->x[i] - b->x[i];
|
diff = ax[i] - bx[i];
|
||||||
distance += diff * diff;
|
distance += diff * diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,18 +505,20 @@ vector_l2_squared_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the inner product of two vectors
|
* Get the inner product of two vectors
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(inner_product);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(inner_product);
|
||||||
Datum
|
Datum
|
||||||
inner_product(PG_FUNCTION_ARGS)
|
inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
float *ax = a->x;
|
||||||
|
float *bx = b->x;
|
||||||
double distance = 0.0;
|
double distance = 0.0;
|
||||||
|
|
||||||
CheckDims(a, b);
|
CheckDims(a, b);
|
||||||
|
|
||||||
for (int i = 0; i < a->dim; i++)
|
for (int i = 0; i < a->dim; i++)
|
||||||
distance += a->x[i] * b->x[i];
|
distance += ax[i] * bx[i];
|
||||||
|
|
||||||
PG_RETURN_FLOAT8(distance);
|
PG_RETURN_FLOAT8(distance);
|
||||||
}
|
}
|
||||||
@@ -455,18 +526,20 @@ inner_product(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the negative inner product of two vectors
|
* Get the negative inner product of two vectors
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_negative_inner_product);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_negative_inner_product);
|
||||||
Datum
|
Datum
|
||||||
vector_negative_inner_product(PG_FUNCTION_ARGS)
|
vector_negative_inner_product(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
float *ax = a->x;
|
||||||
|
float *bx = b->x;
|
||||||
double distance = 0.0;
|
double distance = 0.0;
|
||||||
|
|
||||||
CheckDims(a, b);
|
CheckDims(a, b);
|
||||||
|
|
||||||
for (int i = 0; i < a->dim; i++)
|
for (int i = 0; i < a->dim; i++)
|
||||||
distance += a->x[i] * b->x[i];
|
distance += ax[i] * bx[i];
|
||||||
|
|
||||||
PG_RETURN_FLOAT8(distance * -1);
|
PG_RETURN_FLOAT8(distance * -1);
|
||||||
}
|
}
|
||||||
@@ -474,12 +547,14 @@ vector_negative_inner_product(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the cosine distance between two vectors
|
* Get the cosine distance between two vectors
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(cosine_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(cosine_distance);
|
||||||
Datum
|
Datum
|
||||||
cosine_distance(PG_FUNCTION_ARGS)
|
cosine_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
float *ax = a->x;
|
||||||
|
float *bx = b->x;
|
||||||
double distance = 0.0;
|
double distance = 0.0;
|
||||||
double norma = 0.0;
|
double norma = 0.0;
|
||||||
double normb = 0.0;
|
double normb = 0.0;
|
||||||
@@ -488,9 +563,9 @@ cosine_distance(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
for (int i = 0; i < a->dim; i++)
|
for (int i = 0; i < a->dim; i++)
|
||||||
{
|
{
|
||||||
distance += a->x[i] * b->x[i];
|
distance += ax[i] * bx[i];
|
||||||
norma += a->x[i] * a->x[i];
|
norma += ax[i] * ax[i];
|
||||||
normb += b->x[i] * b->x[i];
|
normb += bx[i] * bx[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
PG_RETURN_FLOAT8(1 - (distance / (sqrt(norma) * sqrt(normb))));
|
PG_RETURN_FLOAT8(1 - (distance / (sqrt(norma) * sqrt(normb))));
|
||||||
@@ -501,7 +576,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)
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_spherical_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_spherical_distance);
|
||||||
Datum
|
Datum
|
||||||
vector_spherical_distance(PG_FUNCTION_ARGS)
|
vector_spherical_distance(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -526,7 +601,7 @@ vector_spherical_distance(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the dimensions of a vector
|
* Get the dimensions of a vector
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_dims);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_dims);
|
||||||
Datum
|
Datum
|
||||||
vector_dims(PG_FUNCTION_ARGS)
|
vector_dims(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -538,15 +613,16 @@ vector_dims(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Get the L2 norm of a vector
|
* Get the L2 norm of a vector
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_norm);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_norm);
|
||||||
Datum
|
Datum
|
||||||
vector_norm(PG_FUNCTION_ARGS)
|
vector_norm(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
|
float *ax = a->x;
|
||||||
double norm = 0.0;
|
double norm = 0.0;
|
||||||
|
|
||||||
for (int i = 0; i < a->dim; i++)
|
for (int i = 0; i < a->dim; i++)
|
||||||
norm += a->x[i] * a->x[i];
|
norm += ax[i] * ax[i];
|
||||||
|
|
||||||
PG_RETURN_FLOAT8(sqrt(norm));
|
PG_RETURN_FLOAT8(sqrt(norm));
|
||||||
}
|
}
|
||||||
@@ -554,20 +630,23 @@ vector_norm(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Add vectors
|
* Add vectors
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_add);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_add);
|
||||||
Datum
|
Datum
|
||||||
vector_add(PG_FUNCTION_ARGS)
|
vector_add(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
float *ax = a->x;
|
||||||
|
float *bx = b->x;
|
||||||
Vector *result;
|
Vector *result;
|
||||||
int i;
|
float *rx;
|
||||||
|
|
||||||
CheckDims(a, b);
|
CheckDims(a, b);
|
||||||
|
|
||||||
result = InitVector(a->dim);
|
result = InitVector(a->dim);
|
||||||
for (i = 0; i < a->dim; i++)
|
rx = result->x;
|
||||||
result->x[i] = a->x[i] + b->x[i];
|
for (int i = 0, imax = a->dim; i < imax; i++)
|
||||||
|
rx[i] = ax[i] + bx[i];
|
||||||
|
|
||||||
PG_RETURN_POINTER(result);
|
PG_RETURN_POINTER(result);
|
||||||
}
|
}
|
||||||
@@ -575,20 +654,23 @@ vector_add(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Subtract vectors
|
* Subtract vectors
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_sub);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_sub);
|
||||||
Datum
|
Datum
|
||||||
vector_sub(PG_FUNCTION_ARGS)
|
vector_sub(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
float *ax = a->x;
|
||||||
|
float *bx = b->x;
|
||||||
Vector *result;
|
Vector *result;
|
||||||
int i;
|
float *rx;
|
||||||
|
|
||||||
CheckDims(a, b);
|
CheckDims(a, b);
|
||||||
|
|
||||||
result = InitVector(a->dim);
|
result = InitVector(a->dim);
|
||||||
for (i = 0; i < a->dim; i++)
|
rx = result->x;
|
||||||
result->x[i] = a->x[i] - b->x[i];
|
for (int i = 0, imax = a->dim; i < imax; i++)
|
||||||
|
rx[i] = ax[i] - bx[i];
|
||||||
|
|
||||||
PG_RETURN_POINTER(result);
|
PG_RETURN_POINTER(result);
|
||||||
}
|
}
|
||||||
@@ -617,7 +699,7 @@ vector_cmp_internal(Vector * a, Vector * b)
|
|||||||
/*
|
/*
|
||||||
* Less than
|
* Less than
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_lt);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_lt);
|
||||||
Datum
|
Datum
|
||||||
vector_lt(PG_FUNCTION_ARGS)
|
vector_lt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -630,7 +712,7 @@ vector_lt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Less than or equal
|
* Less than or equal
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_le);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_le);
|
||||||
Datum
|
Datum
|
||||||
vector_le(PG_FUNCTION_ARGS)
|
vector_le(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -643,7 +725,7 @@ vector_le(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Equal
|
* Equal
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_eq);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_eq);
|
||||||
Datum
|
Datum
|
||||||
vector_eq(PG_FUNCTION_ARGS)
|
vector_eq(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -656,7 +738,7 @@ vector_eq(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Not equal
|
* Not equal
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_ne);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ne);
|
||||||
Datum
|
Datum
|
||||||
vector_ne(PG_FUNCTION_ARGS)
|
vector_ne(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -669,7 +751,7 @@ vector_ne(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than or equal
|
* Greater than or equal
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_ge);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ge);
|
||||||
Datum
|
Datum
|
||||||
vector_ge(PG_FUNCTION_ARGS)
|
vector_ge(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -682,7 +764,7 @@ vector_ge(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Greater than
|
* Greater than
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_gt);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_gt);
|
||||||
Datum
|
Datum
|
||||||
vector_gt(PG_FUNCTION_ARGS)
|
vector_gt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -695,7 +777,7 @@ vector_gt(PG_FUNCTION_ARGS)
|
|||||||
/*
|
/*
|
||||||
* Compare vectors
|
* Compare vectors
|
||||||
*/
|
*/
|
||||||
PG_FUNCTION_INFO_V1(vector_cmp);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_cmp);
|
||||||
Datum
|
Datum
|
||||||
vector_cmp(PG_FUNCTION_ARGS)
|
vector_cmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@@ -704,3 +786,186 @@ vector_cmp(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
PG_RETURN_INT32(vector_cmp_internal(a, b));
|
PG_RETURN_INT32(vector_cmp_internal(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Accumulate vectors
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_accum);
|
||||||
|
Datum
|
||||||
|
vector_accum(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ArrayType *statearray = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
|
Vector *newval = PG_GETARG_VECTOR_P(1);
|
||||||
|
float8 *statevalues;
|
||||||
|
int16 dim;
|
||||||
|
bool newarr;
|
||||||
|
float8 n;
|
||||||
|
Datum *statedatums;
|
||||||
|
float *x = newval->x;
|
||||||
|
ArrayType *result;
|
||||||
|
|
||||||
|
/* Check array before using */
|
||||||
|
statevalues = CheckStateArray(statearray, "vector_accum");
|
||||||
|
dim = STATE_DIMS(statearray);
|
||||||
|
newarr = dim == 0;
|
||||||
|
|
||||||
|
if (newarr)
|
||||||
|
dim = newval->dim;
|
||||||
|
else
|
||||||
|
CheckExpectedDim(dim, newval->dim);
|
||||||
|
|
||||||
|
n = statevalues[0] + 1.0;
|
||||||
|
|
||||||
|
statedatums = CreateStateDatums(dim);
|
||||||
|
statedatums[0] = Float8GetDatumFast(n);
|
||||||
|
|
||||||
|
if (newarr)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < dim; i++)
|
||||||
|
statedatums[i + 1] = Float8GetDatumFast(x[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < dim; i++)
|
||||||
|
{
|
||||||
|
double v = statevalues[i + 1] + x[i];
|
||||||
|
|
||||||
|
if (isinf(v))
|
||||||
|
float_overflow_error();
|
||||||
|
|
||||||
|
statedatums[i + 1] = Float8GetDatumFast(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use float8 array like float4_accum */
|
||||||
|
result = construct_array(statedatums, dim + 1,
|
||||||
|
FLOAT8OID,
|
||||||
|
sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);
|
||||||
|
|
||||||
|
pfree(statedatums);
|
||||||
|
|
||||||
|
PG_RETURN_ARRAYTYPE_P(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Combine vectors
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_combine);
|
||||||
|
Datum
|
||||||
|
vector_combine(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ArrayType *statearray1 = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
|
ArrayType *statearray2 = PG_GETARG_ARRAYTYPE_P(1);
|
||||||
|
float8 *statevalues1;
|
||||||
|
float8 *statevalues2;
|
||||||
|
float8 n;
|
||||||
|
float8 n1;
|
||||||
|
float8 n2;
|
||||||
|
int16 dim;
|
||||||
|
Datum *statedatums;
|
||||||
|
ArrayType *result;
|
||||||
|
|
||||||
|
/* Check arrays before using */
|
||||||
|
statevalues1 = CheckStateArray(statearray1, "vector_combine");
|
||||||
|
statevalues2 = CheckStateArray(statearray2, "vector_combine");
|
||||||
|
|
||||||
|
n1 = statevalues1[0];
|
||||||
|
n2 = statevalues2[0];
|
||||||
|
|
||||||
|
if (n1 == 0.0)
|
||||||
|
{
|
||||||
|
n = n2;
|
||||||
|
dim = STATE_DIMS(statearray2);
|
||||||
|
statedatums = CreateStateDatums(dim);
|
||||||
|
for (int i = 1; i <= dim; i++)
|
||||||
|
statedatums[i] = Float8GetDatumFast(statevalues2[i]);
|
||||||
|
}
|
||||||
|
else if (n2 == 0.0)
|
||||||
|
{
|
||||||
|
n = n1;
|
||||||
|
dim = STATE_DIMS(statearray1);
|
||||||
|
statedatums = CreateStateDatums(dim);
|
||||||
|
for (int i = 1; i <= dim; i++)
|
||||||
|
statedatums[i] = Float8GetDatumFast(statevalues1[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
n = n1 + n2;
|
||||||
|
dim = STATE_DIMS(statearray1);
|
||||||
|
CheckExpectedDim(dim, STATE_DIMS(statearray2));
|
||||||
|
statedatums = CreateStateDatums(dim);
|
||||||
|
for (int i = 1; i <= dim; i++)
|
||||||
|
{
|
||||||
|
double v = statevalues1[i] + statevalues2[i];
|
||||||
|
|
||||||
|
if (isinf(v))
|
||||||
|
float_overflow_error();
|
||||||
|
|
||||||
|
statedatums[i] = Float8GetDatumFast(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statedatums[0] = Float8GetDatumFast(n);
|
||||||
|
|
||||||
|
result = construct_array(statedatums, dim + 1,
|
||||||
|
FLOAT8OID,
|
||||||
|
sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE);
|
||||||
|
|
||||||
|
pfree(statedatums);
|
||||||
|
|
||||||
|
PG_RETURN_ARRAYTYPE_P(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Average vectors
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_avg);
|
||||||
|
Datum
|
||||||
|
vector_avg(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ArrayType *statearray = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
|
float8 *statevalues;
|
||||||
|
float8 n;
|
||||||
|
uint16 dim;
|
||||||
|
Vector *result;
|
||||||
|
float v;
|
||||||
|
|
||||||
|
/* Check array before using */
|
||||||
|
statevalues = CheckStateArray(statearray, "vector_avg");
|
||||||
|
n = statevalues[0];
|
||||||
|
|
||||||
|
/* SQL defines AVG of no values to be NULL */
|
||||||
|
if (n == 0.0)
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
|
||||||
|
/* Create vector */
|
||||||
|
dim = STATE_DIMS(statearray);
|
||||||
|
result = InitVector(dim);
|
||||||
|
for (int i = 0; i < dim; i++)
|
||||||
|
{
|
||||||
|
v = statevalues[i + 1] / n;
|
||||||
|
CheckElement(v);
|
||||||
|
result->x[i] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate a random vector
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(random_vector);
|
||||||
|
Datum
|
||||||
|
random_vector(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
int32 dim = PG_GETARG_INT32(0);
|
||||||
|
Vector *result;
|
||||||
|
|
||||||
|
CheckDim(dim);
|
||||||
|
|
||||||
|
result = InitVector(dim);
|
||||||
|
for (int i = 0; i < dim; i++)
|
||||||
|
result->x[i] = RandomDouble();
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(result);
|
||||||
|
}
|
||||||
|
|||||||
16
src/vector.h
16
src/vector.h
@@ -3,13 +3,27 @@
|
|||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#define VECTOR_MAX_DIM 1024
|
#include "port.h" /* for strtof() and random() */
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 150000
|
||||||
|
#include "common/pg_prng.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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))
|
||||||
#define DatumGetVector(x) ((Vector *) PG_DETOAST_DATUM(x))
|
#define DatumGetVector(x) ((Vector *) PG_DETOAST_DATUM(x))
|
||||||
#define PG_GETARG_VECTOR_P(x) DatumGetVector(PG_GETARG_DATUM(x))
|
#define PG_GETARG_VECTOR_P(x) DatumGetVector(PG_GETARG_DATUM(x))
|
||||||
#define PG_RETURN_VECTOR_P(x) PG_RETURN_POINTER(x)
|
#define PG_RETURN_VECTOR_P(x) PG_RETURN_POINTER(x)
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 150000
|
||||||
|
#define RandomDouble() pg_prng_double(&pg_global_prng_state)
|
||||||
|
#define RandomInt() pg_prng_uint32(&pg_global_prng_state)
|
||||||
|
#else
|
||||||
|
#define RandomDouble() (((double) random()) / MAX_RANDOM_VALUE)
|
||||||
|
#define RandomInt() random()
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct Vector
|
typedef struct Vector
|
||||||
{
|
{
|
||||||
int32 vl_len_; /* varlena header (do not touch directly!) */
|
int32 vl_len_; /* varlena header (do not touch directly!) */
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3));
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SELECT ARRAY[1,2,3]::vector;
|
SELECT ARRAY[1,2,3]::vector;
|
||||||
array
|
array
|
||||||
---------
|
---------
|
||||||
@@ -40,8 +38,8 @@ SELECT '[1,2,3]'::vector::real[];
|
|||||||
{1,2,3}
|
{1,2,3}
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT array_agg(n)::vector FROM generate_series(1, 1025) n;
|
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
|
||||||
ERROR: vector cannot have more than 1024 dimensions
|
ERROR: vector cannot have more than 16000 dimensions
|
||||||
-- ensure no error
|
-- ensure no error
|
||||||
SELECT ARRAY[1,2,3] = ARRAY[1,2,3];
|
SELECT ARRAY[1,2,3] = ARRAY[1,2,3];
|
||||||
?column?
|
?column?
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3));
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
CREATE TABLE t2 (val vector(3));
|
CREATE TABLE t2 (val vector(3));
|
||||||
\copy t TO '/tmp/data.bin' WITH (FORMAT binary)
|
\copy t TO 'results/data.bin' WITH (FORMAT binary)
|
||||||
\copy t2 FROM '/tmp/data.bin' WITH (FORMAT binary)
|
\copy t2 FROM 'results/data.bin' WITH (FORMAT binary)
|
||||||
SELECT * FROM t2 ORDER BY val;
|
SELECT * FROM t2 ORDER BY val;
|
||||||
val
|
val
|
||||||
---------
|
---------
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SELECT '[1,2,3]'::vector + '[4,5,6]';
|
SELECT '[1,2,3]'::vector + '[4,5,6]';
|
||||||
?column?
|
?column?
|
||||||
----------
|
----------
|
||||||
@@ -54,3 +52,23 @@ SELECT cosine_distance('[1,2]', '[0,0]');
|
|||||||
|
|
||||||
SELECT cosine_distance('[1,2]', '[3]');
|
SELECT cosine_distance('[1,2]', '[3]');
|
||||||
ERROR: different vector dimensions 2 and 1
|
ERROR: different vector dimensions 2 and 1
|
||||||
|
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::vector, '[3,5,7]']) v;
|
||||||
|
avg
|
||||||
|
-----------
|
||||||
|
[2,3.5,5]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::vector, '[3,5,7]', NULL]) v;
|
||||||
|
avg
|
||||||
|
-----------
|
||||||
|
[2,3.5,5]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT avg(v) FROM unnest(ARRAY[]::vector[]) v;
|
||||||
|
avg
|
||||||
|
-----
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT avg(v) FROM unnest(ARRAY['[1,2]'::vector, '[3]']) v;
|
||||||
|
ERROR: expected 2 dimensions, not 1
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SELECT '[1,2,3]'::vector;
|
SELECT '[1,2,3]'::vector;
|
||||||
vector
|
vector
|
||||||
---------
|
---------
|
||||||
@@ -12,6 +10,12 @@ SELECT '[-1,2,3]'::vector;
|
|||||||
[-1,2,3]
|
[-1,2,3]
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[1.23456]'::vector;
|
||||||
|
vector
|
||||||
|
-----------
|
||||||
|
[1.23456]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT '[hello,1]'::vector;
|
SELECT '[hello,1]'::vector;
|
||||||
ERROR: invalid input syntax for type vector: "hello"
|
ERROR: invalid input syntax for type vector: "hello"
|
||||||
LINE 1: SELECT '[hello,1]'::vector;
|
LINE 1: SELECT '[hello,1]'::vector;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3));
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3));
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3));
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3));
|
||||||
CREATE INDEX ON t USING ivfflat (val) WITH (lists = 0);
|
CREATE INDEX ON t USING ivfflat (val) WITH (lists = 0);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
CREATE UNLOGGED TABLE t (val vector(3));
|
CREATE UNLOGGED TABLE t (val vector(3));
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
|
|
||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3));
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
|
|
||||||
SELECT ARRAY[1,2,3]::vector;
|
SELECT ARRAY[1,2,3]::vector;
|
||||||
SELECT ARRAY[1.0,2.0,3.0]::vector;
|
SELECT ARRAY[1.0,2.0,3.0]::vector;
|
||||||
SELECT ARRAY[1,2,3]::float4[]::vector;
|
SELECT ARRAY[1,2,3]::float4[]::vector;
|
||||||
@@ -11,7 +8,7 @@ SELECT '{Infinity}'::real[]::vector;
|
|||||||
SELECT '{-Infinity}'::real[]::vector;
|
SELECT '{-Infinity}'::real[]::vector;
|
||||||
SELECT '{}'::real[]::vector;
|
SELECT '{}'::real[]::vector;
|
||||||
SELECT '[1,2,3]'::vector::real[];
|
SELECT '[1,2,3]'::vector::real[];
|
||||||
SELECT array_agg(n)::vector FROM generate_series(1, 1025) n;
|
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
|
||||||
|
|
||||||
-- ensure no error
|
-- ensure no error
|
||||||
SELECT ARRAY[1,2,3] = ARRAY[1,2,3];
|
SELECT ARRAY[1,2,3] = ARRAY[1,2,3];
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
|
|
||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3));
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
||||||
|
|
||||||
CREATE TABLE t2 (val vector(3));
|
CREATE TABLE t2 (val vector(3));
|
||||||
|
|
||||||
\copy t TO '/tmp/data.bin' WITH (FORMAT binary)
|
\copy t TO 'results/data.bin' WITH (FORMAT binary)
|
||||||
\copy t2 FROM '/tmp/data.bin' WITH (FORMAT binary)
|
\copy t2 FROM 'results/data.bin' WITH (FORMAT binary)
|
||||||
|
|
||||||
SELECT * FROM t2 ORDER BY val;
|
SELECT * FROM t2 ORDER BY val;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
|
|
||||||
SELECT '[1,2,3]'::vector + '[4,5,6]';
|
SELECT '[1,2,3]'::vector + '[4,5,6]';
|
||||||
SELECT '[1,2,3]'::vector - '[4,5,6]';
|
SELECT '[1,2,3]'::vector - '[4,5,6]';
|
||||||
|
|
||||||
@@ -16,3 +13,8 @@ SELECT inner_product('[1,2]', '[3]');
|
|||||||
SELECT round(cosine_distance('[1,2]', '[2,4]')::numeric, 5);
|
SELECT round(cosine_distance('[1,2]', '[2,4]')::numeric, 5);
|
||||||
SELECT cosine_distance('[1,2]', '[0,0]');
|
SELECT cosine_distance('[1,2]', '[0,0]');
|
||||||
SELECT cosine_distance('[1,2]', '[3]');
|
SELECT cosine_distance('[1,2]', '[3]');
|
||||||
|
|
||||||
|
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]', NULL]) v;
|
||||||
|
SELECT avg(v) FROM unnest(ARRAY[]::vector[]) v;
|
||||||
|
SELECT avg(v) FROM unnest(ARRAY['[1,2]'::vector, '[3]']) v;
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
|
|
||||||
SELECT '[1,2,3]'::vector;
|
SELECT '[1,2,3]'::vector;
|
||||||
SELECT '[-1,2,3]'::vector;
|
SELECT '[-1,2,3]'::vector;
|
||||||
|
SELECT '[1.23456]'::vector;
|
||||||
SELECT '[hello,1]'::vector;
|
SELECT '[hello,1]'::vector;
|
||||||
SELECT '[NaN,1]'::vector;
|
SELECT '[NaN,1]'::vector;
|
||||||
SELECT '[Infinity,1]'::vector;
|
SELECT '[Infinity,1]'::vector;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
|
|
||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3));
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
|
|
||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3));
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
|
|
||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3));
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
|
|
||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3));
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
SET client_min_messages = warning;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS vector;
|
|
||||||
SET enable_seqscan = off;
|
SET enable_seqscan = off;
|
||||||
|
|
||||||
CREATE UNLOGGED TABLE t (val vector(3));
|
CREATE UNLOGGED TABLE t (val vector(3));
|
||||||
|
|||||||
@@ -20,15 +20,8 @@ sub test_index_replay
|
|||||||
# Wait for replica to catch up
|
# Wait for replica to catch up
|
||||||
my $applname = $node_replica->name;
|
my $applname = $node_replica->name;
|
||||||
|
|
||||||
my $caughtup_query;
|
|
||||||
my $server_version_num = $node_primary->safe_psql("postgres", "SHOW server_version_num");
|
my $server_version_num = $node_primary->safe_psql("postgres", "SHOW server_version_num");
|
||||||
if ($server_version_num >= 100000) {
|
my $caughtup_query = "SELECT pg_current_wal_lsn() <= replay_lsn FROM pg_stat_replication WHERE application_name = '$applname';";
|
||||||
$caughtup_query = "SELECT pg_current_wal_lsn() <= replay_lsn FROM pg_stat_replication WHERE application_name = '$applname';";
|
|
||||||
} else {
|
|
||||||
# TODO figure out why replay location doesn't work
|
|
||||||
$caughtup_query = "SELECT pg_current_xlog_location() <= write_location FROM pg_stat_replication WHERE application_name = '$applname';";
|
|
||||||
}
|
|
||||||
|
|
||||||
$node_primary->poll_query_until('postgres', $caughtup_query)
|
$node_primary->poll_query_until('postgres', $caughtup_query)
|
||||||
or die "Timed out while waiting for replica 1 to catch up";
|
or die "Timed out while waiting for replica 1 to catch up";
|
||||||
|
|
||||||
@@ -51,11 +44,6 @@ sub test_index_replay
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Use ARRAY[random(), random(), random(), ...] over
|
|
||||||
# SELECT array_agg(random()) FROM generate_series(1, $dim)
|
|
||||||
# to generate different values for each row
|
|
||||||
my $array_sql = join(",", ('random()') x $dim);
|
|
||||||
|
|
||||||
# Initialize primary node
|
# Initialize primary node
|
||||||
$node_primary = get_new_node('primary');
|
$node_primary = get_new_node('primary');
|
||||||
$node_primary->init(allows_streaming => 1);
|
$node_primary->init(allows_streaming => 1);
|
||||||
@@ -63,6 +51,9 @@ if ($dim > 32) {
|
|||||||
# TODO use wal_keep_segments for Postgres < 13
|
# TODO use wal_keep_segments for Postgres < 13
|
||||||
$node_primary->append_conf('postgresql.conf', qq(wal_keep_size = 1GB));
|
$node_primary->append_conf('postgresql.conf', qq(wal_keep_size = 1GB));
|
||||||
}
|
}
|
||||||
|
if ($dim > 1500) {
|
||||||
|
$node_primary->append_conf('postgresql.conf', qq(maintenance_work_mem = 128MB));
|
||||||
|
}
|
||||||
$node_primary->start;
|
$node_primary->start;
|
||||||
my $backup_name = 'my_backup';
|
my $backup_name = 'my_backup';
|
||||||
|
|
||||||
@@ -79,7 +70,7 @@ $node_replica->start;
|
|||||||
$node_primary->safe_psql("postgres", "CREATE EXTENSION vector;");
|
$node_primary->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
$node_primary->safe_psql("postgres", "CREATE TABLE tst (i int4, v vector($dim));");
|
$node_primary->safe_psql("postgres", "CREATE TABLE tst (i int4, v vector($dim));");
|
||||||
$node_primary->safe_psql("postgres",
|
$node_primary->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT i % 10, ARRAY[$array_sql] FROM generate_series(1, 100000) i;"
|
"INSERT INTO tst SELECT i % 10, random_vector($dim) FROM generate_series(1, 100000) i;"
|
||||||
);
|
);
|
||||||
$node_primary->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
$node_primary->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
||||||
|
|
||||||
@@ -95,7 +86,7 @@ for my $i (1 .. 10)
|
|||||||
test_index_replay("vacuum $i");
|
test_index_replay("vacuum $i");
|
||||||
my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
|
my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
|
||||||
$node_primary->safe_psql("postgres",
|
$node_primary->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT i % 10, ARRAY[$array_sql] FROM generate_series($start, $end) i;"
|
"INSERT INTO tst SELECT i % 10, random_vector($dim) FROM generate_series($start, $end) i;"
|
||||||
);
|
);
|
||||||
test_index_replay("insert $i");
|
test_index_replay("insert $i");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,15 @@ use PostgresNode;
|
|||||||
use TestLib;
|
use TestLib;
|
||||||
use Test::More tests => 1;
|
use Test::More tests => 1;
|
||||||
|
|
||||||
|
my $dim = 3;
|
||||||
|
|
||||||
|
my @r = ();
|
||||||
|
for (1 .. $dim) {
|
||||||
|
my $v = int(rand(1000)) + 1;
|
||||||
|
push(@r, "i % $v");
|
||||||
|
}
|
||||||
|
my $array_sql = join(", ", @r);
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = get_new_node('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
@@ -11,9 +20,9 @@ $node->start;
|
|||||||
|
|
||||||
# Create table and index
|
# Create table and index
|
||||||
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
$node->safe_psql("postgres", "CREATE TABLE tst (i int4, v vector(3));");
|
$node->safe_psql("postgres", "CREATE TABLE tst (i int4, v vector($dim));");
|
||||||
$node->safe_psql("postgres",
|
$node->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT i % 10, ARRAY[i % 1000, i % 333, i % 55] FROM generate_series(1, 100000) i;"
|
"INSERT INTO tst SELECT i % 10, ARRAY[$array_sql] FROM generate_series(1, 100000) i;"
|
||||||
);
|
);
|
||||||
$node->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
$node->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
||||||
|
|
||||||
@@ -24,7 +33,7 @@ my $size = $node->safe_psql("postgres", "SELECT pg_total_relation_size('tst_v_id
|
|||||||
$node->safe_psql("postgres", "DELETE FROM tst;");
|
$node->safe_psql("postgres", "DELETE FROM tst;");
|
||||||
$node->safe_psql("postgres", "VACUUM tst;");
|
$node->safe_psql("postgres", "VACUUM tst;");
|
||||||
$node->safe_psql("postgres",
|
$node->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT i % 10, ARRAY[i % 1000, i % 333, i % 55] FROM generate_series(1, 100000) i;"
|
"INSERT INTO tst SELECT i % 10, ARRAY[$array_sql] FROM generate_series(1, 100000) i;"
|
||||||
);
|
);
|
||||||
|
|
||||||
# Check size
|
# Check size
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ $node->start;
|
|||||||
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
$node->safe_psql("postgres", "CREATE TABLE tst (i int4, v vector(3));");
|
$node->safe_psql("postgres", "CREATE TABLE tst (i int4, v vector(3));");
|
||||||
$node->safe_psql("postgres",
|
$node->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT i, ARRAY[random(), random(), random()] FROM generate_series(1, 100000) i;"
|
"INSERT INTO tst SELECT i, random_vector(3) FROM generate_series(1, 100000) i;"
|
||||||
);
|
);
|
||||||
|
|
||||||
# Generate queries
|
# Generate queries
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ $node->start;
|
|||||||
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
$node->safe_psql("postgres", "CREATE TABLE tst (i int4 primary key, v vector(3));");
|
$node->safe_psql("postgres", "CREATE TABLE tst (i int4 primary key, v vector(3));");
|
||||||
$node->safe_psql("postgres",
|
$node->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT i, ARRAY[random(), random(), random()] FROM generate_series(1, 100000) i;"
|
"INSERT INTO tst SELECT i, random_vector(3) FROM generate_series(1, 100000) i;"
|
||||||
);
|
);
|
||||||
|
|
||||||
# Check each index type
|
# Check each index type
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ $node->start;
|
|||||||
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
$node->safe_psql("postgres", "CREATE TABLE tst (v vector(3));");
|
$node->safe_psql("postgres", "CREATE TABLE tst (v vector(3));");
|
||||||
$node->safe_psql("postgres",
|
$node->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT ARRAY[random(), random(), random()] FROM generate_series(1, 100000) i;"
|
"INSERT INTO tst SELECT random_vector(3) FROM generate_series(1, 100000) i;"
|
||||||
);
|
);
|
||||||
|
|
||||||
$node->safe_psql("postgres", "CREATE INDEX lists50 ON tst USING ivfflat (v) WITH (lists = 50);");
|
$node->safe_psql("postgres", "CREATE INDEX lists50 ON tst USING ivfflat (v) WITH (lists = 50);");
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ use Test::More tests => 5;
|
|||||||
|
|
||||||
my $dim = 768;
|
my $dim = 768;
|
||||||
|
|
||||||
my $array_sql = join(",", ('random()') x $dim);
|
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = get_new_node('node');
|
my $node = get_new_node('node');
|
||||||
$node->init;
|
$node->init;
|
||||||
@@ -17,7 +15,7 @@ $node->start;
|
|||||||
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
$node->safe_psql("postgres", "CREATE TABLE tst (v vector($dim));");
|
$node->safe_psql("postgres", "CREATE TABLE tst (v vector($dim));");
|
||||||
$node->safe_psql("postgres",
|
$node->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT ARRAY[$array_sql] FROM generate_series(1, 10000) i;"
|
"INSERT INTO tst SELECT random_vector($dim) FROM generate_series(1, 10000) i;"
|
||||||
);
|
);
|
||||||
$node->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
$node->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
||||||
|
|
||||||
@@ -28,7 +26,7 @@ $node->pgbench(
|
|||||||
[qr{^$}],
|
[qr{^$}],
|
||||||
"concurrent INSERTs",
|
"concurrent INSERTs",
|
||||||
{
|
{
|
||||||
"007_inserts" => "INSERT INTO tst SELECT ARRAY[$array_sql] FROM generate_series(1, 10) i;"
|
"007_inserts" => "INSERT INTO tst SELECT random_vector($dim) FROM generate_series(1, 10) i;"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
35
test/t/008_avg.pl
Normal file
35
test/t/008_avg.pl
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use PostgresNode;
|
||||||
|
use TestLib;
|
||||||
|
use Test::More tests => 5;
|
||||||
|
|
||||||
|
# Initialize node
|
||||||
|
my $node = get_new_node('node');
|
||||||
|
$node->init;
|
||||||
|
$node->start;
|
||||||
|
|
||||||
|
# Create table
|
||||||
|
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
|
$node->safe_psql("postgres", "CREATE TABLE tst (r1 real, r2 real, r3 real, v vector(3));");
|
||||||
|
$node->safe_psql("postgres", qq(
|
||||||
|
INSERT INTO tst SELECT r1, r2, r3, ARRAY[r1, r2, r3] FROM (
|
||||||
|
SELECT random() + 1.01 AS r1, random() + 2.01 AS r2, random() + 3.01 AS r3 FROM generate_series(1, 1000000) t
|
||||||
|
) i;
|
||||||
|
));
|
||||||
|
|
||||||
|
# Test avg
|
||||||
|
my $avg = $node->safe_psql("postgres", "SELECT AVG(v) FROM tst;");
|
||||||
|
like($avg, qr/\[1\.5/);
|
||||||
|
like($avg, qr/,2\.5/);
|
||||||
|
like($avg, qr/,3\.5/);
|
||||||
|
|
||||||
|
# Test matches real
|
||||||
|
my $r1 = $node->safe_psql("postgres", "SELECT AVG(r1)::float4 FROM tst;");
|
||||||
|
my $r2 = $node->safe_psql("postgres", "SELECT AVG(r2)::float4 FROM tst;");
|
||||||
|
my $r3 = $node->safe_psql("postgres", "SELECT AVG(r3)::float4 FROM tst;");
|
||||||
|
is($avg, "[$r1,$r2,$r3]");
|
||||||
|
|
||||||
|
# Test explain
|
||||||
|
my $explain = $node->safe_psql("postgres", "EXPLAIN SELECT AVG(v) FROM tst;");
|
||||||
|
like($explain, qr/Partial Aggregate/);
|
||||||
32
test/t/009_storage.pl
Normal file
32
test/t/009_storage.pl
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use PostgresNode;
|
||||||
|
use TestLib;
|
||||||
|
use Test::More tests => 1;
|
||||||
|
|
||||||
|
my $dim = 1024;
|
||||||
|
|
||||||
|
# Initialize node
|
||||||
|
my $node = get_new_node('node');
|
||||||
|
$node->init;
|
||||||
|
$node->start;
|
||||||
|
|
||||||
|
# Create table
|
||||||
|
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
|
$node->safe_psql("postgres", "CREATE TABLE tst (v1 vector(1024), v2 vector(1024), v3 vector(1024));");
|
||||||
|
|
||||||
|
# Test insert succeeds
|
||||||
|
$node->safe_psql("postgres",
|
||||||
|
"INSERT INTO tst SELECT random_vector($dim), random_vector($dim), random_vector($dim)"
|
||||||
|
);
|
||||||
|
|
||||||
|
# Change storage to PLAIN
|
||||||
|
$node->safe_psql("postgres", "ALTER TABLE tst ALTER COLUMN v1 SET STORAGE PLAIN");
|
||||||
|
$node->safe_psql("postgres", "ALTER TABLE tst ALTER COLUMN v2 SET STORAGE PLAIN");
|
||||||
|
$node->safe_psql("postgres", "ALTER TABLE tst ALTER COLUMN v3 SET STORAGE PLAIN");
|
||||||
|
|
||||||
|
# Test insert fails
|
||||||
|
my ($ret, $stdout, $stderr) = $node->psql("postgres",
|
||||||
|
"INSERT INTO tst SELECT random_vector($dim), random_vector($dim), random_vector($dim)"
|
||||||
|
);
|
||||||
|
like($stderr, qr/row is too big/);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
comment = 'vector data type and ivfflat access method'
|
comment = 'vector data type and ivfflat access method'
|
||||||
default_version = '0.3.2'
|
default_version = '0.4.0'
|
||||||
module_pathname = '$libdir/vector'
|
module_pathname = '$libdir/vector'
|
||||||
relocatable = true
|
relocatable = true
|
||||||
|
|||||||
Reference in New Issue
Block a user