Compare commits

..

15 Commits

Author SHA1 Message Date
Andrew Kane
69672cd84d Version bump to 0.4.2 [skip ci] 2023-05-13 20:47:40 -07:00
Andrew Kane
300adba2f1 Updated messages 2023-05-13 20:44:46 -07:00
Andrew Kane
e362279199 Updated changelog [skip ci] 2023-05-12 18:01:25 -07:00
Andrew Kane
53301021f6 Added dimensions check to vector_avg 2023-05-12 17:19:16 -07:00
Andrew Kane
8f589f6d09 Added test for array_to_vector 2023-05-12 17:14:29 -07:00
Nathan Bossart
dcf206128a Check bounds unconditionally in array_to_vector(). (#127)
Presently, array_to_vector()'s call to CheckDim() is skipped when typmod != -1, which allows for bypassing VECTOR_MAX_DIM.  To fix, call Check[Expected]Dim() unconditionally.  CheckExpectedDim() takes no action when typmod == -1, so there's no need to guard it with an 'if' statement.
2023-05-12 17:08:51 -07:00
Andrew Kane
3244d40e8a Added note about --preserve-env [skip ci] 2023-05-10 12:13:15 -07:00
Andrew Kane
7d8dbcaa3c Added note about Homebrew Postgres [skip ci] 2023-05-10 12:06:30 -07:00
Andrew Kane
7f575f55fb Removed block size note - #120 [skip ci] 2023-05-09 14:43:24 -07:00
Andrew Kane
94e7487d5f Fixed link [skip ci] 2023-05-06 12:33:20 -07:00
Andrew Kane
74a3cd597f Improved Docker tasks [skip ci] 2023-05-06 12:11:10 -07:00
Andrew Kane
db8ed738b8 Split Docker tasks [skip ci] 2023-05-06 11:59:46 -07:00
Andrew Kane
54c550420b Added Docker image for linux/arm64 - closes #115 2023-05-06 11:50:59 -07:00
Fabian Fischer
cc539a0a27 docs: aws rds supports pgvector now (#110) 2023-05-03 13:08:06 -07:00
Andrew Kane
d885e2bcfa Added FAQ about results [skip ci] 2023-05-02 10:17:31 -07:00
15 changed files with 106 additions and 110 deletions

View File

@@ -1,6 +1,7 @@
## 0.4.2 (unreleased)
## 0.4.2 (2023-05-13)
- Added notice when index created with little data
- Fixed dimensions check for some direct function calls
- Fixed installation error with Postgres 12.0-12.2
## 0.4.1 (2023-03-21)

View File

@@ -2,7 +2,7 @@
"name": "vector",
"abstract": "Open-source vector similarity search for Postgres",
"description": "Supports L2 distance, inner product, and cosine distance",
"version": "0.4.1",
"version": "0.4.2",
"maintainer": [
"Andrew Kane <andrew@ankane.org>"
],
@@ -20,7 +20,7 @@
"vector": {
"file": "sql/vector.sql",
"docfile": "README.md",
"version": "0.4.1",
"version": "0.4.2",
"abstract": "Open-source vector similarity search for Postgres"
}
},

View File

@@ -1,5 +1,5 @@
EXTENSION = vector
EXTVERSION = 0.4.1
EXTVERSION = 0.4.2
MODULE_big = vector
DATA = $(wildcard sql/*--*.sql)
@@ -63,3 +63,9 @@ dist:
docker:
docker build --pull --no-cache --platform linux/amd64 -t ankane/pgvector:latest .
.PHONY: docker-release
docker-release:
docker buildx build --push --pull --no-cache --platform linux/amd64,linux/arm64 -t ankane/pgvector:latest .
docker buildx build --push --platform linux/amd64,linux/arm64 -t ankane/pgvector:v$(EXTVERSION) .

View File

@@ -1,5 +1,5 @@
EXTENSION = vector
EXTVERSION = 0.4.1
EXTVERSION = 0.4.2
OBJS = src\ivfbuild.obj src\ivfflat.obj src\ivfinsert.obj src\ivfkmeans.obj src\ivfscan.obj src\ivfutils.obj src\ivfvacuum.obj src\vector.obj

View File

@@ -16,7 +16,7 @@ Compile and install the extension (supports Postgres 11+)
```sh
cd /tmp
git clone --branch v0.4.1 https://github.com/pgvector/pgvector.git
git clone --branch v0.4.2 https://github.com/pgvector/pgvector.git
cd pgvector
make
make install # may need sudo
@@ -307,10 +307,11 @@ Yes, pgvector uses the write-ahead log (WAL), which allows for replication and p
#### What if I want to index vectors with more than 2,000 dimensions?
Two things you can try are:
Youll need to use [dimensionality reduction](https://en.wikipedia.org/wiki/Dimensionality_reduction) at the moment.
1. use dimensionality reduction
2. compile Postgres with a larger block size (`./configure --with-blocksize=32`) and edit the limit in `src/ivfflat.h`
#### Why am I seeing less results after adding an index?
The index was likely created with too little data for the number of lists. Drop the index until the table has more data.
## Reference
@@ -354,7 +355,11 @@ If your machine has multiple Postgres installations, specify the path to [pg_con
export PG_CONFIG=/Applications/Postgres.app/Contents/Versions/latest/bin/pg_config
```
Then re-run the installation instructions (run `make clean` before `make` if needed)
Then re-run the installation instructions (run `make clean` before `make` if needed). If `sudo` is needed for `make install`, use:
```sh
sudo --preserve-env=PG_CONFIG make install
```
### Missing Header
@@ -374,7 +379,7 @@ Support for Windows is currently experimental. Use `nmake` to build:
```cmd
set "PGROOT=C:\Program Files\PostgreSQL\15"
git clone --branch v0.4.1 https://github.com/pgvector/pgvector.git
git clone --branch v0.4.2 https://github.com/pgvector/pgvector.git
cd pgvector
nmake /F Makefile.win
nmake /F Makefile.win install
@@ -395,7 +400,7 @@ This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (r
You can also build the image manually:
```sh
git clone --branch v0.4.1 https://github.com/pgvector/pgvector.git
git clone --branch v0.4.2 https://github.com/pgvector/pgvector.git
cd pgvector
docker build -t pgvector .
```
@@ -408,6 +413,8 @@ With Homebrew Postgres, you can use:
brew install pgvector
```
Note: This only adds it to the `postgresql@14` formula
### PGXN
Install from the [PostgreSQL Extension Network](https://pgxn.org/dist/vector) with:
@@ -444,10 +451,9 @@ pgvector is available on [these providers](https://github.com/pgvector/pgvector/
To request a new extension on other providers:
- Amazon RDS - follow the instructions on [this page](https://aws.amazon.com/rds/postgresql/faqs/)
- Google Cloud SQL - vote or comment on [this page](https://issuetracker.google.com/issues/265172065)
- Azure Database - vote or comment on [this page](https://feedback.azure.com/d365community/idea/7b423322-6189-ed11-a81b-000d3ae49307)
- DigitalOcean Managed Databases - vote or comment on [this page](https://ideas.digitalocean.com/app-framework-services/p/pgvector-extension-for-postgresql)
- DigitalOcean Managed Databases - vote or comment on [this page](https://ideas.digitalocean.com/managed-database/p/pgvector-extension-for-postgresql)
- Heroku Postgres - vote or comment on [this page](https://github.com/heroku/roadmap/issues/156)
## Upgrading

View 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.4.2'" to load this file. \quit

View File

@@ -438,8 +438,8 @@ ComputeCenters(IvfflatBuildState * buildstate)
{
ereport(NOTICE,
(errmsg("ivfflat index created with little data"),
errdetail("this will cause poor recall"),
errhint("drop the index until the table has more data")));
errdetail("This will cause low recall."),
errhint("Drop the index until the table has more data.")));
}
}

View File

@@ -187,14 +187,6 @@ typedef struct IvfflatScanList
double distance;
} IvfflatScanList;
typedef struct IvfflatScanItem
{
pairingheap_node ph_node;
BlockNumber searchPage;
double distance;
ItemPointerData tid;
} IvfflatScanItem;
typedef struct IvfflatScanOpaqueData
{
int probes;
@@ -212,13 +204,6 @@ typedef struct IvfflatScanOpaqueData
FmgrInfo *normprocinfo;
Oid collation;
/* Items */
int maxItems;
int itemCount;
pairingheap *itemQueue;
IvfflatScanItem *items;
IvfflatScanItem **sortedItems;
/* Lists */
pairingheap *listQueue;
IvfflatScanList lists[FLEXIBLE_ARRAY_MEMBER]; /* must come last */

View File

@@ -26,21 +26,6 @@ CompareLists(const pairingheap_node *a, const pairingheap_node *b, void *arg)
return 0;
}
/*
* Compare item distances
*/
static int
CompareItems(const pairingheap_node *a, const pairingheap_node *b, void *arg)
{
if (((const IvfflatScanItem *) a)->distance > ((const IvfflatScanItem *) b)->distance)
return 1;
if (((const IvfflatScanItem *) a)->distance < ((const IvfflatScanItem *) b)->distance)
return -1;
return 0;
}
/*
* Get lists and sort by distance
*/
@@ -126,10 +111,13 @@ GetScanItems(IndexScanDesc scan, Datum value)
Datum datum;
bool isnull;
TupleDesc tupdesc = RelationGetDescr(scan->indexRelation);
int i;
double distance;
IvfflatScanItem *scanitem;
double maxDistance = DBL_MAX;
double tuples = 0;
#if PG_VERSION_NUM >= 120000
TupleTableSlot *slot = MakeSingleTupleTableSlot(so->tupdesc, &TTSOpsVirtual);
#else
TupleTableSlot *slot = MakeSingleTupleTableSlot(so->tupdesc);
#endif
/*
* Reuse same set of shared buffers for scan
@@ -155,40 +143,25 @@ GetScanItems(IndexScanDesc scan, Datum value)
{
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offno));
datum = index_getattr(itup, 1, tupdesc, &isnull);
distance = DatumGetFloat8(FunctionCall2Coll(so->procinfo, so->collation, datum, value));
if (so->itemCount < so->maxItems)
{
scanitem = &so->items[so->itemCount];
scanitem->searchPage = searchPage;
scanitem->tid = itup->t_tid;
scanitem->distance = distance;
so->itemCount++;
/*
* Add virtual tuple
*
* Use procinfo from the index instead of scan key for
* performance
*/
ExecClearTuple(slot);
slot->tts_values[0] = FunctionCall2Coll(so->procinfo, so->collation, datum, value);
slot->tts_isnull[0] = false;
slot->tts_values[1] = PointerGetDatum(&itup->t_tid);
slot->tts_isnull[1] = false;
slot->tts_values[2] = Int32GetDatum((int) searchPage);
slot->tts_isnull[2] = false;
ExecStoreVirtualTuple(slot);
/* Add to heap */
pairingheap_add(so->itemQueue, &scanitem->ph_node);
tuplesort_puttupleslot(so->sortstate, slot);
/* Calculate max distance */
if (so->itemCount == so->maxItems)
{
maxDistance = ((IvfflatScanItem *) pairingheap_first(so->itemQueue))->distance;
scanitem = &so->items[so->itemCount];
}
}
else if (distance < maxDistance)
{
/* Reuse */
scanitem->searchPage = searchPage;
scanitem->tid = itup->t_tid;
scanitem->distance = distance;
pairingheap_add(so->itemQueue, &scanitem->ph_node);
/* Remove */
scanitem = (IvfflatScanItem *) pairingheap_remove_first(so->itemQueue);
/* Update max distance */
maxDistance = ((IvfflatScanItem *) pairingheap_first(so->itemQueue))->distance;
}
tuples++;
}
searchPage = IvfflatPageGetOpaque(page)->nextblkno;
@@ -197,10 +170,14 @@ GetScanItems(IndexScanDesc scan, Datum value)
}
}
for (i = 0; i < so->itemCount; i++)
so->sortedItems[i] = (IvfflatScanItem *) pairingheap_remove_first(so->itemQueue);
/* TODO Scan more lists */
if (tuples < 100)
ereport(DEBUG1,
(errmsg("index scan found few tuples"),
errdetail("Index may have been created with little data."),
errhint("Recreate the index and possibly decrease lists.")));
Assert(pairingheap_is_empty(so->itemQueue));
tuplesort_performsort(so->sortstate);
}
/*
@@ -212,6 +189,10 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
IndexScanDesc scan;
IvfflatScanOpaque so;
int lists;
AttrNumber attNums[] = {1};
Oid sortOperators[] = {Float8LessOperator};
Oid sortCollations[] = {InvalidOid};
bool nullsFirstFlags[] = {false};
int probes = ivfflat_probes;
scan = RelationGetIndexScan(index, nkeys, norderbys);
@@ -230,13 +211,26 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
so->normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
so->collation = index->rd_indcollation[0];
so->listQueue = pairingheap_allocate(CompareLists, scan);
/* Create tuple description for sorting */
#if PG_VERSION_NUM >= 120000
so->tupdesc = CreateTemplateTupleDesc(3);
#else
so->tupdesc = CreateTemplateTupleDesc(3, false);
#endif
TupleDescInitEntry(so->tupdesc, (AttrNumber) 1, "distance", FLOAT8OID, -1, 0);
TupleDescInitEntry(so->tupdesc, (AttrNumber) 2, "tid", TIDOID, -1, 0);
TupleDescInitEntry(so->tupdesc, (AttrNumber) 3, "indexblkno", INT4OID, -1, 0);
so->maxItems = 10;
so->itemCount = 0;
so->itemQueue = pairingheap_allocate(CompareItems, scan);
so->items = palloc(sizeof(IvfflatScanItem) * (so->maxItems + 1));
so->sortedItems = palloc(sizeof(IvfflatScanItem *) * so->maxItems);
/* Prep sort */
so->sortstate = tuplesort_begin_heap(so->tupdesc, 1, attNums, sortOperators, sortCollations, nullsFirstFlags, work_mem, NULL, false);
#if PG_VERSION_NUM >= 120000
so->slot = MakeSingleTupleTableSlot(so->tupdesc, &TTSOpsMinimalTuple);
#else
so->slot = MakeSingleTupleTableSlot(so->tupdesc);
#endif
so->listQueue = pairingheap_allocate(CompareLists, scan);
scan->opaque = so;
@@ -251,10 +245,13 @@ ivfflatrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int
{
IvfflatScanOpaque so = (IvfflatScanOpaque) scan->opaque;
#if PG_VERSION_NUM >= 130000
if (!so->first)
tuplesort_reset(so->sortstate);
#endif
so->first = true;
pairingheap_reset(so->listQueue);
pairingheap_reset(so->itemQueue);
so->itemCount = 0;
if (keys && scan->numberOfKeys > 0)
memmove(scan->keyData, keys, scan->numberOfKeys * sizeof(ScanKeyData));
@@ -314,18 +311,15 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
pfree(DatumGetPointer(value));
}
if (so->itemCount > 0)
if (tuplesort_gettupleslot(so->sortstate, true, false, so->slot, NULL))
{
IvfflatScanItem *scanitem;
so->itemCount--;
scanitem = so->sortedItems[so->itemCount];
ItemPointer tid = (ItemPointer) DatumGetPointer(slot_getattr(so->slot, 2, &so->isnull));
BlockNumber indexblkno = DatumGetInt32(slot_getattr(so->slot, 3, &so->isnull));
#if PG_VERSION_NUM >= 120000
scan->xs_heaptid = scanitem->tid;
scan->xs_heaptid = *tid;
#else
scan->xs_ctup.t_self = scanitem->tid;
scan->xs_ctup.t_self = *tid;
#endif
if (BufferIsValid(so->buf))
@@ -337,7 +331,7 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
*
* https://www.postgresql.org/docs/current/index-locking.html
*/
so->buf = ReadBuffer(scan->indexRelation, scanitem->searchPage);
so->buf = ReadBuffer(scan->indexRelation, indexblkno);
scan->xs_recheckorderby = false;
return true;
@@ -359,10 +353,7 @@ ivfflatendscan(IndexScanDesc scan)
ReleaseBuffer(so->buf);
pairingheap_free(so->listQueue);
pairingheap_free(so->itemQueue);
pfree(so->items);
pfree(so->sortedItems);
tuplesort_end(so->sortstate);
pfree(so);
scan->opaque = NULL;

View File

@@ -396,10 +396,8 @@ array_to_vector(PG_FUNCTION_ARGS)
get_typlenbyvalalign(ARR_ELEMTYPE(array), &typlen, &typbyval, &typalign);
deconstruct_array(array, ARR_ELEMTYPE(array), typlen, typbyval, typalign, &elemsp, &nullsp, &nelemsp);
if (typmod == -1)
CheckDim(nelemsp);
else
CheckExpectedDim(typmod, nelemsp);
CheckDim(nelemsp);
CheckExpectedDim(typmod, nelemsp);
result = InitVector(nelemsp);
for (i = 0; i < nelemsp; i++)
@@ -952,6 +950,7 @@ vector_avg(PG_FUNCTION_ARGS)
/* Create vector */
dim = STATE_DIMS(statearray);
CheckDim(dim);
result = InitVector(dim);
for (int i = 0; i < dim; i++)
{

View File

@@ -46,6 +46,8 @@ SELECT '[1,2,3]'::vector::real[];
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
ERROR: vector cannot have more than 16000 dimensions
SELECT array_to_vector(array_agg(n), 16001, false) FROM generate_series(1, 16001) n;
ERROR: vector cannot have more than 16000 dimensions
-- ensure no error
SELECT ARRAY[1,2,3] = ARRAY[1,2,3];
?column?

View File

@@ -102,3 +102,5 @@ SELECT avg(v) FROM unnest(ARRAY[]::vector[]) v;
SELECT avg(v) FROM unnest(ARRAY['[1,2]'::vector, '[3]']) v;
ERROR: expected 2 dimensions, not 1
SELECT vector_avg(array_agg(n)) FROM generate_series(1, 16002) n;
ERROR: vector cannot have more than 16000 dimensions

View File

@@ -10,6 +10,7 @@ SELECT '{-Infinity}'::real[]::vector;
SELECT '{}'::real[]::vector;
SELECT '[1,2,3]'::vector::real[];
SELECT array_agg(n)::vector FROM generate_series(1, 16001) n;
SELECT array_to_vector(array_agg(n), 16001, false) FROM generate_series(1, 16001) n;
-- ensure no error
SELECT ARRAY[1,2,3] = ARRAY[1,2,3];

View File

@@ -24,3 +24,4 @@ SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::vector, '[3,5,7]']) v;
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::vector, '[3,5,7]', NULL]) v;
SELECT avg(v) FROM unnest(ARRAY[]::vector[]) v;
SELECT avg(v) FROM unnest(ARRAY['[1,2]'::vector, '[3]']) v;
SELECT vector_avg(array_agg(n)) FROM generate_series(1, 16002) n;

View File

@@ -1,4 +1,4 @@
comment = 'vector data type and ivfflat access method'
default_version = '0.4.1'
default_version = '0.4.2'
module_pathname = '$libdir/vector'
relocatable = true