mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-22 20:15:46 +08:00
Compare commits
1 Commits
target-clo
...
subscript
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71ee682ed4 |
@@ -1,11 +1,15 @@
|
||||
## 0.6.2 (2024-03-18)
|
||||
## 0.7.0 (unreleased)
|
||||
|
||||
- Added subscript function for vectors
|
||||
|
||||
## 0.6.2 (unreleased)
|
||||
|
||||
- Reduced lock contention with parallel HNSW index builds
|
||||
|
||||
## 0.6.1 (2024-03-04)
|
||||
|
||||
- Fixed error with `ANALYZE` and vectors with different dimensions
|
||||
- Fixed segmentation fault with `shared_preload_libraries`
|
||||
- Fixed error with `shared_preload_libraries`
|
||||
- Fixed vector subtraction being marked as commutative
|
||||
|
||||
## 0.6.0 (2024-01-29)
|
||||
|
||||
@@ -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.6.2",
|
||||
"version": "0.6.1",
|
||||
"maintainer": [
|
||||
"Andrew Kane <andrew@ankane.org>"
|
||||
],
|
||||
@@ -20,7 +20,7 @@
|
||||
"vector": {
|
||||
"file": "sql/vector.sql",
|
||||
"docfile": "README.md",
|
||||
"version": "0.6.2",
|
||||
"version": "0.6.1",
|
||||
"abstract": "Open-source vector similarity search for Postgres"
|
||||
}
|
||||
},
|
||||
|
||||
16
Makefile
16
Makefile
@@ -1,5 +1,5 @@
|
||||
EXTENSION = vector
|
||||
EXTVERSION = 0.6.2
|
||||
EXTVERSION = 0.6.1
|
||||
|
||||
MODULE_big = vector
|
||||
DATA = $(wildcard sql/*--*.sql)
|
||||
@@ -10,15 +10,21 @@ TESTS = $(wildcard test/sql/*.sql)
|
||||
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
|
||||
REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION)
|
||||
|
||||
OPTFLAGS =
|
||||
OPTFLAGS = -march=native
|
||||
|
||||
# Since runtime dispatch not supported
|
||||
# Mac ARM doesn't support -march=native
|
||||
ifeq ($(shell uname -s), Darwin)
|
||||
ifeq ($(shell uname -m), x86_64)
|
||||
OPTFLAGS = -march=native
|
||||
ifeq ($(shell uname -p), arm)
|
||||
# no difference with -march=armv8.5-a
|
||||
OPTFLAGS =
|
||||
endif
|
||||
endif
|
||||
|
||||
# PowerPC doesn't support -march=native
|
||||
ifneq ($(filter ppc64%, $(shell uname -m)), )
|
||||
OPTFLAGS =
|
||||
endif
|
||||
|
||||
# For auto-vectorization:
|
||||
# - 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
EXTENSION = vector
|
||||
EXTVERSION = 0.6.2
|
||||
EXTVERSION = 0.6.1
|
||||
|
||||
OBJS = src\hnsw.obj src\hnswbuild.obj src\hnswinsert.obj src\hnswscan.obj src\hnswutils.obj src\hnswvacuum.obj src\ivfbuild.obj src\ivfflat.obj src\ivfinsert.obj src\ivfkmeans.obj src\ivfscan.obj src\ivfutils.obj src\ivfvacuum.obj src\vector.obj
|
||||
HEADERS = src\vector.h
|
||||
|
||||
26
README.md
26
README.md
@@ -20,7 +20,7 @@ Compile and install the extension (supports Postgres 12+)
|
||||
|
||||
```sh
|
||||
cd /tmp
|
||||
git clone --branch v0.6.2 https://github.com/pgvector/pgvector.git
|
||||
git clone --branch v0.6.1 https://github.com/pgvector/pgvector.git
|
||||
cd pgvector
|
||||
make
|
||||
make install # may need sudo
|
||||
@@ -45,7 +45,7 @@ Then use `nmake` to build:
|
||||
```cmd
|
||||
set "PGROOT=C:\Program Files\PostgreSQL\16"
|
||||
cd %TEMP%
|
||||
git clone --branch v0.6.2 https://github.com/pgvector/pgvector.git
|
||||
git clone --branch v0.6.1 https://github.com/pgvector/pgvector.git
|
||||
cd pgvector
|
||||
nmake /F Makefile.win
|
||||
nmake /F Makefile.win install
|
||||
@@ -604,18 +604,6 @@ and query with:
|
||||
SELECT * FROM items ORDER BY embedding::vector(3) <-> '[3,1,2]' LIMIT 5;
|
||||
```
|
||||
|
||||
#### Are binary vectors supported?
|
||||
|
||||
You can store binary vectors and perform exact nearest neighbor search by Hamming distance in Postgres without an extension ([example](https://github.com/pgvector/pgvector-python/blob/master/examples/hash_image_search.py)).
|
||||
|
||||
```tsql
|
||||
CREATE TABLE items (id bigserial PRIMARY KEY, embedding bit(3));
|
||||
INSERT INTO items (embedding) VALUES (B'000'), (B'111');
|
||||
SELECT * FROM items ORDER BY bit_count(embedding # B'101') LIMIT 5;
|
||||
```
|
||||
|
||||
Indexing is not currently supported.
|
||||
|
||||
#### Do indexes need to fit into memory?
|
||||
|
||||
No, but like other index types, you’ll likely see better performance if they do. You can get the size of an index with:
|
||||
@@ -671,8 +659,6 @@ ALTER TABLE items ALTER COLUMN embedding SET STORAGE PLAIN;
|
||||
|
||||
Results are limited by the size of the dynamic candidate list (`hnsw.ef_search`). There may be even less results due to dead tuples or filtering conditions in the query. We recommend setting `hnsw.ef_search` to at least twice the `LIMIT` of the query. If you need more than 500 results, use an IVFFlat index instead.
|
||||
|
||||
Also, note that `NULL` vectors are not indexed (as well as zero vectors for cosine distance).
|
||||
|
||||
#### Why are there less results for a query after adding an IVFFlat index?
|
||||
|
||||
The index was likely created with too little data for the number of lists. Drop the index until the table has more data.
|
||||
@@ -683,13 +669,11 @@ DROP INDEX index_name;
|
||||
|
||||
Results can also be limited by the number of probes (`ivfflat.probes`).
|
||||
|
||||
Also, note that `NULL` vectors are not indexed (as well as zero vectors for cosine distance).
|
||||
|
||||
## Reference
|
||||
|
||||
### Vector Type
|
||||
|
||||
Each vector takes `4 * dimensions + 8` bytes of storage. Each element is a single-precision floating-point number (like the `real` type in Postgres), and all elements must be finite (no `NaN`, `Infinity` or `-Infinity`). Vectors can have up to 16,000 dimensions.
|
||||
Each vector takes `4 * dimensions + 8` bytes of storage. Each element is a single precision floating-point number (like the `real` type in Postgres), and all elements must be finite (no `NaN`, `Infinity` or `-Infinity`). Vectors can have up to 16,000 dimensions.
|
||||
|
||||
### Vector Operators
|
||||
|
||||
@@ -713,7 +697,7 @@ l1_distance(vector, vector) → double precision | taxicab distance | 0.5.0
|
||||
vector_dims(vector) → integer | number of dimensions |
|
||||
vector_norm(vector) → double precision | Euclidean norm |
|
||||
|
||||
### Vector Aggregate Functions
|
||||
### Aggregate Functions
|
||||
|
||||
Function | Description | Added
|
||||
--- | --- | ---
|
||||
@@ -795,7 +779,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.6.2 https://github.com/pgvector/pgvector.git
|
||||
git clone --branch v0.6.1 https://github.com/pgvector/pgvector.git
|
||||
cd pgvector
|
||||
docker build --build-arg PG_MAJOR=16 -t myuser/pgvector .
|
||||
```
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION vector UPDATE TO '0.6.2'" to load this file. \quit
|
||||
7
sql/vector--0.6.2--0.7.0.sql
Normal file
7
sql/vector--0.6.2--0.7.0.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||
\echo Use "ALTER EXTENSION vector UPDATE TO '0.7.0'" to load this file. \quit
|
||||
|
||||
CREATE FUNCTION vector_subscript(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
ALTER TYPE vector SET (SUBSCRIPT = vector_subscript);
|
||||
@@ -20,12 +20,16 @@ CREATE FUNCTION vector_recv(internal, oid, integer) RETURNS vector
|
||||
CREATE FUNCTION vector_send(vector) RETURNS bytea
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE FUNCTION vector_subscript(internal) RETURNS internal
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
|
||||
CREATE TYPE vector (
|
||||
INPUT = vector_in,
|
||||
OUTPUT = vector_out,
|
||||
TYPMOD_IN = vector_typmod_in,
|
||||
RECEIVE = vector_recv,
|
||||
SEND = vector_send,
|
||||
SUBSCRIPT = vector_subscript,
|
||||
STORAGE = external
|
||||
);
|
||||
|
||||
|
||||
@@ -262,6 +262,7 @@ typedef struct HnswBuildState
|
||||
HnswGraph *graph;
|
||||
double ml;
|
||||
int maxLevel;
|
||||
Vector *normvec;
|
||||
|
||||
/* Memory */
|
||||
MemoryContext graphCtx;
|
||||
@@ -366,7 +367,7 @@ typedef struct HnswVacuumState
|
||||
int HnswGetM(Relation index);
|
||||
int HnswGetEfConstruction(Relation index);
|
||||
FmgrInfo *HnswOptionalProcInfo(Relation index, uint16 procnum);
|
||||
bool HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value);
|
||||
bool HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result);
|
||||
Buffer HnswNewBuffer(Relation index, ForkNumber forkNum);
|
||||
void HnswInitPage(Buffer buf, Page page);
|
||||
void HnswInit(void);
|
||||
|
||||
@@ -489,7 +489,7 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heaptid, Hn
|
||||
/* Normalize if needed */
|
||||
if (buildstate->normprocinfo != NULL)
|
||||
{
|
||||
if (!HnswNormValue(buildstate->normprocinfo, buildstate->collation, &value))
|
||||
if (!HnswNormValue(buildstate->normprocinfo, buildstate->collation, &value, buildstate->normvec))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -703,6 +703,9 @@ InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, Index
|
||||
buildstate->ml = HnswGetMl(buildstate->m);
|
||||
buildstate->maxLevel = HnswGetMaxLevel(buildstate->m);
|
||||
|
||||
/* Reuse for each tuple */
|
||||
buildstate->normvec = InitVector(buildstate->dimensions);
|
||||
|
||||
buildstate->graphCtx = GenerationContextCreate(CurrentMemoryContext,
|
||||
"Hnsw build graph context",
|
||||
#if PG_VERSION_NUM >= 150000
|
||||
@@ -726,6 +729,7 @@ InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, Index
|
||||
static void
|
||||
FreeBuildState(HnswBuildState * buildstate)
|
||||
{
|
||||
pfree(buildstate->normvec);
|
||||
MemoryContextDelete(buildstate->graphCtx);
|
||||
MemoryContextDelete(buildstate->tmpCtx);
|
||||
}
|
||||
|
||||
@@ -622,7 +622,7 @@ HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_ti
|
||||
normprocinfo = HnswOptionalProcInfo(index, HNSW_NORM_PROC);
|
||||
if (normprocinfo != NULL)
|
||||
{
|
||||
if (!HnswNormValue(normprocinfo, collation, &value))
|
||||
if (!HnswNormValue(normprocinfo, collation, &value, NULL))
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ GetScanValue(IndexScanDesc scan)
|
||||
|
||||
/* Fine if normalization fails */
|
||||
if (so->normprocinfo != NULL)
|
||||
HnswNormValue(so->normprocinfo, so->collation, &value);
|
||||
HnswNormValue(so->normprocinfo, so->collation, &value, NULL);
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
@@ -158,14 +158,16 @@ HnswOptionalProcInfo(Relation index, uint16 procnum)
|
||||
* if it's different than the original value
|
||||
*/
|
||||
bool
|
||||
HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value)
|
||||
HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result)
|
||||
{
|
||||
double norm = DatumGetFloat8(FunctionCall1Coll(procinfo, collation, *value));
|
||||
|
||||
if (norm > 0)
|
||||
{
|
||||
Vector *v = DatumGetVector(*value);
|
||||
Vector *result = InitVector(v->dim);
|
||||
|
||||
if (result == NULL)
|
||||
result = InitVector(v->dim);
|
||||
|
||||
for (int i = 0; i < v->dim; i++)
|
||||
result->x[i] = v->x[i] / norm;
|
||||
|
||||
@@ -57,7 +57,7 @@ AddSample(Datum *values, IvfflatBuildState * buildstate)
|
||||
*/
|
||||
if (buildstate->kmeansnormprocinfo != NULL)
|
||||
{
|
||||
if (!IvfflatNormValue(buildstate->kmeansnormprocinfo, buildstate->collation, &value))
|
||||
if (!IvfflatNormValue(buildstate->kmeansnormprocinfo, buildstate->collation, &value, buildstate->normvec))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ AddTupleToSort(Relation index, ItemPointer tid, Datum *values, IvfflatBuildState
|
||||
/* Normalize if needed */
|
||||
if (buildstate->normprocinfo != NULL)
|
||||
{
|
||||
if (!IvfflatNormValue(buildstate->normprocinfo, buildstate->collation, &value))
|
||||
if (!IvfflatNormValue(buildstate->normprocinfo, buildstate->collation, &value, buildstate->normvec))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -356,6 +356,9 @@ InitBuildState(IvfflatBuildState * buildstate, Relation heap, Relation index, In
|
||||
buildstate->centers = VectorArrayInit(buildstate->lists, buildstate->dimensions);
|
||||
buildstate->listInfo = palloc(sizeof(ListInfo) * buildstate->lists);
|
||||
|
||||
/* Reuse for each tuple */
|
||||
buildstate->normvec = InitVector(buildstate->dimensions);
|
||||
|
||||
buildstate->tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"Ivfflat build temporary context",
|
||||
ALLOCSET_DEFAULT_SIZES);
|
||||
@@ -377,6 +380,7 @@ FreeBuildState(IvfflatBuildState * buildstate)
|
||||
{
|
||||
VectorArrayFree(buildstate->centers);
|
||||
pfree(buildstate->listInfo);
|
||||
pfree(buildstate->normvec);
|
||||
|
||||
#ifdef IVFFLAT_KMEANS_DEBUG
|
||||
pfree(buildstate->listSums);
|
||||
|
||||
@@ -172,6 +172,7 @@ typedef struct IvfflatBuildState
|
||||
VectorArray samples;
|
||||
VectorArray centers;
|
||||
ListInfo *listInfo;
|
||||
Vector *normvec;
|
||||
|
||||
#ifdef IVFFLAT_KMEANS_DEBUG
|
||||
double inertia;
|
||||
@@ -266,7 +267,7 @@ void VectorArrayFree(VectorArray arr);
|
||||
void PrintVectorArray(char *msg, VectorArray arr);
|
||||
void IvfflatKmeans(Relation index, VectorArray samples, VectorArray centers);
|
||||
FmgrInfo *IvfflatOptionalProcInfo(Relation index, uint16 procnum);
|
||||
bool IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value);
|
||||
bool IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result);
|
||||
int IvfflatGetLists(Relation index);
|
||||
void IvfflatGetMetaPageInfo(Relation index, int *lists, int *dimensions);
|
||||
void IvfflatUpdateList(Relation index, ListInfo listInfo, BlockNumber insertPage, BlockNumber originalInsertPage, BlockNumber startPage, ForkNumber forkNum);
|
||||
|
||||
@@ -85,7 +85,7 @@ InsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, R
|
||||
normprocinfo = IvfflatOptionalProcInfo(index, IVFFLAT_NORM_PROC);
|
||||
if (normprocinfo != NULL)
|
||||
{
|
||||
if (!IvfflatNormValue(normprocinfo, index->rd_indcollation[0], &value))
|
||||
if (!IvfflatNormValue(normprocinfo, index->rd_indcollation[0], &value, NULL))
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -293,7 +293,7 @@ ivfflatgettuple(IndexScanDesc scan, ScanDirection dir)
|
||||
|
||||
/* Fine if normalization fails */
|
||||
if (so->normprocinfo != NULL)
|
||||
IvfflatNormValue(so->normprocinfo, so->collation, &value);
|
||||
IvfflatNormValue(so->normprocinfo, so->collation, &value, NULL);
|
||||
}
|
||||
|
||||
IvfflatBench("GetScanLists", GetScanLists(scan, value));
|
||||
|
||||
@@ -75,14 +75,16 @@ IvfflatOptionalProcInfo(Relation index, uint16 procnum)
|
||||
* if it's different than the original value
|
||||
*/
|
||||
bool
|
||||
IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value)
|
||||
IvfflatNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result)
|
||||
{
|
||||
double norm = DatumGetFloat8(FunctionCall1Coll(procinfo, collation, *value));
|
||||
|
||||
if (norm > 0)
|
||||
{
|
||||
Vector *v = DatumGetVector(*value);
|
||||
Vector *result = InitVector(v->dim);
|
||||
|
||||
if (result == NULL)
|
||||
result = InitVector(v->dim);
|
||||
|
||||
for (int i = 0; i < v->dim; i++)
|
||||
result->x[i] = v->x[i] / norm;
|
||||
|
||||
231
src/vector.c
231
src/vector.c
@@ -4,11 +4,18 @@
|
||||
|
||||
#include "catalog/pg_type.h"
|
||||
#include "common/shortest_dec.h"
|
||||
#include "executor/execExpr.h"
|
||||
#include "fmgr.h"
|
||||
#include "hnsw.h"
|
||||
#include "ivfflat.h"
|
||||
#include "lib/stringinfo.h"
|
||||
#include "libpq/pqformat.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "nodes/subscripting.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_node.h"
|
||||
#include "port.h" /* for strtof() */
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
@@ -29,15 +36,6 @@
|
||||
#define STATE_DIMS(x) (ARR_DIMS(x)[0] - 1)
|
||||
#define CreateStateDatums(dim) palloc(sizeof(Datum) * (dim + 1))
|
||||
|
||||
#if defined(__x86_64__) && defined(__gnu_linux__) && defined(__has_attribute) && __has_attribute(target_clones)
|
||||
#define RUNTIME_DISPATCH __attribute__((target_clones("default", "avx", "fma", "avx512f")))
|
||||
#elif defined(__aarch64__) && defined(__gnu_linux__) && defined(__has_attribute) && __has_attribute(target_clones)
|
||||
/* TODO Fix error: target does not support function version dispatcher */
|
||||
#define RUNTIME_DISPATCH __attribute__((target_clones("default", "arch=armv8.5-a")))
|
||||
#else
|
||||
#define RUNTIME_DISPATCH
|
||||
#endif
|
||||
|
||||
PG_MODULE_MAGIC;
|
||||
|
||||
/*
|
||||
@@ -427,6 +425,180 @@ vector_send(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform the subscript expressions
|
||||
*/
|
||||
static void
|
||||
vector_subscript_transform(SubscriptingRef *sbsref, List *indirection, ParseState *pstate, bool isSlice, bool isAssignment)
|
||||
{
|
||||
A_Indices *ai;
|
||||
Node *subexpr;
|
||||
|
||||
if (list_length(indirection) != 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("vector allows only one subscript"),
|
||||
parser_errposition(pstate,
|
||||
exprLocation((Node *) indirection))));
|
||||
|
||||
ai = linitial_node(A_Indices, indirection);
|
||||
|
||||
if (isSlice)
|
||||
{
|
||||
if (ai->lidx)
|
||||
{
|
||||
subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
|
||||
/* If it's not int4 already, try to coerce */
|
||||
subexpr = coerce_to_target_type(pstate,
|
||||
subexpr, exprType(subexpr),
|
||||
INT4OID, -1,
|
||||
COERCION_ASSIGNMENT,
|
||||
COERCE_IMPLICIT_CAST,
|
||||
-1);
|
||||
if (subexpr == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("vector subscript must have type integer"),
|
||||
parser_errposition(pstate, exprLocation(ai->lidx))));
|
||||
}
|
||||
else if (!ai->is_slice)
|
||||
{
|
||||
/* Make a constant 1 */
|
||||
subexpr = (Node *) makeConst(INT4OID,
|
||||
-1,
|
||||
InvalidOid,
|
||||
sizeof(int32),
|
||||
Int32GetDatum(1),
|
||||
false,
|
||||
true); /* pass by value */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Slice with omitted lower bound, put NULL into the list */
|
||||
subexpr = NULL;
|
||||
}
|
||||
sbsref->reflowerindexpr = list_make1(subexpr);
|
||||
}
|
||||
else
|
||||
Assert(ai->lidx == NULL && !ai->is_slice);
|
||||
|
||||
if (ai->uidx)
|
||||
{
|
||||
subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
|
||||
/* If it's not int4 already, try to coerce */
|
||||
subexpr = coerce_to_target_type(pstate,
|
||||
subexpr, exprType(subexpr),
|
||||
INT4OID, -1,
|
||||
COERCION_ASSIGNMENT,
|
||||
COERCE_IMPLICIT_CAST,
|
||||
-1);
|
||||
if (subexpr == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("array subscript must have type integer"),
|
||||
parser_errposition(pstate, exprLocation(ai->uidx))));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Slice with omitted upper bound, put NULL into the list */
|
||||
Assert(isSlice && ai->is_slice);
|
||||
subexpr = NULL;
|
||||
}
|
||||
sbsref->refupperindexpr = list_make1(subexpr);
|
||||
|
||||
if (isSlice)
|
||||
sbsref->refrestype = sbsref->refcontainertype;
|
||||
else
|
||||
sbsref->refrestype = FLOAT4OID;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch a vector element
|
||||
*/
|
||||
static void
|
||||
vector_subscript_fetch(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
|
||||
{
|
||||
SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
|
||||
Vector *vec = DatumGetVector(*op->resvalue);
|
||||
int index = DatumGetInt32(sbsrefstate->upperindex[0]);
|
||||
|
||||
if (index < 1 || index > vec->dim)
|
||||
*op->resnull = true;
|
||||
else
|
||||
*op->resvalue = Float4GetDatum(vec->x[index - 1]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch a vector slice
|
||||
*/
|
||||
static void
|
||||
vector_subscript_fetch_slice(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
|
||||
{
|
||||
SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
|
||||
|
||||
if (sbsrefstate->upperprovided[0] && sbsrefstate->upperindexnull[0])
|
||||
*op->resnull = true;
|
||||
else if (sbsrefstate->lowerprovided[0] && sbsrefstate->lowerindexnull[0])
|
||||
*op->resnull = true;
|
||||
else
|
||||
{
|
||||
Vector *vec = DatumGetVector(*op->resvalue);
|
||||
int upperindex = sbsrefstate->upperprovided[0] ? DatumGetInt32(sbsrefstate->upperindex[0]) : vec->dim;
|
||||
int lowerindex = sbsrefstate->lowerprovided[0] ? DatumGetInt32(sbsrefstate->lowerindex[0]) : 1;
|
||||
int dim;
|
||||
Vector *result;
|
||||
|
||||
if (upperindex > vec->dim)
|
||||
upperindex = vec->dim;
|
||||
|
||||
if (lowerindex < 1)
|
||||
lowerindex = 1;
|
||||
|
||||
dim = upperindex - lowerindex + 1;
|
||||
|
||||
CheckDim(dim);
|
||||
|
||||
result = InitVector(dim);
|
||||
for (int i = 0; i < dim; i++)
|
||||
result->x[i] = vec->x[lowerindex + i - 1];
|
||||
|
||||
*op->resvalue = PointerGetDatum(result);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up execution state for a vector subscript operation
|
||||
*/
|
||||
static void
|
||||
vector_exec_setup(const SubscriptingRef *sbsref, SubscriptingRefState *sbsrefstate, SubscriptExecSteps *methods)
|
||||
{
|
||||
methods->sbs_check_subscripts = NULL;
|
||||
if (sbsrefstate->numlower != 0)
|
||||
methods->sbs_fetch = vector_subscript_fetch_slice;
|
||||
else
|
||||
methods->sbs_fetch = vector_subscript_fetch;
|
||||
methods->sbs_assign = NULL;
|
||||
methods->sbs_fetch_old = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Subscript handler
|
||||
*/
|
||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_subscript);
|
||||
Datum
|
||||
vector_subscript(PG_FUNCTION_ARGS)
|
||||
{
|
||||
static const SubscriptRoutines sbsroutines = {
|
||||
.transform = vector_subscript_transform,
|
||||
.exec_setup = vector_exec_setup,
|
||||
.fetch_strict = true, /* fetch returns NULL for NULL inputs */
|
||||
.fetch_leakproof = true, /* fetch returns NULL for bad subscript */
|
||||
.store_leakproof = false /* ... but assignment throws error */
|
||||
};
|
||||
|
||||
PG_RETURN_POINTER(&sbsroutines);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert vector to vector
|
||||
* This is needed to check the type modifier
|
||||
@@ -541,23 +713,6 @@ vector_to_float4(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
RUNTIME_DISPATCH
|
||||
static float
|
||||
l2_squared_distance_impl(int16 dim, float *ax, float *bx)
|
||||
{
|
||||
float distance = 0.0;
|
||||
|
||||
/* Auto-vectorized */
|
||||
for (int16 i = 0; i < dim; i++)
|
||||
{
|
||||
float diff = ax[i] - bx[i];
|
||||
|
||||
distance += diff * diff;
|
||||
}
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the L2 distance between vectors
|
||||
*/
|
||||
@@ -567,11 +722,19 @@ l2_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||
float distance;
|
||||
float *ax = a->x;
|
||||
float *bx = b->x;
|
||||
float distance = 0.0;
|
||||
float diff;
|
||||
|
||||
CheckDims(a, b);
|
||||
|
||||
distance = l2_squared_distance_impl(a->dim, a->x, b->x);
|
||||
/* Auto-vectorized */
|
||||
for (int i = 0; i < a->dim; i++)
|
||||
{
|
||||
diff = ax[i] - bx[i];
|
||||
distance += diff * diff;
|
||||
}
|
||||
|
||||
PG_RETURN_FLOAT8(sqrt((double) distance));
|
||||
}
|
||||
@@ -586,11 +749,19 @@ vector_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
||||
float distance;
|
||||
float *ax = a->x;
|
||||
float *bx = b->x;
|
||||
float distance = 0.0;
|
||||
float diff;
|
||||
|
||||
CheckDims(a, b);
|
||||
|
||||
distance = l2_squared_distance_impl(a->dim, a->x, b->x);
|
||||
/* Auto-vectorized */
|
||||
for (int i = 0; i < a->dim; i++)
|
||||
{
|
||||
diff = ax[i] - bx[i];
|
||||
distance += diff * diff;
|
||||
}
|
||||
|
||||
PG_RETURN_FLOAT8((double) distance);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,112 @@ SELECT '[1e37]'::vector * '[1e37]';
|
||||
ERROR: value out of range: overflow
|
||||
SELECT '[1e-37]'::vector * '[1e-37]';
|
||||
ERROR: value out of range: underflow
|
||||
SELECT ('[1,2,3]'::vector)[0];
|
||||
vector
|
||||
--------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[1];
|
||||
vector
|
||||
--------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[2];
|
||||
vector
|
||||
--------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[3];
|
||||
vector
|
||||
--------
|
||||
3
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[4];
|
||||
vector
|
||||
--------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[1:1];
|
||||
vector
|
||||
--------
|
||||
[1]
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[1:2];
|
||||
vector
|
||||
--------
|
||||
[1,2]
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[2:4];
|
||||
vector
|
||||
--------
|
||||
[2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[-2:2];
|
||||
vector
|
||||
--------
|
||||
[1,2]
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[2:1];
|
||||
ERROR: vector must have at least 1 dimension
|
||||
SELECT ('[1,2,3]'::vector)[:];
|
||||
vector
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[:2];
|
||||
vector
|
||||
--------
|
||||
[1,2]
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[2:];
|
||||
vector
|
||||
--------
|
||||
[2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[:4];
|
||||
vector
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[-2:];
|
||||
vector
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[NULL];
|
||||
vector
|
||||
--------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[NULL:2];
|
||||
vector
|
||||
--------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[2:NULL];
|
||||
vector
|
||||
--------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[1][1];
|
||||
ERROR: vector allows only one subscript
|
||||
SELECT '[1,2,3]'::vector = '[1,2,3]';
|
||||
?column?
|
||||
----------
|
||||
|
||||
@@ -116,30 +116,8 @@ SELECT '[1, ,3]'::vector;
|
||||
ERROR: invalid input syntax for type vector: "[1, ,3]"
|
||||
LINE 1: SELECT '[1, ,3]'::vector;
|
||||
^
|
||||
SELECT '[1,2,3]'::vector(3);
|
||||
vector
|
||||
---------
|
||||
[1,2,3]
|
||||
(1 row)
|
||||
|
||||
SELECT '[1,2,3]'::vector(2);
|
||||
ERROR: expected 2 dimensions, not 3
|
||||
SELECT '[1,2,3]'::vector(3, 2);
|
||||
ERROR: invalid type modifier
|
||||
LINE 1: SELECT '[1,2,3]'::vector(3, 2);
|
||||
^
|
||||
SELECT '[1,2,3]'::vector('a');
|
||||
ERROR: invalid input syntax for type integer: "a"
|
||||
LINE 1: SELECT '[1,2,3]'::vector('a');
|
||||
^
|
||||
SELECT '[1,2,3]'::vector(0);
|
||||
ERROR: dimensions for type vector must be at least 1
|
||||
LINE 1: SELECT '[1,2,3]'::vector(0);
|
||||
^
|
||||
SELECT '[1,2,3]'::vector(16001);
|
||||
ERROR: dimensions for type vector cannot exceed 16000
|
||||
LINE 1: SELECT '[1,2,3]'::vector(16001);
|
||||
^
|
||||
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::vector[]);
|
||||
unnest
|
||||
---------
|
||||
|
||||
@@ -6,6 +6,26 @@ SELECT '[1,2,3]'::vector * '[4,5,6]';
|
||||
SELECT '[1e37]'::vector * '[1e37]';
|
||||
SELECT '[1e-37]'::vector * '[1e-37]';
|
||||
|
||||
SELECT ('[1,2,3]'::vector)[0];
|
||||
SELECT ('[1,2,3]'::vector)[1];
|
||||
SELECT ('[1,2,3]'::vector)[2];
|
||||
SELECT ('[1,2,3]'::vector)[3];
|
||||
SELECT ('[1,2,3]'::vector)[4];
|
||||
SELECT ('[1,2,3]'::vector)[1:1];
|
||||
SELECT ('[1,2,3]'::vector)[1:2];
|
||||
SELECT ('[1,2,3]'::vector)[2:4];
|
||||
SELECT ('[1,2,3]'::vector)[-2:2];
|
||||
SELECT ('[1,2,3]'::vector)[2:1];
|
||||
SELECT ('[1,2,3]'::vector)[:];
|
||||
SELECT ('[1,2,3]'::vector)[:2];
|
||||
SELECT ('[1,2,3]'::vector)[2:];
|
||||
SELECT ('[1,2,3]'::vector)[:4];
|
||||
SELECT ('[1,2,3]'::vector)[-2:];
|
||||
SELECT ('[1,2,3]'::vector)[NULL];
|
||||
SELECT ('[1,2,3]'::vector)[NULL:2];
|
||||
SELECT ('[1,2,3]'::vector)[2:NULL];
|
||||
SELECT ('[1,2,3]'::vector)[1][1];
|
||||
|
||||
SELECT '[1,2,3]'::vector = '[1,2,3]';
|
||||
SELECT '[1,2,3]'::vector = '[1,2]';
|
||||
|
||||
|
||||
@@ -22,13 +22,7 @@ SELECT '[1,]'::vector;
|
||||
SELECT '[1a]'::vector;
|
||||
SELECT '[1,,3]'::vector;
|
||||
SELECT '[1, ,3]'::vector;
|
||||
|
||||
SELECT '[1,2,3]'::vector(3);
|
||||
SELECT '[1,2,3]'::vector(2);
|
||||
SELECT '[1,2,3]'::vector(3, 2);
|
||||
SELECT '[1,2,3]'::vector('a');
|
||||
SELECT '[1,2,3]'::vector(0);
|
||||
SELECT '[1,2,3]'::vector(16001);
|
||||
|
||||
SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::vector[]);
|
||||
SELECT '{"[1,2,3]"}'::vector(2)[];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
comment = 'vector data type and ivfflat and hnsw access methods'
|
||||
default_version = '0.6.2'
|
||||
default_version = '0.6.1'
|
||||
module_pathname = '$libdir/vector'
|
||||
relocatable = true
|
||||
|
||||
Reference in New Issue
Block a user