mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 12:07:34 +08:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d8b7e5f81 | ||
|
|
cebae5e5ff | ||
|
|
4c3332fc4d | ||
|
|
386a3b5dd5 | ||
|
|
fb819eb8b2 | ||
|
|
3f8407460e | ||
|
|
059e88dda5 | ||
|
|
12f158d4bb | ||
|
|
2a3c0a769a | ||
|
|
349d844fa1 | ||
|
|
a57ca81d8f | ||
|
|
dc4a79a7e7 | ||
|
|
1652a130c5 | ||
|
|
a6743bd206 | ||
|
|
e25627074f | ||
|
|
b3cad93f3a | ||
|
|
379a76098e |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@
|
|||||||
regression.*
|
regression.*
|
||||||
*.o
|
*.o
|
||||||
*.so
|
*.so
|
||||||
|
*.bc
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
## 0.3.1 (2022-11-02)
|
||||||
|
|
||||||
|
If upgrading from 0.2.7 or 0.3.0, [recreate](https://github.com/pgvector/pgvector#031) all `ivfflat` indexes after upgrading to ensure all data is indexed.
|
||||||
|
|
||||||
|
- Fixed issue with inserts silently corrupting `ivfflat` indexes (introduced in 0.2.7)
|
||||||
|
- Fixed segmentation fault with index creation when lists > 6500
|
||||||
|
|
||||||
## 0.3.0 (2022-10-15)
|
## 0.3.0 (2022-10-15)
|
||||||
|
|
||||||
- Added support for Postgres 15
|
- Added support for Postgres 15
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "vector",
|
"name": "vector",
|
||||||
"abstract": "Open-source vector similarity search for Postgres",
|
"abstract": "Open-source vector similarity search for Postgres",
|
||||||
"description": "Supports L2 distance, inner product, and cosine distance",
|
"description": "Supports L2 distance, inner product, and cosine distance",
|
||||||
"version": "0.3.0",
|
"version": "0.3.1",
|
||||||
"maintainer": [
|
"maintainer": [
|
||||||
"Andrew Kane <andrew@ankane.org>"
|
"Andrew Kane <andrew@ankane.org>"
|
||||||
],
|
],
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
"prereqs": {
|
"prereqs": {
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"requires": {
|
"requires": {
|
||||||
"PostgreSQL": "10.0"
|
"PostgreSQL": "10.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
"vector": {
|
"vector": {
|
||||||
"file": "sql/vector.sql",
|
"file": "sql/vector.sql",
|
||||||
"docfile": "README.md",
|
"docfile": "README.md",
|
||||||
"version": "0.3.0",
|
"version": "0.3.1",
|
||||||
"abstract": "Open-source vector similarity search for Postgres"
|
"abstract": "Open-source vector similarity search for Postgres"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
|||||||
EXTENSION = vector
|
EXTENSION = vector
|
||||||
EXTVERSION = 0.3.0
|
EXTVERSION = 0.3.1
|
||||||
|
|
||||||
MODULE_big = vector
|
MODULE_big = vector
|
||||||
DATA = $(wildcard sql/*--*.sql)
|
DATA = $(wildcard sql/*--*.sql)
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -17,7 +17,7 @@ Supports L2 distance, inner product, and cosine distance
|
|||||||
Compile and install the extension (supports Postgres 10+)
|
Compile and install the extension (supports Postgres 10+)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone --branch v0.3.0 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.3.1 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
make
|
make
|
||||||
make install # may need sudo
|
make install # may need sudo
|
||||||
@@ -232,7 +232,7 @@ This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres).
|
|||||||
You can also build the image manually
|
You can also build the image manually
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone --branch v0.3.0 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.3.1 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
docker build -t pgvector .
|
docker build -t pgvector .
|
||||||
```
|
```
|
||||||
@@ -270,6 +270,22 @@ Install the latest version and run:
|
|||||||
ALTER EXTENSION vector UPDATE;
|
ALTER EXTENSION vector UPDATE;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Upgrade Notes
|
||||||
|
|
||||||
|
### 0.3.1
|
||||||
|
|
||||||
|
If upgrading from 0.2.7 or 0.3.0, recreate all `ivfflat` indexes after upgrading to ensure all data is indexed.
|
||||||
|
|
||||||
|
```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;
|
||||||
|
```
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
Thanks to:
|
Thanks to:
|
||||||
|
|||||||
2
sql/vector--0.3.0--0.3.1.sql
Normal file
2
sql/vector--0.3.0--0.3.1.sql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION vector UPDATE TO '0.3.1'" to load this file. \quit
|
||||||
@@ -233,8 +233,8 @@ InsertTuples(Relation index, IvfflatBuildState * buildstate, ForkNumber forkNum)
|
|||||||
GenericXLogState *state;
|
GenericXLogState *state;
|
||||||
int list;
|
int list;
|
||||||
IndexTuple itup = NULL; /* silence compiler warning */
|
IndexTuple itup = NULL; /* silence compiler warning */
|
||||||
BlockNumber startPage = InvalidBlockNumber;
|
BlockNumber startPage;
|
||||||
BlockNumber insertPage = InvalidBlockNumber;
|
BlockNumber insertPage;
|
||||||
Size itemsz;
|
Size itemsz;
|
||||||
int i;
|
int i;
|
||||||
int64 inserted = 0;
|
int64 inserted = 0;
|
||||||
|
|||||||
@@ -97,9 +97,21 @@ InsertTuple(Relation rel, IndexTuple itup, Relation heapRel, Datum *values)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Buffer metabuf;
|
||||||
|
Buffer newbuf;
|
||||||
|
Page newpage;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* From ReadBufferExtended: Caller is responsible for ensuring
|
||||||
|
* that only one backend tries to extend a relation at the same
|
||||||
|
* time!
|
||||||
|
*/
|
||||||
|
metabuf = ReadBuffer(rel, IVFFLAT_METAPAGE_BLKNO);
|
||||||
|
LockBuffer(metabuf, BUFFER_LOCK_EXCLUSIVE);
|
||||||
|
|
||||||
/* Add a new page */
|
/* Add a new page */
|
||||||
Buffer newbuf = IvfflatNewBuffer(rel, MAIN_FORKNUM);
|
newbuf = IvfflatNewBuffer(rel, MAIN_FORKNUM);
|
||||||
Page newpage = GenericXLogRegisterBuffer(state, buf, GENERIC_XLOG_FULL_IMAGE);
|
newpage = GenericXLogRegisterBuffer(state, newbuf, GENERIC_XLOG_FULL_IMAGE);
|
||||||
|
|
||||||
insertPage = BufferGetBlockNumber(newbuf);
|
insertPage = BufferGetBlockNumber(newbuf);
|
||||||
|
|
||||||
@@ -117,6 +129,7 @@ InsertTuple(Relation rel, IndexTuple itup, Relation heapRel, Datum *values)
|
|||||||
/* Unlock */
|
/* Unlock */
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
UnlockReleaseBuffer(newbuf);
|
UnlockReleaseBuffer(newbuf);
|
||||||
|
UnlockReleaseBuffer(metabuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ InitCenters(Relation index, VectorArray samples, VectorArray centers, float *low
|
|||||||
FmgrInfo *procinfo;
|
FmgrInfo *procinfo;
|
||||||
Oid collation;
|
Oid collation;
|
||||||
int i;
|
int i;
|
||||||
int j;
|
int64 j;
|
||||||
double distance;
|
double distance;
|
||||||
double sum;
|
double sum;
|
||||||
double choice;
|
double choice;
|
||||||
@@ -182,8 +182,8 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers)
|
|||||||
Vector *vec;
|
Vector *vec;
|
||||||
Vector *newCenter;
|
Vector *newCenter;
|
||||||
int iteration;
|
int iteration;
|
||||||
int j;
|
int64 j;
|
||||||
int k;
|
int64 k;
|
||||||
int dimensions = centers->dim;
|
int dimensions = centers->dim;
|
||||||
int numCenters = centers->maxlen;
|
int numCenters = centers->maxlen;
|
||||||
int numSamples = samples->length;
|
int numSamples = samples->length;
|
||||||
@@ -227,6 +227,10 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers)
|
|||||||
errmsg("memory required is %zu MB, maintenance_work_mem is %d MB",
|
errmsg("memory required is %zu MB, maintenance_work_mem is %d MB",
|
||||||
totalSize / (1024 * 1024) + 1, maintenance_work_mem / 1024)));
|
totalSize / (1024 * 1024) + 1, maintenance_work_mem / 1024)));
|
||||||
|
|
||||||
|
/* Ensure indexing does not overflow */
|
||||||
|
if (numCenters * numCenters > INT_MAX)
|
||||||
|
elog(ERROR, "Indexing overflow detected. Please report a bug.");
|
||||||
|
|
||||||
/* Set support functions */
|
/* Set support functions */
|
||||||
procinfo = index_getprocinfo(index, 1, IVFFLAT_KMEANS_DISTANCE_PROC);
|
procinfo = index_getprocinfo(index, 1, IVFFLAT_KMEANS_DISTANCE_PROC);
|
||||||
normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
||||||
@@ -259,8 +263,6 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers)
|
|||||||
minDistance = DBL_MAX;
|
minDistance = DBL_MAX;
|
||||||
closestCenter = -1;
|
closestCenter = -1;
|
||||||
|
|
||||||
vec = VectorArrayGet(samples, j);
|
|
||||||
|
|
||||||
/* Find closest center */
|
/* Find closest center */
|
||||||
for (k = 0; k < numCenters; k++)
|
for (k = 0; k < numCenters; k++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ sub test_index_replay
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Use ARRAY[random(), random(), random(), ...] over
|
||||||
|
# SELECT array_agg(random()) FROM generate_series(1, $dim)
|
||||||
|
# to generate different values for each row
|
||||||
|
my $array_sql = join(",", ('random()') x $dim);
|
||||||
|
|
||||||
# Initialize primary node
|
# Initialize primary node
|
||||||
$node_primary = get_new_node('primary');
|
$node_primary = get_new_node('primary');
|
||||||
$node_primary->init(allows_streaming => 1);
|
$node_primary->init(allows_streaming => 1);
|
||||||
@@ -74,7 +79,7 @@ $node_replica->start;
|
|||||||
$node_primary->safe_psql("postgres", "CREATE EXTENSION vector;");
|
$node_primary->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
$node_primary->safe_psql("postgres", "CREATE TABLE tst (i int4, v vector($dim));");
|
$node_primary->safe_psql("postgres", "CREATE TABLE tst (i int4, v vector($dim));");
|
||||||
$node_primary->safe_psql("postgres",
|
$node_primary->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT i % 10, (SELECT array_agg(random()) FROM generate_series(1, $dim)) FROM generate_series(1, 100000) i;"
|
"INSERT INTO tst SELECT i % 10, ARRAY[$array_sql] FROM generate_series(1, 100000) i;"
|
||||||
);
|
);
|
||||||
$node_primary->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
$node_primary->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
||||||
|
|
||||||
@@ -90,7 +95,7 @@ for my $i (1 .. 10)
|
|||||||
test_index_replay("vacuum $i");
|
test_index_replay("vacuum $i");
|
||||||
my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
|
my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
|
||||||
$node_primary->safe_psql("postgres",
|
$node_primary->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT i % 10, (SELECT array_agg(random()) FROM generate_series(1, $dim)) FROM generate_series($start, $end) i;"
|
"INSERT INTO tst SELECT i % 10, ARRAY[$array_sql] FROM generate_series($start, $end) i;"
|
||||||
);
|
);
|
||||||
test_index_replay("insert $i");
|
test_index_replay("insert $i");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
use PostgresNode;
|
use PostgresNode;
|
||||||
use TestLib;
|
use TestLib;
|
||||||
use Test::More tests => 3;
|
use Test::More tests => 5;
|
||||||
|
|
||||||
|
my $dim = 768;
|
||||||
|
|
||||||
|
my $array_sql = join(",", ('random()') x $dim);
|
||||||
|
|
||||||
# Initialize node
|
# Initialize node
|
||||||
my $node = get_new_node('node');
|
my $node = get_new_node('node');
|
||||||
@@ -11,9 +15,9 @@ $node->start;
|
|||||||
|
|
||||||
# Create table and index
|
# Create table and index
|
||||||
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
$node->safe_psql("postgres", "CREATE TABLE tst (v vector(768));");
|
$node->safe_psql("postgres", "CREATE TABLE tst (v vector($dim));");
|
||||||
$node->safe_psql("postgres",
|
$node->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT (SELECT array_agg(random()) FROM generate_series(1, 768)) FROM generate_series(1, 10000) i;"
|
"INSERT INTO tst SELECT ARRAY[$array_sql] FROM generate_series(1, 10000) i;"
|
||||||
);
|
);
|
||||||
$node->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
$node->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
||||||
|
|
||||||
@@ -24,10 +28,18 @@ $node->pgbench(
|
|||||||
[qr{^$}],
|
[qr{^$}],
|
||||||
"concurrent INSERTs",
|
"concurrent INSERTs",
|
||||||
{
|
{
|
||||||
"007_concurrent" => q(
|
"007_inserts" => "INSERT INTO tst SELECT ARRAY[$array_sql] FROM generate_series(1, 10) i;"
|
||||||
BEGIN;
|
|
||||||
INSERT INTO tst SELECT (SELECT array_agg(random()) FROM generate_series(1, 768)) FROM generate_series(1, 10) i;
|
|
||||||
COMMIT;
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
my $expected = 10000 + 5 * 100 * 10;
|
||||||
|
|
||||||
|
my $count = $node->safe_psql("postgres", "SELECT COUNT(*) FROM tst;");
|
||||||
|
is($count, $expected);
|
||||||
|
|
||||||
|
$count = $node->safe_psql("postgres", qq(
|
||||||
|
SET enable_seqscan = off;
|
||||||
|
SET ivfflat.probes = 100;
|
||||||
|
SELECT COUNT(*) FROM (SELECT v FROM tst ORDER BY v <-> (SELECT v FROM tst LIMIT 1)) t;
|
||||||
|
));
|
||||||
|
is($count, $expected);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
comment = 'vector data type and ivfflat access method'
|
comment = 'vector data type and ivfflat access method'
|
||||||
default_version = '0.3.0'
|
default_version = '0.3.1'
|
||||||
module_pathname = '$libdir/vector'
|
module_pathname = '$libdir/vector'
|
||||||
relocatable = true
|
relocatable = true
|
||||||
|
|||||||
Reference in New Issue
Block a user