mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 12:07:34 +08:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f52127bee | ||
|
|
4778039ffa | ||
|
|
e3c9d70acc | ||
|
|
5971e28f8c | ||
|
|
2da27e2f01 | ||
|
|
7dd934913d | ||
|
|
9b46987e73 | ||
|
|
868e1f99a4 | ||
|
|
354271178f | ||
|
|
fe9b218903 | ||
|
|
3cdd1c16fb | ||
|
|
4f7a5c92da | ||
|
|
9399575e02 | ||
|
|
e7539fe548 | ||
|
|
61860ee6d9 | ||
|
|
eb99730d87 | ||
|
|
e5da5cc532 | ||
|
|
f59b1184fc | ||
|
|
cab103a2e7 | ||
|
|
0094b70a36 | ||
|
|
46ed56d80f | ||
|
|
d390cc9fa6 | ||
|
|
02ae98791c | ||
|
|
b64c7d6228 | ||
|
|
76f117c81c | ||
|
|
5414b2bc9e | ||
|
|
6ba583b587 | ||
|
|
da88217fd8 | ||
|
|
d5da22f24b | ||
|
|
062e220936 | ||
|
|
3d2b867a58 | ||
|
|
5aeabc3648 | ||
|
|
687ec6d8d7 | ||
|
|
51dc18d5b7 | ||
|
|
cb108ebfd2 | ||
|
|
12bc4eb036 | ||
|
|
fd0e472316 | ||
|
|
9b11b05cd7 | ||
|
|
ab079c1911 | ||
|
|
f4eaa3de1f | ||
|
|
e47675d6ae | ||
|
|
20167ddc33 |
@@ -1,6 +1,6 @@
|
|||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*.{c,h}]
|
[*.{c,h,pl}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = tab
|
indent_size = tab
|
||||||
tab_width = 4
|
tab_width = 4
|
||||||
|
|||||||
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,6 +1,17 @@
|
|||||||
|
## 0.2.5 (2022-02-11)
|
||||||
|
|
||||||
|
- Reduced memory usage during index creation
|
||||||
|
- Fixed index creation exceeding `maintenance_work_mem`
|
||||||
|
- Fixed error with index creation when lists > 1600
|
||||||
|
|
||||||
|
## 0.2.4 (2022-02-06)
|
||||||
|
|
||||||
|
- Added support for parallel vacuum
|
||||||
|
- Fixed issue with index not reusing space
|
||||||
|
|
||||||
## 0.2.3 (2022-01-30)
|
## 0.2.3 (2022-01-30)
|
||||||
|
|
||||||
- Added build progress for Postgres 12+
|
- Added indexing progress for Postgres 12+
|
||||||
- Improved interrupt handling during index creation
|
- Improved interrupt handling during index creation
|
||||||
|
|
||||||
## 0.2.2 (2022-01-15)
|
## 0.2.2 (2022-01-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.2.3",
|
"version": "0.2.5",
|
||||||
"maintainer": [
|
"maintainer": [
|
||||||
"Andrew Kane <andrew@ankane.org>"
|
"Andrew Kane <andrew@ankane.org>"
|
||||||
],
|
],
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
"vector": {
|
"vector": {
|
||||||
"file": "sql/vector.sql",
|
"file": "sql/vector.sql",
|
||||||
"docfile": "README.md",
|
"docfile": "README.md",
|
||||||
"version": "0.2.3",
|
"version": "0.2.5",
|
||||||
"abstract": "Open-source vector similarity search for Postgres"
|
"abstract": "Open-source vector similarity search for Postgres"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -1,5 +1,5 @@
|
|||||||
EXTENSION = vector
|
EXTENSION = vector
|
||||||
EXTVERSION = 0.2.3
|
EXTVERSION = 0.2.5
|
||||||
|
|
||||||
MODULE_big = vector
|
MODULE_big = vector
|
||||||
DATA = $(wildcard sql/*--*.sql)
|
DATA = $(wildcard sql/*--*.sql)
|
||||||
@@ -21,7 +21,7 @@ endif
|
|||||||
# For auto-vectorization:
|
# For auto-vectorization:
|
||||||
# - GCC (needs -ftree-vectorize OR -O3) - https://gcc.gnu.org/projects/tree-ssa/vectorization.html
|
# - 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
|
# - Clang (could use pragma instead) - https://llvm.org/docs/Vectorizers.html
|
||||||
PG_CFLAGS = $(OPTFLAGS) -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
|
# Debug GCC auto-vectorization
|
||||||
# PG_CFLAGS += -fopt-info-vec
|
# PG_CFLAGS += -fopt-info-vec
|
||||||
|
|||||||
27
README.md
27
README.md
@@ -17,7 +17,7 @@ Supports L2 distance, inner product, and cosine distance
|
|||||||
Compile and install the extension (supports Postgres 9.6+)
|
Compile and install the extension (supports Postgres 9.6+)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone --branch v0.2.3 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.2.5 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
make
|
make
|
||||||
make install # may need sudo
|
make install # may need sudo
|
||||||
@@ -77,19 +77,7 @@ Cosine distance
|
|||||||
CREATE INDEX ON table USING ivfflat (column vector_cosine_ops);
|
CREATE INDEX ON table USING ivfflat (column vector_cosine_ops);
|
||||||
```
|
```
|
||||||
|
|
||||||
Indexes should be created after the table has data for optimal clustering. If the distribution of data changes significantly, you can reindex without downtime:
|
Indexes should be created after the table has some data for optimal clustering. Also, unlike typical indexes which only affect performance, you may see different results for queries after adding an approximate index.
|
||||||
|
|
||||||
```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
|
### Index Options
|
||||||
|
|
||||||
@@ -120,7 +108,7 @@ SELECT ...
|
|||||||
COMMIT;
|
COMMIT;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Indexing Progress [unreleased]
|
### Indexing Progress
|
||||||
|
|
||||||
Check [indexing progress](https://www.postgresql.org/docs/current/progress-reporting.html#CREATE-INDEX-PROGRESS-REPORTING) with Postgres 12+
|
Check [indexing progress](https://www.postgresql.org/docs/current/progress-reporting.html#CREATE-INDEX-PROGRESS-REPORTING) with Postgres 12+
|
||||||
|
|
||||||
@@ -194,6 +182,7 @@ Libraries that use pgvector:
|
|||||||
|
|
||||||
- [pgvector-python](https://github.com/pgvector/pgvector-python) (Python)
|
- [pgvector-python](https://github.com/pgvector/pgvector-python) (Python)
|
||||||
- [Neighbor](https://github.com/ankane/neighbor) (Ruby)
|
- [Neighbor](https://github.com/ankane/neighbor) (Ruby)
|
||||||
|
- [pgvector-ruby](https://github.com/pgvector/pgvector-ruby) (Ruby)
|
||||||
- [pgvector-node](https://github.com/pgvector/pgvector-node) (Node.js)
|
- [pgvector-node](https://github.com/pgvector/pgvector-node) (Node.js)
|
||||||
- [pgvector-go](https://github.com/pgvector/pgvector-go) (Go)
|
- [pgvector-go](https://github.com/pgvector/pgvector-go) (Go)
|
||||||
- [pgvector-rust](https://github.com/pgvector/pgvector-rust) (Rust)
|
- [pgvector-rust](https://github.com/pgvector/pgvector-rust) (Rust)
|
||||||
@@ -231,7 +220,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.2.3 https://github.com/pgvector/pgvector.git
|
git clone --branch v0.2.5 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
docker build -t pgvector .
|
docker build -t pgvector .
|
||||||
```
|
```
|
||||||
@@ -315,6 +304,12 @@ make installcheck REGRESS=functions # regression test
|
|||||||
make prove_installcheck PROVE_TESTS=test/t/001_wal.pl # TAP test
|
make prove_installcheck PROVE_TESTS=test/t/001_wal.pl # TAP test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To enable benchmarking:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make clean && PG_CFLAGS=-DIVFFLAT_BENCH make && make install
|
||||||
|
```
|
||||||
|
|
||||||
Resources for contributors
|
Resources for contributors
|
||||||
|
|
||||||
- [Extension Building Infrastructure](https://www.postgresql.org/docs/current/extend-pgxs.html)
|
- [Extension Building Infrastructure](https://www.postgresql.org/docs/current/extend-pgxs.html)
|
||||||
|
|||||||
2
sql/vector--0.2.3--0.2.4.sql
Normal file
2
sql/vector--0.2.3--0.2.4.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.2.4'" to load this file. \quit
|
||||||
2
sql/vector--0.2.4--0.2.5.sql
Normal file
2
sql/vector--0.2.4--0.2.5.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.2.5'" to load this file. \quit
|
||||||
@@ -288,7 +288,7 @@ InsertTuples(Relation index, IvfflatBuildState * buildstate, ForkNumber forkNum)
|
|||||||
IvfflatCommitBuffer(buf, state);
|
IvfflatCommitBuffer(buf, state);
|
||||||
|
|
||||||
/* Set the start and insert pages */
|
/* Set the start and insert pages */
|
||||||
IvfflatUpdateList(index, state, buildstate->listInfo[i], insertPage, startPage, forkNum);
|
IvfflatUpdateList(index, state, buildstate->listInfo[i], insertPage, InvalidBlockNumber, startPage, forkNum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,14 +377,19 @@ ComputeCenters(IvfflatBuildState * buildstate)
|
|||||||
if (numSamples < 10000)
|
if (numSamples < 10000)
|
||||||
numSamples = 10000;
|
numSamples = 10000;
|
||||||
|
|
||||||
/* Sample samples */
|
/* Skip samples for unlogged table */
|
||||||
|
if (buildstate->heap == NULL)
|
||||||
|
numSamples = 1;
|
||||||
|
|
||||||
|
/* Sample rows */
|
||||||
|
/* TODO Ensure within maintenance_work_mem */
|
||||||
buildstate->samples = VectorArrayInit(numSamples, buildstate->dimensions);
|
buildstate->samples = VectorArrayInit(numSamples, buildstate->dimensions);
|
||||||
if (buildstate->heap != NULL)
|
if (buildstate->heap != NULL)
|
||||||
SampleRows(buildstate);
|
SampleRows(buildstate);
|
||||||
|
|
||||||
/* Calculate centers */
|
/* Calculate centers */
|
||||||
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_KMEANS);
|
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_KMEANS);
|
||||||
IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers);
|
IvfflatBench("k-means", IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers));
|
||||||
|
|
||||||
/* Free samples before we allocate more memory */
|
/* Free samples before we allocate more memory */
|
||||||
pfree(buildstate->samples);
|
pfree(buildstate->samples);
|
||||||
@@ -517,7 +522,7 @@ BuildIndex(Relation heap, Relation index, IndexInfo *indexInfo,
|
|||||||
/* Create pages */
|
/* Create pages */
|
||||||
CreateMetaPage(index, buildstate->dimensions, buildstate->lists, forkNum);
|
CreateMetaPage(index, buildstate->dimensions, buildstate->lists, forkNum);
|
||||||
CreateListPages(index, buildstate->centers, buildstate->dimensions, buildstate->lists, forkNum, &buildstate->listInfo);
|
CreateListPages(index, buildstate->centers, buildstate->dimensions, buildstate->lists, forkNum, &buildstate->listInfo);
|
||||||
CreateEntryPages(buildstate, forkNum);
|
IvfflatBench("CreateEntryPages", CreateEntryPages(buildstate, forkNum));
|
||||||
|
|
||||||
FreeBuildState(buildstate);
|
FreeBuildState(buildstate);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,6 +159,11 @@ ivfflatvalidate(Oid opclassoid)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define index handler
|
||||||
|
*
|
||||||
|
* See https://www.postgresql.org/docs/current/index-api.html
|
||||||
|
*/
|
||||||
PG_FUNCTION_INFO_V1(ivfflathandler);
|
PG_FUNCTION_INFO_V1(ivfflathandler);
|
||||||
Datum
|
Datum
|
||||||
ivfflathandler(PG_FUNCTION_ARGS)
|
ivfflathandler(PG_FUNCTION_ARGS)
|
||||||
@@ -189,16 +194,17 @@ ivfflathandler(PG_FUNCTION_ARGS)
|
|||||||
#endif
|
#endif
|
||||||
#if PG_VERSION_NUM >= 130000
|
#if PG_VERSION_NUM >= 130000
|
||||||
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
|
amroutine->amusemaintenanceworkmem = false; /* not used during VACUUM */
|
||||||
amroutine->amparallelvacuumoptions = VACUUM_OPTION_NO_PARALLEL; /* TODO support parallel */
|
amroutine->amparallelvacuumoptions = VACUUM_OPTION_PARALLEL_BULKDEL;
|
||||||
#endif
|
#endif
|
||||||
amroutine->amkeytype = InvalidOid;
|
amroutine->amkeytype = InvalidOid;
|
||||||
|
|
||||||
|
/* Interface functions */
|
||||||
amroutine->ambuild = ivfflatbuild;
|
amroutine->ambuild = ivfflatbuild;
|
||||||
amroutine->ambuildempty = ivfflatbuildempty;
|
amroutine->ambuildempty = ivfflatbuildempty;
|
||||||
amroutine->aminsert = ivfflatinsert;
|
amroutine->aminsert = ivfflatinsert;
|
||||||
amroutine->ambulkdelete = ivfflatbulkdelete;
|
amroutine->ambulkdelete = ivfflatbulkdelete;
|
||||||
amroutine->amvacuumcleanup = ivfflatvacuumcleanup;
|
amroutine->amvacuumcleanup = ivfflatvacuumcleanup;
|
||||||
amroutine->amcanreturn = NULL;
|
amroutine->amcanreturn = NULL; /* tuple not included in heapsort */
|
||||||
amroutine->amcostestimate = ivfflatcostestimate;
|
amroutine->amcostestimate = ivfflatcostestimate;
|
||||||
amroutine->amoptions = ivfflatoptions;
|
amroutine->amoptions = ivfflatoptions;
|
||||||
amroutine->amproperty = NULL; /* TODO AMPROP_DISTANCE_ORDERABLE */
|
amroutine->amproperty = NULL; /* TODO AMPROP_DISTANCE_ORDERABLE */
|
||||||
@@ -206,6 +212,9 @@ ivfflathandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->ambuildphasename = ivfflatbuildphasename;
|
amroutine->ambuildphasename = ivfflatbuildphasename;
|
||||||
#endif
|
#endif
|
||||||
amroutine->amvalidate = ivfflatvalidate;
|
amroutine->amvalidate = ivfflatvalidate;
|
||||||
|
#if PG_VERSION_NUM >= 140000
|
||||||
|
amroutine->amadjustmembers = NULL;
|
||||||
|
#endif
|
||||||
amroutine->ambeginscan = ivfflatbeginscan;
|
amroutine->ambeginscan = ivfflatbeginscan;
|
||||||
amroutine->amrescan = ivfflatrescan;
|
amroutine->amrescan = ivfflatrescan;
|
||||||
amroutine->amgettuple = ivfflatgettuple;
|
amroutine->amgettuple = ivfflatgettuple;
|
||||||
@@ -213,6 +222,8 @@ ivfflathandler(PG_FUNCTION_ARGS)
|
|||||||
amroutine->amendscan = ivfflatendscan;
|
amroutine->amendscan = ivfflatendscan;
|
||||||
amroutine->ammarkpos = NULL;
|
amroutine->ammarkpos = NULL;
|
||||||
amroutine->amrestrpos = NULL;
|
amroutine->amrestrpos = NULL;
|
||||||
|
|
||||||
|
/* Interface functions to support parallel index scans */
|
||||||
#if PG_VERSION_NUM >= 100000
|
#if PG_VERSION_NUM >= 100000
|
||||||
amroutine->amestimateparallelscan = NULL;
|
amroutine->amestimateparallelscan = NULL;
|
||||||
amroutine->aminitparallelscan = NULL;
|
amroutine->aminitparallelscan = NULL;
|
||||||
|
|||||||
@@ -10,6 +10,14 @@
|
|||||||
#include "utils/tuplesort.h"
|
#include "utils/tuplesort.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
|
#ifdef IVFFLAT_BENCH
|
||||||
|
#include "portability/instr_time.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 90600
|
||||||
|
#error "Requires PostgreSQL 9.6+"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Support functions */
|
/* Support functions */
|
||||||
#define IVFFLAT_DISTANCE_PROC 1
|
#define IVFFLAT_DISTANCE_PROC 1
|
||||||
#define IVFFLAT_NORM_PROC 2
|
#define IVFFLAT_NORM_PROC 2
|
||||||
@@ -39,6 +47,21 @@
|
|||||||
#define IvfflatPageGetOpaque(page) ((IvfflatPageOpaque) PageGetSpecialPointer(page))
|
#define IvfflatPageGetOpaque(page) ((IvfflatPageOpaque) PageGetSpecialPointer(page))
|
||||||
#define IvfflatPageGetMeta(page) ((IvfflatMetaPageData *) PageGetContents(page))
|
#define IvfflatPageGetMeta(page) ((IvfflatMetaPageData *) PageGetContents(page))
|
||||||
|
|
||||||
|
#ifdef IVFFLAT_BENCH
|
||||||
|
#define IvfflatBench(name, code) \
|
||||||
|
do { \
|
||||||
|
instr_time start; \
|
||||||
|
instr_time duration; \
|
||||||
|
INSTR_TIME_SET_CURRENT(start); \
|
||||||
|
(code); \
|
||||||
|
INSTR_TIME_SET_CURRENT(duration); \
|
||||||
|
INSTR_TIME_SUBTRACT(duration, start); \
|
||||||
|
elog(INFO, "%s: %.3f ms", name, INSTR_TIME_GET_MILLISEC(duration)); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define IvfflatBench(name, code) (code)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 100000
|
#if PG_VERSION_NUM < 100000
|
||||||
#define ItemPointerGetBlockNumberNoCheck ItemPointerGetBlockNumber
|
#define ItemPointerGetBlockNumberNoCheck ItemPointerGetBlockNumber
|
||||||
#define ItemPointerGetOffsetNumberNoCheck ItemPointerGetOffsetNumber
|
#define ItemPointerGetOffsetNumberNoCheck ItemPointerGetOffsetNumber
|
||||||
@@ -177,7 +200,7 @@ void IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers);
|
|||||||
FmgrInfo *IvfflatOptionalProcInfo(Relation rel, uint16 procnum);
|
FmgrInfo *IvfflatOptionalProcInfo(Relation rel, uint16 procnum);
|
||||||
bool IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result);
|
bool IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result);
|
||||||
int IvfflatGetLists(Relation index);
|
int IvfflatGetLists(Relation index);
|
||||||
void IvfflatUpdateList(Relation index, GenericXLogState *state, ListInfo listInfo, BlockNumber insertPage, BlockNumber startPage, ForkNumber forkNum);
|
void IvfflatUpdateList(Relation index, GenericXLogState *state, ListInfo listInfo, BlockNumber insertPage, BlockNumber originalInsertPage, BlockNumber startPage, ForkNumber forkNum);
|
||||||
void IvfflatCommitBuffer(Buffer buf, GenericXLogState *state);
|
void IvfflatCommitBuffer(Buffer buf, GenericXLogState *state);
|
||||||
void IvfflatAppendPage(Relation index, Buffer *buf, Page *page, GenericXLogState **state, ForkNumber forkNum);
|
void IvfflatAppendPage(Relation index, Buffer *buf, Page *page, GenericXLogState **state, ForkNumber forkNum);
|
||||||
Buffer IvfflatNewBuffer(Relation index, ForkNumber forkNum);
|
Buffer IvfflatNewBuffer(Relation index, ForkNumber forkNum);
|
||||||
|
|||||||
@@ -77,11 +77,12 @@ InsertTuple(Relation rel, IndexTuple itup, Relation heapRel, Datum *values)
|
|||||||
Size itemsz;
|
Size itemsz;
|
||||||
BlockNumber insertPage = InvalidBlockNumber;
|
BlockNumber insertPage = InvalidBlockNumber;
|
||||||
ListInfo listInfo;
|
ListInfo listInfo;
|
||||||
bool newPage = false;
|
BlockNumber originalInsertPage;
|
||||||
|
|
||||||
/* Find the insert page - sets the page and list info */
|
/* Find the insert page - sets the page and list info */
|
||||||
FindInsertPage(rel, values, &insertPage, &listInfo);
|
FindInsertPage(rel, values, &insertPage, &listInfo);
|
||||||
Assert(BlockNumberIsValid(insertPage));
|
Assert(BlockNumberIsValid(insertPage));
|
||||||
|
originalInsertPage = insertPage;
|
||||||
|
|
||||||
itemsz = MAXALIGN(IndexTupleSize(itup));
|
itemsz = MAXALIGN(IndexTupleSize(itup));
|
||||||
Assert(itemsz <= BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(IvfflatPageOpaqueData)));
|
Assert(itemsz <= BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(IvfflatPageOpaqueData)));
|
||||||
@@ -107,7 +108,6 @@ InsertTuple(Relation rel, IndexTuple itup, Relation heapRel, Datum *values)
|
|||||||
IvfflatAppendPage(rel, &buf, &page, &state, MAIN_FORKNUM);
|
IvfflatAppendPage(rel, &buf, &page, &state, MAIN_FORKNUM);
|
||||||
|
|
||||||
insertPage = BufferGetBlockNumber(buf);
|
insertPage = BufferGetBlockNumber(buf);
|
||||||
newPage = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,8 +118,8 @@ InsertTuple(Relation rel, IndexTuple itup, Relation heapRel, Datum *values)
|
|||||||
IvfflatCommitBuffer(buf, state);
|
IvfflatCommitBuffer(buf, state);
|
||||||
|
|
||||||
/* Update the insert page */
|
/* Update the insert page */
|
||||||
if (newPage)
|
if (insertPage != originalInsertPage)
|
||||||
IvfflatUpdateList(rel, state, listInfo, insertPage, InvalidBlockNumber, MAIN_FORKNUM);
|
IvfflatUpdateList(rel, state, listInfo, insertPage, originalInsertPage, InvalidBlockNumber, MAIN_FORKNUM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
* https://theory.stanford.edu/~sergei/papers/kMeansPP-soda.pdf
|
* https://theory.stanford.edu/~sergei/papers/kMeansPP-soda.pdf
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
InitCenters(Relation index, VectorArray samples, VectorArray centers, double *lowerBound)
|
InitCenters(Relation index, VectorArray samples, VectorArray centers, float *lowerBound)
|
||||||
{
|
{
|
||||||
FmgrInfo *procinfo;
|
FmgrInfo *procinfo;
|
||||||
Oid collation;
|
Oid collation;
|
||||||
@@ -21,7 +21,7 @@ InitCenters(Relation index, VectorArray samples, VectorArray centers, double *lo
|
|||||||
double sum;
|
double sum;
|
||||||
double choice;
|
double choice;
|
||||||
Vector *vec;
|
Vector *vec;
|
||||||
double *weight = palloc(samples->length * sizeof(double));
|
float *weight = palloc(samples->length * sizeof(float));
|
||||||
int numCenters = centers->maxlen;
|
int numCenters = centers->maxlen;
|
||||||
int numSamples = samples->length;
|
int numSamples = samples->length;
|
||||||
|
|
||||||
@@ -121,15 +121,18 @@ QuickCenters(Relation index, VectorArray samples, VectorArray centers)
|
|||||||
FmgrInfo *normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
FmgrInfo *normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
||||||
|
|
||||||
/* Copy existing vectors while avoiding duplicates */
|
/* Copy existing vectors while avoiding duplicates */
|
||||||
qsort(samples->items, samples->length, VECTOR_SIZE(samples->dim), CompareVectors);
|
if (samples->length > 0)
|
||||||
for (i = 0; i < samples->length; i++)
|
|
||||||
{
|
{
|
||||||
vec = VectorArrayGet(samples, i);
|
qsort(samples->items, samples->length, VECTOR_SIZE(samples->dim), CompareVectors);
|
||||||
|
for (i = 0; i < samples->length; i++)
|
||||||
if (i == 0 || CompareVectors(vec, VectorArrayGet(samples, i - 1)) != 0)
|
|
||||||
{
|
{
|
||||||
VectorArraySet(centers, centers->length, vec);
|
vec = VectorArrayGet(samples, i);
|
||||||
centers->length++;
|
|
||||||
|
if (i == 0 || CompareVectors(vec, VectorArrayGet(samples, i - 1)) != 0)
|
||||||
|
{
|
||||||
|
VectorArraySet(centers, centers->length, vec);
|
||||||
|
centers->length++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,11 +180,11 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers)
|
|||||||
VectorArray newCenters;
|
VectorArray newCenters;
|
||||||
int *centerCounts;
|
int *centerCounts;
|
||||||
int *closestCenters;
|
int *closestCenters;
|
||||||
double *lowerBound;
|
float *lowerBound;
|
||||||
double *upperBound;
|
float *upperBound;
|
||||||
double *s;
|
float *s;
|
||||||
double *halfcdist;
|
float *halfcdist;
|
||||||
double *newcdist;
|
float *newcdist;
|
||||||
int changes;
|
int changes;
|
||||||
double minDistance;
|
double minDistance;
|
||||||
int closestCenter;
|
int closestCenter;
|
||||||
@@ -191,19 +194,43 @@ ElkanKmeans(Relation index, VectorArray samples, VectorArray centers)
|
|||||||
double dxcx;
|
double dxcx;
|
||||||
double dxc;
|
double dxc;
|
||||||
|
|
||||||
|
/* Calculate allocation sizes */
|
||||||
|
Size samplesSize = VECTOR_ARRAY_SIZE(samples->maxlen, samples->dim);
|
||||||
|
Size centersSize = VECTOR_ARRAY_SIZE(centers->maxlen, centers->dim);
|
||||||
|
Size newCentersSize = VECTOR_ARRAY_SIZE(numCenters, dimensions);
|
||||||
|
Size centerCountsSize = sizeof(int) * numCenters;
|
||||||
|
Size closestCentersSize = sizeof(int) * numSamples;
|
||||||
|
Size lowerBoundSize = sizeof(float) * numSamples * numCenters;
|
||||||
|
Size upperBoundSize = sizeof(float) * numSamples;
|
||||||
|
Size sSize = sizeof(float) * numCenters;
|
||||||
|
Size halfcdistSize = sizeof(float) * numCenters * numCenters;
|
||||||
|
Size newcdistSize = sizeof(float) * numCenters;
|
||||||
|
|
||||||
|
/* Calculate total size */
|
||||||
|
Size totalSize = samplesSize + centersSize + newCentersSize + centerCountsSize + closestCentersSize + lowerBoundSize + upperBoundSize + sSize + halfcdistSize + newcdistSize;
|
||||||
|
|
||||||
|
/* Check memory requirements */
|
||||||
|
/* Add one to error message to ceil */
|
||||||
|
if (totalSize / 1024 > maintenance_work_mem)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
||||||
|
errmsg("memory required is %zu MB, maintenance_work_mem is %d MB",
|
||||||
|
totalSize / (1024 * 1024) + 1, maintenance_work_mem / 1024)));
|
||||||
|
|
||||||
/* 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);
|
||||||
collation = index->rd_indcollation[0];
|
collation = index->rd_indcollation[0];
|
||||||
|
|
||||||
/* Allocate space */
|
/* Allocate space */
|
||||||
centerCounts = palloc(sizeof(int) * numCenters);
|
/* Use float instead of double to save memory */
|
||||||
closestCenters = palloc(sizeof(int) * numSamples);
|
centerCounts = palloc(centerCountsSize);
|
||||||
lowerBound = palloc(sizeof(double) * numSamples * numCenters);
|
closestCenters = palloc(closestCentersSize);
|
||||||
upperBound = palloc(sizeof(double) * numSamples);
|
lowerBound = palloc_extended(lowerBoundSize, MCXT_ALLOC_HUGE);
|
||||||
s = palloc(sizeof(double) * numCenters);
|
upperBound = palloc(upperBoundSize);
|
||||||
halfcdist = palloc(sizeof(double) * numCenters * numCenters);
|
s = palloc(sSize);
|
||||||
newcdist = palloc(sizeof(double) * numCenters);
|
halfcdist = palloc(halfcdistSize);
|
||||||
|
newcdist = palloc(newcdistSize);
|
||||||
|
|
||||||
newCenters = VectorArrayInit(numCenters, dimensions);
|
newCenters = VectorArrayInit(numCenters, dimensions);
|
||||||
for (j = 0; j < numCenters; j++)
|
for (j = 0; j < numCenters; j++)
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ GetScanLists(IndexScanDesc scan, Datum value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Sort by distance */
|
/* Sort by distance */
|
||||||
|
/* TODO Use heap for performance */
|
||||||
qsort(so->lists, listCount, sizeof(IvfflatScanList), CompareLists);
|
qsort(so->lists, listCount, sizeof(IvfflatScanList), CompareLists);
|
||||||
|
|
||||||
if (so->probes > listCount)
|
if (so->probes > listCount)
|
||||||
@@ -153,6 +154,8 @@ GetScanItems(IndexScanDesc scan, Datum value)
|
|||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tuplesort_performsort(so->sortstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -268,9 +271,8 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetScanLists(scan, value);
|
IvfflatBench("GetScanLists", GetScanLists(scan, value));
|
||||||
GetScanItems(scan, value);
|
IvfflatBench("GetScanItems", GetScanItems(scan, value));
|
||||||
tuplesort_performsort(so->sortstate);
|
|
||||||
so->first = false;
|
so->first = false;
|
||||||
|
|
||||||
/* Clean up if we allocated a new value */
|
/* Clean up if we allocated a new value */
|
||||||
|
|||||||
@@ -153,11 +153,13 @@ IvfflatAppendPage(Relation index, Buffer *buf, Page *page, GenericXLogState **st
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
IvfflatUpdateList(Relation index, GenericXLogState *state, ListInfo listInfo,
|
IvfflatUpdateList(Relation index, GenericXLogState *state, ListInfo listInfo,
|
||||||
BlockNumber insertPage, BlockNumber startPage, ForkNumber forkNum)
|
BlockNumber insertPage, BlockNumber originalInsertPage,
|
||||||
|
BlockNumber startPage, ForkNumber forkNum)
|
||||||
{
|
{
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
Page page;
|
Page page;
|
||||||
IvfflatList list;
|
IvfflatList list;
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
buf = ReadBufferExtended(index, forkNum, listInfo.blkno, RBM_NORMAL, NULL);
|
buf = ReadBufferExtended(index, forkNum, listInfo.blkno, RBM_NORMAL, NULL);
|
||||||
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
||||||
@@ -165,12 +167,29 @@ IvfflatUpdateList(Relation index, GenericXLogState *state, ListInfo listInfo,
|
|||||||
page = GenericXLogRegisterBuffer(state, buf, 0);
|
page = GenericXLogRegisterBuffer(state, buf, 0);
|
||||||
list = (IvfflatList) PageGetItem(page, PageGetItemId(page, listInfo.offno));
|
list = (IvfflatList) PageGetItem(page, PageGetItemId(page, listInfo.offno));
|
||||||
|
|
||||||
if (BlockNumberIsValid(insertPage))
|
if (BlockNumberIsValid(insertPage) && insertPage != list->insertPage)
|
||||||
list->insertPage = insertPage;
|
{
|
||||||
|
/* Skip update if insert page is lower than original insert page */
|
||||||
|
/* This is needed to prevent insert from overwriting vacuum */
|
||||||
|
if (!BlockNumberIsValid(originalInsertPage) || insertPage >= originalInsertPage)
|
||||||
|
{
|
||||||
|
list->insertPage = insertPage;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (BlockNumberIsValid(startPage))
|
if (BlockNumberIsValid(startPage) && startPage != list->startPage)
|
||||||
|
{
|
||||||
list->startPage = startPage;
|
list->startPage = startPage;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Could only commit if changed, but extra complexity isn't needed */
|
/* Only commit if changed */
|
||||||
IvfflatCommitBuffer(buf, state);
|
if (changed)
|
||||||
|
IvfflatCommitBuffer(buf, state);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GenericXLogAbort(state);
|
||||||
|
UnlockReleaseBuffer(buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ ivfflatbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
|
|||||||
ItemPointer htup;
|
ItemPointer htup;
|
||||||
OffsetNumber deletable[MaxOffsetNumber];
|
OffsetNumber deletable[MaxOffsetNumber];
|
||||||
int ndeletable;
|
int ndeletable;
|
||||||
OffsetNumber startPages[MaxOffsetNumber];
|
BlockNumber startPages[MaxOffsetNumber];
|
||||||
BlockNumber nextblkno = IVFFLAT_HEAD_BLKNO;
|
BlockNumber nextblkno = IVFFLAT_HEAD_BLKNO;
|
||||||
BlockNumber searchPage;
|
BlockNumber searchPage;
|
||||||
BlockNumber insertPage;
|
BlockNumber insertPage;
|
||||||
@@ -98,6 +98,11 @@ ivfflatbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
|
|||||||
stats->num_index_tuples++;
|
stats->num_index_tuples++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set to first free page */
|
||||||
|
/* Must be set before searchPage is updated */
|
||||||
|
if (!BlockNumberIsValid(insertPage) && ndeletable > 0)
|
||||||
|
insertPage = searchPage;
|
||||||
|
|
||||||
searchPage = IvfflatPageGetOpaque(page)->nextblkno;
|
searchPage = IvfflatPageGetOpaque(page)->nextblkno;
|
||||||
|
|
||||||
if (ndeletable > 0)
|
if (ndeletable > 0)
|
||||||
@@ -106,10 +111,6 @@ ivfflatbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
|
|||||||
PageIndexMultiDelete(page, deletable, ndeletable);
|
PageIndexMultiDelete(page, deletable, ndeletable);
|
||||||
MarkBufferDirty(buf);
|
MarkBufferDirty(buf);
|
||||||
GenericXLogFinish(state);
|
GenericXLogFinish(state);
|
||||||
|
|
||||||
/* Set to first free page */
|
|
||||||
if (!BlockNumberIsValid(insertPage))
|
|
||||||
insertPage = searchPage;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
GenericXLogAbort(state);
|
GenericXLogAbort(state);
|
||||||
@@ -123,10 +124,10 @@ ivfflatbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
|
|||||||
* We don't add or delete items from lists pages, so offset won't
|
* We don't add or delete items from lists pages, so offset won't
|
||||||
* change.
|
* change.
|
||||||
*/
|
*/
|
||||||
if (!BlockNumberIsValid(insertPage))
|
if (BlockNumberIsValid(insertPage))
|
||||||
{
|
{
|
||||||
listInfo.offno = coffno;
|
listInfo.offno = coffno;
|
||||||
IvfflatUpdateList(index, state, listInfo, insertPage, InvalidBlockNumber, MAIN_FORKNUM);
|
IvfflatUpdateList(index, state, listInfo, insertPage, InvalidBlockNumber, InvalidBlockNumber, MAIN_FORKNUM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,3 +53,12 @@ LINE 1: SELECT '[1,]'::vector;
|
|||||||
^
|
^
|
||||||
SELECT '[1,2,3]'::vector(2);
|
SELECT '[1,2,3]'::vector(2);
|
||||||
ERROR: expected 2 dimensions, not 3
|
ERROR: expected 2 dimensions, not 3
|
||||||
|
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::vector[]);
|
||||||
|
unnest
|
||||||
|
---------
|
||||||
|
[1,2,3]
|
||||||
|
[4,5,6]
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT '{"[1,2,3]"}'::vector(2)[];
|
||||||
|
ERROR: expected 2 dimensions, not 3
|
||||||
|
|||||||
@@ -13,3 +13,6 @@ SELECT '1,2,3'::vector;
|
|||||||
SELECT '[]'::vector;
|
SELECT '[]'::vector;
|
||||||
SELECT '[1,]'::vector;
|
SELECT '[1,]'::vector;
|
||||||
SELECT '[1,2,3]'::vector(2);
|
SELECT '[1,2,3]'::vector(2);
|
||||||
|
|
||||||
|
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::vector[]);
|
||||||
|
SELECT '{"[1,2,3]"}'::vector(2)[];
|
||||||
|
|||||||
@@ -34,9 +34,10 @@ sub test_index_replay
|
|||||||
my $r2 = rand();
|
my $r2 = rand();
|
||||||
my $r3 = rand();
|
my $r3 = rand();
|
||||||
|
|
||||||
my $queries = qq(SET enable_seqscan=off;
|
my $queries = qq(
|
||||||
SELECT * FROM tst ORDER BY v <-> '[$r1,$r2,$r3]' LIMIT 10;
|
SET enable_seqscan = off;
|
||||||
);
|
SELECT * FROM tst ORDER BY v <-> '[$r1,$r2,$r3]' LIMIT 10;
|
||||||
|
);
|
||||||
|
|
||||||
# Run test queries and compare their result
|
# Run test queries and compare their result
|
||||||
my $primary_result = $node_primary->safe_psql("postgres", $queries);
|
my $primary_result = $node_primary->safe_psql("postgres", $queries);
|
||||||
@@ -65,10 +66,9 @@ $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(3));");
|
$node_primary->safe_psql("postgres", "CREATE TABLE tst (i int4, v vector(3));");
|
||||||
$node_primary->safe_psql("postgres",
|
$node_primary->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT i%10, ARRAY[random(), random(), random()] FROM generate_series(1,100000) i;"
|
"INSERT INTO tst SELECT i % 10, ARRAY[random(), random(), random()] FROM generate_series(1, 100000) i;"
|
||||||
);
|
);
|
||||||
$node_primary->safe_psql("postgres",
|
$node_primary->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
||||||
"CREATE INDEX ON tst USING ivfflat (v);");
|
|
||||||
|
|
||||||
# Test that queries give same result
|
# Test that queries give same result
|
||||||
test_index_replay('initial');
|
test_index_replay('initial');
|
||||||
@@ -82,7 +82,7 @@ for my $i (1 .. 10)
|
|||||||
test_index_replay("vacuum $i");
|
test_index_replay("vacuum $i");
|
||||||
my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
|
my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
|
||||||
$node_primary->safe_psql("postgres",
|
$node_primary->safe_psql("postgres",
|
||||||
"INSERT INTO tst SELECT i%10, ARRAY[random(), random(), random()] FROM generate_series($start,$end) i;"
|
"INSERT INTO tst SELECT i % 10, ARRAY[random(), random(), random()] FROM generate_series($start, $end) i;"
|
||||||
);
|
);
|
||||||
test_index_replay("insert $i");
|
test_index_replay("insert $i");
|
||||||
}
|
}
|
||||||
|
|||||||
32
test/t/002_vacuum.pl
Normal file
32
test/t/002_vacuum.pl
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use PostgresNode;
|
||||||
|
use TestLib;
|
||||||
|
use Test::More tests => 1;
|
||||||
|
|
||||||
|
# Initialize node
|
||||||
|
my $node = get_new_node('node');
|
||||||
|
$node->init;
|
||||||
|
$node->start;
|
||||||
|
|
||||||
|
# Create table and index
|
||||||
|
$node->safe_psql("postgres", "CREATE EXTENSION vector;");
|
||||||
|
$node->safe_psql("postgres", "CREATE TABLE tst (i int4, v vector(3));");
|
||||||
|
$node->safe_psql("postgres",
|
||||||
|
"INSERT INTO tst SELECT i%10, ARRAY[i%1000, i%333, i%55] FROM generate_series(1, 100000) i;"
|
||||||
|
);
|
||||||
|
$node->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
||||||
|
|
||||||
|
# Get size
|
||||||
|
my $size = $node->safe_psql("postgres", "SELECT pg_total_relation_size('tst_v_idx');");
|
||||||
|
|
||||||
|
# Delete all, vacuum, and insert same data
|
||||||
|
$node->safe_psql("postgres", "DELETE FROM tst;");
|
||||||
|
$node->safe_psql("postgres", "VACUUM tst;");
|
||||||
|
$node->safe_psql("postgres",
|
||||||
|
"INSERT INTO tst SELECT i % 10, ARRAY[i % 1000, i % 333, i % 55] FROM generate_series(1, 100000) i;"
|
||||||
|
);
|
||||||
|
|
||||||
|
# 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");
|
||||||
72
test/t/003_recall.pl
Normal file
72
test/t/003_recall.pl
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use PostgresNode;
|
||||||
|
use TestLib;
|
||||||
|
use Test::More tests => 2;
|
||||||
|
|
||||||
|
my $node;
|
||||||
|
my @queries = ();
|
||||||
|
my @expected = ();
|
||||||
|
|
||||||
|
sub test_recall
|
||||||
|
{
|
||||||
|
my ($probes, $min) = @_;
|
||||||
|
my $correct = 0;
|
||||||
|
my $total = 0;
|
||||||
|
|
||||||
|
for my $i (0 .. $#queries) {
|
||||||
|
my $actual = $node->safe_psql("postgres", qq(
|
||||||
|
SET enable_seqscan = off;
|
||||||
|
SET ivfflat.probes = $probes;
|
||||||
|
SELECT i FROM tst ORDER BY v <-> '$queries[$i]' LIMIT 10;
|
||||||
|
));
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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 vector(3));");
|
||||||
|
$node->safe_psql("postgres",
|
||||||
|
"INSERT INTO tst SELECT i, ARRAY[random(), random(), random()] FROM generate_series(1, 100000) i;"
|
||||||
|
);
|
||||||
|
|
||||||
|
# Generate queries
|
||||||
|
for (1..20) {
|
||||||
|
my $r1 = rand();
|
||||||
|
my $r2 = rand();
|
||||||
|
my $r3 = rand();
|
||||||
|
push(@queries, "[$r1,$r2,$r3]");
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get exact results
|
||||||
|
foreach (@queries) {
|
||||||
|
my $res = $node->safe_psql("postgres", "SELECT i FROM tst ORDER BY v <-> '$_' LIMIT 10;");
|
||||||
|
push(@expected, $res);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add index
|
||||||
|
$node->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v);");
|
||||||
|
|
||||||
|
# Test approximate results
|
||||||
|
test_recall(1, 0.8);
|
||||||
|
|
||||||
|
# Test probes
|
||||||
|
test_recall(100, 1.0);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
comment = 'vector data type and ivfflat access method'
|
comment = 'vector data type and ivfflat access method'
|
||||||
default_version = '0.2.3'
|
default_version = '0.2.5'
|
||||||
module_pathname = '$libdir/vector'
|
module_pathname = '$libdir/vector'
|
||||||
relocatable = true
|
relocatable = true
|
||||||
|
|||||||
Reference in New Issue
Block a user