mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 12:07:34 +08:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12bc4eb036 | ||
|
|
fd0e472316 | ||
|
|
9b11b05cd7 | ||
|
|
ab079c1911 | ||
|
|
f4eaa3de1f | ||
|
|
e47675d6ae | ||
|
|
20167ddc33 | ||
|
|
e14fa82626 | ||
|
|
2ae4de83fe | ||
|
|
5d2c4883dc | ||
|
|
fc1de9806a | ||
|
|
e375dd33f3 | ||
|
|
b643ef637a | ||
|
|
0ee5bfda65 | ||
|
|
c5bec2a534 | ||
|
|
40f65dc8ba | ||
|
|
9ec174137f | ||
|
|
423e478a85 | ||
|
|
8b301197fe | ||
|
|
c02feaa9ad | ||
|
|
729374a9b8 | ||
|
|
a8245d6cda | ||
|
|
5220a6ec7e |
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@@ -2,7 +2,6 @@ name: build
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -34,6 +33,6 @@ jobs:
|
|||||||
- if: ${{ startsWith(matrix.os, 'macos') }}
|
- if: ${{ startsWith(matrix.os, 'macos') }}
|
||||||
run: |
|
run: |
|
||||||
brew install cpanm && cpanm IPC::Run
|
brew install cpanm && cpanm IPC::Run
|
||||||
wget -q https://github.com/postgres/postgres/archive/refs/tags/REL_13_2.tar.gz
|
wget -q https://github.com/postgres/postgres/archive/refs/tags/REL_14_1.tar.gz
|
||||||
tar xf REL_13_2.tar.gz
|
tar xf REL_14_1.tar.gz
|
||||||
make prove_installcheck PROVE=prove PERL5LIB=postgres-REL_13_2/src/test/perl
|
make prove_installcheck PROVE=prove PERL5LIB=postgres-REL_14_1/src/test/perl
|
||||||
|
|||||||
10
CHANGELOG.md
10
CHANGELOG.md
@@ -1,3 +1,13 @@
|
|||||||
|
## 0.2.4 (2022-02-06)
|
||||||
|
|
||||||
|
- Added support for parallel vacuum
|
||||||
|
- Fixed issue with index not reusing space
|
||||||
|
|
||||||
|
## 0.2.3 (2022-01-30)
|
||||||
|
|
||||||
|
- Added indexing progress for Postgres 12+
|
||||||
|
- Improved interrupt handling during index creation
|
||||||
|
|
||||||
## 0.2.2 (2022-01-15)
|
## 0.2.2 (2022-01-15)
|
||||||
|
|
||||||
- Fixed compilation error on Mac ARM
|
- Fixed compilation error on Mac ARM
|
||||||
|
|||||||
12
META.json
12
META.json
@@ -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.2",
|
"version": "0.2.4",
|
||||||
"maintainer": [
|
"maintainer": [
|
||||||
"Andrew Kane <andrew@ankane.org>"
|
"Andrew Kane <andrew@ankane.org>"
|
||||||
],
|
],
|
||||||
@@ -20,18 +20,18 @@
|
|||||||
"vector": {
|
"vector": {
|
||||||
"file": "sql/vector.sql",
|
"file": "sql/vector.sql",
|
||||||
"docfile": "README.md",
|
"docfile": "README.md",
|
||||||
"version": "0.2.2",
|
"version": "0.2.4",
|
||||||
"abstract": "Open-source vector similarity search for Postgres"
|
"abstract": "Open-source vector similarity search for Postgres"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resources": {
|
"resources": {
|
||||||
"homepage": "https://github.com/ankane/pgvector",
|
"homepage": "https://github.com/pgvector/pgvector",
|
||||||
"bugtracker": {
|
"bugtracker": {
|
||||||
"web": "https://github.com/ankane/pgvector/issues"
|
"web": "https://github.com/pgvector/pgvector/issues"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/ankane/pgvector.git",
|
"url": "https://github.com/pgvector/pgvector.git",
|
||||||
"web": "https://github.com/ankane/pgvector",
|
"web": "https://github.com/pgvector/pgvector",
|
||||||
"type": "git"
|
"type": "git"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -1,5 +1,5 @@
|
|||||||
EXTENSION = vector
|
EXTENSION = vector
|
||||||
EXTVERSION = 0.2.2
|
EXTVERSION = 0.2.4
|
||||||
|
|
||||||
MODULE_big = vector
|
MODULE_big = vector
|
||||||
DATA = $(wildcard sql/*--*.sql)
|
DATA = $(wildcard sql/*--*.sql)
|
||||||
|
|||||||
65
README.md
65
README.md
@@ -10,14 +10,14 @@ SELECT * FROM table ORDER BY column <-> '[1,2,3]' LIMIT 5;
|
|||||||
|
|
||||||
Supports L2 distance, inner product, and cosine distance
|
Supports L2 distance, inner product, and cosine distance
|
||||||
|
|
||||||
[](https://github.com/ankane/pgvector/actions)
|
[](https://github.com/pgvector/pgvector/actions)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
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.2 https://github.com/ankane/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,6 +120,24 @@ SELECT ...
|
|||||||
COMMIT;
|
COMMIT;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Indexing Progress
|
||||||
|
|
||||||
|
Check [indexing progress](https://www.postgresql.org/docs/current/progress-reporting.html#CREATE-INDEX-PROGRESS-REPORTING) with Postgres 12+
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT phase, tuples_done, tuples_total FROM pg_stat_progress_create_index;
|
||||||
|
```
|
||||||
|
|
||||||
|
The phases are:
|
||||||
|
|
||||||
|
1. `initializing`
|
||||||
|
2. `sampling table`
|
||||||
|
3. `performing k-means`
|
||||||
|
4. `sorting tuples`
|
||||||
|
5. `loading tuples`
|
||||||
|
|
||||||
|
Note: `tuples_done` and `tuples_total` are only populated during the `loading tuples` phase
|
||||||
|
|
||||||
### Partial Indexes
|
### Partial Indexes
|
||||||
|
|
||||||
Consider [partial indexes](https://www.postgresql.org/docs/current/indexes-partial.html) for queries with a `WHERE` clause
|
Consider [partial indexes](https://www.postgresql.org/docs/current/indexes-partial.html) for queries with a `WHERE` clause
|
||||||
@@ -174,18 +192,35 @@ vector_norm(vector) | Euclidean norm
|
|||||||
|
|
||||||
Libraries that use pgvector:
|
Libraries that use pgvector:
|
||||||
|
|
||||||
- [pgvector-python](https://github.com/ankane/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-node](https://github.com/ankane/pgvector-node) (Node.js)
|
- [pgvector-node](https://github.com/pgvector/pgvector-node) (Node.js)
|
||||||
- [pgvector-go](https://github.com/ankane/pgvector-go) (Go)
|
- [pgvector-go](https://github.com/pgvector/pgvector-go) (Go)
|
||||||
- [pgvector-rust](https://github.com/ankane/pgvector-rust) (Rust)
|
- [pgvector-rust](https://github.com/pgvector/pgvector-rust) (Rust)
|
||||||
- [pgvector-cpp](https://github.com/ankane/pgvector-cpp) (C++)
|
- [pgvector-cpp](https://github.com/pgvector/pgvector-cpp) (C++)
|
||||||
|
|
||||||
|
## Frequently Asked Questions
|
||||||
|
|
||||||
|
#### How many vectors can be stored in a single table?
|
||||||
|
|
||||||
|
A non-partitioned table has a limit of 32 TB by default in Postgres. A partitioned table can have thousands of partitions of that size.
|
||||||
|
|
||||||
|
#### Is replication supported?
|
||||||
|
|
||||||
|
Yes, pgvector uses the write-ahead log (WAL), which allows for replication and point-in-time recovery.
|
||||||
|
|
||||||
|
#### What if my data has more than 1024 dimensions?
|
||||||
|
|
||||||
|
Two things you can try are:
|
||||||
|
|
||||||
|
1. use dimensionality reduction
|
||||||
|
2. compile Postgres with a larger block size (`./configure --with-blocksize=32`) and edit the limit in `src/vector.h`
|
||||||
|
|
||||||
## Additional Installation Methods
|
## Additional Installation Methods
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
Get the [Docker image](https://hub.docker.com/repository/docker/ankane/pgvector) with:
|
Get the [Docker image](https://hub.docker.com/r/ankane/pgvector) with:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker pull ankane/pgvector
|
docker pull ankane/pgvector
|
||||||
@@ -196,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.2 https://github.com/ankane/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 .
|
||||||
```
|
```
|
||||||
@@ -206,7 +241,7 @@ docker build -t pgvector .
|
|||||||
On Mac with Homebrew Postgres, you can use:
|
On Mac with Homebrew Postgres, you can use:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
brew install ankane/brew/pgvector
|
brew install pgvector/brew/pgvector
|
||||||
```
|
```
|
||||||
|
|
||||||
### PGXN
|
### PGXN
|
||||||
@@ -223,7 +258,7 @@ Some Postgres providers only support specific extensions. To request a new exten
|
|||||||
|
|
||||||
- Amazon RDS - follow the instructions on [this page](https://aws.amazon.com/rds/postgresql/faqs/)
|
- Amazon RDS - follow the instructions on [this page](https://aws.amazon.com/rds/postgresql/faqs/)
|
||||||
- Google Cloud SQL - follow the instructions on [this page](https://cloud.google.com/sql/docs/postgres/extensions#requesting-support-for-a-new-extension)
|
- Google Cloud SQL - follow the instructions on [this page](https://cloud.google.com/sql/docs/postgres/extensions#requesting-support-for-a-new-extension)
|
||||||
- DigitalOcean Managed Databases - follow the instructions on [this page](https://docs.digitalocean.com/products/databases/postgresql/resources/supported-extensions/#supported-extensions)
|
- DigitalOcean Managed Databases - vote or comment on [this page](https://ideas.digitalocean.com/app-framework-services/p/pgvector-extension-for-postgresql)
|
||||||
- Azure Database for PostgreSQL - follow the instructions on [this page](https://docs.microsoft.com/en-us/azure/postgresql/concepts-extensions#next-steps)
|
- Azure Database for PostgreSQL - follow the instructions on [this page](https://docs.microsoft.com/en-us/azure/postgresql/concepts-extensions#next-steps)
|
||||||
|
|
||||||
## Upgrading
|
## Upgrading
|
||||||
@@ -246,21 +281,21 @@ Thanks to:
|
|||||||
|
|
||||||
## History
|
## History
|
||||||
|
|
||||||
View the [changelog](https://github.com/ankane/pgvector/blob/master/CHANGELOG.md)
|
View the [changelog](https://github.com/pgvector/pgvector/blob/master/CHANGELOG.md)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Everyone is encouraged to help improve this project. Here are a few ways you can help:
|
Everyone is encouraged to help improve this project. Here are a few ways you can help:
|
||||||
|
|
||||||
- [Report bugs](https://github.com/ankane/pgvector/issues)
|
- [Report bugs](https://github.com/pgvector/pgvector/issues)
|
||||||
- Fix bugs and [submit pull requests](https://github.com/ankane/pgvector/pulls)
|
- Fix bugs and [submit pull requests](https://github.com/pgvector/pgvector/pulls)
|
||||||
- Write, clarify, or fix documentation
|
- Write, clarify, or fix documentation
|
||||||
- Suggest or add new features
|
- Suggest or add new features
|
||||||
|
|
||||||
To get started with development:
|
To get started with development:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/ankane/pgvector.git
|
git clone https://github.com/pgvector/pgvector.git
|
||||||
cd pgvector
|
cd pgvector
|
||||||
make
|
make
|
||||||
make install
|
make install
|
||||||
|
|||||||
2
sql/vector--0.2.2--0.2.3.sql
Normal file
2
sql/vector--0.2.2--0.2.3.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.3'" to load this file. \quit
|
||||||
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
|
||||||
@@ -7,8 +7,19 @@
|
|||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 140000
|
||||||
|
#include "utils/backend_progress.h"
|
||||||
|
#elif PG_VERSION_NUM >= 120000
|
||||||
|
#include "pgstat.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 120000
|
#if PG_VERSION_NUM >= 120000
|
||||||
#include "access/tableam.h"
|
#include "access/tableam.h"
|
||||||
|
#include "commands/progress.h"
|
||||||
|
#else
|
||||||
|
#define PROGRESS_CREATEIDX_SUBPHASE 0
|
||||||
|
#define PROGRESS_CREATEIDX_TUPLES_TOTAL 0
|
||||||
|
#define PROGRESS_CREATEIDX_TUPLES_DONE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 110000
|
#if PG_VERSION_NUM >= 110000
|
||||||
@@ -25,6 +36,17 @@
|
|||||||
#define CALLBACK_ITEM_POINTER HeapTuple hup
|
#define CALLBACK_ITEM_POINTER HeapTuple hup
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update build phase progress
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
UpdateProgress(int index, int64 val)
|
||||||
|
{
|
||||||
|
#if PG_VERSION_NUM >= 120000
|
||||||
|
pgstat_progress_update_param(index, val);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Callback for sampling
|
* Callback for sampling
|
||||||
*/
|
*/
|
||||||
@@ -82,6 +104,8 @@ SampleRows(IvfflatBuildState * buildstate)
|
|||||||
int targsamples = buildstate->samples->maxlen;
|
int targsamples = buildstate->samples->maxlen;
|
||||||
BlockNumber totalblocks = RelationGetNumberOfBlocks(buildstate->heap);
|
BlockNumber totalblocks = RelationGetNumberOfBlocks(buildstate->heap);
|
||||||
|
|
||||||
|
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_SAMPLE);
|
||||||
|
|
||||||
buildstate->rowstoskip = -1;
|
buildstate->rowstoskip = -1;
|
||||||
|
|
||||||
BlockSampler_Init(&buildstate->bs, totalblocks, targsamples, random());
|
BlockSampler_Init(&buildstate->bs, totalblocks, targsamples, random());
|
||||||
@@ -165,6 +189,8 @@ BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
|||||||
* need not save it.
|
* need not save it.
|
||||||
*/
|
*/
|
||||||
tuplesort_puttupleslot(buildstate->sortstate, slot);
|
tuplesort_puttupleslot(buildstate->sortstate, slot);
|
||||||
|
|
||||||
|
buildstate->indtuples++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -212,6 +238,7 @@ InsertTuples(Relation index, IvfflatBuildState * buildstate, ForkNumber forkNum)
|
|||||||
BlockNumber insertPage = InvalidBlockNumber;
|
BlockNumber insertPage = InvalidBlockNumber;
|
||||||
Size itemsz;
|
Size itemsz;
|
||||||
int i;
|
int i;
|
||||||
|
int64 inserted = 0;
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 120000
|
#if PG_VERSION_NUM >= 120000
|
||||||
TupleTableSlot *slot = MakeSingleTupleTableSlot(buildstate->tupdesc, &TTSOpsMinimalTuple);
|
TupleTableSlot *slot = MakeSingleTupleTableSlot(buildstate->tupdesc, &TTSOpsMinimalTuple);
|
||||||
@@ -220,10 +247,18 @@ InsertTuples(Relation index, IvfflatBuildState * buildstate, ForkNumber forkNum)
|
|||||||
#endif
|
#endif
|
||||||
TupleDesc tupdesc = RelationGetDescr(index);
|
TupleDesc tupdesc = RelationGetDescr(index);
|
||||||
|
|
||||||
|
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_LOAD);
|
||||||
|
|
||||||
|
UpdateProgress(PROGRESS_CREATEIDX_TUPLES_TOTAL, buildstate->indtuples);
|
||||||
|
|
||||||
GetNextTuple(buildstate->sortstate, tupdesc, slot, &itup, &list);
|
GetNextTuple(buildstate->sortstate, tupdesc, slot, &itup, &list);
|
||||||
|
|
||||||
for (i = 0; i < buildstate->centers->length; i++)
|
for (i = 0; i < buildstate->centers->length; i++)
|
||||||
{
|
{
|
||||||
|
/* Can take a while, so ensure we can interrupt */
|
||||||
|
/* Needs to be called when no buffer locks are held */
|
||||||
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
buf = IvfflatNewBuffer(index, forkNum);
|
buf = IvfflatNewBuffer(index, forkNum);
|
||||||
IvfflatInitPage(index, &buf, &page, &state);
|
IvfflatInitPage(index, &buf, &page, &state);
|
||||||
|
|
||||||
@@ -243,7 +278,7 @@ InsertTuples(Relation index, IvfflatBuildState * buildstate, ForkNumber forkNum)
|
|||||||
|
|
||||||
pfree(itup);
|
pfree(itup);
|
||||||
|
|
||||||
buildstate->indtuples += 1;
|
UpdateProgress(PROGRESS_CREATEIDX_TUPLES_DONE, ++inserted);
|
||||||
|
|
||||||
GetNextTuple(buildstate->sortstate, tupdesc, slot, &itup, &list);
|
GetNextTuple(buildstate->sortstate, tupdesc, slot, &itup, &list);
|
||||||
}
|
}
|
||||||
@@ -253,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,6 +318,12 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
|
|||||||
buildstate->kmeansnormprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
buildstate->kmeansnormprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_KMEANS_NORM_PROC);
|
||||||
buildstate->collation = index->rd_indcollation[0];
|
buildstate->collation = index->rd_indcollation[0];
|
||||||
|
|
||||||
|
/* Require more than one dimension for spherical k-means */
|
||||||
|
/* Lists check for backwards compatibility */
|
||||||
|
/* TODO Remove lists check in 0.3.0 */
|
||||||
|
if (buildstate->kmeansnormprocinfo != NULL && buildstate->dimensions == 1 && buildstate->lists > 1)
|
||||||
|
elog(ERROR, "dimensions must be greater than one for this opclass");
|
||||||
|
|
||||||
/* Create tuple description for sorting */
|
/* Create tuple description for sorting */
|
||||||
#if PG_VERSION_NUM >= 120000
|
#if PG_VERSION_NUM >= 120000
|
||||||
buildstate->tupdesc = CreateTemplateTupleDesc(4);
|
buildstate->tupdesc = CreateTemplateTupleDesc(4);
|
||||||
@@ -342,6 +383,7 @@ ComputeCenters(IvfflatBuildState * buildstate)
|
|||||||
SampleRows(buildstate);
|
SampleRows(buildstate);
|
||||||
|
|
||||||
/* Calculate centers */
|
/* Calculate centers */
|
||||||
|
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_KMEANS);
|
||||||
IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers);
|
IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers);
|
||||||
|
|
||||||
/* Free samples before we allocate more memory */
|
/* Free samples before we allocate more memory */
|
||||||
@@ -432,6 +474,8 @@ CreateEntryPages(IvfflatBuildState * buildstate, ForkNumber forkNum)
|
|||||||
Oid sortCollations[] = {InvalidOid};
|
Oid sortCollations[] = {InvalidOid};
|
||||||
bool nullsFirstFlags[] = {false};
|
bool nullsFirstFlags[] = {false};
|
||||||
|
|
||||||
|
UpdateProgress(PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_IVFFLAT_PHASE_SORT);
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 110000
|
#if PG_VERSION_NUM >= 110000
|
||||||
buildstate->sortstate = tuplesort_begin_heap(buildstate->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, maintenance_work_mem, NULL, false);
|
buildstate->sortstate = tuplesort_begin_heap(buildstate->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, maintenance_work_mem, NULL, false);
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
#include "utils/selfuncs.h"
|
#include "utils/selfuncs.h"
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM >= 120000
|
||||||
|
#include "commands/progress.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
int ivfflat_probes;
|
int ivfflat_probes;
|
||||||
static relopt_kind ivfflat_relopt_kind;
|
static relopt_kind ivfflat_relopt_kind;
|
||||||
|
|
||||||
@@ -30,6 +34,31 @@ _PG_init(void)
|
|||||||
1, 1, IVFFLAT_MAX_LISTS, PGC_USERSET, 0, NULL, NULL, NULL);
|
1, 1, IVFFLAT_MAX_LISTS, PGC_USERSET, 0, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the name of index build phase
|
||||||
|
*/
|
||||||
|
#if PG_VERSION_NUM >= 120000
|
||||||
|
static char *
|
||||||
|
ivfflatbuildphasename(int64 phasenum)
|
||||||
|
{
|
||||||
|
switch (phasenum)
|
||||||
|
{
|
||||||
|
case PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE:
|
||||||
|
return "initializing";
|
||||||
|
case PROGRESS_IVFFLAT_PHASE_SAMPLE:
|
||||||
|
return "sampling table";
|
||||||
|
case PROGRESS_IVFFLAT_PHASE_KMEANS:
|
||||||
|
return "performing k-means";
|
||||||
|
case PROGRESS_IVFFLAT_PHASE_SORT:
|
||||||
|
return "sorting tuples";
|
||||||
|
case PROGRESS_IVFFLAT_PHASE_LOAD:
|
||||||
|
return "loading tuples";
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Estimate the cost of an index scan
|
* Estimate the cost of an index scan
|
||||||
*/
|
*/
|
||||||
@@ -160,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;
|
||||||
|
|
||||||
@@ -169,12 +198,12 @@ 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 */
|
||||||
#if PG_VERSION_NUM >= 120000
|
#if PG_VERSION_NUM >= 120000
|
||||||
amroutine->ambuildphasename = NULL;
|
amroutine->ambuildphasename = ivfflatbuildphasename;
|
||||||
#endif
|
#endif
|
||||||
amroutine->amvalidate = ivfflatvalidate;
|
amroutine->amvalidate = ivfflatvalidate;
|
||||||
amroutine->ambeginscan = ivfflatbeginscan;
|
amroutine->ambeginscan = ivfflatbeginscan;
|
||||||
|
|||||||
@@ -27,6 +27,13 @@
|
|||||||
#define IVFFLAT_DEFAULT_LISTS 100
|
#define IVFFLAT_DEFAULT_LISTS 100
|
||||||
#define IVFFLAT_MAX_LISTS 32768
|
#define IVFFLAT_MAX_LISTS 32768
|
||||||
|
|
||||||
|
/* Build phases */
|
||||||
|
/* PROGRESS_CREATEIDX_SUBPHASE_INITIALIZE is 1 */
|
||||||
|
#define PROGRESS_IVFFLAT_PHASE_SAMPLE 2
|
||||||
|
#define PROGRESS_IVFFLAT_PHASE_KMEANS 3
|
||||||
|
#define PROGRESS_IVFFLAT_PHASE_SORT 4
|
||||||
|
#define PROGRESS_IVFFLAT_PHASE_LOAD 5
|
||||||
|
|
||||||
#define IVFFLAT_LIST_SIZE(_dim) (offsetof(IvfflatListData, center) + VECTOR_SIZE(_dim))
|
#define IVFFLAT_LIST_SIZE(_dim) (offsetof(IvfflatListData, center) + VECTOR_SIZE(_dim))
|
||||||
|
|
||||||
#define IvfflatPageGetOpaque(page) ((IvfflatPageOpaque) PageGetSpecialPointer(page))
|
#define IvfflatPageGetOpaque(page) ((IvfflatPageOpaque) PageGetSpecialPointer(page))
|
||||||
@@ -170,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.2'
|
default_version = '0.2.4'
|
||||||
module_pathname = '$libdir/vector'
|
module_pathname = '$libdir/vector'
|
||||||
relocatable = true
|
relocatable = true
|
||||||
|
|||||||
Reference in New Issue
Block a user