mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 03:57:34 +08:00
Compare commits
17 Commits
hnsw-datum
...
ivfflat-pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b2c593873 | ||
|
|
447ef4d27a | ||
|
|
b36cd22ccc | ||
|
|
b447ae4989 | ||
|
|
efed873a3e | ||
|
|
14b278dec9 | ||
|
|
133a728e48 | ||
|
|
ca10cbaa7d | ||
|
|
eb29019a14 | ||
|
|
334c386a45 | ||
|
|
1cbd204f52 | ||
|
|
5ba62fca84 | ||
|
|
22cb2a3fe7 | ||
|
|
72b144906a | ||
|
|
a618c1bc78 | ||
|
|
f43cd0ed98 | ||
|
|
51df640961 |
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
dev-files: true
|
||||
- run: make
|
||||
env:
|
||||
PG_CFLAGS: -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
|
||||
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
|
||||
- run: |
|
||||
export PG_CONFIG=`which pg_config`
|
||||
sudo --preserve-env=PG_CONFIG make install
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
postgres-version: 14
|
||||
- run: make
|
||||
env:
|
||||
PG_CFLAGS: -Wall -Wextra -Werror -Wno-unused-parameter
|
||||
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter
|
||||
- run: make install
|
||||
- run: make installcheck
|
||||
- if: ${{ failure() }}
|
||||
@@ -97,4 +97,15 @@ jobs:
|
||||
sudo -u postgres make installcheck
|
||||
sudo -u postgres make prove_installcheck
|
||||
env:
|
||||
PG_CFLAGS: -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
|
||||
PG_CFLAGS: -DUSE_ASSERT_CHECKING -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare
|
||||
valgrind:
|
||||
if: ${{ !startsWith(github.ref_name, 'mac') && !startsWith(github.ref_name, 'windows') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ankane/setup-postgres-valgrind@v1
|
||||
with:
|
||||
postgres-version: 16
|
||||
- run: make
|
||||
- run: sudo --preserve-env=PG_CONFIG make install
|
||||
- run: make installcheck
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
## 0.6.1 (unreleased)
|
||||
|
||||
- Fixed error with `ANALYZE` and vectors with different dimensions
|
||||
- Fixed error with `shared_preload_libraries`
|
||||
|
||||
## 0.6.0 (2024-01-29)
|
||||
|
||||
|
||||
6
Makefile
6
Makefile
@@ -71,11 +71,9 @@ PG_MAJOR ?= 16
|
||||
.PHONY: docker
|
||||
|
||||
docker:
|
||||
docker build --pull --no-cache --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:pg$(PG_MAJOR) .
|
||||
docker build --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:$(EXTVERSION)-pg$(PG_MAJOR) .
|
||||
docker build --pull --no-cache --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:pg$(PG_MAJOR) -t pgvector/pgvector:$(EXTVERSION)-pg$(PG_MAJOR) .
|
||||
|
||||
.PHONY: docker-release
|
||||
|
||||
docker-release:
|
||||
docker buildx build --push --pull --no-cache --platform linux/amd64,linux/arm64 --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:pg$(PG_MAJOR) .
|
||||
docker buildx build --push --platform linux/amd64,linux/arm64 --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:$(EXTVERSION)-pg$(PG_MAJOR) .
|
||||
docker buildx build --push --pull --no-cache --platform linux/amd64,linux/arm64 --build-arg PG_MAJOR=$(PG_MAJOR) -t pgvector/pgvector:pg$(PG_MAJOR) -t pgvector/pgvector:$(EXTVERSION)-pg$(PG_MAJOR) .
|
||||
|
||||
17
README.md
17
README.md
@@ -28,7 +28,7 @@ make install # may need sudo
|
||||
|
||||
See the [installation notes](#installation-notes) if you run into issues
|
||||
|
||||
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). There are also instructions for [GitHub Actions](https://github.com/pgvector/setup-pgvector).
|
||||
You can also install it with [Docker](#docker), [Homebrew](#homebrew), [PGXN](#pgxn), [APT](#apt), [Yum](#yum), [pkg](#pkg), or [conda-forge](#conda-forge), and it comes preinstalled with [Postgres.app](#postgresapp) and many [hosted providers](#hosted-postgres). There are also instructions for [GitHub Actions](https://github.com/pgvector/setup-pgvector).
|
||||
|
||||
### Windows
|
||||
|
||||
@@ -716,6 +716,21 @@ sudo dnf install pgvector_16
|
||||
|
||||
Note: Replace `16` with your Postgres server version
|
||||
|
||||
### pkg
|
||||
|
||||
Install the FreeBSD package with:
|
||||
|
||||
```sh
|
||||
pkg install postgresql15-pg_vector
|
||||
```
|
||||
|
||||
or the port with:
|
||||
|
||||
```sh
|
||||
cd /usr/ports/databases/pgvector
|
||||
make install
|
||||
```
|
||||
|
||||
### conda-forge
|
||||
|
||||
With Conda Postgres, install from [conda-forge](https://anaconda.org/conda-forge/pgvector) with:
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "commands/progress.h"
|
||||
#include "commands/vacuum.h"
|
||||
#include "hnsw.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/guc.h"
|
||||
#include "utils/selfuncs.h"
|
||||
|
||||
@@ -28,7 +29,7 @@ static relopt_kind hnsw_relopt_kind;
|
||||
* this grows bigger, we should use a shmem_request_hook and
|
||||
* RequestAddinShmemSpace() to pre-reserve space for this.
|
||||
*/
|
||||
static void
|
||||
void
|
||||
HnswInitLockTranche(void)
|
||||
{
|
||||
int *tranche_ids;
|
||||
@@ -53,7 +54,8 @@ HnswInitLockTranche(void)
|
||||
void
|
||||
HnswInit(void)
|
||||
{
|
||||
HnswInitLockTranche();
|
||||
if (!process_shared_preload_libraries_in_progress)
|
||||
HnswInitLockTranche();
|
||||
|
||||
hnsw_relopt_kind = add_reloption_kind();
|
||||
add_int_reloption(hnsw_relopt_kind, "m", "Max number of connections",
|
||||
|
||||
@@ -389,6 +389,7 @@ void HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation i
|
||||
void HnswSetElementTuple(char *base, HnswElementTuple etup, HnswElement element);
|
||||
void HnswUpdateConnection(char *base, HnswElement element, HnswCandidate * hc, int lm, int lc, int *updateIdx, Relation index, FmgrInfo *procinfo, Oid collation);
|
||||
void HnswLoadNeighbors(HnswElement element, Relation index, int m);
|
||||
void HnswInitLockTranche(void);
|
||||
PGDLLEXPORT void HnswParallelBuildMain(dsm_segment *seg, shm_toc *toc);
|
||||
|
||||
/* Index access methods */
|
||||
|
||||
@@ -176,7 +176,7 @@ CreateGraphPages(HnswBuildState * buildstate)
|
||||
Size etupSize;
|
||||
Size ntupSize;
|
||||
Size combinedSize;
|
||||
void *valuePtr = HnswPtrAccess(base, element->value);
|
||||
Pointer valuePtr = HnswPtrAccess(base, element->value);
|
||||
|
||||
/* Update iterator */
|
||||
iter = element->next;
|
||||
@@ -601,6 +601,9 @@ BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
||||
static void
|
||||
InitGraph(HnswGraph * graph, char *base, long memoryTotal)
|
||||
{
|
||||
/* Initialize the lock tranche if needed */
|
||||
HnswInitLockTranche();
|
||||
|
||||
HnswPtrStore(base, graph->head, (HnswElement) NULL);
|
||||
HnswPtrStore(base, graph->entryPoint, (HnswElement) NULL);
|
||||
graph->memoryUsed = 0;
|
||||
@@ -669,6 +672,10 @@ InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, Index
|
||||
buildstate->efConstruction = HnswGetEfConstruction(index);
|
||||
buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod;
|
||||
|
||||
/* Require column to have dimensions to be indexed */
|
||||
if (buildstate->dimensions < 0)
|
||||
elog(ERROR, "column does not have dimensions");
|
||||
|
||||
if (buildstate->dimensions > HNSW_MAX_DIM)
|
||||
elog(ERROR, "column cannot have more than %d dimensions for hnsw index", HNSW_MAX_DIM);
|
||||
|
||||
@@ -970,6 +977,14 @@ HnswBeginParallel(HnswBuildState * buildstate, bool isconcurrent, int request)
|
||||
/* Report less than allocated so never fails */
|
||||
InitGraph(&hnswshared->graphData, hnswarea, esthnswarea - 1024 * 1024);
|
||||
|
||||
/*
|
||||
* Avoid base address for relptr for Postgres < 14.5
|
||||
* https://github.com/postgres/postgres/commit/7201cd18627afc64850537806da7f22150d1a83b
|
||||
*/
|
||||
#if PG_VERSION_NUM < 140005
|
||||
hnswshared->graphData.memoryUsed += MAXALIGN(1);
|
||||
#endif
|
||||
|
||||
shm_toc_insert(pcxt->toc, PARALLEL_KEY_HNSW_SHARED, hnswshared);
|
||||
shm_toc_insert(pcxt->toc, PARALLEL_KEY_HNSW_AREA, hnswarea);
|
||||
|
||||
|
||||
@@ -904,6 +904,27 @@ IvfflatBeginParallel(IvfflatBuildState * buildstate, bool isconcurrent, int requ
|
||||
WaitForParallelWorkersToAttach(pcxt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute parallel workers
|
||||
*/
|
||||
static int
|
||||
ComputeParallelWorkers(Relation heap, Relation index)
|
||||
{
|
||||
int parallel_workers;
|
||||
|
||||
/* Make sure it's safe to use parallel workers */
|
||||
parallel_workers = plan_create_index_workers(RelationGetRelid(heap), RelationGetRelid(index));
|
||||
if (parallel_workers == 0)
|
||||
return 0;
|
||||
|
||||
/* Use parallel_workers storage parameter on table if set */
|
||||
parallel_workers = RelationGetParallelWorkers(heap, -1);
|
||||
if (parallel_workers != -1)
|
||||
return Min(parallel_workers, max_parallel_maintenance_workers);
|
||||
|
||||
return max_parallel_maintenance_workers;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan table for tuples to index
|
||||
*/
|
||||
@@ -923,7 +944,7 @@ AssignTuples(IvfflatBuildState * buildstate)
|
||||
|
||||
/* Calculate parallel workers */
|
||||
if (buildstate->heap != NULL)
|
||||
parallel_workers = plan_create_index_workers(RelationGetRelid(buildstate->heap), RelationGetRelid(buildstate->index));
|
||||
parallel_workers = ComputeParallelWorkers(buildstate->heap, buildstate->index);
|
||||
|
||||
/* Attempt to launch parallel worker scan when required */
|
||||
if (parallel_workers > 0)
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
use PostgresNode;
|
||||
use TestLib;
|
||||
use Test::More;
|
||||
|
||||
my $node;
|
||||
my @queries = ();
|
||||
my @expected;
|
||||
my $limit = 20;
|
||||
|
||||
sub test_recall
|
||||
{
|
||||
my ($min, $operator) = @_;
|
||||
my $correct = 0;
|
||||
my $total = 0;
|
||||
|
||||
my $explain = $node->safe_psql("postgres", qq(
|
||||
SET enable_seqscan = off;
|
||||
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;
|
||||
SELECT i FROM tst ORDER BY v $operator '$queries[$i]' LIMIT $limit;
|
||||
));
|
||||
my @actual_ids = split("\n", $actual);
|
||||
my %actual_set = map { $_ => 1 } @actual_ids;
|
||||
|
||||
my @expected_ids = split("\n", $expected[$i]);
|
||||
|
||||
foreach (@expected_ids)
|
||||
{
|
||||
if (exists($actual_set{$_}))
|
||||
{
|
||||
$correct++;
|
||||
}
|
||||
$total++;
|
||||
}
|
||||
}
|
||||
|
||||
cmp_ok($correct / $total, ">=", $min, $operator);
|
||||
}
|
||||
|
||||
# Initialize node
|
||||
$node = get_new_node('node');
|
||||
$node->init;
|
||||
$node->start;
|
||||
|
||||
# Create table
|
||||
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||
$node->safe_psql("postgres", "CREATE TABLE tst (i int4, v float4[3]);");
|
||||
$node->safe_psql("postgres",
|
||||
"INSERT INTO tst SELECT i, ARRAY[random(), random(), random()] FROM generate_series(1, 10000) i;"
|
||||
);
|
||||
|
||||
$node->safe_psql("postgres", qq(
|
||||
CREATE FUNCTION float4_l2_distance(float4[], float4[]) RETURNS float8
|
||||
AS 'BEGIN RETURN l2_distance(\$1::vector, \$2::vector); END;'
|
||||
LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION float4_l2_squared_distance(float4[], float4[]) RETURNS float8
|
||||
AS 'BEGIN RETURN vector_l2_squared_distance(\$1::vector, \$2::vector); END;'
|
||||
LANGUAGE plpgsql IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE OPERATOR <-> (
|
||||
LEFTARG = float4[], RIGHTARG = float4[], PROCEDURE = float4_l2_distance,
|
||||
COMMUTATOR = '<->'
|
||||
);
|
||||
|
||||
CREATE OPERATOR CLASS float4_l2_ops
|
||||
FOR TYPE float4[] USING hnsw AS
|
||||
OPERATOR 1 <-> (float4[], float4[]) FOR ORDER BY float_ops,
|
||||
FUNCTION 1 float4_l2_squared_distance(float4[], float4[]);
|
||||
));
|
||||
|
||||
# Generate queries
|
||||
for (1 .. 20)
|
||||
{
|
||||
my $r1 = rand();
|
||||
my $r2 = rand();
|
||||
my $r3 = rand();
|
||||
push(@queries, "{$r1,$r2,$r3}");
|
||||
}
|
||||
|
||||
# Check each index type
|
||||
my @operators = ("<->");
|
||||
my @opclasses = ("float4_l2_ops");
|
||||
|
||||
for my $i (0 .. $#operators)
|
||||
{
|
||||
my $operator = $operators[$i];
|
||||
my $opclass = $opclasses[$i];
|
||||
|
||||
# Get exact results
|
||||
@expected = ();
|
||||
foreach (@queries)
|
||||
{
|
||||
my $res = $node->safe_psql("postgres", "SELECT i FROM tst ORDER BY v $operator '$_' LIMIT $limit;");
|
||||
push(@expected, $res);
|
||||
}
|
||||
|
||||
# Add index
|
||||
$node->safe_psql("postgres", "CREATE INDEX ON tst USING hnsw (v $opclass);");
|
||||
|
||||
my $min = $operator eq "<#>" ? 0.80 : 0.99;
|
||||
test_recall($min, $operator);
|
||||
}
|
||||
|
||||
done_testing();
|
||||
Reference in New Issue
Block a user