mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-25 05:12:43 +08:00
Compare commits
6 Commits
v0.6.0
...
hnsw-query
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d8cc82908 | ||
|
|
2716a223a6 | ||
|
|
3697043898 | ||
|
|
c22740962c | ||
|
|
a55ecf3281 | ||
|
|
fecdd5794e |
@@ -1,10 +1,14 @@
|
|||||||
|
## 0.6.1 (unreleased)
|
||||||
|
|
||||||
|
- Fixed error with `ANALYZE` and vectors with different dimensions
|
||||||
|
|
||||||
## 0.6.0 (2024-01-29)
|
## 0.6.0 (2024-01-29)
|
||||||
|
|
||||||
If upgrading with Postgres 12 or Docker, see [these notes](https://github.com/pgvector/pgvector#060).
|
If upgrading with Postgres 12 or Docker, see [these notes](https://github.com/pgvector/pgvector#060).
|
||||||
|
|
||||||
- Changed storage for vector from `extended` to `external`
|
|
||||||
- Added support for parallel index builds for HNSW
|
- Added support for parallel index builds for HNSW
|
||||||
- Added validation for GUC parameters
|
- Added validation for GUC parameters
|
||||||
|
- Changed storage for vector from `extended` to `external`
|
||||||
- Improved performance of HNSW
|
- Improved performance of HNSW
|
||||||
- Reduced memory usage for HNSW index builds
|
- Reduced memory usage for HNSW index builds
|
||||||
- Reduced WAL generation for HNSW index builds
|
- Reduced WAL generation for HNSW index builds
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -16,7 +16,7 @@ Plus [ACID](https://en.wikipedia.org/wiki/ACID) compliance, point-in-time recove
|
|||||||
|
|
||||||
### Linux and Mac
|
### Linux and Mac
|
||||||
|
|
||||||
Compile and install the extension (supports Postgres 11+)
|
Compile and install the extension (supports Postgres 12+)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /tmp
|
cd /tmp
|
||||||
@@ -430,6 +430,15 @@ To speed up queries with an IVFFlat index, increase the number of inverted lists
|
|||||||
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 1000);
|
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 1000);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Vacuuming
|
||||||
|
|
||||||
|
Vacuuming can take a while for HNSW indexes. Speed it up by reindexing first.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
REINDEX INDEX CONCURRENTLY index_name;
|
||||||
|
VACUUM table_name;
|
||||||
|
```
|
||||||
|
|
||||||
## Languages
|
## Languages
|
||||||
|
|
||||||
Use pgvector from any language with a Postgres client. You can even generate and store vectors in one language and query them in another.
|
Use pgvector from any language with a Postgres client. You can even generate and store vectors in one language and query them in another.
|
||||||
@@ -657,7 +666,7 @@ Get the [Docker image](https://hub.docker.com/r/pgvector/pgvector) with:
|
|||||||
docker pull pgvector/pgvector:pg16
|
docker pull pgvector/pgvector:pg16
|
||||||
```
|
```
|
||||||
|
|
||||||
This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (run it the same way).
|
This adds pgvector to the [Postgres image](https://hub.docker.com/_/postgres) (replace `16` with your Postgres server version, and run it the same way).
|
||||||
|
|
||||||
You can also build the image manually:
|
You can also build the image manually:
|
||||||
|
|
||||||
|
|||||||
11
src/hnsw.h
11
src/hnsw.h
@@ -362,6 +362,11 @@ typedef struct HnswVacuumState
|
|||||||
MemoryContext tmpCtx;
|
MemoryContext tmpCtx;
|
||||||
} HnswVacuumState;
|
} HnswVacuumState;
|
||||||
|
|
||||||
|
typedef struct HnswQuery
|
||||||
|
{
|
||||||
|
Datum value;
|
||||||
|
} HnswQuery;
|
||||||
|
|
||||||
/* Methods */
|
/* Methods */
|
||||||
int HnswGetM(Relation index);
|
int HnswGetM(Relation index);
|
||||||
int HnswGetEfConstruction(Relation index);
|
int HnswGetEfConstruction(Relation index);
|
||||||
@@ -370,14 +375,14 @@ bool HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * re
|
|||||||
Buffer HnswNewBuffer(Relation index, ForkNumber forkNum);
|
Buffer HnswNewBuffer(Relation index, ForkNumber forkNum);
|
||||||
void HnswInitPage(Buffer buf, Page page);
|
void HnswInitPage(Buffer buf, Page page);
|
||||||
void HnswInit(void);
|
void HnswInit(void);
|
||||||
List *HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, FmgrInfo *procinfo, Oid collation, int m, bool inserting, HnswElement skipElement);
|
List *HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation index, FmgrInfo *procinfo, Oid collation, int m, bool inserting, HnswElement skipElement);
|
||||||
HnswElement HnswGetEntryPoint(Relation index);
|
HnswElement HnswGetEntryPoint(Relation index);
|
||||||
void HnswGetMetaPageInfo(Relation index, int *m, HnswElement * entryPoint);
|
void HnswGetMetaPageInfo(Relation index, int *m, HnswElement * entryPoint);
|
||||||
void *HnswAlloc(HnswAllocator * allocator, Size size);
|
void *HnswAlloc(HnswAllocator * allocator, Size size);
|
||||||
HnswElement HnswInitElement(char *base, ItemPointer tid, int m, double ml, int maxLevel, HnswAllocator * alloc);
|
HnswElement HnswInitElement(char *base, ItemPointer tid, int m, double ml, int maxLevel, HnswAllocator * alloc);
|
||||||
HnswElement HnswInitElementFromBlock(BlockNumber blkno, OffsetNumber offno);
|
HnswElement HnswInitElementFromBlock(BlockNumber blkno, OffsetNumber offno);
|
||||||
void HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, FmgrInfo *procinfo, Oid collation, int m, int efConstruction, bool existing);
|
void HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint, Relation index, FmgrInfo *procinfo, Oid collation, int m, int efConstruction, bool existing);
|
||||||
HnswCandidate *HnswEntryCandidate(char *base, HnswElement em, Datum q, Relation rel, FmgrInfo *procinfo, Oid collation, bool loadVec);
|
HnswCandidate *HnswEntryCandidate(char *base, HnswElement em, HnswQuery * q, Relation rel, FmgrInfo *procinfo, Oid collation, bool loadVec);
|
||||||
void HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum, bool building);
|
void HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum, bool building);
|
||||||
void HnswSetNeighborTuple(char *base, HnswNeighborTuple ntup, HnswElement e, int m);
|
void HnswSetNeighborTuple(char *base, HnswNeighborTuple ntup, HnswElement e, int m);
|
||||||
void HnswAddHeapTid(HnswElement element, ItemPointer heaptid);
|
void HnswAddHeapTid(HnswElement element, ItemPointer heaptid);
|
||||||
@@ -385,7 +390,7 @@ void HnswInitNeighbors(char *base, HnswElement element, int m, HnswAllocator *
|
|||||||
bool HnswInsertTupleOnDisk(Relation index, Datum value, Datum *values, bool *isnull, ItemPointer heap_tid, bool building);
|
bool HnswInsertTupleOnDisk(Relation index, Datum value, Datum *values, bool *isnull, ItemPointer heap_tid, bool building);
|
||||||
void HnswUpdateNeighborsOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting, bool building);
|
void HnswUpdateNeighborsOnDisk(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting, bool building);
|
||||||
void HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHeaptids, bool loadVec);
|
void HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHeaptids, bool loadVec);
|
||||||
void HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec);
|
void HnswLoadElement(HnswElement element, float *distance, HnswQuery * q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec);
|
||||||
void HnswSetElementTuple(char *base, HnswElementTuple etup, HnswElement element);
|
void HnswSetElementTuple(char *base, HnswElementTuple etup, HnswElement element);
|
||||||
void HnswUpdateConnection(char *base, HnswElement element, HnswCandidate * hc, int lm, int lc, int *updateIdx, Relation index, FmgrInfo *procinfo, Oid collation);
|
void HnswUpdateConnection(char *base, HnswElement element, HnswCandidate * hc, int lm, int lc, int *updateIdx, Relation index, FmgrInfo *procinfo, Oid collation);
|
||||||
void HnswLoadNeighbors(HnswElement element, Relation index, int m);
|
void HnswLoadNeighbors(HnswElement element, Relation index, int m);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
* Algorithm 5 from paper
|
* Algorithm 5 from paper
|
||||||
*/
|
*/
|
||||||
static List *
|
static List *
|
||||||
GetScanItems(IndexScanDesc scan, Datum q)
|
GetScanItems(IndexScanDesc scan, Datum value)
|
||||||
{
|
{
|
||||||
HnswScanOpaque so = (HnswScanOpaque) scan->opaque;
|
HnswScanOpaque so = (HnswScanOpaque) scan->opaque;
|
||||||
Relation index = scan->indexRelation;
|
Relation index = scan->indexRelation;
|
||||||
@@ -22,6 +22,9 @@ GetScanItems(IndexScanDesc scan, Datum q)
|
|||||||
int m;
|
int m;
|
||||||
HnswElement entryPoint;
|
HnswElement entryPoint;
|
||||||
char *base = NULL;
|
char *base = NULL;
|
||||||
|
HnswQuery q;
|
||||||
|
|
||||||
|
q.value = value;
|
||||||
|
|
||||||
/* Get m and entry point */
|
/* Get m and entry point */
|
||||||
HnswGetMetaPageInfo(index, &m, &entryPoint);
|
HnswGetMetaPageInfo(index, &m, &entryPoint);
|
||||||
@@ -29,15 +32,15 @@ GetScanItems(IndexScanDesc scan, Datum q)
|
|||||||
if (entryPoint == NULL)
|
if (entryPoint == NULL)
|
||||||
return NIL;
|
return NIL;
|
||||||
|
|
||||||
ep = list_make1(HnswEntryCandidate(base, entryPoint, q, index, procinfo, collation, false));
|
ep = list_make1(HnswEntryCandidate(base, entryPoint, &q, index, procinfo, collation, false));
|
||||||
|
|
||||||
for (int lc = entryPoint->level; lc >= 1; lc--)
|
for (int lc = entryPoint->level; lc >= 1; lc--)
|
||||||
{
|
{
|
||||||
w = HnswSearchLayer(base, q, ep, 1, lc, index, procinfo, collation, m, false, NULL);
|
w = HnswSearchLayer(base, &q, ep, 1, lc, index, procinfo, collation, m, false, NULL);
|
||||||
ep = w;
|
ep = w;
|
||||||
}
|
}
|
||||||
|
|
||||||
return HnswSearchLayer(base, q, ep, hnsw_ef_search, 0, index, procinfo, collation, m, false, NULL);
|
return HnswSearchLayer(base, &q, ep, hnsw_ef_search, 0, index, procinfo, collation, m, false, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -555,7 +555,7 @@ HnswLoadElementFromTuple(HnswElement element, HnswElementTuple etup, bool loadHe
|
|||||||
* Load an element and optionally get its distance from q
|
* Load an element and optionally get its distance from q
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec)
|
HnswLoadElement(HnswElement element, float *distance, HnswQuery * q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec)
|
||||||
{
|
{
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
Page page;
|
Page page;
|
||||||
@@ -575,7 +575,7 @@ HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index,
|
|||||||
|
|
||||||
/* Calculate distance */
|
/* Calculate distance */
|
||||||
if (distance != NULL)
|
if (distance != NULL)
|
||||||
*distance = (float) DatumGetFloat8(FunctionCall2Coll(procinfo, collation, *q, PointerGetDatum(&etup->data)));
|
*distance = (float) DatumGetFloat8(FunctionCall2Coll(procinfo, collation, q->value, PointerGetDatum(&etup->data)));
|
||||||
|
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
}
|
}
|
||||||
@@ -584,19 +584,19 @@ HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index,
|
|||||||
* Get the distance for a candidate
|
* Get the distance for a candidate
|
||||||
*/
|
*/
|
||||||
static float
|
static float
|
||||||
GetCandidateDistance(char *base, HnswCandidate * hc, Datum q, FmgrInfo *procinfo, Oid collation)
|
GetCandidateDistance(char *base, HnswCandidate * hc, HnswQuery * q, FmgrInfo *procinfo, Oid collation)
|
||||||
{
|
{
|
||||||
HnswElement hce = HnswPtrAccess(base, hc->element);
|
HnswElement hce = HnswPtrAccess(base, hc->element);
|
||||||
Datum value = HnswGetValue(base, hce);
|
Datum value = HnswGetValue(base, hce);
|
||||||
|
|
||||||
return DatumGetFloat8(FunctionCall2Coll(procinfo, collation, q, value));
|
return DatumGetFloat8(FunctionCall2Coll(procinfo, collation, q->value, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a candidate for the entry point
|
* Create a candidate for the entry point
|
||||||
*/
|
*/
|
||||||
HnswCandidate *
|
HnswCandidate *
|
||||||
HnswEntryCandidate(char *base, HnswElement entryPoint, Datum q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec)
|
HnswEntryCandidate(char *base, HnswElement entryPoint, HnswQuery * q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec)
|
||||||
{
|
{
|
||||||
HnswCandidate *hc = palloc(sizeof(HnswCandidate));
|
HnswCandidate *hc = palloc(sizeof(HnswCandidate));
|
||||||
|
|
||||||
@@ -604,7 +604,7 @@ HnswEntryCandidate(char *base, HnswElement entryPoint, Datum q, Relation index,
|
|||||||
if (index == NULL)
|
if (index == NULL)
|
||||||
hc->distance = GetCandidateDistance(base, hc, q, procinfo, collation);
|
hc->distance = GetCandidateDistance(base, hc, q, procinfo, collation);
|
||||||
else
|
else
|
||||||
HnswLoadElement(entryPoint, &hc->distance, &q, index, procinfo, collation, loadVec);
|
HnswLoadElement(entryPoint, &hc->distance, q, index, procinfo, collation, loadVec);
|
||||||
return hc;
|
return hc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -722,7 +722,7 @@ CountElement(char *base, HnswElement skipElement, HnswCandidate * hc)
|
|||||||
* Algorithm 2 from paper
|
* Algorithm 2 from paper
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, FmgrInfo *procinfo, Oid collation, int m, bool inserting, HnswElement skipElement)
|
HnswSearchLayer(char *base, HnswQuery * q, List *ep, int ef, int lc, Relation index, FmgrInfo *procinfo, Oid collation, int m, bool inserting, HnswElement skipElement)
|
||||||
{
|
{
|
||||||
List *w = NIL;
|
List *w = NIL;
|
||||||
pairingheap *C = pairingheap_allocate(CompareNearestCandidates, NULL);
|
pairingheap *C = pairingheap_allocate(CompareNearestCandidates, NULL);
|
||||||
@@ -806,7 +806,7 @@ HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, F
|
|||||||
if (index == NULL)
|
if (index == NULL)
|
||||||
eDistance = GetCandidateDistance(base, e, q, procinfo, collation);
|
eDistance = GetCandidateDistance(base, e, q, procinfo, collation);
|
||||||
else
|
else
|
||||||
HnswLoadElement(eElement, &eDistance, &q, index, procinfo, collation, inserting);
|
HnswLoadElement(eElement, &eDistance, q, index, procinfo, collation, inserting);
|
||||||
|
|
||||||
Assert(!eElement->deleted);
|
Assert(!eElement->deleted);
|
||||||
|
|
||||||
@@ -1091,7 +1091,9 @@ HnswUpdateConnection(char *base, HnswElement element, HnswCandidate * hc, int lm
|
|||||||
/* Load elements on insert */
|
/* Load elements on insert */
|
||||||
if (index != NULL)
|
if (index != NULL)
|
||||||
{
|
{
|
||||||
Datum q = HnswGetValue(base, hce);
|
HnswQuery q;
|
||||||
|
|
||||||
|
q.value = HnswGetValue(base, hce);
|
||||||
|
|
||||||
for (int i = 0; i < currentNeighbors->length; i++)
|
for (int i = 0; i < currentNeighbors->length; i++)
|
||||||
{
|
{
|
||||||
@@ -1101,7 +1103,7 @@ HnswUpdateConnection(char *base, HnswElement element, HnswCandidate * hc, int lm
|
|||||||
if (HnswPtrIsNull(base, hc3Element->value))
|
if (HnswPtrIsNull(base, hc3Element->value))
|
||||||
HnswLoadElement(hc3Element, &hc3->distance, &q, index, procinfo, collation, true);
|
HnswLoadElement(hc3Element, &hc3->distance, &q, index, procinfo, collation, true);
|
||||||
else
|
else
|
||||||
hc3->distance = GetCandidateDistance(base, hc3, q, procinfo, collation);
|
hc3->distance = GetCandidateDistance(base, hc3, &q, procinfo, collation);
|
||||||
|
|
||||||
/* Prune element if being deleted */
|
/* Prune element if being deleted */
|
||||||
if (hc3Element->heaptidsLength == 0)
|
if (hc3Element->heaptidsLength == 0)
|
||||||
@@ -1201,9 +1203,11 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
|
|||||||
List *w;
|
List *w;
|
||||||
int level = element->level;
|
int level = element->level;
|
||||||
int entryLevel;
|
int entryLevel;
|
||||||
Datum q = HnswGetValue(base, element);
|
HnswQuery q;
|
||||||
HnswElement skipElement = existing ? element : NULL;
|
HnswElement skipElement = existing ? element : NULL;
|
||||||
|
|
||||||
|
q.value = HnswGetValue(base, element);
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 130000
|
#if PG_VERSION_NUM >= 130000
|
||||||
/* Precompute hash */
|
/* Precompute hash */
|
||||||
if (index == NULL)
|
if (index == NULL)
|
||||||
@@ -1215,13 +1219,13 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* Get entry point and level */
|
/* Get entry point and level */
|
||||||
ep = list_make1(HnswEntryCandidate(base, entryPoint, q, index, procinfo, collation, true));
|
ep = list_make1(HnswEntryCandidate(base, entryPoint, &q, index, procinfo, collation, true));
|
||||||
entryLevel = entryPoint->level;
|
entryLevel = entryPoint->level;
|
||||||
|
|
||||||
/* 1st phase: greedy search to insert level */
|
/* 1st phase: greedy search to insert level */
|
||||||
for (int lc = entryLevel; lc >= level + 1; lc--)
|
for (int lc = entryLevel; lc >= level + 1; lc--)
|
||||||
{
|
{
|
||||||
w = HnswSearchLayer(base, q, ep, 1, lc, index, procinfo, collation, m, true, skipElement);
|
w = HnswSearchLayer(base, &q, ep, 1, lc, index, procinfo, collation, m, true, skipElement);
|
||||||
ep = w;
|
ep = w;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1239,7 +1243,7 @@ HnswFindElementNeighbors(char *base, HnswElement element, HnswElement entryPoint
|
|||||||
List *neighbors;
|
List *neighbors;
|
||||||
List *lw;
|
List *lw;
|
||||||
|
|
||||||
w = HnswSearchLayer(base, q, ep, efConstruction, lc, index, procinfo, collation, m, true, skipElement);
|
w = HnswSearchLayer(base, &q, ep, efConstruction, lc, index, procinfo, collation, m, true, skipElement);
|
||||||
|
|
||||||
/* Elements being deleted or skipped can help with search */
|
/* Elements being deleted or skipped can help with search */
|
||||||
/* but should be removed before selecting neighbors */
|
/* but should be removed before selecting neighbors */
|
||||||
|
|||||||
30
src/vector.c
30
src/vector.c
@@ -866,9 +866,10 @@ vector_mul(PG_FUNCTION_ARGS)
|
|||||||
int
|
int
|
||||||
vector_cmp_internal(Vector * a, Vector * b)
|
vector_cmp_internal(Vector * a, Vector * b)
|
||||||
{
|
{
|
||||||
CheckDims(a, b);
|
int dim = Min(a->dim, b->dim);
|
||||||
|
|
||||||
for (int i = 0; i < a->dim; i++)
|
/* Check values before dimensions to be consistent with Postgres arrays */
|
||||||
|
for (int i = 0; i < dim; i++)
|
||||||
{
|
{
|
||||||
if (a->x[i] < b->x[i])
|
if (a->x[i] < b->x[i])
|
||||||
return -1;
|
return -1;
|
||||||
@@ -876,6 +877,13 @@ vector_cmp_internal(Vector * a, Vector * b)
|
|||||||
if (a->x[i] > b->x[i])
|
if (a->x[i] > b->x[i])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (a->dim < b->dim)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (a->dim > b->dim)
|
||||||
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -889,6 +897,9 @@ vector_lt(PG_FUNCTION_ARGS)
|
|||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
|
/* TODO Remove in 0.7.0 */
|
||||||
|
CheckDims(a, b);
|
||||||
|
|
||||||
PG_RETURN_BOOL(vector_cmp_internal(a, b) < 0);
|
PG_RETURN_BOOL(vector_cmp_internal(a, b) < 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -902,6 +913,9 @@ vector_le(PG_FUNCTION_ARGS)
|
|||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
|
/* TODO Remove in 0.7.0 */
|
||||||
|
CheckDims(a, b);
|
||||||
|
|
||||||
PG_RETURN_BOOL(vector_cmp_internal(a, b) <= 0);
|
PG_RETURN_BOOL(vector_cmp_internal(a, b) <= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -915,6 +929,9 @@ vector_eq(PG_FUNCTION_ARGS)
|
|||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
|
/* TODO Remove in 0.7.0 */
|
||||||
|
CheckDims(a, b);
|
||||||
|
|
||||||
PG_RETURN_BOOL(vector_cmp_internal(a, b) == 0);
|
PG_RETURN_BOOL(vector_cmp_internal(a, b) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -928,6 +945,9 @@ vector_ne(PG_FUNCTION_ARGS)
|
|||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
|
/* TODO Remove in 0.7.0 */
|
||||||
|
CheckDims(a, b);
|
||||||
|
|
||||||
PG_RETURN_BOOL(vector_cmp_internal(a, b) != 0);
|
PG_RETURN_BOOL(vector_cmp_internal(a, b) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -941,6 +961,9 @@ vector_ge(PG_FUNCTION_ARGS)
|
|||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
|
/* TODO Remove in 0.7.0 */
|
||||||
|
CheckDims(a, b);
|
||||||
|
|
||||||
PG_RETURN_BOOL(vector_cmp_internal(a, b) >= 0);
|
PG_RETURN_BOOL(vector_cmp_internal(a, b) >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -954,6 +977,9 @@ vector_gt(PG_FUNCTION_ARGS)
|
|||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
|
/* TODO Remove in 0.7.0 */
|
||||||
|
CheckDims(a, b);
|
||||||
|
|
||||||
PG_RETURN_BOOL(vector_cmp_internal(a, b) > 0);
|
PG_RETURN_BOOL(vector_cmp_internal(a, b) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,56 @@ SELECT '[1e37]'::vector * '[1e37]';
|
|||||||
ERROR: value out of range: overflow
|
ERROR: value out of range: overflow
|
||||||
SELECT '[1e-37]'::vector * '[1e-37]';
|
SELECT '[1e-37]'::vector * '[1e-37]';
|
||||||
ERROR: value out of range: underflow
|
ERROR: value out of range: underflow
|
||||||
|
SELECT '[1,2,3]'::vector = '[1,2,3]';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '[1,2,3]'::vector = '[1,2]';
|
||||||
|
ERROR: different vector dimensions 3 and 2
|
||||||
|
SELECT vector_cmp('[1,2,3]', '[1,2,3]');
|
||||||
|
vector_cmp
|
||||||
|
------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT vector_cmp('[1,2,3]', '[0,0,0]');
|
||||||
|
vector_cmp
|
||||||
|
------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT vector_cmp('[0,0,0]', '[1,2,3]');
|
||||||
|
vector_cmp
|
||||||
|
------------
|
||||||
|
-1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT vector_cmp('[1,2]', '[1,2,3]');
|
||||||
|
vector_cmp
|
||||||
|
------------
|
||||||
|
-1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT vector_cmp('[1,2,3]', '[1,2]');
|
||||||
|
vector_cmp
|
||||||
|
------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT vector_cmp('[1,2]', '[2,3,4]');
|
||||||
|
vector_cmp
|
||||||
|
------------
|
||||||
|
-1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT vector_cmp('[2,3]', '[1,2,3]');
|
||||||
|
vector_cmp
|
||||||
|
------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT vector_dims('[1,2,3]');
|
SELECT vector_dims('[1,2,3]');
|
||||||
vector_dims
|
vector_dims
|
||||||
-------------
|
-------------
|
||||||
|
|||||||
@@ -6,6 +6,17 @@ SELECT '[1,2,3]'::vector * '[4,5,6]';
|
|||||||
SELECT '[1e37]'::vector * '[1e37]';
|
SELECT '[1e37]'::vector * '[1e37]';
|
||||||
SELECT '[1e-37]'::vector * '[1e-37]';
|
SELECT '[1e-37]'::vector * '[1e-37]';
|
||||||
|
|
||||||
|
SELECT '[1,2,3]'::vector = '[1,2,3]';
|
||||||
|
SELECT '[1,2,3]'::vector = '[1,2]';
|
||||||
|
|
||||||
|
SELECT vector_cmp('[1,2,3]', '[1,2,3]');
|
||||||
|
SELECT vector_cmp('[1,2,3]', '[0,0,0]');
|
||||||
|
SELECT vector_cmp('[0,0,0]', '[1,2,3]');
|
||||||
|
SELECT vector_cmp('[1,2]', '[1,2,3]');
|
||||||
|
SELECT vector_cmp('[1,2,3]', '[1,2]');
|
||||||
|
SELECT vector_cmp('[1,2]', '[2,3,4]');
|
||||||
|
SELECT vector_cmp('[2,3]', '[1,2,3]');
|
||||||
|
|
||||||
SELECT vector_dims('[1,2,3]');
|
SELECT vector_dims('[1,2,3]');
|
||||||
|
|
||||||
SELECT round(vector_norm('[1,1]')::numeric, 5);
|
SELECT round(vector_norm('[1,1]')::numeric, 5);
|
||||||
|
|||||||
Reference in New Issue
Block a user