Compare commits

...

96 Commits

Author SHA1 Message Date
Andrew Kane
e14fa82626 Version bump to 0.2.3 [skip ci] 2022-01-30 19:59:47 -08:00
Andrew Kane
2ae4de83fe Improved error message for spherical k-means with one dimension 2022-01-30 11:57:52 -08:00
Andrew Kane
5d2c4883dc Fixed undeclared error 2022-01-29 23:28:23 -08:00
Andrew Kane
fc1de9806a Added tuples_done and tuples_total 2022-01-29 23:15:46 -08:00
Andrew Kane
e375dd33f3 Added FAQ to readme [skip ci] 2022-01-29 22:17:38 -08:00
Andrew Kane
b643ef637a Added indexing progress to readme [skip ci] 2022-01-29 20:25:06 -08:00
Andrew Kane
0ee5bfda65 Added comment [skip ci] 2022-01-29 20:12:00 -08:00
Andrew Kane
c5bec2a534 Improved interrupt handling during index creation 2022-01-29 19:50:03 -08:00
Andrew Kane
40f65dc8ba Updated changelog [skip ci] 2022-01-29 19:23:44 -08:00
Andrew Kane
9ec174137f Added build phase progress for Postgres 12+ 2022-01-29 19:19:16 -08:00
Andrew Kane
423e478a85 Fixed Docker link [skip ci] 2022-01-29 00:46:48 -08:00
Andrew Kane
8b301197fe Updated Homebrew tap [skip ci] 2022-01-24 16:55:28 -08:00
Andrew Kane
c02feaa9ad Updated CI 2022-01-24 16:39:01 -08:00
Andrew Kane
729374a9b8 Updated urls [skip ci] 2022-01-24 16:21:51 -08:00
Andrew Kane
a8245d6cda Updated urls [skip ci] 2022-01-24 16:16:54 -08:00
Andrew Kane
5220a6ec7e Updated DigitalOcean link [skip ci] 2022-01-23 19:10:13 -08:00
Andrew Kane
ddd873f369 Version bump to 0.2.2 [skip ci] 2022-01-15 14:23:32 -05:00
Andrew Kane
8735c4782a Fixed compilation on Mac ARM - #22 2022-01-14 01:18:33 -05:00
Andrew Kane
17ec3f3852 Moved test 2022-01-02 19:24:50 -05:00
Andrew Kane
8df12a62e7 Updated changelog [skip ci] 2022-01-02 19:20:00 -05:00
Andrew Kane
647c9002be Version bump to 0.2.1 [skip ci] 2022-01-02 19:11:37 -05:00
Andrew Kane
3e6345f02a Moved function 2022-01-02 19:09:37 -05:00
Andrew Kane
2b3484ddcd Fixed CI 2022-01-02 19:03:15 -05:00
Andrew Kane
1a6debf281 Fixed operator is not unique error - fixes #20 2022-01-02 18:47:03 -05:00
Andrew Kane
bff4d7ea68 Updated link [skip ci] 2021-10-13 14:29:09 -07:00
Andrew Kane
d5b979e4fa Added guidance on lists [skip ci] 2021-10-13 14:27:01 -07:00
Andrew Kane
d52426b3fa Improved examples [skip ci] 2021-10-13 14:06:55 -07:00
Andrew Kane
b125b2debb Added section on performance [skip ci] 2021-10-13 13:56:03 -07:00
Andrew Kane
3d7186a17c Added Docker to Makefile [skip ci] 2021-10-13 13:53:29 -07:00
Andrew Kane
58ba2139ce Added instructions for reindexing - #17 [skip ci] 2021-10-13 13:48:54 -07:00
Andrew Kane
fd93c9db86 Version bump to 0.2.0 [skip ci] 2021-10-03 20:32:45 -07:00
Andrew Kane
8dde26920c Added support for Postgres 14 2021-09-30 16:28:25 -07:00
Andrew Kane
9153a545a7 Added link to pgvector-cpp [skip ci] 2021-09-10 15:22:44 -07:00
Andrew Kane
9c20f04a11 Version bump to 0.1.8 [skip ci] 2021-09-07 14:10:58 -07:00
Andrew Kane
7c12d100ce Fixed build for Postgres < 13 2021-06-20 22:06:14 -07:00
Andrew Kane
d97d9a6561 Added cast for vector to real[] 2021-06-20 21:53:06 -07:00
Andrew Kane
f15ea301c5 Added link to Node library [skip ci] 2021-06-18 15:01:32 -07:00
Andrew Kane
cfa23f2c7a Added note on partitioning [skip ci] 2021-06-17 14:20:43 -07:00
Andrew Kane
b5b7035edc Restored section on partial indexes [skip ci] 2021-06-17 14:05:48 -07:00
Andrew Kane
478d62cdbd Added link to Go library [skip ci] 2021-06-16 17:33:49 -07:00
Andrew Kane
c7df9ef3a1 Updated changelog [skip ci] 2021-06-13 12:58:10 -07:00
Andrew Kane
ea1b094d8e Version bump to 0.1.7 [skip ci] 2021-06-13 12:52:55 -07:00
Andrew Kane
63d2965e8e Added link to pgvector-python [skip ci] 2021-06-12 06:49:44 -07:00
Andrew Kane
e5e7a6ec15 Fixed message [skip ci] 2021-06-11 03:36:06 -07:00
Andrew Kane
ede572d80b Fixed identation [skip ci] 2021-06-11 03:35:17 -07:00
Andrew Kane
154e4334fb Added cast for numeric[] 2021-06-11 03:32:47 -07:00
Andrew Kane
77d54333f6 Updated link [skip ci] 2021-06-09 22:01:38 -07:00
Andrew Kane
c95732340a Version bump to 0.1.6 [skip ci] 2021-06-09 20:48:50 -07:00
Andrew Kane
2af9bdefbb Added link to pgvector.rs [skip ci] 2021-06-09 19:42:40 -07:00
Andrew Kane
bd36231871 Less space [skip ci] 2021-06-09 04:03:06 -07:00
Andrew Kane
523788d964 Improved code 2021-06-09 03:59:00 -07:00
Andrew Kane
1eed57310a Use DBL_MAX for cost 2021-06-09 03:31:41 -07:00
Andrew Kane
57e6a01214 Fixed segmentation fault with COUNT - fixes #9 2021-06-09 03:13:22 -07:00
Andrew Kane
b713cbc3b6 Added instructions for Azure [skip ci] 2021-06-08 23:36:47 -07:00
Andrew Kane
ebeac156c0 Require unused to be 0 2021-05-26 13:40:38 -07:00
Andrew Kane
60bc93f938 Version bump to 0.1.5 [skip ci] 2021-05-25 17:31:02 -07:00
Andrew Kane
87a1b3365e Simplified logic 2021-05-24 18:58:11 -07:00
Andrew Kane
e9a762223b Moved line [skip ci] 2021-05-24 18:48:49 -07:00
Andrew Kane
db83f4faa8 Reduced memory usage during index build - #7 2021-05-24 18:40:34 -07:00
Andrew Kane
2cf71ec6bd Moved test [skip ci] 2021-05-15 00:29:52 -07:00
Andrew Kane
a7510430e3 Added test for max dimensions 2021-05-15 00:28:02 -07:00
Andrew Kane
ea8f06e8f4 Updated readme [skip ci] 2021-05-14 22:05:07 -07:00
Andrew Kane
4e0c1a137c Version bump to 0.1.4 [skip ci] 2021-05-09 14:21:29 -07:00
Andrew Kane
c758b7b188 Simplified upgrade statement [skip ci] 2021-05-09 14:19:05 -07:00
Andrew Kane
3dffc01e31 Improved comment [skip ci] 2021-05-06 22:40:04 -07:00
Andrew Kane
b8f3688e41 Fixed kmeans for inner product 2021-05-06 22:31:17 -07:00
Andrew Kane
67e317141b Fixed multiple definition error with GCC 10 - #4 2021-05-06 16:25:43 -07:00
Andrew Kane
de2ce0b1eb Added dockerignore [skip ci] 2021-05-06 15:30:02 -07:00
Andrew Kane
86c1c95ef4 Updated readme [skip ci] 2021-05-06 14:50:59 -07:00
Andrew Kane
38be81cf20 Version bump to 0.1.3 [skip ci] 2021-05-06 14:35:17 -07:00
Andrew Kane
62cc3398ed Added Dockerfile - #4 [skip ci] 2021-05-06 14:31:32 -07:00
Andrew Kane
af86392a34 Updated tag [skip ci] 2021-05-06 02:34:22 -07:00
Andrew Kane
a9067a8101 Updated tags [skip ci] 2021-05-06 02:33:09 -07:00
Andrew Kane
6deca154e6 Updated tag [skip ci] 2021-05-06 02:28:15 -07:00
Andrew Kane
6342439eb8 Removed version in meta [skip ci] 2021-05-06 02:24:47 -07:00
Andrew Kane
21967765a3 Updated meta [skip ci] 2021-05-06 02:24:09 -07:00
Andrew Kane
d31b7e3997 Updated changelog [skip ci] 2021-05-06 02:18:19 -07:00
Andrew Kane
efa85aaba1 Use recommended pattern 2021-05-06 02:17:30 -07:00
Andrew Kane
3c820b5a50 Updated gitignore [skip ci] 2021-05-06 02:08:18 -07:00
Andrew Kane
199c0e5e34 Renamed test [skip ci] 2021-05-06 02:07:04 -07:00
Andrew Kane
bcab0ad8ae Fixed version 2021-05-06 01:56:05 -07:00
Andrew Kane
09e95fc8f9 Improved Makefile [skip ci] 2021-05-06 01:41:54 -07:00
Andrew Kane
6119cced74 Improved Makefile [skip ci] 2021-05-06 01:40:32 -07:00
Andrew Kane
f8c07edba1 Updated readme [skip ci] 2021-05-05 15:16:45 -07:00
Andrew Kane
baabbaeff9 Moved sql and test files 2021-05-05 15:04:52 -07:00
Andrew Kane
f0993282fe Updated readme [skip ci] 2021-05-05 14:48:58 -07:00
Andrew Kane
01584d3df6 Added PGXN docs [skip ci] 2021-05-05 14:41:45 -07:00
Andrew Kane
6f50c4f5bd Added PGXN files [skip ci] 2021-05-05 14:35:34 -07:00
Andrew Kane
31a953f8db Updated readme [skip ci] 2021-05-03 17:05:02 -07:00
Andrew Kane
7b3d0f2595 Updated readme [skip ci] 2021-05-03 16:53:25 -07:00
Andrew Kane
099b9e9027 Added partial indexes section [skip ci] 2021-05-03 16:44:36 -07:00
Andrew Kane
e1f3caba49 Added OPTFLAGS 2021-05-02 14:49:34 -07:00
Andrew Kane
3f0d2bbd2f Updated readme [skip ci] 2021-04-30 18:45:25 -07:00
Andrew Kane
c989b85f75 Updated readme [skip ci] 2021-04-30 18:40:18 -07:00
Andrew Kane
a9bbee4198 Added section on hosted Postgres [skip ci] 2021-04-30 18:01:07 -07:00
Andrew Kane
fb274f1197 Added Homebrew docs [skip ci] 2021-04-30 16:30:55 -07:00
49 changed files with 800 additions and 278 deletions

8
.dockerignore Normal file
View File

@@ -0,0 +1,8 @@
/.git/
/dist/
/results/
/tmp_check/
/sql/vector--?.?.?.sql
regression.*
*.o
*.so

View File

@@ -2,16 +2,15 @@ name: build
on: [push, pull_request]
jobs:
build:
if: "!contains(github.event.head_commit.message, '[skip ci]')"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
postgres: [13, 12, 11, 10, 9.6]
postgres: [14, 13, 12, 11, 10, 9.6]
include:
- os: macos-latest
postgres: 13
postgres: 14
steps:
- uses: actions/checkout@v2
- uses: ankane/setup-postgres@v1
@@ -34,6 +33,6 @@ jobs:
- if: ${{ startsWith(matrix.os, 'macos') }}
run: |
brew install cpanm && cpanm IPC::Run
wget -q https://github.com/postgres/postgres/archive/refs/tags/REL_13_2.tar.gz
tar xf REL_13_2.tar.gz
make prove_installcheck PROVE=prove PERL5LIB=postgres-REL_13_2/src/test/perl
wget -q https://github.com/postgres/postgres/archive/refs/tags/REL_14_1.tar.gz
tar xf REL_14_1.tar.gz
make prove_installcheck PROVE=prove PERL5LIB=postgres-REL_14_1/src/test/perl

6
.gitignore vendored
View File

@@ -1,5 +1,7 @@
results
tmp_check
/dist/
/results/
/tmp_check/
/sql/vector--?.?.?.sql
regression.*
*.o
*.so

View File

@@ -1,3 +1,46 @@
## 0.2.3 (2022-01-30)
- Added build progress for Postgres 12+
- Improved interrupt handling during index creation
## 0.2.2 (2022-01-15)
- Fixed compilation error on Mac ARM
## 0.2.1 (2022-01-02)
- Fixed `operator is not unique` error
## 0.2.0 (2021-10-03)
- Added support for Postgres 14
## 0.1.8 (2021-09-07)
- Added cast for `vector` to `real[]`
## 0.1.7 (2021-06-13)
- Added cast for `numeric[]` to `vector`
## 0.1.6 (2021-06-09)
- Fixed segmentation fault with `COUNT`
## 0.1.5 (2021-05-25)
- Reduced memory usage during index creation
## 0.1.4 (2021-05-09)
- Fixed kmeans for inner product
- Fixed multiple definition error with GCC 10
## 0.1.3 (2021-05-06)
- Added Dockerfile
- Fixed version
## 0.1.2 (2021-04-26)
- Vectorized distance calculations

16
Dockerfile Normal file
View File

@@ -0,0 +1,16 @@
FROM postgres:14
COPY . /tmp/pgvector
RUN apt-get update && \
apt-get install -y --no-install-recommends build-essential postgresql-server-dev-14 && \
cd /tmp/pgvector && \
make clean && \
make OPTFLAGS="" && \
make install && \
mkdir /usr/share/doc/pgvector && \
cp LICENSE README.md /usr/share/doc/pgvector && \
rm -r /tmp/pgvector && \
apt-get remove -y build-essential postgresql-server-dev-14 && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*

49
META.json Normal file
View File

@@ -0,0 +1,49 @@
{
"name": "vector",
"abstract": "Open-source vector similarity search for Postgres",
"description": "Supports L2 distance, inner product, and cosine distance",
"version": "0.2.3",
"maintainer": [
"Andrew Kane <andrew@ankane.org>"
],
"license": {
"PostgreSQL": "http://www.postgresql.org/about/licence"
},
"prereqs": {
"runtime": {
"requires": {
"PostgreSQL": "9.6.0"
}
}
},
"provides": {
"vector": {
"file": "sql/vector.sql",
"docfile": "README.md",
"version": "0.2.3",
"abstract": "Open-source vector similarity search for Postgres"
}
},
"resources": {
"homepage": "https://github.com/pgvector/pgvector",
"bugtracker": {
"web": "https://github.com/pgvector/pgvector/issues"
},
"repository": {
"url": "https://github.com/pgvector/pgvector.git",
"web": "https://github.com/pgvector/pgvector",
"type": "git"
}
},
"generated_by": "Andrew Kane",
"meta-spec": {
"version": "1.0.0",
"url": "http://pgxn.org/meta/spec.txt"
},
"tags": [
"vectors",
"datatype",
"nearest neighbor search",
"approximate nearest neighbors"
]
}

View File

@@ -1,16 +1,27 @@
EXTENSION = vector
DATA = vector--0.1.1.sql vector--0.1.0--0.1.1.sql
EXTVERSION = 0.2.3
MODULE_big = vector
DATA = $(wildcard sql/*--*.sql)
OBJS = src/ivfbuild.o src/ivfflat.o src/ivfinsert.o src/ivfkmeans.o src/ivfscan.o src/ivfutils.o src/ivfvacuum.o src/vector.o
TESTS = $(wildcard sql/*.sql)
TESTS = $(wildcard test/sql/*.sql)
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
REGRESS_OPTS = --inputdir=test
REGRESS = btree cast copy functions ivfflat_cosine ivfflat_ip ivfflat_l2 ivfflat_unlogged vector
OPTFLAGS = -march=native
# Mac ARM doesn't support -march=native
ifeq ($(shell uname -s), Darwin)
ifeq ($(shell uname -p), arm)
OPTFLAGS =
endif
endif
# For auto-vectorization:
# - GCC (needs -ftree-vectorize OR -O3) - https://gcc.gnu.org/projects/tree-ssa/vectorization.html
# - Clang (could use pragma instead) - https://llvm.org/docs/Vectorizers.html
PG_CFLAGS = -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math
PG_CFLAGS = $(OPTFLAGS) -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math
# Debug GCC auto-vectorization
# PG_CFLAGS += -fopt-info-vec
@@ -18,10 +29,28 @@ PG_CFLAGS = -march=native -ftree-vectorize -fassociative-math -fno-signed-zeros
# Debug Clang auto-vectorization
# PG_CFLAGS += -Rpass=loop-vectorize -Rpass-analysis=loop-vectorize
all: sql/$(EXTENSION)--$(EXTVERSION).sql
sql/$(EXTENSION)--$(EXTVERSION).sql: sql/$(EXTENSION).sql
cp $< $@
EXTRA_CLEAN = sql/$(EXTENSION)--$(EXTVERSION).sql
PG_CONFIG ?= pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
prove_installcheck:
rm -rf $(CURDIR)/tmp_check
cd $(srcdir) && TESTDIR='$(CURDIR)' PATH="$(bindir):$$PATH" PGPORT='6$(DEF_PGPORT)' PG_REGRESS='$(top_builddir)/src/test/regress/pg_regress' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) $(if $(PROVE_TESTS),$(PROVE_TESTS),t/*.pl)
cd $(srcdir) && TESTDIR='$(CURDIR)' PATH="$(bindir):$$PATH" PGPORT='6$(DEF_PGPORT)' PG_REGRESS='$(top_builddir)/src/test/regress/pg_regress' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) $(if $(PROVE_TESTS),$(PROVE_TESTS),test/t/*.pl)
.PHONY: dist
dist:
mkdir -p dist
git archive --format zip --prefix=$(EXTENSION)-$(EXTVERSION)/ --output dist/$(EXTENSION)-$(EXTVERSION).zip master
.PHONY: docker
docker:
docker build --pull --no-cache -t ankane/pgvector:latest .

161
README.md
View File

@@ -4,20 +4,20 @@ Open-source vector similarity search for Postgres
```sql
CREATE TABLE table (column vector(3));
CREATE INDEX ON table USING ivfflat (column);
CREATE INDEX ON table USING ivfflat (column vector_l2_ops);
SELECT * FROM table ORDER BY column <-> '[1,2,3]' LIMIT 5;
```
Supports L2 distance, inner product, and cosine distance
[![Build Status](https://github.com/ankane/pgvector/workflows/build/badge.svg?branch=master)](https://github.com/ankane/pgvector/actions)
[![Build Status](https://github.com/pgvector/pgvector/workflows/build/badge.svg?branch=master)](https://github.com/pgvector/pgvector/actions)
## Installation
Compile and install the extension (supports Postgres 9.6+)
```sh
git clone --branch v0.1.2 https://github.com/ankane/pgvector.git
git clone --branch v0.2.3 https://github.com/pgvector/pgvector.git
cd pgvector
make
make install # may need sudo
@@ -29,6 +29,8 @@ Then load it in databases where you want to use it
CREATE EXTENSION vector;
```
You can also install it with [Docker](#docker), [Homebrew](#homebrew), or [PGXN](#pgxn)
## Getting Started
Create a vector column with 3 dimensions (replace `table` and `column` with non-reserved names)
@@ -60,7 +62,7 @@ Speed up queries with an approximate index. Add an index for each distance funct
L2 distance
```sql
CREATE INDEX ON table USING ivfflat (column);
CREATE INDEX ON table USING ivfflat (column vector_l2_ops);
```
Inner product
@@ -75,16 +77,30 @@ Cosine distance
CREATE INDEX ON table USING ivfflat (column vector_cosine_ops);
```
Indexes should be created after the table has 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 data for optimal clustering. If the distribution of data changes significantly, you can reindex without downtime:
```sql
-- Postgres 12+
REINDEX INDEX CONCURRENTLY index_name;
-- Postgres < 12
CREATE INDEX CONCURRENTLY temp_name ON table USING ivfflat (column opclass);
DROP INDEX CONCURRENTLY index_name;
ALTER INDEX temp_name RENAME TO index_name;
```
Also, unlike typical indexes which only affect performance, you may see different results for queries after adding an approximate index.
### Index Options
Specify the number of inverted lists (100 by default)
```sql
CREATE INDEX ON table USING ivfflat (column) WITH (lists = 100);
CREATE INDEX ON table USING ivfflat (column opclass) WITH (lists = 100);
```
A [good place to start](https://github.com/facebookresearch/faiss/issues/112) is `4 * sqrt(rows)`
### Query Options
Specify the number of probes (1 by default)
@@ -104,6 +120,48 @@ SELECT ...
COMMIT;
```
### Indexing Progress [unreleased]
Check [indexing progress](https://www.postgresql.org/docs/current/progress-reporting.html#CREATE-INDEX-PROGRESS-REPORTING) with Postgres 12+
```sql
SELECT phase, tuples_done, tuples_total FROM pg_stat_progress_create_index;
```
The phases are:
1. `initializing`
2. `sampling table`
3. `performing k-means`
4. `sorting tuples`
5. `loading tuples`
Note: `tuples_done` and `tuples_total` are only populated during the `loading tuples` phase
### Partial Indexes
Consider [partial indexes](https://www.postgresql.org/docs/current/indexes-partial.html) for queries with a `WHERE` clause
```sql
CREATE INDEX ON table USING ivfflat (column opclass) WHERE (other_column = 123);
```
To index many different values of `other_column`, consider [partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html) on `other_column`.
## Performance
To speed up queries without an index, increase `max_parallel_workers_per_gather`.
```sql
SET max_parallel_workers_per_gather = 4;
```
To speed up queries with an index, increase the number of inverted lists (at the expense of recall).
```sql
CREATE INDEX ON table USING ivfflat (column opclass) WITH (lists = 1000);
```
## Reference
### Vector Type
@@ -134,16 +192,81 @@ vector_norm(vector) | Euclidean norm
Libraries that use pgvector:
- [pgvector-python](https://github.com/pgvector/pgvector-python) (Python)
- [Neighbor](https://github.com/ankane/neighbor) (Ruby)
- [pgvector-node](https://github.com/pgvector/pgvector-node) (Node.js)
- [pgvector-go](https://github.com/pgvector/pgvector-go) (Go)
- [pgvector-rust](https://github.com/pgvector/pgvector-rust) (Rust)
- [pgvector-cpp](https://github.com/pgvector/pgvector-cpp) (C++)
## Frequently Asked Questions
#### How many vectors can be stored in a single table?
A non-partitioned table has a limit of 32 TB by default in Postgres. A partitioned table can have thousands of partitions of that size.
#### Is replication supported?
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?
Two things you can try are:
1. use dimensionality reduction
2. compile Postgres with a larger block size (`./configure --with-blocksize=32`) and edit the limit in `src/vector.h`
## Additional Installation Methods
### Docker
Get the [Docker image](https://hub.docker.com/r/ankane/pgvector) with:
```sh
docker pull ankane/pgvector
```
This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres).
You can also build the image manually
```sh
git clone --branch v0.2.3 https://github.com/pgvector/pgvector.git
cd pgvector
docker build -t pgvector .
```
### Homebrew
On Mac with Homebrew Postgres, you can use:
```sh
brew install pgvector/brew/pgvector
```
### PGXN
Install from the [PostgreSQL Extension Network](https://pgxn.org/dist/vector) with:
```sh
pgxn install vector
```
## Hosted Postgres
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/)
- Google Cloud SQL - follow the instructions on [this page](https://cloud.google.com/sql/docs/postgres/extensions#requesting-support-for-a-new-extension)
- 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)
## Upgrading
### 0.1.1
Compile and install the latest version and run:
Install the latest version and run:
```sql
ALTER EXTENSION vector UPDATE TO '0.1.1';
ALTER EXTENSION vector UPDATE;
```
## Thanks
@@ -158,21 +281,21 @@ Thanks to:
## History
View the [changelog](https://github.com/ankane/pgvector/blob/master/CHANGELOG.md)
View the [changelog](https://github.com/pgvector/pgvector/blob/master/CHANGELOG.md)
## Contributing
Everyone is encouraged to help improve this project. Here are a few ways you can help:
- [Report bugs](https://github.com/ankane/pgvector/issues)
- Fix bugs and [submit pull requests](https://github.com/ankane/pgvector/pulls)
- [Report bugs](https://github.com/pgvector/pgvector/issues)
- Fix bugs and [submit pull requests](https://github.com/pgvector/pgvector/pulls)
- Write, clarify, or fix documentation
- Suggest or add new features
To get started with development:
```sh
git clone https://github.com/ankane/pgvector.git
git clone https://github.com/pgvector/pgvector.git
cd pgvector
make
make install
@@ -188,16 +311,10 @@ make prove_installcheck # TAP tests
To run single tests:
```sh
make installcheck REGRESS=vector # regression test
make prove_installcheck PROVE_TESTS=t/001_wal.pl # TAP test
make installcheck REGRESS=functions # regression test
make prove_installcheck PROVE_TESTS=test/t/001_wal.pl # TAP test
```
Directories
- `expected` - expected output for regression tests
- `sql` - regression tests
- `t` - TAP tests
Resources for contributors
- [Extension Building Infrastructure](https://www.postgresql.org/docs/current/extend-pgxs.html)

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION vector UPDATE TO '0.1.7'" to load this file. \quit
CREATE FUNCTION array_to_vector(numeric[], integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE CAST (numeric[] AS vector)
WITH FUNCTION array_to_vector(numeric[], integer, boolean) AS IMPLICIT;

View File

@@ -0,0 +1,8 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION vector UPDATE TO '0.1.8'" to load this file. \quit
CREATE FUNCTION vector_to_float4(vector, integer, boolean) RETURNS real[]
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE CAST (vector AS real[])
WITH FUNCTION vector_to_float4(vector, integer, boolean) AS IMPLICIT;

View File

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

View File

@@ -0,0 +1,19 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION vector UPDATE TO '0.2.1'" to load this file. \quit
DROP CAST (integer[] AS vector);
DROP CAST (real[] AS vector);
DROP CAST (double precision[] AS vector);
DROP CAST (numeric[] AS vector);
CREATE CAST (integer[] AS vector)
WITH FUNCTION array_to_vector(integer[], integer, boolean) AS ASSIGNMENT;
CREATE CAST (real[] AS vector)
WITH FUNCTION array_to_vector(real[], integer, boolean) AS ASSIGNMENT;
CREATE CAST (double precision[] AS vector)
WITH FUNCTION array_to_vector(double precision[], integer, boolean) AS ASSIGNMENT;
CREATE CAST (numeric[] AS vector)
WITH FUNCTION array_to_vector(numeric[], integer, boolean) AS ASSIGNMENT;

View File

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

View File

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

View File

@@ -1,15 +1,230 @@
SET client_min_messages = warning;
CREATE EXTENSION IF NOT EXISTS vector;
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION vector" to load this file. \quit
SELECT '[1,2,3]'::vector;
SELECT '[-1,2,3]'::vector;
SELECT '[hello,1]'::vector;
SELECT '[NaN,1]'::vector;
SELECT '[Infinity,1]'::vector;
SELECT '[-Infinity,1]'::vector;
SELECT '[1,2,3'::vector;
SELECT '[1,2,3]9'::vector;
SELECT '1,2,3'::vector;
SELECT '[]'::vector;
SELECT '[1,]'::vector;
SELECT '[1,2,3]'::vector(2);
-- type
CREATE TYPE vector;
CREATE FUNCTION vector_in(cstring, oid, integer) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_out(vector) RETURNS cstring
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_typmod_in(cstring[]) RETURNS integer
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_recv(internal, oid, integer) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_send(vector) RETURNS bytea
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE TYPE vector (
INPUT = vector_in,
OUTPUT = vector_out,
TYPMOD_IN = vector_typmod_in,
RECEIVE = vector_recv,
SEND = vector_send
);
-- functions
CREATE FUNCTION l2_distance(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION inner_product(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION cosine_distance(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_dims(vector) RETURNS integer
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_norm(vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_add(vector, vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_sub(vector, vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- private functions
CREATE FUNCTION vector_lt(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_le(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_eq(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_ne(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_ge(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_gt(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_cmp(vector, vector) RETURNS int4
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_l2_squared_distance(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_negative_inner_product(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_spherical_distance(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- cast functions
CREATE FUNCTION vector(vector, integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION array_to_vector(integer[], integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION array_to_vector(real[], integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION array_to_vector(double precision[], integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION array_to_vector(numeric[], integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_to_float4(vector, integer, boolean) RETURNS real[]
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- casts
CREATE CAST (vector AS vector)
WITH FUNCTION vector(vector, integer, boolean) AS IMPLICIT;
CREATE CAST (vector AS real[])
WITH FUNCTION vector_to_float4(vector, integer, boolean) AS IMPLICIT;
CREATE CAST (integer[] AS vector)
WITH FUNCTION array_to_vector(integer[], integer, boolean) AS ASSIGNMENT;
CREATE CAST (real[] AS vector)
WITH FUNCTION array_to_vector(real[], integer, boolean) AS ASSIGNMENT;
CREATE CAST (double precision[] AS vector)
WITH FUNCTION array_to_vector(double precision[], integer, boolean) AS ASSIGNMENT;
CREATE CAST (numeric[] AS vector)
WITH FUNCTION array_to_vector(numeric[], integer, boolean) AS ASSIGNMENT;
-- operators
CREATE OPERATOR <-> (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = l2_distance,
COMMUTATOR = '<->'
);
CREATE OPERATOR <#> (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_negative_inner_product,
COMMUTATOR = '<#>'
);
CREATE OPERATOR <=> (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = cosine_distance,
COMMUTATOR = '<=>'
);
CREATE OPERATOR + (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_add,
COMMUTATOR = +
);
CREATE OPERATOR - (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_sub,
COMMUTATOR = -
);
CREATE OPERATOR < (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_lt,
COMMUTATOR = > , NEGATOR = >= ,
RESTRICT = scalarltsel, JOIN = scalarltjoinsel
);
-- should use scalarlesel and scalarlejoinsel, but not supported in Postgres < 11
CREATE OPERATOR <= (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_le,
COMMUTATOR = >= , NEGATOR = > ,
RESTRICT = scalarltsel, JOIN = scalarltjoinsel
);
CREATE OPERATOR = (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_eq,
COMMUTATOR = = , NEGATOR = <> ,
RESTRICT = eqsel, JOIN = eqjoinsel
);
CREATE OPERATOR <> (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_ne,
COMMUTATOR = <> , NEGATOR = = ,
RESTRICT = eqsel, JOIN = eqjoinsel
);
-- should use scalargesel and scalargejoinsel, but not supported in Postgres < 11
CREATE OPERATOR >= (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_ge,
COMMUTATOR = <= , NEGATOR = < ,
RESTRICT = scalargtsel, JOIN = scalargtjoinsel
);
CREATE OPERATOR > (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_gt,
COMMUTATOR = < , NEGATOR = <= ,
RESTRICT = scalargtsel, JOIN = scalargtjoinsel
);
-- access method
CREATE FUNCTION ivfflathandler(internal) RETURNS index_am_handler
AS 'MODULE_PATHNAME' LANGUAGE C;
CREATE ACCESS METHOD ivfflat TYPE INDEX HANDLER ivfflathandler;
COMMENT ON ACCESS METHOD ivfflat IS 'ivfflat index access method';
-- opclasses
CREATE OPERATOR CLASS vector_ops
DEFAULT FOR TYPE vector USING btree AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 vector_cmp(vector, vector);
CREATE OPERATOR CLASS vector_l2_ops
DEFAULT FOR TYPE vector USING ivfflat AS
OPERATOR 1 <-> (vector, vector) FOR ORDER BY float_ops,
FUNCTION 1 vector_l2_squared_distance(vector, vector),
FUNCTION 3 l2_distance(vector, vector);
CREATE OPERATOR CLASS vector_ip_ops
FOR TYPE vector USING ivfflat AS
OPERATOR 1 <#> (vector, vector) FOR ORDER BY float_ops,
FUNCTION 1 vector_negative_inner_product(vector, vector),
FUNCTION 3 vector_spherical_distance(vector, vector),
FUNCTION 4 vector_norm(vector);
CREATE OPERATOR CLASS vector_cosine_ops
FOR TYPE vector USING ivfflat AS
OPERATOR 1 <=> (vector, vector) FOR ORDER BY float_ops,
FUNCTION 1 vector_negative_inner_product(vector, vector),
FUNCTION 2 vector_norm(vector),
FUNCTION 3 vector_spherical_distance(vector, vector),
FUNCTION 4 vector_norm(vector);

View File

@@ -7,8 +7,19 @@
#include "miscadmin.h"
#include "storage/bufmgr.h"
#if PG_VERSION_NUM >= 140000
#include "utils/backend_progress.h"
#elif PG_VERSION_NUM >= 120000
#include "pgstat.h"
#endif
#if PG_VERSION_NUM >= 120000
#include "access/tableam.h"
#include "commands/progress.h"
#else
#define PROGRESS_CREATEIDX_SUBPHASE 0
#define PROGRESS_CREATEIDX_TUPLES_TOTAL 0
#define PROGRESS_CREATEIDX_TUPLES_DONE 0
#endif
#if PG_VERSION_NUM >= 110000
@@ -25,6 +36,17 @@
#define CALLBACK_ITEM_POINTER HeapTuple hup
#endif
/*
* Update build phase progress
*/
static inline void
UpdateProgress(int index, int64 val)
{
#if PG_VERSION_NUM >= 120000
pgstat_progress_update_param(index, val);
#endif
}
/*
* Callback for sampling
*/
@@ -41,10 +63,13 @@ SampleCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
if (isnull[0])
return;
/* Normalize the value */
if (buildstate->normprocinfo != NULL)
/*
* Normalize with KMEANS_NORM_PROC since spherical distance function
* expects unit vectors
*/
if (buildstate->kmeansnormprocinfo != NULL)
{
if (!IvfflatNormValue(buildstate->normprocinfo, buildstate->collation, &value, buildstate->normvec))
if (!IvfflatNormValue(buildstate->kmeansnormprocinfo, buildstate->collation, &value, buildstate->normvec))
return;
}
@@ -79,6 +104,8 @@ SampleRows(IvfflatBuildState * buildstate)
int targsamples = buildstate->samples->maxlen;
BlockNumber totalblocks = RelationGetNumberOfBlocks(buildstate->heap);
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_SAMPLE);
buildstate->rowstoskip = -1;
BlockSampler_Init(&buildstate->bs, totalblocks, targsamples, random());
@@ -162,6 +189,8 @@ BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
* need not save it.
*/
tuplesort_puttupleslot(buildstate->sortstate, slot);
buildstate->indtuples++;
}
/*
@@ -209,6 +238,7 @@ InsertTuples(Relation index, IvfflatBuildState * buildstate, ForkNumber forkNum)
BlockNumber insertPage = InvalidBlockNumber;
Size itemsz;
int i;
int64 inserted = 0;
#if PG_VERSION_NUM >= 120000
TupleTableSlot *slot = MakeSingleTupleTableSlot(buildstate->tupdesc, &TTSOpsMinimalTuple);
@@ -217,10 +247,18 @@ InsertTuples(Relation index, IvfflatBuildState * buildstate, ForkNumber forkNum)
#endif
TupleDesc tupdesc = RelationGetDescr(index);
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_LOAD);
UpdateProgress(PROGRESS_CREATEIDX_TUPLES_TOTAL, buildstate->indtuples);
GetNextTuple(buildstate->sortstate, tupdesc, slot, &itup, &list);
for (i = 0; i < buildstate->centers->length; i++)
{
/* Can take a while, so ensure we can interrupt */
/* Needs to be called when no buffer locks are held */
CHECK_FOR_INTERRUPTS();
buf = IvfflatNewBuffer(index, forkNum);
IvfflatInitPage(index, &buf, &page, &state);
@@ -238,7 +276,9 @@ InsertTuples(Relation index, IvfflatBuildState * buildstate, ForkNumber forkNum)
if (PageAddItem(page, (Item) itup, itemsz, InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
buildstate->indtuples += 1;
pfree(itup);
UpdateProgress(PROGRESS_CREATEIDX_TUPLES_DONE, ++inserted);
GetNextTuple(buildstate->sortstate, tupdesc, slot, &itup, &list);
}
@@ -275,8 +315,15 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
/* Get support functions */
buildstate->procinfo = index_getprocinfo(index, 1, IVFFLAT_DISTANCE_PROC);
buildstate->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
buildstate->kmeansnormprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
buildstate->collation = index->rd_indcollation[0];
/* Require more than one dimension for spherical k-means */
/* Lists check for backwards compatibility */
/* TODO Remove lists check in 0.3.0 */
if (buildstate->kmeansnormprocinfo != NULL && buildstate->dimensions == 1 && buildstate->lists > 1)
elog(ERROR, "dimensions must be greater than one for this opclass");
/* Create tuple description for sorting */
#if PG_VERSION_NUM >= 120000
buildstate->tupdesc = CreateTemplateTupleDesc(4);
@@ -336,6 +383,7 @@ ComputeCenters(IvfflatBuildState * buildstate)
SampleRows(buildstate);
/* Calculate centers */
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_KMEANS);
IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers);
/* Free samples before we allocate more memory */
@@ -426,6 +474,8 @@ CreateEntryPages(IvfflatBuildState * buildstate, ForkNumber forkNum)
Oid sortCollations[] = {InvalidOid};
bool nullsFirstFlags[] = {false};
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);
#else

View File

@@ -1,11 +1,18 @@
#include "postgres.h"
#include <float.h>
#include "access/amapi.h"
#include "commands/vacuum.h"
#include "ivfflat.h"
#include "utils/guc.h"
#include "utils/selfuncs.h"
#if PG_VERSION_NUM >= 120000
#include "commands/progress.h"
#endif
int ivfflat_probes;
static relopt_kind ivfflat_relopt_kind;
/*
@@ -27,6 +34,31 @@ _PG_init(void)
1, 1, IVFFLAT_MAX_LISTS, PGC_USERSET, 0, NULL, NULL, NULL);
}
/*
* Get the name of index build phase
*/
#if PG_VERSION_NUM >= 120000
static char *
ivfflatbuildphasename(int64 phasenum)
{
switch (phasenum)
{
case PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE:
return "initializing";
case PROGRESS_IVFFLAT_PHASE_SAMPLE:
return "sampling table";
case PROGRESS_IVFFLAT_PHASE_KMEANS:
return "performing k-means";
case PROGRESS_IVFFLAT_PHASE_SORT:
return "sorting tuples";
case PROGRESS_IVFFLAT_PHASE_LOAD:
return "loading tuples";
default:
return NULL;
}
}
#endif
/*
* Estimate the cost of an index scan
*/
@@ -43,18 +75,29 @@ ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
int lists;
double ratio;
Relation indexRel;
#if PG_VERSION_NUM < 120000
List *qinfos;
qinfos = deconstruct_indexquals(path);
#endif
/* Never use index without order */
if (path->indexorderbys == NULL)
{
*indexStartupCost = DBL_MAX;
*indexTotalCost = DBL_MAX;
*indexSelectivity = 0;
*indexCorrelation = 0;
#if PG_VERSION_NUM >= 100000
*indexPages = 0;
#endif
return;
}
MemSet(&costs, 0, sizeof(costs));
#if PG_VERSION_NUM >= 120000
genericcostestimate(root, path, loop_count, &costs);
#else
qinfos = deconstruct_indexquals(path);
genericcostestimate(root, path, loop_count, qinfos, &costs);
#endif
@@ -160,7 +203,7 @@ ivfflathandler(PG_FUNCTION_ARGS)
amroutine->amoptions = ivfflatoptions;
amroutine->amproperty = NULL; /* TODO AMPROP_DISTANCE_ORDERABLE */
#if PG_VERSION_NUM >= 120000
amroutine->ambuildphasename = NULL;
amroutine->ambuildphasename = ivfflatbuildphasename;
#endif
amroutine->amvalidate = ivfflatvalidate;
amroutine->ambeginscan = ivfflatbeginscan;

View File

@@ -27,6 +27,13 @@
#define IVFFLAT_DEFAULT_LISTS 100
#define IVFFLAT_MAX_LISTS 32768
/* Build phases */
/* PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE is 1 */
#define PROGRESS_IVFFLAT_PHASE_SAMPLE 2
#define PROGRESS_IVFFLAT_PHASE_KMEANS 3
#define PROGRESS_IVFFLAT_PHASE_SORT 4
#define PROGRESS_IVFFLAT_PHASE_LOAD 5
#define IVFFLAT_LIST_SIZE(_dim) (offsetof(IvfflatListData, center) + VECTOR_SIZE(_dim))
#define IvfflatPageGetOpaque(page) ((IvfflatPageOpaque) PageGetSpecialPointer(page))
@@ -38,7 +45,7 @@
#endif
/* Variables */
int ivfflat_probes;
extern int ivfflat_probes;
typedef struct VectorArrayData
{
@@ -81,6 +88,7 @@ typedef struct IvfflatBuildState
/* Support functions */
FmgrInfo *procinfo;
FmgrInfo *normprocinfo;
FmgrInfo *kmeansnormprocinfo;
Oid collation;
/* Variables */
@@ -179,6 +187,9 @@ void IvfflatInitPage(Relation index, Buffer *buf, Page *page, GenericXLogState
IndexBuildResult *ivfflatbuild(Relation heap, Relation index, IndexInfo *indexInfo);
void ivfflatbuildempty(Relation index);
bool ivfflatinsert(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heap, IndexUniqueCheck checkUnique
#if PG_VERSION_NUM >= 140000
,bool indexUnchanged
#endif
#if PG_VERSION_NUM >= 100000
,IndexInfo *indexInfo
#endif

View File

@@ -128,6 +128,9 @@ InsertTuple(Relation rel, IndexTuple itup, Relation heapRel, Datum *values)
bool
ivfflatinsert(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid,
Relation heap, IndexUniqueCheck checkUnique
#if PG_VERSION_NUM >= 140000
,bool indexUnchanged
#endif
#if PG_VERSION_NUM >= 100000
,IndexInfo *indexInfo
#endif

View File

@@ -251,6 +251,10 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
{
Datum value;
/* Safety check */
if (scan->orderByData == NULL)
elog(ERROR, "cannot scan ivfflat index without order");
/* No items will match if null */
if (scan->orderByData->sk_flags & SK_ISNULL)
return false;

View File

@@ -10,11 +10,16 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/numeric.h"
#if PG_VERSION_NUM >= 120000
#include "utils/float.h"
#endif
#if PG_VERSION_NUM < 130000
#define TYPALIGN_INT 'i'
#endif
PG_MODULE_MAGIC;
/*
@@ -244,14 +249,20 @@ vector_recv(PG_FUNCTION_ARGS)
int32 typmod = PG_GETARG_INT32(2);
Vector *result;
int16 dim;
int16 unused;
int i;
dim = pq_getmsgint(buf, sizeof(int16));
pq_getmsgint(buf, sizeof(int16)); /* unused */
unused = pq_getmsgint(buf, sizeof(int16));
CheckDim(dim);
CheckExpectedDim(typmod, dim);
if (unused != 0)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("expected unused to be 0, not %d", unused)));
result = InitVector(dim);
for (i = 0; i < dim; i++)
result->x[i] = pq_getmsgfloat4(buf);
@@ -339,6 +350,8 @@ array_to_vector(PG_FUNCTION_ARGS)
result->x[i] = DatumGetFloat8(elemsp[i]);
else if (ARR_ELEMTYPE(array) == FLOAT4OID)
result->x[i] = DatumGetFloat4(elemsp[i]);
else if (ARR_ELEMTYPE(array) == NUMERICOID)
result->x[i] = DatumGetFloat4(DirectFunctionCall1(numeric_float4, NumericGetDatum(elemsp[i])));
else
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
@@ -350,6 +363,29 @@ array_to_vector(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(result);
}
/*
* Convert vector to float4[]
*/
PG_FUNCTION_INFO_V1(vector_to_float4);
Datum
vector_to_float4(PG_FUNCTION_ARGS)
{
Vector *vec = PG_GETARG_VECTOR_P(0);
Datum *d;
ArrayType *result;
int i;
d = (Datum *) palloc(sizeof(Datum) * vec->dim);
for (i = 0; i < vec->dim; i++)
d[i] = Float4GetDatum(vec->x[i]);
/* Use TYPALIGN_INT for float4 */
result = construct_array(d, vec->dim, FLOAT4OID, sizeof(float4), true, TYPALIGN_INT);
PG_RETURN_POINTER(result);
}
/*
* Get the L2 distance between vectors
*/

View File

@@ -6,6 +6,12 @@ SELECT ARRAY[1,2,3]::vector;
[1,2,3]
(1 row)
SELECT ARRAY[1.0,2.0,3.0]::vector;
array
---------
[1,2,3]
(1 row)
SELECT ARRAY[1,2,3]::float4[]::vector;
array
---------
@@ -28,3 +34,18 @@ SELECT '{-Infinity}'::real[]::vector;
ERROR: infinite value not allowed in vector
SELECT '{}'::real[]::vector;
ERROR: vector must have at least 1 dimension
SELECT '[1,2,3]'::vector::real[];
float4
---------
{1,2,3}
(1 row)
SELECT array_agg(n)::vector FROM generate_series(1, 1025) n;
ERROR: vector cannot have more than 1024 dimensions
-- ensure no error
SELECT ARRAY[1,2,3] = ARRAY[1,2,3];
?column?
----------
t
(1 row)

View File

@@ -19,4 +19,10 @@ SELECT * FROM t ORDER BY val <-> (SELECT NULL::vector);
-----
(0 rows)
SELECT COUNT(*) FROM t;
count
-------
5
(1 row)
DROP TABLE t;

View File

@@ -0,0 +1,17 @@
SET client_min_messages = warning;
CREATE EXTENSION IF NOT EXISTS vector;
SET enable_seqscan = off;
CREATE TABLE t (val vector(3));
CREATE INDEX ON t USING ivfflat (val) WITH (lists = 0);
ERROR: value 0 out of bounds for option "lists"
DETAIL: Valid values are between "1" and "32768".
CREATE INDEX ON t USING ivfflat (val) WITH (lists = 32769);
ERROR: value 32769 out of bounds for option "lists"
DETAIL: Valid values are between "1" and "32768".
SHOW ivfflat.probes;
ivfflat.probes
----------------
1
(1 row)
DROP TABLE t;

View File

@@ -2,6 +2,7 @@ SET client_min_messages = warning;
CREATE EXTENSION IF NOT EXISTS vector;
SELECT ARRAY[1,2,3]::vector;
SELECT ARRAY[1.0,2.0,3.0]::vector;
SELECT ARRAY[1,2,3]::float4[]::vector;
SELECT ARRAY[1,2,3]::float8[]::vector;
SELECT '{NULL}'::real[]::vector;
@@ -9,3 +10,8 @@ SELECT '{NaN}'::real[]::vector;
SELECT '{Infinity}'::real[]::vector;
SELECT '{-Infinity}'::real[]::vector;
SELECT '{}'::real[]::vector;
SELECT '[1,2,3]'::vector::real[];
SELECT array_agg(n)::vector FROM generate_series(1, 1025) n;
-- ensure no error
SELECT ARRAY[1,2,3] = ARRAY[1,2,3];

15
test/sql/input.sql Normal file
View File

@@ -0,0 +1,15 @@
SET client_min_messages = warning;
CREATE EXTENSION IF NOT EXISTS vector;
SELECT '[1,2,3]'::vector;
SELECT '[-1,2,3]'::vector;
SELECT '[hello,1]'::vector;
SELECT '[NaN,1]'::vector;
SELECT '[Infinity,1]'::vector;
SELECT '[-Infinity,1]'::vector;
SELECT '[1,2,3'::vector;
SELECT '[1,2,3]9'::vector;
SELECT '1,2,3'::vector;
SELECT '[]'::vector;
SELECT '[1,]'::vector;
SELECT '[1,2,3]'::vector(2);

View File

@@ -10,5 +10,6 @@ INSERT INTO t (val) VALUES ('[1,2,4]');
SELECT * FROM t ORDER BY val <-> '[3,3,3]';
SELECT * FROM t ORDER BY val <-> (SELECT NULL::vector);
SELECT COUNT(*) FROM t;
DROP TABLE t;

View File

@@ -1,218 +0,0 @@
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION vector" to load this file. \quit
-- type
CREATE TYPE vector;
CREATE FUNCTION vector_in(cstring, oid, integer) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_out(vector) RETURNS cstring
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_typmod_in(cstring[]) RETURNS integer
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_recv(internal, oid, integer) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_send(vector) RETURNS bytea
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE TYPE vector (
INPUT = vector_in,
OUTPUT = vector_out,
TYPMOD_IN = vector_typmod_in,
RECEIVE = vector_recv,
SEND = vector_send
);
-- functions
CREATE FUNCTION l2_distance(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION inner_product(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION cosine_distance(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_dims(vector) RETURNS integer
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_norm(vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_add(vector, vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_sub(vector, vector) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- private functions
CREATE FUNCTION vector_lt(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_le(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_eq(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_ne(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_ge(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_gt(vector, vector) RETURNS bool
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_cmp(vector, vector) RETURNS int4
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_l2_squared_distance(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_negative_inner_product(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION vector_spherical_distance(vector, vector) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- cast functions
CREATE FUNCTION vector(vector, integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION array_to_vector(integer[], integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION array_to_vector(real[], integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION array_to_vector(double precision[], integer, boolean) RETURNS vector
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
-- casts
CREATE CAST (vector AS vector)
WITH FUNCTION vector(vector, integer, boolean) AS IMPLICIT;
CREATE CAST (integer[] AS vector)
WITH FUNCTION array_to_vector(integer[], integer, boolean) AS IMPLICIT;
CREATE CAST (real[] AS vector)
WITH FUNCTION array_to_vector(real[], integer, boolean) AS IMPLICIT;
CREATE CAST (double precision[] AS vector)
WITH FUNCTION array_to_vector(double precision[], integer, boolean) AS IMPLICIT;
-- operators
CREATE OPERATOR <-> (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = l2_distance,
COMMUTATOR = '<->'
);
CREATE OPERATOR <#> (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_negative_inner_product,
COMMUTATOR = '<#>'
);
CREATE OPERATOR <=> (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = cosine_distance,
COMMUTATOR = '<=>'
);
CREATE OPERATOR + (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_add,
COMMUTATOR = +
);
CREATE OPERATOR - (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_sub,
COMMUTATOR = -
);
CREATE OPERATOR < (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_lt,
COMMUTATOR = > , NEGATOR = >= ,
RESTRICT = scalarltsel, JOIN = scalarltjoinsel
);
-- should use scalarlesel and scalarlejoinsel, but not supported in Postgres < 11
CREATE OPERATOR <= (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_le,
COMMUTATOR = >= , NEGATOR = > ,
RESTRICT = scalarltsel, JOIN = scalarltjoinsel
);
CREATE OPERATOR = (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_eq,
COMMUTATOR = = , NEGATOR = <> ,
RESTRICT = eqsel, JOIN = eqjoinsel
);
CREATE OPERATOR <> (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_ne,
COMMUTATOR = <> , NEGATOR = = ,
RESTRICT = eqsel, JOIN = eqjoinsel
);
-- should use scalargesel and scalargejoinsel, but not supported in Postgres < 11
CREATE OPERATOR >= (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_ge,
COMMUTATOR = <= , NEGATOR = < ,
RESTRICT = scalargtsel, JOIN = scalargtjoinsel
);
CREATE OPERATOR > (
LEFTARG = vector, RIGHTARG = vector, PROCEDURE = vector_gt,
COMMUTATOR = < , NEGATOR = <= ,
RESTRICT = scalargtsel, JOIN = scalargtjoinsel
);
-- access method
CREATE FUNCTION ivfflathandler(internal) RETURNS index_am_handler
AS 'MODULE_PATHNAME' LANGUAGE C;
CREATE ACCESS METHOD ivfflat TYPE INDEX HANDLER ivfflathandler;
COMMENT ON ACCESS METHOD ivfflat IS 'ivfflat index access method';
-- opclasses
CREATE OPERATOR CLASS vector_ops
DEFAULT FOR TYPE vector USING btree AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 vector_cmp(vector, vector);
CREATE OPERATOR CLASS vector_l2_ops
DEFAULT FOR TYPE vector USING ivfflat AS
OPERATOR 1 <-> (vector, vector) FOR ORDER BY float_ops,
FUNCTION 1 vector_l2_squared_distance(vector, vector),
FUNCTION 3 l2_distance(vector, vector);
CREATE OPERATOR CLASS vector_ip_ops
FOR TYPE vector USING ivfflat AS
OPERATOR 1 <#> (vector, vector) FOR ORDER BY float_ops,
FUNCTION 1 vector_negative_inner_product(vector, vector),
FUNCTION 3 vector_spherical_distance(vector, vector),
FUNCTION 4 vector_norm(vector);
CREATE OPERATOR CLASS vector_cosine_ops
FOR TYPE vector USING ivfflat AS
OPERATOR 1 <=> (vector, vector) FOR ORDER BY float_ops,
FUNCTION 1 vector_negative_inner_product(vector, vector),
FUNCTION 2 vector_norm(vector),
FUNCTION 3 vector_spherical_distance(vector, vector),
FUNCTION 4 vector_norm(vector);

View File

@@ -1,4 +1,4 @@
comment = 'vector data type and ivfflat access method'
default_version = '0.1.1'
default_version = '0.2.3'
module_pathname = '$libdir/vector'
relocatable = true