Removed dimensions from sparsevec

This commit is contained in:
Andrew Kane
2024-04-03 20:55:03 -07:00
parent 35d0fe88b9
commit dde3a2aacd
17 changed files with 166 additions and 289 deletions

View File

@@ -173,9 +173,6 @@ CREATE FUNCTION sparsevec_in(cstring, oid, integer) RETURNS sparsevec
CREATE FUNCTION sparsevec_out(sparsevec) RETURNS cstring CREATE FUNCTION sparsevec_out(sparsevec) RETURNS cstring
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_typmod_in(cstring[]) RETURNS integer
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_recv(internal, oid, integer) RETURNS sparsevec CREATE FUNCTION sparsevec_recv(internal, oid, integer) RETURNS sparsevec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -185,7 +182,6 @@ CREATE FUNCTION sparsevec_send(sparsevec) RETURNS bytea
CREATE TYPE sparsevec ( CREATE TYPE sparsevec (
INPUT = sparsevec_in, INPUT = sparsevec_in,
OUTPUT = sparsevec_out, OUTPUT = sparsevec_out,
TYPMOD_IN = sparsevec_typmod_in,
RECEIVE = sparsevec_recv, RECEIVE = sparsevec_recv,
SEND = sparsevec_send, SEND = sparsevec_send,
STORAGE = external STORAGE = external

View File

@@ -480,9 +480,6 @@ CREATE FUNCTION sparsevec_in(cstring, oid, integer) RETURNS sparsevec
CREATE FUNCTION sparsevec_out(sparsevec) RETURNS cstring CREATE FUNCTION sparsevec_out(sparsevec) RETURNS cstring
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_typmod_in(cstring[]) RETURNS integer
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
CREATE FUNCTION sparsevec_recv(internal, oid, integer) RETURNS sparsevec CREATE FUNCTION sparsevec_recv(internal, oid, integer) RETURNS sparsevec
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
@@ -492,7 +489,6 @@ CREATE FUNCTION sparsevec_send(sparsevec) RETURNS bytea
CREATE TYPE sparsevec ( CREATE TYPE sparsevec (
INPUT = sparsevec_in, INPUT = sparsevec_in,
OUTPUT = sparsevec_out, OUTPUT = sparsevec_out,
TYPMOD_IN = sparsevec_typmod_in,
RECEIVE = sparsevec_recv, RECEIVE = sparsevec_recv,
SEND = sparsevec_send, SEND = sparsevec_send,
STORAGE = external STORAGE = external

View File

@@ -681,8 +681,6 @@ GetMaxDimensions(HnswType type)
maxDimensions *= 2; maxDimensions *= 2;
else if (type == HNSW_TYPE_BIT) else if (type == HNSW_TYPE_BIT)
maxDimensions *= 32; maxDimensions *= 32;
else if (type == HNSW_TYPE_SPARSEVEC)
maxDimensions = INT_MAX;
return maxDimensions; return maxDimensions;
} }
@@ -693,8 +691,6 @@ GetMaxDimensions(HnswType type)
static void static void
InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, IndexInfo *indexInfo, ForkNumber forkNum) InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, IndexInfo *indexInfo, ForkNumber forkNum)
{ {
int maxDimensions;
buildstate->heap = heap; buildstate->heap = heap;
buildstate->index = index; buildstate->index = index;
buildstate->indexInfo = indexInfo; buildstate->indexInfo = indexInfo;
@@ -705,14 +701,17 @@ InitBuildState(HnswBuildState * buildstate, Relation heap, Relation index, Index
buildstate->efConstruction = HnswGetEfConstruction(index); buildstate->efConstruction = HnswGetEfConstruction(index);
buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod; buildstate->dimensions = TupleDescAttr(index->rd_att, 0)->atttypmod;
maxDimensions = GetMaxDimensions(buildstate->type);
/* Require column to have dimensions to be indexed */ if (buildstate->type != HNSW_TYPE_SPARSEVEC)
if (buildstate->dimensions < 0) {
elog(ERROR, "column does not have dimensions"); int maxDimensions = GetMaxDimensions(buildstate->type);
if (buildstate->dimensions > maxDimensions) if (buildstate->dimensions < 0)
elog(ERROR, "column cannot have more than %d dimensions for hnsw index", maxDimensions); elog(ERROR, "column does not have dimensions");
if (buildstate->dimensions > maxDimensions)
elog(ERROR, "column cannot have more than %d dimensions for hnsw index", maxDimensions);
}
if (buildstate->efConstruction < 2 * buildstate->m) if (buildstate->efConstruction < 2 * buildstate->m)
elog(ERROR, "ef_construction must be greater than or equal to 2 * m"); elog(ERROR, "ef_construction must be greater than or equal to 2 * m");

View File

@@ -229,7 +229,7 @@ HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, HnswType type)
else if (type == HNSW_TYPE_SPARSEVEC) else if (type == HNSW_TYPE_SPARSEVEC)
{ {
SparseVector *v = DatumGetSparseVector(*value); SparseVector *v = DatumGetSparseVector(*value);
SparseVector *result = InitSparseVector(v->dim, v->nnz); SparseVector *result = InitSparseVector(v->nnz);
float *vx = SPARSEVEC_VALUES(v); float *vx = SPARSEVEC_VALUES(v);
float *rx = SPARSEVEC_VALUES(result); float *rx = SPARSEVEC_VALUES(result);

View File

@@ -18,69 +18,28 @@
#include "utils/builtins.h" #include "utils/builtins.h"
#endif #endif
/*
* Ensure same dimensions
*/
static inline void
CheckDims(SparseVector * a, SparseVector * b)
{
if (a->dim != b->dim)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("different sparsevec dimensions %d and %d", a->dim, b->dim)));
}
/*
* Ensure expected dimensions
*/
static inline void
CheckExpectedDim(int32 typmod, int dim)
{
if (typmod != -1 && typmod != dim)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("expected %d dimensions, not %d", typmod, dim)));
}
/*
* Ensure valid dimensions
*/
static inline void
CheckDim(int dim)
{
if (dim < 1)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("sparsevec must have at least 1 dimension")));
if (dim > SPARSEVEC_MAX_DIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("sparsevec cannot have more than %d dimensions", SPARSEVEC_MAX_DIM)));
}
/* /*
* Ensure valid nnz * Ensure valid nnz
*/ */
static inline void static inline void
CheckNnz(int nnz, int dim) CheckNnz(int nnz)
{ {
if (nnz < 0) if (nnz < 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION), (errcode(ERRCODE_DATA_EXCEPTION),
errmsg("sparsevec must have at least one element"))); errmsg("sparsevec must have at least one element")));
if (nnz > dim) if (nnz > SPARSEVEC_MAX_NNZ)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("sparsevec cannot have more elements than dimensions"))); errmsg("sparsevec cannot have more elements than non-zero elements")));
} }
/* /*
* Ensure valid index * Ensure valid index
*/ */
static inline void static inline void
CheckIndex(int32 *indices, int i, int dim) CheckIndex(int32 *indices, int i)
{ {
int32 index = indices[i]; int32 index = indices[i];
@@ -89,11 +48,6 @@ CheckIndex(int32 *indices, int i, int dim)
(errcode(ERRCODE_DATA_EXCEPTION), (errcode(ERRCODE_DATA_EXCEPTION),
errmsg("index must not be negative"))); errmsg("index must not be negative")));
if (index >= dim)
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("index must be less than dimensions")));
if (i > 0) if (i > 0)
{ {
if (index < indices[i - 1]) if (index < indices[i - 1])
@@ -129,7 +83,7 @@ CheckElement(float value)
* Allocate and initialize a new sparse vector * Allocate and initialize a new sparse vector
*/ */
SparseVector * SparseVector *
InitSparseVector(int dim, int nnz) InitSparseVector(int nnz)
{ {
SparseVector *result; SparseVector *result;
int size; int size;
@@ -137,7 +91,6 @@ InitSparseVector(int dim, int nnz)
size = SPARSEVEC_SIZE(nnz); size = SPARSEVEC_SIZE(nnz);
result = (SparseVector *) palloc0(size); result = (SparseVector *) palloc0(size);
SET_VARSIZE(result, size); SET_VARSIZE(result, size);
result->dim = dim;
result->nnz = nnz; result->nnz = nnz;
return result; return result;
@@ -167,8 +120,6 @@ Datum
sparsevec_in(PG_FUNCTION_ARGS) sparsevec_in(PG_FUNCTION_ARGS)
{ {
char *lit = PG_GETARG_CSTRING(0); char *lit = PG_GETARG_CSTRING(0);
int32 typmod = PG_GETARG_INT32(2);
int dim;
char *pt; char *pt;
char *stringEnd; char *stringEnd;
SparseVector *result; SparseVector *result;
@@ -190,6 +141,11 @@ sparsevec_in(PG_FUNCTION_ARGS)
pt++; pt++;
} }
if (maxNnz > SPARSEVEC_MAX_NNZ)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("sparsevec cannot have more than %d non-zero elements", SPARSEVEC_MAX_NNZ)));
indices = palloc(maxNnz * sizeof(int32)); indices = palloc(maxNnz * sizeof(int32));
values = palloc(maxNnz * sizeof(float)); values = palloc(maxNnz * sizeof(float));
@@ -297,24 +253,6 @@ sparsevec_in(PG_FUNCTION_ARGS)
stringEnd++; stringEnd++;
if (*stringEnd != '/')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed sparsevec literal: \"%s\"", lit),
errdetail("Unexpected end of input.")));
stringEnd++;
/* Use similar logic as int2vectorin */
errno = 0;
pt = stringEnd;
dim = strtol(pt, &stringEnd, 10);
if (stringEnd == pt)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type sparsevec: \"%s\"", lit)));
/* Only whitespace is allowed after the closing brace */ /* Only whitespace is allowed after the closing brace */
while (sparsevec_isspace(*stringEnd)) while (sparsevec_isspace(*stringEnd))
stringEnd++; stringEnd++;
@@ -323,21 +261,18 @@ sparsevec_in(PG_FUNCTION_ARGS)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed sparsevec literal: \"%s\"", lit), errmsg("malformed sparsevec literal: \"%s\"", lit),
errdetail("Junk after closing."))); errdetail("Junk after closing right brace.")));
pfree(litcopy); pfree(litcopy);
CheckDim(dim); result = InitSparseVector(nnz);
CheckExpectedDim(typmod, dim);
result = InitSparseVector(dim, nnz);
rvalues = SPARSEVEC_VALUES(result); rvalues = SPARSEVEC_VALUES(result);
for (int i = 0; i < nnz; i++) for (int i = 0; i < nnz; i++)
{ {
result->indices[i] = indices[i]; result->indices[i] = indices[i];
rvalues[i] = values[i]; rvalues[i] = values[i];
CheckIndex(result->indices, i, dim); CheckIndex(result->indices, i);
CheckElement(rvalues[i]); CheckElement(rvalues[i]);
} }
@@ -382,11 +317,9 @@ sparsevec_out(PG_FUNCTION_ARGS)
* *
* nnz - 1 bytes for , * nnz - 1 bytes for ,
* *
* 10 bytes for dimensions * 3 bytes for {, }, and \0
*
* 4 bytes for {, }, /, and \0
*/ */
buf = (char *) palloc((11 + FLOAT_SHORTEST_DECIMAL_LEN) * sparsevec->nnz + 13); buf = (char *) palloc((11 + FLOAT_SHORTEST_DECIMAL_LEN) * sparsevec->nnz + 2);
ptr = buf; ptr = buf;
AppendChar(ptr, '{'); AppendChar(ptr, '{');
@@ -402,45 +335,12 @@ sparsevec_out(PG_FUNCTION_ARGS)
} }
AppendChar(ptr, '}'); AppendChar(ptr, '}');
AppendChar(ptr, '/');
AppendInt(ptr, sparsevec->dim);
*ptr = '\0'; *ptr = '\0';
PG_FREE_IF_COPY(sparsevec, 0); PG_FREE_IF_COPY(sparsevec, 0);
PG_RETURN_CSTRING(buf); PG_RETURN_CSTRING(buf);
} }
/*
* Convert type modifier
*/
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec_typmod_in);
Datum
sparsevec_typmod_in(PG_FUNCTION_ARGS)
{
ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
int32 *tl;
int n;
tl = ArrayGetIntegerTypmods(ta, &n);
if (n != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid type modifier")));
if (*tl < 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("dimensions for type sparsevec must be at least 1")));
if (*tl > SPARSEVEC_MAX_DIM)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("dimensions for type sparsevec cannot exceed %d", SPARSEVEC_MAX_DIM)));
PG_RETURN_INT32(*tl);
}
/* /*
* Convert external binary representation to internal representation * Convert external binary representation to internal representation
*/ */
@@ -449,33 +349,30 @@ Datum
sparsevec_recv(PG_FUNCTION_ARGS) sparsevec_recv(PG_FUNCTION_ARGS)
{ {
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
int32 typmod = PG_GETARG_INT32(2);
SparseVector *result; SparseVector *result;
int32 dim;
int32 nnz; int32 nnz;
int32 unused; int32 unused;
int32 unused2;
float *values; float *values;
dim = pq_getmsgint(buf, sizeof(int32));
nnz = pq_getmsgint(buf, sizeof(int32)); nnz = pq_getmsgint(buf, sizeof(int32));
unused = pq_getmsgint(buf, sizeof(int32)); unused = pq_getmsgint(buf, sizeof(int32));
unused2 = pq_getmsgint(buf, sizeof(int32));
CheckDim(dim); CheckNnz(nnz);
CheckNnz(nnz, dim);
CheckExpectedDim(typmod, dim);
if (unused != 0) if (unused != 0 || unused2 != 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION), (errcode(ERRCODE_DATA_EXCEPTION),
errmsg("expected unused to be 0, not %d", unused))); errmsg("expected unused to be 0, not %d", unused)));
result = InitSparseVector(dim, nnz); result = InitSparseVector(nnz);
values = SPARSEVEC_VALUES(result); values = SPARSEVEC_VALUES(result);
for (int i = 0; i < nnz; i++) for (int i = 0; i < nnz; i++)
{ {
result->indices[i] = pq_getmsgint(buf, sizeof(int32)); result->indices[i] = pq_getmsgint(buf, sizeof(int32));
CheckIndex(result->indices, i, dim); CheckIndex(result->indices, i);
} }
for (int i = 0; i < nnz; i++) for (int i = 0; i < nnz; i++)
@@ -499,9 +396,9 @@ sparsevec_send(PG_FUNCTION_ARGS)
StringInfoData buf; StringInfoData buf;
pq_begintypsend(&buf); pq_begintypsend(&buf);
pq_sendint(&buf, svec->dim, sizeof(int32));
pq_sendint(&buf, svec->nnz, sizeof(int32)); pq_sendint(&buf, svec->nnz, sizeof(int32));
pq_sendint(&buf, svec->unused, sizeof(int32)); pq_sendint(&buf, svec->unused, sizeof(int32));
pq_sendint(&buf, svec->unused2, sizeof(int32));
for (int i = 0; i < svec->nnz; i++) for (int i = 0; i < svec->nnz; i++)
pq_sendint(&buf, svec->indices[i], sizeof(int32)); pq_sendint(&buf, svec->indices[i], sizeof(int32));
for (int i = 0; i < svec->nnz; i++) for (int i = 0; i < svec->nnz; i++)
@@ -512,16 +409,12 @@ sparsevec_send(PG_FUNCTION_ARGS)
/* /*
* Convert sparse vector to sparse vector * Convert sparse vector to sparse vector
* This is needed to check the type modifier
*/ */
PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec); PGDLLEXPORT PG_FUNCTION_INFO_V1(sparsevec);
Datum Datum
sparsevec(PG_FUNCTION_ARGS) sparsevec(PG_FUNCTION_ARGS)
{ {
SparseVector *svec = PG_GETARG_SPARSEVEC_P(0); SparseVector *svec = PG_GETARG_SPARSEVEC_P(0);
int32 typmod = PG_GETARG_INT32(1);
CheckExpectedDim(typmod, svec->dim);
PG_RETURN_POINTER(svec); PG_RETURN_POINTER(svec);
} }
@@ -534,23 +427,20 @@ Datum
vector_to_sparsevec(PG_FUNCTION_ARGS) vector_to_sparsevec(PG_FUNCTION_ARGS)
{ {
Vector *vec = PG_GETARG_VECTOR_P(0); Vector *vec = PG_GETARG_VECTOR_P(0);
int32 typmod = PG_GETARG_INT32(1);
SparseVector *result; SparseVector *result;
int dim = vec->dim; int dim = vec->dim;
int nnz = 0; int nnz = 0;
float *values; float *values;
int j = 0; int j = 0;
CheckDim(dim);
CheckExpectedDim(typmod, dim);
for (int i = 0; i < dim; i++) for (int i = 0; i < dim; i++)
{ {
if (vec->x[i] != 0) if (vec->x[i] != 0)
nnz++; nnz++;
} }
result = InitSparseVector(dim, nnz); CheckNnz(nnz);
result = InitSparseVector(nnz);
values = SPARSEVEC_VALUES(result); values = SPARSEVEC_VALUES(result);
for (int i = 0; i < dim; i++) for (int i = 0; i < dim; i++)
{ {
@@ -627,8 +517,6 @@ sparsevec_l2_distance(PG_FUNCTION_ARGS)
SparseVector *a = PG_GETARG_SPARSEVEC_P(0); SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1); SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
CheckDims(a, b);
PG_RETURN_FLOAT8(sqrt(l2_distance_squared_internal(a, b))); PG_RETURN_FLOAT8(sqrt(l2_distance_squared_internal(a, b)));
} }
@@ -643,8 +531,6 @@ sparsevec_l2_squared_distance(PG_FUNCTION_ARGS)
SparseVector *a = PG_GETARG_SPARSEVEC_P(0); SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1); SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
CheckDims(a, b);
PG_RETURN_FLOAT8(l2_distance_squared_internal(a, b)); PG_RETURN_FLOAT8(l2_distance_squared_internal(a, b));
} }
@@ -694,8 +580,6 @@ sparsevec_inner_product(PG_FUNCTION_ARGS)
SparseVector *a = PG_GETARG_SPARSEVEC_P(0); SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1); SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
CheckDims(a, b);
PG_RETURN_FLOAT8(inner_product_internal(a, b)); PG_RETURN_FLOAT8(inner_product_internal(a, b));
} }
@@ -709,8 +593,6 @@ sparsevec_negative_inner_product(PG_FUNCTION_ARGS)
SparseVector *a = PG_GETARG_SPARSEVEC_P(0); SparseVector *a = PG_GETARG_SPARSEVEC_P(0);
SparseVector *b = PG_GETARG_SPARSEVEC_P(1); SparseVector *b = PG_GETARG_SPARSEVEC_P(1);
CheckDims(a, b);
PG_RETURN_FLOAT8(-inner_product_internal(a, b)); PG_RETURN_FLOAT8(-inner_product_internal(a, b));
} }
@@ -729,8 +611,6 @@ sparsevec_cosine_distance(PG_FUNCTION_ARGS)
float normb = 0.0; float normb = 0.0;
double similarity; double similarity;
CheckDims(a, b);
similarity = inner_product_internal(a, b); similarity = inner_product_internal(a, b);
/* Auto-vectorized */ /* Auto-vectorized */

View File

@@ -1,7 +1,7 @@
#ifndef SPARSEVEC_H #ifndef SPARSEVEC_H
#define SPARSEVEC_H #define SPARSEVEC_H
#define SPARSEVEC_MAX_DIM 100000 #define SPARSEVEC_MAX_NNZ 100000
/* Ensure values are aligned */ /* Ensure values are aligned */
#define SPARSEVEC_SIZE(_nnz) (offsetof(SparseVector, indices) + MAXALIGN((_nnz) * sizeof(int32)) + (_nnz * sizeof(float))) #define SPARSEVEC_SIZE(_nnz) (offsetof(SparseVector, indices) + MAXALIGN((_nnz) * sizeof(int32)) + (_nnz * sizeof(float)))
@@ -13,12 +13,12 @@
typedef struct SparseVector typedef struct SparseVector
{ {
int32 vl_len_; /* varlena header (do not touch directly!) */ int32 vl_len_; /* varlena header (do not touch directly!) */
int32 dim; /* number of dimensions */
int32 nnz; int32 nnz;
int32 unused; int32 unused;
int32 unused2;
int32 indices[FLEXIBLE_ARRAY_MEMBER]; int32 indices[FLEXIBLE_ARRAY_MEMBER];
} SparseVector; } SparseVector;
SparseVector *InitSparseVector(int dim, int nnz); SparseVector *InitSparseVector(int nnz);
#endif #endif

View File

@@ -1236,11 +1236,19 @@ sparsevec_to_vector(PG_FUNCTION_ARGS)
SparseVector *svec = PG_GETARG_SPARSEVEC_P(0); SparseVector *svec = PG_GETARG_SPARSEVEC_P(0);
int32 typmod = PG_GETARG_INT32(1); int32 typmod = PG_GETARG_INT32(1);
Vector *result; Vector *result;
int dim = svec->dim; int dim;
float *values = SPARSEVEC_VALUES(svec); float *values = SPARSEVEC_VALUES(svec);
int maxIndex = svec->nnz == 0 ? -1 : svec->indices[svec->nnz - 1];
if (typmod == -1)
dim = maxIndex + 1;
else
dim = typmod;
CheckDim(dim); CheckDim(dim);
CheckExpectedDim(typmod, dim);
if (dim < maxIndex + 1)
elog(ERROR, "Vector must have at least %d dimensions", maxIndex + 1);
result = InitVector(dim); result = InitVector(dim);
for (int i = 0; i < svec->nnz; i++) for (int i = 0; i < svec->nnz; i++)

View File

@@ -1,17 +1,17 @@
SET enable_seqscan = off; SET enable_seqscan = off;
CREATE TABLE t (val sparsevec(3)); CREATE TABLE t (val sparsevec);
INSERT INTO t (val) VALUES ('{}/3'), ('{0:1,1:2,2:3}/3'), ('{0:1,1:1,2:1}/3'), (NULL); INSERT INTO t (val) VALUES ('{}'), ('{0:1,1:2,2:3}'), ('{0:1,1:1,2:1}'), (NULL);
CREATE INDEX ON t USING hnsw (val sparsevec_cosine_ops); CREATE INDEX ON t USING hnsw (val sparsevec_cosine_ops);
INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}/3'); INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}');
SELECT * FROM t ORDER BY val <=> '{0:3,1:3,2:3}/3'; SELECT * FROM t ORDER BY val <=> '{0:3,1:3,2:3}';
val val
----------------- ---------------
{0:1,1:1,2:1}/3 {0:1,1:1,2:1}
{0:1,1:2,2:3}/3 {0:1,1:2,2:3}
{0:1,1:2,2:4}/3 {0:1,1:2,2:4}
(3 rows) (3 rows)
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '{}/3') t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '{}') t2;
count count
------- -------
3 3

View File

@@ -1,15 +1,15 @@
SET enable_seqscan = off; SET enable_seqscan = off;
CREATE TABLE t (val sparsevec(3)); CREATE TABLE t (val sparsevec);
INSERT INTO t (val) VALUES ('{}/3'), ('{0:1,1:2,2:3}/3'), ('{0:1,1:1,2:1}/3'), (NULL); INSERT INTO t (val) VALUES ('{}'), ('{0:1,1:2,2:3}'), ('{0:1,1:1,2:1}'), (NULL);
CREATE INDEX ON t USING hnsw (val sparsevec_ip_ops); CREATE INDEX ON t USING hnsw (val sparsevec_ip_ops);
INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}/3'); INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}');
SELECT * FROM t ORDER BY val <#> '{0:3,1:3,2:3}/3'; SELECT * FROM t ORDER BY val <#> '{0:3,1:3,2:3}';
val val
----------------- ---------------
{0:1,1:2,2:4}/3 {0:1,1:2,2:4}
{0:1,1:2,2:3}/3 {0:1,1:2,2:3}
{0:1,1:1,2:1}/3 {0:1,1:1,2:1}
{}/3 {}
(4 rows) (4 rows)
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::sparsevec)) t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::sparsevec)) t2;

View File

@@ -1,15 +1,15 @@
SET enable_seqscan = off; SET enable_seqscan = off;
CREATE TABLE t (val sparsevec(3)); CREATE TABLE t (val sparsevec);
INSERT INTO t (val) VALUES ('{}/3'), ('{0:1,1:2,2:3}/3'), ('{0:1,1:1,2:1}/3'), (NULL); INSERT INTO t (val) VALUES ('{}'), ('{0:1,1:2,2:3}'), ('{0:1,1:1,2:1}'), (NULL);
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops); CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}/3'); INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}');
SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}/3'; SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}';
val val
----------------- ---------------
{0:1,1:2,2:3}/3 {0:1,1:2,2:3}
{0:1,1:2,2:4}/3 {0:1,1:2,2:4}
{0:1,1:1,2:1}/3 {0:1,1:1,2:1}
{}/3 {}
(4 rows) (4 rows)
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::sparsevec)) t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::sparsevec)) t2;
@@ -25,14 +25,14 @@ SELECT COUNT(*) FROM t;
(1 row) (1 row)
TRUNCATE t; TRUNCATE t;
SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}/3'; SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}';
val val
----- -----
(0 rows) (0 rows)
DROP TABLE t; DROP TABLE t;
-- TODO move -- TODO move
CREATE TABLE t (val sparsevec(1001)); CREATE TABLE t (val sparsevec);
INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec); INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec);
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops); CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
ERROR: sparsevec cannot have more than 1000 non-zero elements for hnsw index ERROR: sparsevec cannot have more than 1000 non-zero elements for hnsw index

View File

@@ -1,62 +1,60 @@
SELECT l2_distance('{}/2'::sparsevec, '{0:3,1:4}/2'); SELECT l2_distance('{}'::sparsevec, '{0:3,1:4}');
l2_distance l2_distance
------------- -------------
5 5
(1 row) (1 row)
SELECT l2_distance('{}/2'::sparsevec, '{1:1}/2'); SELECT l2_distance('{}'::sparsevec, '{1:1}');
l2_distance l2_distance
------------- -------------
1 1
(1 row) (1 row)
SELECT '{}/2'::sparsevec <-> '{0:3,1:4}/2'; SELECT '{}'::sparsevec <-> '{0:3,1:4}';
?column? ?column?
---------- ----------
5 5
(1 row) (1 row)
SELECT inner_product('{0:1,1:2}/2'::sparsevec, '{0:2,1:4}/2'); SELECT inner_product('{0:1,1:2}'::sparsevec, '{0:2,1:4}');
inner_product inner_product
--------------- ---------------
10 10
(1 row) (1 row)
SELECT sparsevec_negative_inner_product('{0:1,1:2}/2', '{0:2,1:4}/2'); SELECT sparsevec_negative_inner_product('{0:1,1:2}', '{0:2,1:4}');
sparsevec_negative_inner_product sparsevec_negative_inner_product
---------------------------------- ----------------------------------
-10 -10
(1 row) (1 row)
SELECT cosine_distance('{0:1,1:2}/2'::sparsevec, '{0:2,1:4}/2'); SELECT cosine_distance('{0:1,1:2}'::sparsevec, '{0:2,1:4}');
cosine_distance cosine_distance
----------------- -----------------
0 0
(1 row) (1 row)
SELECT cosine_distance('{0:1,1:2}/2'::sparsevec, '{}/2'); SELECT cosine_distance('{0:1,1:2}'::sparsevec, '{}');
cosine_distance cosine_distance
----------------- -----------------
NaN NaN
(1 row) (1 row)
SELECT cosine_distance('{0:1,1:1}/2'::sparsevec, '{0:-1,1:-1}/2'); SELECT cosine_distance('{0:1,1:1}'::sparsevec, '{0:-1,1:-1}');
cosine_distance cosine_distance
----------------- -----------------
2 2
(1 row) (1 row)
SELECT cosine_distance('{0:1}/2'::sparsevec, '{1:2}/2'); SELECT cosine_distance('{0:1}'::sparsevec, '{1:2}');
cosine_distance cosine_distance
----------------- -----------------
1 1
(1 row) (1 row)
SELECT cosine_distance('{}/1'::sparsevec, '{}/1'); SELECT cosine_distance('{}'::sparsevec, '{}');
cosine_distance cosine_distance
----------------- -----------------
NaN NaN
(1 row) (1 row)
SELECT cosine_distance('{0:1}/2'::sparsevec, '{0:1}/3');
ERROR: different sparsevec dimensions 2 and 3

View File

@@ -1,62 +1,64 @@
SELECT '{0:1.5,2:3.5}/5'::sparsevec; SELECT '{0:1.5,2:3.5}'::sparsevec;
sparsevec sparsevec
----------------- ---------------
{0:1.5,2:3.5}/5 {0:1.5,2:3.5}
(1 row) (1 row)
SELECT '{0:1.5,2:3.5}/5'::sparsevec::vector; SELECT '{0:1.5,2:3.5}'::sparsevec::vector;
vector
-------------
[1.5,0,3.5]
(1 row)
SELECT '{0:1.5,2:3.5}'::sparsevec::vector(5);
vector vector
----------------- -----------------
[1.5,0,3.5,0,0] [1.5,0,3.5,0,0]
(1 row) (1 row)
SELECT '{0:1.5,2:3.5}/5'::sparsevec::vector(5); SELECT '{0:1.5,2:3.5}'::sparsevec::vector(4);
vector vector
----------------- ---------------
[1.5,0,3.5,0,0] [1.5,0,3.5,0]
(1 row) (1 row)
SELECT '{0:1.5,2:3.5}/5'::sparsevec::vector(4); SELECT '{0:1.5,2:3.5}'::sparsevec::vector(2);
ERROR: expected 4 dimensions, not 5 ERROR: Vector must have at least 3 dimensions
SELECT '[0,1.5,0,3.5,0]'::vector::sparsevec; SELECT '[0,1.5,0,3.5,0]'::vector::sparsevec;
sparsevec sparsevec
----------------- ---------------
{1:1.5,3:3.5}/5 {1:1.5,3:3.5}
(1 row) (1 row)
SELECT '{0:0,1:1,2:0}/3'::sparsevec; SELECT '{0:0,1:1,2:0}'::sparsevec;
sparsevec sparsevec
----------- -----------
{1:1}/3 {1:1}
(1 row) (1 row)
SELECT '{1:1,0:1}/2'::sparsevec; SELECT '{1:1,0:1}'::sparsevec;
ERROR: indexes must be in ascending order ERROR: indexes must be in ascending order
LINE 1: SELECT '{1:1,0:1}/2'::sparsevec; LINE 1: SELECT '{1:1,0:1}'::sparsevec;
^ ^
SELECT '{}/5'::sparsevec; SELECT '{}'::sparsevec;
sparsevec sparsevec
----------- -----------
{}/5 {}
(1 row) (1 row)
SELECT '{}/-1'::sparsevec; SELECT '{}'::sparsevec::vector;
ERROR: sparsevec must have at least 1 dimension ERROR: vector must have at least 1 dimension
LINE 1: SELECT '{}/-1'::sparsevec; SELECT '{-1:1}'::sparsevec;
^
SELECT '{}/100001'::sparsevec;
ERROR: sparsevec cannot have more than 100000 dimensions
LINE 1: SELECT '{}/100001'::sparsevec;
^
SELECT '{}/16001'::sparsevec::vector;
ERROR: vector cannot have more than 16000 dimensions
SELECT '{-1:1}/1'::sparsevec;
ERROR: index "-1" is out of range for type sparsevec ERROR: index "-1" is out of range for type sparsevec
LINE 1: SELECT '{-1:1}/1'::sparsevec; LINE 1: SELECT '{-1:1}'::sparsevec;
^ ^
SELECT '{1:1}/1'::sparsevec; SELECT '{1:1}'::sparsevec;
ERROR: index must be less than dimensions sparsevec
LINE 1: SELECT '{1:1}/1'::sparsevec; -----------
^ {1:1}
SELECT '{}/1'::sparsevec(2); (1 row)
ERROR: expected 2 dimensions, not 1
SELECT '{}'::sparsevec(2);
ERROR: type modifier is not allowed for type "sparsevec"
LINE 1: SELECT '{}'::sparsevec(2);
^

View File

@@ -1,13 +1,13 @@
SET enable_seqscan = off; SET enable_seqscan = off;
CREATE TABLE t (val sparsevec(3)); CREATE TABLE t (val sparsevec);
INSERT INTO t (val) VALUES ('{}/3'), ('{0:1,1:2,2:3}/3'), ('{0:1,1:1,2:1}/3'), (NULL); INSERT INTO t (val) VALUES ('{}'), ('{0:1,1:2,2:3}'), ('{0:1,1:1,2:1}'), (NULL);
CREATE INDEX ON t USING hnsw (val sparsevec_cosine_ops); CREATE INDEX ON t USING hnsw (val sparsevec_cosine_ops);
INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}/3'); INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}');
SELECT * FROM t ORDER BY val <=> '{0:3,1:3,2:3}/3'; SELECT * FROM t ORDER BY val <=> '{0:3,1:3,2:3}';
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '{}/3') t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> '{}') t2;
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::sparsevec)) t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <=> (SELECT NULL::sparsevec)) t2;
DROP TABLE t; DROP TABLE t;

View File

@@ -1,12 +1,12 @@
SET enable_seqscan = off; SET enable_seqscan = off;
CREATE TABLE t (val sparsevec(3)); CREATE TABLE t (val sparsevec);
INSERT INTO t (val) VALUES ('{}/3'), ('{0:1,1:2,2:3}/3'), ('{0:1,1:1,2:1}/3'), (NULL); INSERT INTO t (val) VALUES ('{}'), ('{0:1,1:2,2:3}'), ('{0:1,1:1,2:1}'), (NULL);
CREATE INDEX ON t USING hnsw (val sparsevec_ip_ops); CREATE INDEX ON t USING hnsw (val sparsevec_ip_ops);
INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}/3'); INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}');
SELECT * FROM t ORDER BY val <#> '{0:3,1:3,2:3}/3'; SELECT * FROM t ORDER BY val <#> '{0:3,1:3,2:3}';
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::sparsevec)) t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <#> (SELECT NULL::sparsevec)) t2;
DROP TABLE t; DROP TABLE t;

View File

@@ -1,22 +1,22 @@
SET enable_seqscan = off; SET enable_seqscan = off;
CREATE TABLE t (val sparsevec(3)); CREATE TABLE t (val sparsevec);
INSERT INTO t (val) VALUES ('{}/3'), ('{0:1,1:2,2:3}/3'), ('{0:1,1:1,2:1}/3'), (NULL); INSERT INTO t (val) VALUES ('{}'), ('{0:1,1:2,2:3}'), ('{0:1,1:1,2:1}'), (NULL);
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops); CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}/3'); INSERT INTO t (val) VALUES ('{0:1,1:2,2:4}');
SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}/3'; SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}';
SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::sparsevec)) t2; SELECT COUNT(*) FROM (SELECT * FROM t ORDER BY val <-> (SELECT NULL::sparsevec)) t2;
SELECT COUNT(*) FROM t; SELECT COUNT(*) FROM t;
TRUNCATE t; TRUNCATE t;
SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}/3'; SELECT * FROM t ORDER BY val <-> '{0:3,1:3,2:3}';
DROP TABLE t; DROP TABLE t;
-- TODO move -- TODO move
CREATE TABLE t (val sparsevec(1001)); CREATE TABLE t (val sparsevec);
INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec); INSERT INTO t (val) VALUES (array_fill(1, ARRAY[1001])::vector::sparsevec);
CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops); CREATE INDEX ON t USING hnsw (val sparsevec_l2_ops);
TRUNCATE t; TRUNCATE t;

View File

@@ -1,13 +1,12 @@
SELECT l2_distance('{}/2'::sparsevec, '{0:3,1:4}/2'); SELECT l2_distance('{}'::sparsevec, '{0:3,1:4}');
SELECT l2_distance('{}/2'::sparsevec, '{1:1}/2'); SELECT l2_distance('{}'::sparsevec, '{1:1}');
SELECT '{}/2'::sparsevec <-> '{0:3,1:4}/2'; SELECT '{}'::sparsevec <-> '{0:3,1:4}';
SELECT inner_product('{0:1,1:2}/2'::sparsevec, '{0:2,1:4}/2'); SELECT inner_product('{0:1,1:2}'::sparsevec, '{0:2,1:4}');
SELECT sparsevec_negative_inner_product('{0:1,1:2}/2', '{0:2,1:4}/2'); SELECT sparsevec_negative_inner_product('{0:1,1:2}', '{0:2,1:4}');
SELECT cosine_distance('{0:1,1:2}/2'::sparsevec, '{0:2,1:4}/2'); SELECT cosine_distance('{0:1,1:2}'::sparsevec, '{0:2,1:4}');
SELECT cosine_distance('{0:1,1:2}/2'::sparsevec, '{}/2'); SELECT cosine_distance('{0:1,1:2}'::sparsevec, '{}');
SELECT cosine_distance('{0:1,1:1}/2'::sparsevec, '{0:-1,1:-1}/2'); SELECT cosine_distance('{0:1,1:1}'::sparsevec, '{0:-1,1:-1}');
SELECT cosine_distance('{0:1}/2'::sparsevec, '{1:2}/2'); SELECT cosine_distance('{0:1}'::sparsevec, '{1:2}');
SELECT cosine_distance('{}/1'::sparsevec, '{}/1'); SELECT cosine_distance('{}'::sparsevec, '{}');
SELECT cosine_distance('{0:1}/2'::sparsevec, '{0:1}/3');

View File

@@ -1,19 +1,18 @@
SELECT '{0:1.5,2:3.5}/5'::sparsevec; SELECT '{0:1.5,2:3.5}'::sparsevec;
SELECT '{0:1.5,2:3.5}/5'::sparsevec::vector; SELECT '{0:1.5,2:3.5}'::sparsevec::vector;
SELECT '{0:1.5,2:3.5}/5'::sparsevec::vector(5); SELECT '{0:1.5,2:3.5}'::sparsevec::vector(5);
SELECT '{0:1.5,2:3.5}/5'::sparsevec::vector(4); SELECT '{0:1.5,2:3.5}'::sparsevec::vector(4);
SELECT '{0:1.5,2:3.5}'::sparsevec::vector(2);
SELECT '[0,1.5,0,3.5,0]'::vector::sparsevec; SELECT '[0,1.5,0,3.5,0]'::vector::sparsevec;
SELECT '{0:0,1:1,2:0}/3'::sparsevec; SELECT '{0:0,1:1,2:0}'::sparsevec;
SELECT '{1:1,0:1}/2'::sparsevec; SELECT '{1:1,0:1}'::sparsevec;
SELECT '{}/5'::sparsevec; SELECT '{}'::sparsevec;
SELECT '{}/-1'::sparsevec; SELECT '{}'::sparsevec::vector;
SELECT '{}/100001'::sparsevec;
SELECT '{}/16001'::sparsevec::vector;
SELECT '{-1:1}/1'::sparsevec; SELECT '{-1:1}'::sparsevec;
SELECT '{1:1}/1'::sparsevec; SELECT '{1:1}'::sparsevec;
SELECT '{}/1'::sparsevec(2); SELECT '{}'::sparsevec(2);