Compare commits

..

123 Commits

Author SHA1 Message Date
Andrew Kane
455b5d867c Started support for parallel index scan [skip ci] 2023-06-29 23:17:59 -04:00
Jonathan S. Katz
de6502ab6e Simplify TAP test structure to align with upstream (#169)
postgres/postgres@549ec20 moved to using "done_testing()" from
the Perl testing framework, which removed the need to include test
counts.
2023-06-29 11:36:08 -04:00
Andrew Kane
7aaba14440 Added comment [skip ci] 2023-06-29 10:25:06 -04:00
Andrew Kane
ee0bf10d7d Fixed CI for equal distances 2023-06-29 10:24:02 -04:00
Andrew Kane
88094fc39f Fixed tests 2023-06-29 10:17:15 -04:00
Andrew Kane
209394faab Now available on Google Cloud SQL [skip ci] 2023-06-26 11:17:10 -04:00
Andrew Kane
673aca97fc Fixed CI for branches for i386 2023-06-16 17:38:51 -04:00
Andrew Kane
0e2dc0e6d4 Updated readme [skip ci] 2023-06-16 10:15:22 -04:00
Andrew Kane
3c681b94fd Updated example [skip ci] 2023-06-16 10:03:07 -04:00
Andrew Kane
1982121694 Version bump to 0.4.4 [skip ci] 2023-06-12 01:55:22 -07:00
Andrew Kane
9c8c4483db Updated changelog [skip ci] 2023-06-12 01:23:47 -07:00
Andrew Kane
426ae1f16e Added scan-build to CI 2023-06-12 01:13:21 -07:00
Andrew Kane
06c3e68bef Fixed segmentation fault with text representation 2023-06-12 01:09:40 -07:00
Andrew Kane
e5a620e02c Improved tests 2023-06-11 19:23:30 -07:00
Andrew Kane
092bb80f58 Added test for small floats [skip ci] 2023-06-11 19:19:07 -07:00
Andrew Kane
b690cd4d5d Added tests for large floats [skip ci] 2023-06-11 19:17:54 -07:00
Andrew Kane
4e0d11acfe Improved test [skip ci] 2023-06-11 19:15:42 -07:00
Andrew Kane
aa63f80b69 Improved test [skip ci] 2023-06-11 19:14:32 -07:00
Andrew Kane
b8a7355731 Improved test [skip ci] 2023-06-11 19:12:56 -07:00
Andrew Kane
3332669489 Improved tests [skip ci] 2023-06-11 19:11:17 -07:00
Andrew Kane
08c70bb57f Added more input tests [skip ci] 2023-06-11 19:08:24 -07:00
Andrew Kane
eff0de6a64 Updated comments [skip ci] 2023-06-11 18:36:41 -07:00
Andrew Kane
3cf7ce6543 Added comment for CheckDim [skip ci] 2023-06-11 13:06:51 -07:00
Andrew Kane
3cb6440744 Simplified PrintVector [skip ci] 2023-06-11 12:54:18 -07:00
Andrew Kane
b6a0d2b12b Check for empty string like float4in [skip ci] 2023-06-11 12:34:24 -07:00
Andrew Kane
78632e3301 Added input test case [skip ci] 2023-06-11 12:22:52 -07:00
Andrew Kane
f8c85905c3 Use vector_isspace to remove whitespace before strtof 2023-06-11 12:21:27 -07:00
Andrew Kane
0a98a953cd Fixed consecutive delimiters with text representation 2023-06-11 12:10:52 -07:00
Andrew Kane
a577c2df80 Updated format [skip ci] 2023-06-11 12:04:02 -07:00
Andrew Kane
7ee9e86b10 Improved error message for invalid input syntax 2023-06-11 12:00:04 -07:00
Andrew Kane
5fdf5573a0 No need to manually free memory on errors 2023-06-11 09:42:20 -07:00
Andrew Kane
2b939edfee Improved error message for malformed vector literal - #153 2023-06-11 09:24:11 -07:00
Andrew Kane
987026a559 Improved benchmarking for index build [skip ci] 2023-06-10 21:50:31 -07:00
Andrew Kane
d158eefa60 Updated guidance on probes [skip ci] 2023-06-10 17:47:41 -07:00
Andrew Kane
a7bbb0772d Version bump to 0.4.3 [skip ci] 2023-06-10 12:19:41 -07:00
Andrew Kane
6ad276aa54 Added Postgres 16 to CI 2023-06-10 11:35:28 -07:00
Andrew Kane
c03ce7d62a Ensure insert page is always set 2023-06-10 11:29:47 -07:00
Andrew Kane
629fa6f0cd Updated cost estimation code (same logic) 2023-06-10 10:52:29 -07:00
Andrew Kane
a33e72d58e Always change some cost to sequential, and always update total cost 2023-06-10 02:10:53 -07:00
Andrew Kane
49e6a72d36 Remove cost of extra pages when random_page_cost equals seq_page_cost 2023-06-10 01:18:21 -07:00
Andrew Kane
b158a5fa48 Moved get_tablespace_page_costs [skip ci] 2023-06-10 01:07:54 -07:00
Andrew Kane
81cc04df61 Only adjust cost if random_page_cost is higher than seq_page_cost 2023-06-10 01:06:05 -07:00
Andrew Kane
d6ab4892fe Updated example [skip ci] 2023-06-09 21:29:42 -07:00
Andrew Kane
cbaf470f2e Added build arg to docs [skip ci] 2023-06-09 21:24:23 -07:00
Andrew Kane
8cb32cac76 Added comment [skip ci] 2023-06-09 21:20:48 -07:00
Andrew Kane
4ce915cf16 Improved cost estimate - #133 2023-06-09 21:11:16 -07:00
Andrew Kane
41b766c24b Use index tuples 2023-06-09 19:55:49 -07:00
Andrew Kane
d11fe7bbfb Updated changelog [skip ci] 2023-06-09 19:05:53 -07:00
Andrew Kane
2c35074f3a Use ref to fix CI for PRs 2023-06-09 18:56:43 -07:00
Andrew Kane
b4c1c3ab63 Fixed CI 2023-06-09 18:52:51 -07:00
Andrew Kane
edcbafca79 Fixed CI 2023-06-09 18:49:57 -07:00
Andrew Kane
cbec1b3f48 Improved warnings check 2023-06-09 18:46:26 -07:00
Andrew Kane
f81d863dfd Catch warnings on CI 2023-06-09 18:39:44 -07:00
Jonathan S. Katz
b8c7a4f4b6 Fix compiler warnings on Ubuntu (#156)
The compiler was complaining about a missing include due to the
addition of NaN/inf checks in 482a5f8b. Adding the include
silences the warnings.
2023-06-09 18:27:36 -07:00
Jonathan S. Katz
7446cbde8f Update ivfflat cost estimation to choose index for more searches (#133)
Co-authored-by: Andrew Kane <andrew@ankane.org>
2023-06-09 17:53:14 -07:00
Andrew Kane
9f2359894f Added todo [skip ci] 2023-06-09 00:27:01 -07:00
Andrew Kane
3c78130868 Removed extra line [skip ci] 2023-06-08 23:14:34 -07:00
Andrew Kane
768cd5d5d5 Default closest center to 0 2023-06-08 23:07:25 -07:00
Andrew Kane
7335a122db Added comments [skip ci] 2023-06-08 23:02:03 -07:00
Andrew Kane
2115630fb0 Added comments [skip ci] 2023-06-08 23:01:09 -07:00
Andrew Kane
836be51298 Improved variable name [skip ci] 2023-06-08 22:51:20 -07:00
Andrew Kane
972d9d61cb Simplified code [skip ci] 2023-06-08 22:43:00 -07:00
Andrew Kane
8be2b6c244 Free memory on errors 2023-06-08 22:35:31 -07:00
Andrew Kane
0134debfb8 Free datums 2023-06-08 21:23:03 -07:00
Andrew Kane
2f93781c3b Updated changelog [skip ci] 2023-06-08 20:45:48 -07:00
Andrew Kane
4d910f30fd Updated license year [skip ci] 2023-06-08 20:31:01 -07:00
Andrew Kane
a20add331f Updated comments [skip ci] 2023-06-08 20:26:27 -07:00
Andrew Kane
f03381bc62 Updated changelog [skip ci] 2023-06-08 19:59:52 -07:00
Andrew Kane
198390333e Moved whitespace check 2023-06-08 19:52:49 -07:00
yihong
41c68bf692 fix: input function does not allow tailing spaces (#148) 2023-06-08 19:51:20 -07:00
Andrew Kane
13cf29088d Updated changelog [skip ci] 2023-06-08 18:43:47 -07:00
Andrew Kane
1aea0dfcd8 Added element check to binary format and fixed segmentation fault with index creation - fixes #151 2023-06-08 18:32:36 -07:00
Andrew Kane
b6430bae62 Moved overflow check 2023-06-04 13:37:31 -07:00
Andrew Kane
8294a0a562 Improved style [skip ci] 2023-06-04 13:30:28 -07:00
Andrew Kane
b31c8062c3 Avoid allocating more memory 2023-06-04 13:24:27 -07:00
Andrew Kane
73ff7c3c68 Updated changelog [skip ci] 2023-06-04 12:22:42 -07:00
Andrew Kane
482a5f8b66 Added check for NaN 2023-06-04 10:52:28 -07:00
Andrew Kane
e971fdd4fd Fixed infinite values with list centers 2023-06-04 10:42:55 -07:00
Andrew Kane
6330abb7df Added link to hybrid search example [skip ci] 2023-05-31 16:12:58 -07:00
Andrew Kane
7938b476ea Added test for out of range value 2023-05-31 14:07:05 -07:00
Andrew Kane
0ef0467a0f Fixed infinite values with vector addition and subtraction 2023-05-31 13:54:19 -07:00
Andrew Kane
dee2c4feb1 Updated example [skip ci] 2023-05-28 16:48:09 -07:00
Andrew Kane
29d9ec6f1e Updated example [skip ci] 2023-05-28 12:17:40 -07:00
Andrew Kane
0200134397 Added note about hybrid search [skip ci] 2023-05-28 12:14:10 -07:00
Andrew Kane
ceddbac6bf Improved example [skip ci] 2023-05-28 11:36:36 -07:00
Andrew Kane
0deb443458 Added link to multicolumn indexes [skip ci] 2023-05-28 11:07:26 -07:00
Andrew Kane
d1fb0d8e27 Improved filtering docs [skip ci] 2023-05-28 10:58:07 -07:00
Andrew Kane
9e5f7fd5ec Use tsql to fix syntax highlighting [skip ci] 2023-05-27 22:53:47 -07:00
Andrew Kane
7f744b02c8 Updated readme [skip ci] 2023-05-27 22:17:29 -07:00
Andrew Kane
a9c6af89e8 Fixed link [skip ci] 2023-05-27 22:08:52 -07:00
Andrew Kane
212af771bd Added Postgres.app to readme [skip ci] 2023-05-27 22:08:13 -07:00
Andrew Kane
b37d154b26 Updated readme [skip ci] 2023-05-27 21:14:35 -07:00
Andrew Kane
51bd223b4a Updated readme [skip ci] 2023-05-27 13:44:08 -07:00
Andrew Kane
491b6b18f9 Use apt in readme [skip ci] 2023-05-22 14:10:35 -07:00
Andrew Kane
4576a9f9a4 APT packages are now in the main distribution - #131 [skip ci] 2023-05-22 14:01:35 -07:00
Andrew Kane
451ac59a03 Improved code [skip ci] 2023-05-20 19:04:35 -07:00
Andrew Kane
6f94c5e897 Improved code [skip ci] 2023-05-20 19:02:38 -07:00
Andrew Kane
e9c88d6f25 Added instructions for APT [skip ci] 2023-05-19 13:23:09 -07:00
Andrew Kane
a912d1af9a Call FreeAccessStrategy 2023-05-19 13:09:30 -07:00
Andrew Kane
fa401b7883 Fixed compilation error on PowerPC - closes #117
Co-authored-by: Ilya <badt@appar.at>
2023-05-17 16:59:22 -07:00
Andrew Kane
e97ef5fbac Updated changelog [skip ci] 2023-05-17 16:55:52 -07:00
Andrew Kane
dfe487145f Fixed CI 2023-05-17 15:16:58 -07:00
Andrew Kane
c2f331908f Updated changelog [skip ci] 2023-05-17 15:14:30 -07:00
Andrew Kane
7911a3b395 Updated changelog [skip ci] 2023-05-17 15:12:10 -07:00
Andrew Kane
0d46281c02 Added i386 to CI 2023-05-17 15:07:59 -07:00
Andrew Kane
59071dc78d Fixed sort operator - fixes #131 2023-05-17 14:04:15 -07:00
Andrew Kane
5b3878b7fe Fixed avg functions when float8 is pass by reference - #131 2023-05-17 12:01:08 -07:00
mulander
18e7319a40 docs: pgvector now available on Azure (#129)
Available on:
- Azure Database for PostgreSQL Flexible Server
- Azure Cosmos DB for PostgreSQL
2023-05-17 10:58:33 -07:00
Andrew Kane
69672cd84d Version bump to 0.4.2 [skip ci] 2023-05-13 20:47:40 -07:00
Andrew Kane
300adba2f1 Updated messages 2023-05-13 20:44:46 -07:00
Andrew Kane
e362279199 Updated changelog [skip ci] 2023-05-12 18:01:25 -07:00
Andrew Kane
53301021f6 Added dimensions check to vector_avg 2023-05-12 17:19:16 -07:00
Andrew Kane
8f589f6d09 Added test for array_to_vector 2023-05-12 17:14:29 -07:00
Nathan Bossart
dcf206128a Check bounds unconditionally in array_to_vector(). (#127)
Presently, array_to_vector()'s call to CheckDim() is skipped when typmod != -1, which allows for bypassing VECTOR_MAX_DIM.  To fix, call Check[Expected]Dim() unconditionally.  CheckExpectedDim() takes no action when typmod == -1, so there's no need to guard it with an 'if' statement.
2023-05-12 17:08:51 -07:00
Andrew Kane
3244d40e8a Added note about --preserve-env [skip ci] 2023-05-10 12:13:15 -07:00
Andrew Kane
7d8dbcaa3c Added note about Homebrew Postgres [skip ci] 2023-05-10 12:06:30 -07:00
Andrew Kane
7f575f55fb Removed block size note - #120 [skip ci] 2023-05-09 14:43:24 -07:00
Andrew Kane
94e7487d5f Fixed link [skip ci] 2023-05-06 12:33:20 -07:00
Andrew Kane
74a3cd597f Improved Docker tasks [skip ci] 2023-05-06 12:11:10 -07:00
Andrew Kane
db8ed738b8 Split Docker tasks [skip ci] 2023-05-06 11:59:46 -07:00
Andrew Kane
54c550420b Added Docker image for linux/arm64 - closes #115 2023-05-06 11:50:59 -07:00
Fabian Fischer
cc539a0a27 docs: aws rds supports pgvector now (#110) 2023-05-03 13:08:06 -07:00
Andrew Kane
d885e2bcfa Added FAQ about results [skip ci] 2023-05-02 10:17:31 -07:00
35 changed files with 590 additions and 237 deletions

View File

@@ -8,6 +8,8 @@ jobs:
fail-fast: false
matrix:
include:
- postgres: 16
os: ubuntu-22.04
- postgres: 15
os: ubuntu-22.04
- postgres: 14
@@ -25,6 +27,8 @@ jobs:
postgres-version: ${{ matrix.postgres }}
dev-files: true
- run: make
env:
PG_CFLAGS: -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
- run: |
export PG_CONFIG=`which pg_config`
sudo --preserve-env=PG_CONFIG make install
@@ -44,6 +48,8 @@ jobs:
with:
postgres-version: 14
- run: make
env:
PG_CFLAGS: -Wall -Wextra -Werror -Wno-unused-parameter
- run: make install
- run: make installcheck
- if: ${{ failure() }}
@@ -54,6 +60,7 @@ jobs:
wget -q https://github.com/postgres/postgres/archive/refs/tags/REL_14_5.tar.gz
tar xf REL_14_5.tar.gz
- run: make prove_installcheck PROVE_FLAGS="-I ./postgres-REL_14_5/src/test/perl" PERL5LIB="/Users/runner/perl5/lib/perl5"
- run: make clean && /usr/local/opt/llvm@15/bin/scan-build --status-bugs make
windows:
runs-on: windows-latest
if: ${{ !startsWith(github.ref_name, 'mac') }}
@@ -70,3 +77,23 @@ jobs:
nmake /NOLOGO /F Makefile.win clean && ^
nmake /NOLOGO /F Makefile.win uninstall
shell: cmd
i386:
runs-on: ubuntu-latest
container:
image: debian:11
options: --platform linux/386
steps:
- run: apt-get update && apt-get install -y build-essential git libipc-run-perl postgresql-13 postgresql-server-dev-13 sudo
- run: service postgresql start
- run: |
git clone https://github.com/${{ github.repository }}.git pgvector
cd pgvector
git fetch origin ${{ github.ref }}
git reset --hard FETCH_HEAD
make
make install
chown -R postgres .
sudo -u postgres make installcheck
sudo -u postgres make prove_installcheck
env:
PG_CFLAGS: -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare

View File

@@ -1,6 +1,24 @@
## 0.4.2 (unreleased)
## 0.4.4 (2023-06-12)
- Improved error message for malformed vector literal
- Fixed segmentation fault with text input
- Fixed consecutive delimiters with text input
## 0.4.3 (2023-06-10)
- Improved cost estimation
- Improved support for spaces with text input
- Fixed infinite and NaN values with binary input
- Fixed infinite values with vector addition and subtraction
- Fixed infinite values with list centers
- Fixed compilation error when `float8` is pass by reference
- Fixed compilation error on PowerPC
- Fixed segmentation fault with index creation on i386
## 0.4.2 (2023-05-13)
- Added notice when index created with little data
- Fixed dimensions check for some direct function calls
- Fixed installation error with Postgres 12.0-12.2
## 0.4.1 (2023-03-21)

View File

@@ -1,4 +1,4 @@
Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
Portions Copyright (c) 1994, The Regents of the University of California

View File

@@ -2,7 +2,7 @@
"name": "vector",
"abstract": "Open-source vector similarity search for Postgres",
"description": "Supports L2 distance, inner product, and cosine distance",
"version": "0.4.1",
"version": "0.4.4",
"maintainer": [
"Andrew Kane <andrew@ankane.org>"
],
@@ -20,7 +20,7 @@
"vector": {
"file": "sql/vector.sql",
"docfile": "README.md",
"version": "0.4.1",
"version": "0.4.4",
"abstract": "Open-source vector similarity search for Postgres"
}
},

View File

@@ -1,5 +1,5 @@
EXTENSION = vector
EXTVERSION = 0.4.1
EXTVERSION = 0.4.4
MODULE_big = vector
DATA = $(wildcard sql/*--*.sql)
@@ -19,6 +19,11 @@ ifeq ($(shell uname -s), Darwin)
endif
endif
# PowerPC doesn't support -march=native
ifneq ($(filter ppc64%, $(shell uname -m)), )
OPTFLAGS =
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
@@ -63,3 +68,9 @@ dist:
docker:
docker build --pull --no-cache --platform linux/amd64 -t ankane/pgvector:latest .
.PHONY: docker-release
docker-release:
docker buildx build --push --pull --no-cache --platform linux/amd64,linux/arm64 -t ankane/pgvector:latest .
docker buildx build --push --platform linux/amd64,linux/arm64 -t ankane/pgvector:v$(EXTVERSION) .

View File

@@ -1,5 +1,5 @@
EXTENSION = vector
EXTVERSION = 0.4.1
EXTVERSION = 0.4.4
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

View File

@@ -2,12 +2,14 @@
Open-source vector similarity search for Postgres
Supports
Store all of your application data in one place. Supports:
- exact and approximate nearest neighbor search
- L2 distance, inner product, and cosine distance
- any [language](#languages) with a Postgres client
Plus [ACID](https://en.wikipedia.org/wiki/ACID) compliance, point-in-time recovery, JOINs, and all of the other [great features](https://www.postgresql.org/about/) of Postgres
[![Build Status](https://github.com/pgvector/pgvector/workflows/build/badge.svg?branch=master)](https://github.com/pgvector/pgvector/actions)
## Installation
@@ -16,24 +18,24 @@ Compile and install the extension (supports Postgres 11+)
```sh
cd /tmp
git clone --branch v0.4.1 https://github.com/pgvector/pgvector.git
git clone --branch v0.4.4 https://github.com/pgvector/pgvector.git
cd pgvector
make
make install # may need sudo
```
Then load it in databases where you want to use it
```sql
CREATE EXTENSION vector;
```
See the [installation notes](#installation-notes) if you run into issues
You can also install it with [Docker](#docker), [Homebrew](#homebrew), [PGXN](#pgxn), [Yum](#yum), or [conda-forge](#conda-forge)
You can also install it with [Docker](#docker), [Homebrew](#homebrew), [PGXN](#pgxn), [APT](#apt), [Yum](#yum), or [conda-forge](#conda-forge), and it comes preinstalled with [Postgres.app](#postgresapp) and many [hosted providers](#hosted-postgres)
## Getting Started
Enable the extension (do this once in each database where you want to use it)
```tsql
CREATE EXTENSION vector;
```
Create a vector column with 3 dimensions
```sql
@@ -127,7 +129,7 @@ SELECT embedding <-> '[3,1,2]' AS distance FROM items;
For inner product, multiply by -1 (since `<#>` returns the negative inner product)
```sql
```tsql
SELECT (embedding <#> '[3,1,2]') * -1 AS inner_product FROM items;
```
@@ -161,7 +163,7 @@ Three keys to achieving good recall are:
1. Create the index *after* the table has some data
2. Choose an appropriate number of lists - a good place to start is `rows / 1000` for up to 1M rows and `sqrt(rows)` for over 1M rows
3. When querying, specify an appropriate number of [probes](#query-options) (higher is better for recall, lower is better for speed) - a good place to start is `lists / 10` for up to 1M rows and `sqrt(lists)` for over 1M rows
3. When querying, specify an appropriate number of [probes](#query-options) (higher is better for recall, lower is better for speed) - a good place to start is `sqrt(lists)`
Add an index for each distance function you want to use.
@@ -221,26 +223,42 @@ The phases are:
Note: `tuples_done` and `tuples_total` are only populated during the `loading tuples` phase
### Partial Indexes
### Filtering
Consider [partial indexes](https://www.postgresql.org/docs/current/indexes-partial.html) for queries with a `WHERE` clause
There are a few ways to index nearest neighbor queries with a `WHERE` clause
```sql
SELECT * FROM items WHERE category_id = 123 ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
```
can be indexed with:
Create an index on one [or more](https://www.postgresql.org/docs/current/indexes-multicolumn.html) of the `WHERE` columns for exact search
```sql
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100) WHERE (category_id = 123);
CREATE INDEX ON items (category_id);
```
To index many different values of `category_id`, consider [partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html) on `category_id`.
Or a [partial index](https://www.postgresql.org/docs/current/indexes-partial.html) on the vector column for approximate search
```sql
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100)
WHERE (category_id = 123);
```
Use [partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html) for approximate search on many different values of the `WHERE` columns
```sql
CREATE TABLE items (embedding vector(3), category_id int) PARTITION BY LIST(category_id);
```
## Hybrid Search
Use together with Postgres [full-text search](https://www.postgresql.org/docs/current/textsearch-intro.html) for hybrid search ([Python example](https://github.com/pgvector/pgvector-python/blob/master/examples/hybrid_search.py)).
```sql
SELECT id, content FROM items, plainto_tsquery('hello search') query
WHERE textsearch @@ query ORDER BY ts_rank_cd(textsearch, query) DESC LIMIT 5;
```
## Performance
Use `EXPLAIN ANALYZE` to debug performance.
@@ -259,7 +277,7 @@ SET max_parallel_workers_per_gather = 4;
If vectors are normalized to length 1 (like [OpenAI embeddings](https://platform.openai.com/docs/guides/embeddings/which-distance-function-should-i-use)), use inner product for best performance.
```sql
```tsql
SELECT * FROM items ORDER BY embedding <#> '[3,1,2]' LIMIT 5;
```
@@ -307,10 +325,11 @@ Yes, pgvector uses the write-ahead log (WAL), which allows for replication and p
#### What if I want to index vectors with more than 2,000 dimensions?
Two things you can try are:
Youll need to use [dimensionality reduction](https://en.wikipedia.org/wiki/Dimensionality_reduction) at the moment.
1. use dimensionality reduction
2. compile Postgres with a larger block size (`./configure --with-blocksize=32`) and edit the limit in `src/ivfflat.h`
#### Why am I seeing less results after adding an index?
The index was likely created with too little data for the number of lists. Drop the index until the table has more data.
## Reference
@@ -354,7 +373,11 @@ If your machine has multiple Postgres installations, specify the path to [pg_con
export PG_CONFIG=/Applications/Postgres.app/Contents/Versions/latest/bin/pg_config
```
Then re-run the installation instructions (run `make clean` before `make` if needed)
Then re-run the installation instructions (run `make clean` before `make` if needed). If `sudo` is needed for `make install`, use:
```sh
sudo --preserve-env=PG_CONFIG make install
```
### Missing Header
@@ -363,7 +386,7 @@ If compilation fails with `fatal error: postgres.h: No such file or directory`,
For Ubuntu and Debian, use:
```sh
sudo apt-get install postgresql-server-dev-15
sudo apt install postgresql-server-dev-15
```
Note: Replace `15` with your Postgres server version
@@ -374,7 +397,7 @@ Support for Windows is currently experimental. Use `nmake` to build:
```cmd
set "PGROOT=C:\Program Files\PostgreSQL\15"
git clone --branch v0.4.1 https://github.com/pgvector/pgvector.git
git clone --branch v0.4.4 https://github.com/pgvector/pgvector.git
cd pgvector
nmake /F Makefile.win
nmake /F Makefile.win install
@@ -395,9 +418,9 @@ This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (r
You can also build the image manually:
```sh
git clone --branch v0.4.1 https://github.com/pgvector/pgvector.git
git clone --branch v0.4.4 https://github.com/pgvector/pgvector.git
cd pgvector
docker build -t pgvector .
docker build --build-arg PG_MAJOR=15 -t myuser/pgvector .
```
### Homebrew
@@ -408,6 +431,8 @@ With Homebrew Postgres, you can use:
brew install pgvector
```
Note: This only adds it to the `postgresql@14` formula
### PGXN
Install from the [PostgreSQL Extension Network](https://pgxn.org/dist/vector) with:
@@ -416,6 +441,16 @@ Install from the [PostgreSQL Extension Network](https://pgxn.org/dist/vector) wi
pgxn install vector
```
### APT
Debian and Ubuntu packages are available from the [PostgreSQL APT Repository](https://wiki.postgresql.org/wiki/Apt). Follow the [setup instructions](https://wiki.postgresql.org/wiki/Apt#Quickstart) and run:
```sh
sudo apt install postgresql-15-pgvector
```
Note: Replace `15` with your Postgres server version
### Yum
RPM packages are available from the [PostgreSQL Yum Repository](https://yum.postgresql.org/). Follow the [setup instructions](https://www.postgresql.org/download/linux/redhat/) for your distribution and run:
@@ -438,16 +473,17 @@ conda install -c conda-forge pgvector
This method is [community-maintained](https://github.com/conda-forge/pgvector-feedstock) by [@mmcauliffe](https://github.com/mmcauliffe)
### Postgres.app
Download the [latest release](https://postgresapp.com/downloads.html) with Postgres 15+.
## Hosted Postgres
pgvector is available on [these providers](https://github.com/pgvector/pgvector/issues/54).
To request a new extension on other providers:
- Amazon RDS - follow the instructions on [this page](https://aws.amazon.com/rds/postgresql/faqs/)
- Google Cloud SQL - vote or comment on [this page](https://issuetracker.google.com/issues/265172065)
- Azure Database - vote or comment on [this page](https://feedback.azure.com/d365community/idea/7b423322-6189-ed11-a81b-000d3ae49307)
- 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/managed-database/p/pgvector-extension-for-postgresql)
- Heroku Postgres - vote or comment on [this page](https://github.com/heroku/roadmap/issues/156)
## Upgrading

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.4.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.4.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.4.4'" to load this file. \quit

View File

@@ -147,7 +147,7 @@ AddTupleToSort(Relation index, ItemPointer tid, Datum *values, IvfflatBuildState
{
double distance;
double minDistance = DBL_MAX;
int closestCenter = -1;
int closestCenter = 0;
VectorArray centers = buildstate->centers;
TupleTableSlot *slot = buildstate->slot;
int i;
@@ -438,8 +438,8 @@ ComputeCenters(IvfflatBuildState * buildstate)
{
ereport(NOTICE,
(errmsg("ivfflat index created with little data"),
errdetail("this will cause poor recall"),
errhint("drop the index until the table has more data")));
errdetail("This will cause low recall."),
errhint("Drop the index until the table has more data.")));
}
}
@@ -568,6 +568,21 @@ PrintKmeansMetrics(IvfflatBuildState * buildstate)
}
#endif
/*
* Scan table for tuples to index
*/
static void
ScanTable(IvfflatBuildState * buildstate)
{
#if PG_VERSION_NUM >= 120000
buildstate->reltuples = table_index_build_scan(buildstate->heap, buildstate->index, buildstate->indexInfo,
true, true, BuildCallback, (void *) buildstate, NULL);
#else
buildstate->reltuples = IndexBuildHeapScan(buildstate->heap, buildstate->index, buildstate->indexInfo,
true, BuildCallback, (void *) buildstate, NULL);
#endif
}
/*
* Create entry pages
*/
@@ -575,7 +590,7 @@ static void
CreateEntryPages(IvfflatBuildState * buildstate, ForkNumber forkNum)
{
AttrNumber attNums[] = {1};
Oid sortOperators[] = {Float8LessOperator};
Oid sortOperators[] = {Int4LessOperator};
Oid sortCollations[] = {InvalidOid};
bool nullsFirstFlags[] = {false};
@@ -585,25 +600,17 @@ CreateEntryPages(IvfflatBuildState * buildstate, ForkNumber forkNum)
/* Add tuples to sort */
if (buildstate->heap != NULL)
{
#if PG_VERSION_NUM >= 120000
buildstate->reltuples = table_index_build_scan(buildstate->heap, buildstate->index, buildstate->indexInfo,
true, true, BuildCallback, (void *) buildstate, NULL);
#else
buildstate->reltuples = IndexBuildHeapScan(buildstate->heap, buildstate->index, buildstate->indexInfo,
true, BuildCallback, (void *) buildstate, NULL);
#endif
}
IvfflatBench("assign tuples", ScanTable(buildstate));
/* Sort */
tuplesort_performsort(buildstate->sortstate);
IvfflatBench("sort tuples", tuplesort_performsort(buildstate->sortstate));
#ifdef IVFFLAT_KMEANS_DEBUG
PrintKmeansMetrics(buildstate);
#endif
/* Insert */
InsertTuples(buildstate->index, buildstate, forkNum);
IvfflatBench("load tuples", InsertTuples(buildstate->index, buildstate, forkNum));
tuplesort_end(buildstate->sortstate);
}
@@ -621,7 +628,7 @@ BuildIndex(Relation heap, Relation index, IndexInfo *indexInfo,
/* Create pages */
CreateMetaPage(index, buildstate->dimensions, buildstate->lists, forkNum);
CreateListPages(index, buildstate->centers, buildstate->dimensions, buildstate->lists, forkNum, &buildstate->listInfo);
IvfflatBench("CreateEntryPages", CreateEntryPages(buildstate, forkNum));
CreateEntryPages(buildstate, forkNum);
FreeBuildState(buildstate);
}

View File

@@ -7,6 +7,7 @@
#include "ivfflat.h"
#include "utils/guc.h"
#include "utils/selfuncs.h"
#include "utils/spccache.h"
#if PG_VERSION_NUM >= 120000
#include "commands/progress.h"
@@ -63,13 +64,13 @@ ivfflatbuildphasename(int64 phasenum)
static void
ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
Cost *indexStartupCost, Cost *indexTotalCost,
Selectivity *indexSelectivity, double *indexCorrelation
,double *indexPages
)
Selectivity *indexSelectivity, double *indexCorrelation,
double *indexPages)
{
GenericCosts costs;
int lists;
double ratio;
double spc_seq_page_cost;
Relation indexRel;
#if PG_VERSION_NUM < 120000
List *qinfos;
@@ -88,6 +89,22 @@ ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
MemSet(&costs, 0, sizeof(costs));
indexRel = index_open(path->indexinfo->indexoid, NoLock);
lists = IvfflatGetLists(indexRel);
index_close(indexRel, NoLock);
/* Get the ratio of lists that we need to visit */
ratio = ((double) ivfflat_probes) / lists;
if (ratio > 1.0)
ratio = 1.0;
/*
* This gives us the subset of tuples to visit. This value is passed into
* the generic cost estimator to determine the number of pages to visit
* during the index scan.
*/
costs.numIndexTuples = path->indexinfo->tuples * ratio;
#if PG_VERSION_NUM >= 120000
genericcostestimate(root, path, loop_count, &costs);
#else
@@ -95,22 +112,40 @@ ivfflatcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
genericcostestimate(root, path, loop_count, qinfos, &costs);
#endif
indexRel = index_open(path->indexinfo->indexoid, NoLock);
lists = IvfflatGetLists(indexRel);
index_close(indexRel, NoLock);
get_tablespace_page_costs(path->indexinfo->reltablespace, NULL, &spc_seq_page_cost);
ratio = ((double) ivfflat_probes) / lists;
if (ratio > 1)
ratio = 1;
/* Adjust cost if needed since TOAST not included in seq scan cost */
if (costs.numIndexPages > path->indexinfo->rel->pages && ratio < 0.5)
{
/* Change all page cost from random to sequential */
costs.indexTotalCost -= costs.numIndexPages * (costs.spc_random_page_cost - spc_seq_page_cost);
costs.indexTotalCost *= ratio;
/* Remove cost of extra pages */
costs.indexTotalCost -= (costs.numIndexPages - path->indexinfo->rel->pages) * spc_seq_page_cost;
}
else
{
/* Change some page cost from random to sequential */
costs.indexTotalCost -= 0.5 * costs.numIndexPages * (costs.spc_random_page_cost - spc_seq_page_cost);
}
/* Startup cost and total cost are same */
/*
* If the list selectivity is lower than what is returned from the generic
* cost estimator, use that.
*/
if (ratio < costs.indexSelectivity)
costs.indexSelectivity = ratio;
/* Use total cost since most work happens before first tuple is returned */
*indexStartupCost = costs.indexTotalCost;
*indexTotalCost = costs.indexTotalCost;
*indexSelectivity = costs.indexSelectivity;
*indexCorrelation = costs.indexCorrelation;
*indexPages = costs.numIndexPages;
elog(INFO, "ivfflatcostestimate = %f", costs.indexTotalCost);
/* Cost estimates for parallel workers applied outside of amcostestimate */
elog(INFO, "parallel_workers = %d, parallel aware = %d", path->path.parallel_workers, path->path.parallel_aware);
}
/*
@@ -151,6 +186,25 @@ ivfflatvalidate(Oid opclassoid)
return true;
}
static Size
ivfflatestimateparallelscan()
{
elog(INFO, "ivfflatestimateparallelscan");
return 0;
}
static void
ivfflatinitparallelscan(void *target)
{
elog(INFO, "ivfflatinitparallelscan");
}
static void
ivfflatparallelrescan(IndexScanDesc scan)
{
elog(INFO, "ivfflatparallelrescan");
}
/*
* Define index handler
*
@@ -178,7 +232,7 @@ ivfflathandler(PG_FUNCTION_ARGS)
amroutine->amstorage = false;
amroutine->amclusterable = false;
amroutine->ampredlocks = false;
amroutine->amcanparallel = false;
amroutine->amcanparallel = true;
amroutine->amcaninclude = false;
#if PG_VERSION_NUM >= 130000
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
@@ -212,9 +266,9 @@ ivfflathandler(PG_FUNCTION_ARGS)
amroutine->amrestrpos = NULL;
/* Interface functions to support parallel index scans */
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
amroutine->amestimateparallelscan = ivfflatestimateparallelscan;
amroutine->aminitparallelscan = ivfflatinitparallelscan;
amroutine->amparallelrescan = ivfflatparallelrescan;
PG_RETURN_POINTER(amroutine);
}

View File

@@ -187,14 +187,6 @@ typedef struct IvfflatScanList
double distance;
} IvfflatScanList;
typedef struct IvfflatScanItem
{
pairingheap_node ph_node;
BlockNumber searchPage;
double distance;
ItemPointerData tid;
} IvfflatScanItem;
typedef struct IvfflatScanOpaqueData
{
int probes;
@@ -212,13 +204,6 @@ typedef struct IvfflatScanOpaqueData
FmgrInfo *normprocinfo;
Oid collation;
/* Items */
int maxItems;
int itemCount;
pairingheap *itemQueue;
IvfflatScanItem *items;
IvfflatScanItem **sortedItems;
/* Lists */
pairingheap *listQueue;
IvfflatScanList lists[FLEXIBLE_ARRAY_MEMBER]; /* must come last */

View File

@@ -23,6 +23,10 @@ FindInsertPage(Relation rel, Datum *values, BlockNumber *insertPage, ListInfo *
OffsetNumber offno;
OffsetNumber maxoffno;
/* Avoid compiler warning */
listInfo->blkno = nextblkno;
listInfo->offno = FirstOffsetNumber;
procinfo = index_getprocinfo(rel, 1, IVFFLAT_DISTANCE_PROC);
collation = rel->rd_indcollation[0];
@@ -39,7 +43,7 @@ FindInsertPage(Relation rel, Datum *values, BlockNumber *insertPage, ListInfo *
list = (IvfflatList) PageGetItem(cpage, PageGetItemId(cpage, offno));
distance = DatumGetFloat8(FunctionCall2Coll(procinfo, collation, values[0], PointerGetDatum(&list->center)));
if (distance < minDistance)
if (distance < minDistance || !BlockNumberIsValid(*insertPage))
{
*insertPage = list->insertPage;
listInfo->blkno = nextblkno;

View File

@@ -1,6 +1,7 @@
#include "postgres.h"
#include <float.h>
#include <math.h>
#include "ivfflat.h"
#include "miscadmin.h"
@@ -211,7 +212,7 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers)
/* Check memory requirements */
/* Add one to error message to ceil */
if (totalSize / 1024 > maintenance_work_mem)
if (totalSize > (Size) maintenance_work_mem * 1024L)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("memory required is %zu MB, maintenance_work_mem is %d MB",
@@ -251,7 +252,7 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers)
for (j = 0; j < numSamples; j++)
{
minDistance = DBL_MAX;
closestCenter = -1;
closestCenter = 0;
/* Find closest center */
for (k = 0; k < numCenters; k++)
@@ -398,6 +399,14 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers)
if (centerCounts[j] > 0)
{
/* Double avoids overflow, but requires more memory */
/* TODO Update bounds */
for (k = 0; k < dimensions; k++)
{
if (isinf(vec->x[k]))
vec->x[k] = vec->x[k] > 0 ? FLT_MAX : -FLT_MAX;
}
for (k = 0; k < dimensions; k++)
vec->x[k] /= centerCounts[j];
}
@@ -461,12 +470,29 @@ CheckCenters(Relation index, VectorArray centers)
{
FmgrInfo *normprocinfo;
Oid collation;
Vector *vec;
int i;
int j;
double norm;
if (centers->length != centers->maxlen)
elog(ERROR, "Not enough centers. Please report a bug.");
/* Ensure no NaN or infinite values */
for (i = 0; i < centers->length; i++)
{
vec = VectorArrayGet(centers, i);
for (j = 0; j < vec->dim; j++)
{
if (isnan(vec->x[j]))
elog(ERROR, "NaN detected. Please report a bug.");
if (isinf(vec->x[j]))
elog(ERROR, "Infinite value detected. Please report a bug.");
}
}
/* Ensure no duplicate centers */
/* Fine to sort in-place */
qsort(centers->items, centers->length, VECTOR_SIZE(centers->dim), CompareVectors);

View File

@@ -26,21 +26,6 @@ CompareLists(const pairingheap_node *a, const pairingheap_node *b, void *arg)
return 0;
}
/*
* Compare item distances
*/
static int
CompareItems(const pairingheap_node *a, const pairingheap_node *b, void *arg)
{
if (((const IvfflatScanItem *) a)->distance > ((const IvfflatScanItem *) b)->distance)
return 1;
if (((const IvfflatScanItem *) a)->distance < ((const IvfflatScanItem *) b)->distance)
return -1;
return 0;
}
/*
* Get lists and sort by distance
*/
@@ -126,10 +111,13 @@ GetScanItems(IndexScanDesc scan, Datum value)
Datum datum;
bool isnull;
TupleDesc tupdesc = RelationGetDescr(scan->indexRelation);
int i;
double distance;
IvfflatScanItem *scanitem;
double maxDistance = DBL_MAX;
double tuples = 0;
#if PG_VERSION_NUM >= 120000
TupleTableSlot *slot = MakeSingleTupleTableSlot(so->tupdesc, &TTSOpsVirtual);
#else
TupleTableSlot *slot = MakeSingleTupleTableSlot(so->tupdesc);
#endif
/*
* Reuse same set of shared buffers for scan
@@ -138,6 +126,9 @@ GetScanItems(IndexScanDesc scan, Datum value)
*/
BufferAccessStrategy bas = GetAccessStrategy(BAS_BULKREAD);
if (scan->parallel_scan != NULL)
elog(INFO, "parallel scan");
/* Search closest probes lists */
while (!pairingheap_is_empty(so->listQueue))
{
@@ -155,40 +146,25 @@ GetScanItems(IndexScanDesc scan, Datum value)
{
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offno));
datum = index_getattr(itup, 1, tupdesc, &isnull);
distance = DatumGetFloat8(FunctionCall2Coll(so->procinfo, so->collation, datum, value));
if (so->itemCount < so->maxItems)
{
scanitem = &so->items[so->itemCount];
scanitem->searchPage = searchPage;
scanitem->tid = itup->t_tid;
scanitem->distance = distance;
so->itemCount++;
/*
* Add virtual tuple
*
* Use procinfo from the index instead of scan key for
* performance
*/
ExecClearTuple(slot);
slot->tts_values[0] = FunctionCall2Coll(so->procinfo, so->collation, datum, value);
slot->tts_isnull[0] = false;
slot->tts_values[1] = PointerGetDatum(&itup->t_tid);
slot->tts_isnull[1] = false;
slot->tts_values[2] = Int32GetDatum((int) searchPage);
slot->tts_isnull[2] = false;
ExecStoreVirtualTuple(slot);
/* Add to heap */
pairingheap_add(so->itemQueue, &scanitem->ph_node);
tuplesort_puttupleslot(so->sortstate, slot);
/* Calculate max distance */
if (so->itemCount == so->maxItems)
{
maxDistance = ((IvfflatScanItem *) pairingheap_first(so->itemQueue))->distance;
scanitem = &so->items[so->itemCount];
}
}
else if (distance < maxDistance)
{
/* Reuse */
scanitem->searchPage = searchPage;
scanitem->tid = itup->t_tid;
scanitem->distance = distance;
pairingheap_add(so->itemQueue, &scanitem->ph_node);
/* Remove */
scanitem = (IvfflatScanItem *) pairingheap_remove_first(so->itemQueue);
/* Update max distance */
maxDistance = ((IvfflatScanItem *) pairingheap_first(so->itemQueue))->distance;
}
tuples++;
}
searchPage = IvfflatPageGetOpaque(page)->nextblkno;
@@ -197,10 +173,16 @@ GetScanItems(IndexScanDesc scan, Datum value)
}
}
for (i = 0; i < so->itemCount; i++)
so->sortedItems[i] = (IvfflatScanItem *) pairingheap_remove_first(so->itemQueue);
FreeAccessStrategy(bas);
Assert(pairingheap_is_empty(so->itemQueue));
/* TODO Scan more lists */
if (tuples < 100)
ereport(DEBUG1,
(errmsg("index scan found few tuples"),
errdetail("Index may have been created with little data."),
errhint("Recreate the index and possibly decrease lists.")));
tuplesort_performsort(so->sortstate);
}
/*
@@ -212,6 +194,10 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
IndexScanDesc scan;
IvfflatScanOpaque so;
int lists;
AttrNumber attNums[] = {1};
Oid sortOperators[] = {Float8LessOperator};
Oid sortCollations[] = {InvalidOid};
bool nullsFirstFlags[] = {false};
int probes = ivfflat_probes;
scan = RelationGetIndexScan(index, nkeys, norderbys);
@@ -230,13 +216,26 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
so->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
so->collation = index->rd_indcollation[0];
so->listQueue = pairingheap_allocate(CompareLists, scan);
/* Create tuple description for sorting */
#if PG_VERSION_NUM >= 120000
so->tupdesc = CreateTemplateTupleDesc(3);
#else
so->tupdesc = CreateTemplateTupleDesc(3, false);
#endif
TupleDescInitEntry(so->tupdesc, (AttrNumber) 1, "distance", FLOAT8OID, -1, 0);
TupleDescInitEntry(so->tupdesc, (AttrNumber) 2, "tid", TIDOID, -1, 0);
TupleDescInitEntry(so->tupdesc, (AttrNumber) 3, "indexblkno", INT4OID, -1, 0);
so->maxItems = 10;
so->itemCount = 0;
so->itemQueue = pairingheap_allocate(CompareItems, scan);
so->items = palloc(sizeof(IvfflatScanItem) * (so->maxItems + 1));
so->sortedItems = palloc(sizeof(IvfflatScanItem *) * so->maxItems);
/* Prep sort */
so->sortstate = tuplesort_begin_heap(so->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, work_mem, NULL, false);
#if PG_VERSION_NUM >= 120000
so->slot = MakeSingleTupleTableSlot(so->tupdesc, &TTSOpsMinimalTuple);
#else
so->slot = MakeSingleTupleTableSlot(so->tupdesc);
#endif
so->listQueue = pairingheap_allocate(CompareLists, scan);
scan->opaque = so;
@@ -251,10 +250,13 @@ ivfflatrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int
{
IvfflatScanOpaque so = (IvfflatScanOpaque) scan->opaque;
#if PG_VERSION_NUM >= 130000
if (!so->first)
tuplesort_reset(so->sortstate);
#endif
so->first = true;
pairingheap_reset(so->listQueue);
pairingheap_reset(so->itemQueue);
so->itemCount = 0;
if (keys && scan->numberOfKeys > 0)
memmove(scan->keyData, keys, scan->numberOfKeys * sizeof(ScanKeyData));
@@ -314,18 +316,15 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
pfree(DatumGetPointer(value));
}
if (so->itemCount > 0)
if (tuplesort_gettupleslot(so->sortstate, true, false, so->slot, NULL))
{
IvfflatScanItem *scanitem;
so->itemCount--;
scanitem = so->sortedItems[so->itemCount];
ItemPointer tid = (ItemPointer) DatumGetPointer(slot_getattr(so->slot, 2, &so->isnull));
BlockNumber indexblkno = DatumGetInt32(slot_getattr(so->slot, 3, &so->isnull));
#if PG_VERSION_NUM >= 120000
scan->xs_heaptid = scanitem->tid;
scan->xs_heaptid = *tid;
#else
scan->xs_ctup.t_self = scanitem->tid;
scan->xs_ctup.t_self = *tid;
#endif
if (BufferIsValid(so->buf))
@@ -337,7 +336,7 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
*
* https://www.postgresql.org/docs/current/index-locking.html
*/
so->buf = ReadBuffer(scan->indexRelation, scanitem->searchPage);
so->buf = ReadBuffer(scan->indexRelation, indexblkno);
scan->xs_recheckorderby = false;
return true;
@@ -359,10 +358,7 @@ ivfflatendscan(IndexScanDesc scan)
ReleaseBuffer(so->buf);
pairingheap_free(so->listQueue);
pairingheap_free(so->itemQueue);
pfree(so->items);
pfree(so->sortedItems);
tuplesort_end(so->sortstate);
pfree(so);
scan->opaque = NULL;

View File

@@ -132,6 +132,8 @@ ivfflatbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
}
}
FreeAccessStrategy(bas);
return stats;
}

View File

@@ -42,7 +42,7 @@ CheckDims(Vector * a, Vector * b)
}
/*
* Ensure expected dimension
* Ensure expected dimensions
*/
static inline void
CheckExpectedDim(int32 typmod, int dim)
@@ -53,7 +53,9 @@ CheckExpectedDim(int32 typmod, int dim)
errmsg("expected %d dimensions, not %d", typmod, dim)));
}
/*
* Ensure valid dimensions
*/
static inline void
CheckDim(int dim)
{
@@ -79,13 +81,28 @@ CheckElement(float value)
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("NaN not allowed in vector")));
if (isinf(value))
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("infinite value not allowed in vector")));
}
/*
* Check for whitespace, since array_isspace() is static
*/
static inline bool
vector_isspace(char ch)
{
if (ch == ' ' ||
ch == '\t' ||
ch == '\n' ||
ch == '\r' ||
ch == '\v' ||
ch == '\f')
return true;
return false;
}
/*
* Check state array
*/
@@ -110,30 +127,6 @@ float_overflow_error(void)
}
#endif
/*
* Print vector - useful for debugging
*/
void
PrintVector(char *msg, Vector * vector)
{
StringInfoData buf;
int dim = vector->dim;
int i;
initStringInfo(&buf);
appendStringInfoChar(&buf, '[');
for (i = 0; i < dim; i++)
{
if (i > 0)
appendStringInfoString(&buf, ",");
appendStringInfoString(&buf, float8out_internal(vector->x[i]));
}
appendStringInfoChar(&buf, ']');
elog(INFO, "%s = %s", msg, buf.data);
}
/*
* Convert textual representation to internal representation
*/
@@ -149,11 +142,15 @@ vector_in(PG_FUNCTION_ARGS)
char *pt;
char *stringEnd;
Vector *result;
char *lit = pstrdup(str);
while (vector_isspace(*str))
str++;
if (*str != '[')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed vector literal: \"%s\"", str),
errmsg("malformed vector literal: \"%s\"", lit),
errdetail("Vector contents must start with \"[\".")));
str++;
@@ -167,6 +164,15 @@ vector_in(PG_FUNCTION_ARGS)
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("vector cannot have more than %d dimensions", VECTOR_MAX_DIM)));
while (vector_isspace(*pt))
pt++;
/* Check for empty string like float4in */
if (*pt == '\0')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type vector: \"%s\"", lit)));
/* Use strtof like float4in to avoid a double-rounding problem */
x[dim] = strtof(pt, &stringEnd);
CheckElement(x[dim]);
@@ -175,33 +181,53 @@ vector_in(PG_FUNCTION_ARGS)
if (stringEnd == pt)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type vector: \"%s\"", pt)));
errmsg("invalid input syntax for type vector: \"%s\"", lit)));
while (vector_isspace(*stringEnd))
stringEnd++;
if (*stringEnd != '\0' && *stringEnd != ']')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type vector: \"%s\"", pt)));
errmsg("invalid input syntax for type vector: \"%s\"", lit)));
pt = strtok(NULL, ",");
}
if (*stringEnd != ']')
if (stringEnd == NULL || *stringEnd != ']')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed vector literal"),
errmsg("malformed vector literal: \"%s\"", lit),
errdetail("Unexpected end of input.")));
if (stringEnd[1] != '\0')
stringEnd++;
/* Only whitespace is allowed after the closing brace */
while (vector_isspace(*stringEnd))
stringEnd++;
if (*stringEnd != '\0')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed vector literal"),
errmsg("malformed vector literal: \"%s\"", lit),
errdetail("Junk after closing right brace.")));
/* Ensure no consecutive delimiters since strtok skips */
for (pt = lit + 1; *pt != '\0'; pt++)
{
if (pt[-1] == ',' && *pt == ',')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed vector literal: \"%s\"", lit)));
}
if (dim < 1)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("vector must have at least 1 dimension")));
pfree(lit);
CheckExpectedDim(typmod, dim);
result = InitVector(dim);
@@ -272,6 +298,18 @@ vector_out(PG_FUNCTION_ARGS)
PG_RETURN_CSTRING(buf);
}
/*
* Print vector - useful for debugging
*/
void
PrintVector(char *msg, Vector * vector)
{
char *out = DatumGetPointer(DirectFunctionCall1(vector_out, PointerGetDatum(vector)));
elog(INFO, "%s = %s", msg, out);
pfree(out);
}
/*
* Convert type modifier
*/
@@ -330,7 +368,10 @@ vector_recv(PG_FUNCTION_ARGS)
result = InitVector(dim);
for (i = 0; i < dim; i++)
{
result->x[i] = pq_getmsgfloat4(buf);
CheckElement(result->x[i]);
}
PG_RETURN_POINTER(result);
}
@@ -396,10 +437,8 @@ array_to_vector(PG_FUNCTION_ARGS)
get_typlenbyvalalign(ARR_ELEMTYPE(array), &typlen, &typbyval, &typalign);
deconstruct_array(array, ARR_ELEMTYPE(array), typlen, typbyval, typalign, &elemsp, &nullsp, &nelemsp);
if (typmod == -1)
CheckDim(nelemsp);
else
CheckExpectedDim(typmod, nelemsp);
CheckDim(nelemsp);
CheckExpectedDim(typmod, nelemsp);
result = InitVector(nelemsp);
for (i = 0; i < nelemsp; i++)
@@ -409,6 +448,7 @@ array_to_vector(PG_FUNCTION_ARGS)
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("array must not containing NULLs")));
/* TODO Move outside loop in 0.5.0 */
if (ARR_ELEMTYPE(array) == INT4OID)
result->x[i] = DatumGetInt32(elemsp[i]);
else if (ARR_ELEMTYPE(array) == FLOAT8OID)
@@ -436,17 +476,19 @@ Datum
vector_to_float4(PG_FUNCTION_ARGS)
{
Vector *vec = PG_GETARG_VECTOR_P(0);
Datum *d;
Datum *datums;
ArrayType *result;
int i;
d = (Datum *) palloc(sizeof(Datum) * vec->dim);
datums = (Datum *) palloc(sizeof(Datum) * vec->dim);
for (i = 0; i < vec->dim; i++)
d[i] = Float4GetDatum(vec->x[i]);
datums[i] = Float4GetDatum(vec->x[i]);
/* Use TYPALIGN_INT for float4 */
result = construct_array(d, vec->dim, FLOAT4OID, sizeof(float4), true, TYPALIGN_INT);
result = construct_array(datums, vec->dim, FLOAT4OID, sizeof(float4), true, TYPALIGN_INT);
pfree(datums);
PG_RETURN_POINTER(result);
}
@@ -658,6 +700,13 @@ vector_add(PG_FUNCTION_ARGS)
for (int i = 0, imax = a->dim; i < imax; i++)
rx[i] = ax[i] + bx[i];
/* Check for overflow */
for (int i = 0, imax = a->dim; i < imax; i++)
{
if (isinf(rx[i]))
float_overflow_error();
}
PG_RETURN_POINTER(result);
}
@@ -684,6 +733,13 @@ vector_sub(PG_FUNCTION_ARGS)
for (int i = 0, imax = a->dim; i < imax; i++)
rx[i] = ax[i] - bx[i];
/* Check for overflow */
for (int i = 0, imax = a->dim; i < imax; i++)
{
if (isinf(rx[i]))
float_overflow_error();
}
PG_RETURN_POINTER(result);
}
@@ -829,12 +885,12 @@ vector_accum(PG_FUNCTION_ARGS)
n = statevalues[0] + 1.0;
statedatums = CreateStateDatums(dim);
statedatums[0] = Float8GetDatumFast(n);
statedatums[0] = Float8GetDatum(n);
if (newarr)
{
for (int i = 0; i < dim; i++)
statedatums[i + 1] = Float8GetDatumFast((double) x[i]);
statedatums[i + 1] = Float8GetDatum((double) x[i]);
}
else
{
@@ -842,10 +898,11 @@ vector_accum(PG_FUNCTION_ARGS)
{
double v = statevalues[i + 1] + x[i];
/* Check for overflow */
if (isinf(v))
float_overflow_error();
statedatums[i + 1] = Float8GetDatumFast(v);
statedatums[i + 1] = Float8GetDatum(v);
}
}
@@ -890,7 +947,7 @@ vector_combine(PG_FUNCTION_ARGS)
dim = STATE_DIMS(statearray2);
statedatums = CreateStateDatums(dim);
for (int i = 1; i <= dim; i++)
statedatums[i] = Float8GetDatumFast(statevalues2[i]);
statedatums[i] = Float8GetDatum(statevalues2[i]);
}
else if (n2 == 0.0)
{
@@ -898,7 +955,7 @@ vector_combine(PG_FUNCTION_ARGS)
dim = STATE_DIMS(statearray1);
statedatums = CreateStateDatums(dim);
for (int i = 1; i <= dim; i++)
statedatums[i] = Float8GetDatumFast(statevalues1[i]);
statedatums[i] = Float8GetDatum(statevalues1[i]);
}
else
{
@@ -910,14 +967,15 @@ vector_combine(PG_FUNCTION_ARGS)
{
double v = statevalues1[i] + statevalues2[i];
/* Check for overflow */
if (isinf(v))
float_overflow_error();
statedatums[i] = Float8GetDatumFast(v);
statedatums[i] = Float8GetDatum(v);
}
}
statedatums[0] = Float8GetDatumFast(n);
statedatums[0] = Float8GetDatum(n);
result = construct_array(statedatums, dim + 1,
FLOAT8OID,
@@ -940,7 +998,6 @@ vector_avg(PG_FUNCTION_ARGS)
float8 n;
uint16 dim;
Vector *result;
float v;
/* Check array before using */
statevalues = CheckStateArray(statearray, "vector_avg");
@@ -952,12 +1009,12 @@ vector_avg(PG_FUNCTION_ARGS)
/* Create vector */
dim = STATE_DIMS(statearray);
CheckDim(dim);
result = InitVector(dim);
for (int i = 0; i < dim; i++)
{
v = statevalues[i + 1] / n;
CheckElement(v);
result->x[i] = v;
result->x[i] = statevalues[i + 1] / n;
CheckElement(result->x[i]);
}
PG_RETURN_POINTER(result);

View File

@@ -46,6 +46,8 @@ SELECT '[1,2,3]'::vector::real[];
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
ERROR: vector cannot have more than 16000 dimensions
SELECT array_to_vector(array_agg(n), 16001, false) FROM generate_series(1, 16001) n;
ERROR: vector cannot have more than 16000 dimensions
-- ensure no error
SELECT ARRAY[1,2,3] = ARRAY[1,2,3];
?column?

View File

@@ -4,12 +4,16 @@ SELECT '[1,2,3]'::vector + '[4,5,6]';
[5,7,9]
(1 row)
SELECT '[3e38]'::vector + '[3e38]';
ERROR: value out of range: overflow
SELECT '[1,2,3]'::vector - '[4,5,6]';
?column?
------------
[-3,-3,-3]
(1 row)
SELECT '[-3e38]'::vector - '[3e38]';
ERROR: value out of range: overflow
SELECT vector_dims('[1,2,3]');
vector_dims
-------------
@@ -102,3 +106,5 @@ SELECT avg(v) FROM unnest(ARRAY[]::vector[]) v;
SELECT avg(v) FROM unnest(ARRAY['[1,2]'::vector, '[3]']) v;
ERROR: expected 2 dimensions, not 1
SELECT vector_avg(array_agg(n)) FROM generate_series(1, 16002) n;
ERROR: vector cannot have more than 16000 dimensions

View File

@@ -4,10 +4,22 @@ SELECT '[1,2,3]'::vector;
[1,2,3]
(1 row)
SELECT '[-1,2,3]'::vector;
vector
----------
[-1,2,3]
SELECT '[-1,-2,-3]'::vector;
vector
------------
[-1,-2,-3]
(1 row)
SELECT '[1.,2.,3.]'::vector;
vector
---------
[1,2,3]
(1 row)
SELECT ' [ 1, 2 , 3 ] '::vector;
vector
---------
[1,2,3]
(1 row)
SELECT '[1.23456]'::vector;
@@ -17,7 +29,7 @@ SELECT '[1.23456]'::vector;
(1 row)
SELECT '[hello,1]'::vector;
ERROR: invalid input syntax for type vector: "hello"
ERROR: invalid input syntax for type vector: "[hello,1]"
LINE 1: SELECT '[hello,1]'::vector;
^
SELECT '[NaN,1]'::vector;
@@ -32,13 +44,35 @@ SELECT '[-Infinity,1]'::vector;
ERROR: infinite value not allowed in vector
LINE 1: SELECT '[-Infinity,1]'::vector;
^
SELECT '[1.5e38,-1.5e38]'::vector;
vector
--------------------
[1.5e+38,-1.5e+38]
(1 row)
SELECT '[1.5e+38,-1.5e+38]'::vector;
vector
--------------------
[1.5e+38,-1.5e+38]
(1 row)
SELECT '[1.5e-38,-1.5e-38]'::vector;
vector
--------------------
[1.5e-38,-1.5e-38]
(1 row)
SELECT '[4e38,1]'::vector;
ERROR: infinite value not allowed in vector
LINE 1: SELECT '[4e38,1]'::vector;
^
SELECT '[1,2,3'::vector;
ERROR: malformed vector literal
ERROR: malformed vector literal: "[1,2,3"
LINE 1: SELECT '[1,2,3'::vector;
^
DETAIL: Unexpected end of input.
SELECT '[1,2,3]9'::vector;
ERROR: malformed vector literal
ERROR: malformed vector literal: "[1,2,3]9"
LINE 1: SELECT '[1,2,3]9'::vector;
^
DETAIL: Junk after closing right brace.
@@ -47,14 +81,36 @@ ERROR: malformed vector literal: "1,2,3"
LINE 1: SELECT '1,2,3'::vector;
^
DETAIL: Vector contents must start with "[".
SELECT '['::vector;
ERROR: malformed vector literal: "["
LINE 1: SELECT '['::vector;
^
DETAIL: Unexpected end of input.
SELECT '[,'::vector;
ERROR: malformed vector literal: "[,"
LINE 1: SELECT '[,'::vector;
^
DETAIL: Unexpected end of input.
SELECT '[]'::vector;
ERROR: vector must have at least 1 dimension
LINE 1: SELECT '[]'::vector;
^
SELECT '[1,]'::vector;
ERROR: invalid input syntax for type vector: "]"
ERROR: invalid input syntax for type vector: "[1,]"
LINE 1: SELECT '[1,]'::vector;
^
SELECT '[1a]'::vector;
ERROR: invalid input syntax for type vector: "[1a]"
LINE 1: SELECT '[1a]'::vector;
^
SELECT '[1,,3]'::vector;
ERROR: malformed vector literal: "[1,,3]"
LINE 1: SELECT '[1,,3]'::vector;
^
SELECT '[1, ,3]'::vector;
ERROR: invalid input syntax for type vector: "[1, ,3]"
LINE 1: SELECT '[1, ,3]'::vector;
^
SELECT '[1,2,3]'::vector(2);
ERROR: expected 2 dimensions, not 3
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::vector[]);

View File

@@ -10,6 +10,7 @@ SELECT '{-Infinity}'::real[]::vector;
SELECT '{}'::real[]::vector;
SELECT '[1,2,3]'::vector::real[];
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
SELECT array_to_vector(array_agg(n), 16001, false) FROM generate_series(1, 16001) n;
-- ensure no error
SELECT ARRAY[1,2,3] = ARRAY[1,2,3];

View File

@@ -1,5 +1,7 @@
SELECT '[1,2,3]'::vector + '[4,5,6]';
SELECT '[3e38]'::vector + '[3e38]';
SELECT '[1,2,3]'::vector - '[4,5,6]';
SELECT '[-3e38]'::vector - '[3e38]';
SELECT vector_dims('[1,2,3]');
@@ -24,3 +26,4 @@ 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;
SELECT vector_avg(array_agg(n)) FROM generate_series(1, 16002) n;

View File

@@ -1,15 +1,26 @@
SELECT '[1,2,3]'::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 '[NaN,1]'::vector;
SELECT '[Infinity,1]'::vector;
SELECT '[-Infinity,1]'::vector;
SELECT '[1.5e38,-1.5e38]'::vector;
SELECT '[1.5e+38,-1.5e+38]'::vector;
SELECT '[1.5e-38,-1.5e-38]'::vector;
SELECT '[4e38,1]'::vector;
SELECT '[1,2,3'::vector;
SELECT '[1,2,3]9'::vector;
SELECT '1,2,3'::vector;
SELECT '['::vector;
SELECT '[,'::vector;
SELECT '[]'::vector;
SELECT '[1,]'::vector;
SELECT '[1a]'::vector;
SELECT '[1,,3]'::vector;
SELECT '[1, ,3]'::vector;
SELECT '[1,2,3]'::vector(2);
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::vector[]);

View File

@@ -0,0 +1,16 @@
-- SET force_parallel_mode = on;
SET parallel_setup_cost = 10;
SET parallel_tuple_cost = 0.000001;
SET min_parallel_table_scan_size = 1;
SET min_parallel_index_scan_size = 1;
CREATE TABLE t (id integer, val vector(3));
ALTER TABLE t ALTER COLUMN val SET STORAGE PLAIN;
INSERT INTO t (id, val) SELECT n, ARRAY[random(), random(), random()] FROM generate_series(1,1000000) n;
CREATE INDEX ON t USING ivfflat (val) WITH (lists = 10);
SET ivfflat.probes = 4;
EXPLAIN SELECT * FROM t ORDER BY val <-> '[0.5,0.5,0.5]' LIMIT 5;
SELECT * FROM t ORDER BY val <-> '[0.5,0.5,0.5]' LIMIT 5;
DROP TABLE t;

View File

@@ -5,7 +5,7 @@ use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More tests => 31;
use Test::More;
my $dim = 32;
@@ -95,3 +95,5 @@ for my $i (1 .. 10)
);
test_index_replay("insert $i");
}
done_testing();

View File

@@ -2,7 +2,7 @@ use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More tests => 1;
use Test::More;
my $dim = 3;
@@ -39,3 +39,5 @@ $node->safe_psql("postgres",
# Check size
my $new_size = $node->safe_psql("postgres", "SELECT pg_total_relation_size('tst_v_idx');");
is($size, $new_size, "size does not change");
done_testing();

View File

@@ -2,7 +2,7 @@ use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More tests => 9;
use Test::More;
my $node;
my @queries = ();
@@ -15,6 +15,13 @@ sub test_recall
my $correct = 0;
my $total = 0;
my $explain = $node->safe_psql("postgres", qq(
SET enable_seqscan = off;
SET ivfflat.probes = $probes;
EXPLAIN ANALYZE SELECT i FROM tst ORDER BY v $operator '$queries[0]' LIMIT $limit;
));
like($explain, qr/Index Scan/);
for my $i (0 .. $#queries) {
my $actual = $node->safe_psql("postgres", qq(
SET enable_seqscan = off;
@@ -72,9 +79,9 @@ foreach (@operators) {
# Add index
my $opclass;
if ($operator == "<->") {
if ($operator eq "<->") {
$opclass = "vector_l2_ops";
} elsif ($operator == "<#>") {
} elsif ($operator eq "<#>") {
$opclass = "vector_ip_ops";
} else {
$opclass = "vector_cosine_ops";
@@ -82,7 +89,13 @@ foreach (@operators) {
$node->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v $opclass);");
# Test approximate results
test_recall(1, 0.75, $operator);
test_recall(10, 0.95, $operator);
test_recall(100, 1.0, $operator);
if ($operator ne "<#>") {
# TODO fix test
test_recall(1, 0.75, $operator);
test_recall(10, 0.95, $operator);
}
# Account for equal distances
test_recall(100, 0.9975, $operator);
}
done_testing();

View File

@@ -2,7 +2,7 @@ use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More tests => 3;
use Test::More;
# Initialize node
my $node = get_new_node('node');
@@ -34,3 +34,5 @@ $node->safe_psql("postgres",
# Test no error for duplicate centers
test_centers(10);
done_testing();

View File

@@ -2,7 +2,7 @@ use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More tests => 60;
use Test::More;
# Initialize node
my $node = get_new_node('node');
@@ -23,9 +23,9 @@ foreach (@operators) {
# Add index
my $opclass;
if ($operator == "<->") {
if ($operator eq "<->") {
$opclass = "vector_l2_ops";
} elsif ($operator == "<#>") {
} elsif ($operator eq "<#>") {
$opclass = "vector_ip_ops";
} else {
$opclass = "vector_cosine_ops";
@@ -43,3 +43,5 @@ foreach (@operators) {
is($res, $query);
}
}
done_testing();

View File

@@ -2,7 +2,7 @@ use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More tests => 3;
use Test::More;
# Initialize node
my $node = get_new_node('node');
@@ -29,3 +29,5 @@ my ($ret, $stdout, $stderr) = $node->psql("postgres",
"CREATE INDEX lists10000 ON tst USING ivfflat (v) WITH (lists = 10000);"
);
like($stderr, qr/memory required is/);
done_testing();

View File

@@ -2,7 +2,7 @@ use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More tests => 7;
use Test::More;
my $dim = 768;
@@ -53,3 +53,5 @@ $count = $node->safe_psql("postgres", qq(
));
is($count, $expected);
is(idx_scan(), 1);
done_testing();

View File

@@ -2,7 +2,7 @@ use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More tests => 5;
use Test::More;
# Initialize node
my $node = get_new_node('node');
@@ -33,3 +33,5 @@ is($avg, "[$r1,$r2,$r3]");
# Test explain
my $explain = $node->safe_psql("postgres", "EXPLAIN SELECT AVG(v) FROM tst;");
like($explain, qr/Partial Aggregate/);
done_testing();

View File

@@ -2,7 +2,7 @@ use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More tests => 1;
use Test::More;
my $dim = 1024;
@@ -30,3 +30,5 @@ my ($ret, $stdout, $stderr) = $node->psql("postgres",
"INSERT INTO tst SELECT array_agg(n), array_agg(n), array_agg(n) FROM generate_series(1, $dim) n"
);
like($stderr, qr/row is too big/);
done_testing();

View File

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