mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-24 04:45:45 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12bc4eb036 | ||
|
|
fd0e472316 | ||
|
|
9b11b05cd7 | ||
|
|
ab079c1911 | ||
|
|
f4eaa3de1f | ||
|
|
e47675d6ae | ||
|
|
20167ddc33 |
@@ -1,6 +1,11 @@
|
|||||||
|
## 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.4",
|
||||||
"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.4",
|
||||||
"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.2.3
|
EXTVERSION = 0.2.4
|
||||||
|
|
||||||
MODULE_big = vector
|
MODULE_big = vector
|
||||||
DATA = $(wildcard sql/*--*.sql)
|
DATA = $(wildcard sql/*--*.sql)
|
||||||
|
|||||||
@@ -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.4 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
make
|
make
|
||||||
make install # may need sudo
|
make install # may need sudo
|
||||||
@@ -120,7 +120,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+
|
||||||
|
|
||||||
@@ -231,7 +231,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.4 https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
docker build -t pgvector .
|
docker build -t pgvector .
|
||||||
```
|
```
|
||||||
|
|||||||
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
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ 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;
|
||||||
|
|
||||||
@@ -198,7 +198,7 @@ ivfflathandler(PG_FUNCTION_ARGS)
|
|||||||
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 */
|
||||||
|
|||||||
@@ -177,7 +177,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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");
|
||||||
@@ -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.4'
|
||||||
module_pathname = '$libdir/vector'
|
module_pathname = '$libdir/vector'
|
||||||
relocatable = true
|
relocatable = true
|
||||||
|
|||||||
Reference in New Issue
Block a user