From 035a31ac913403e0423bf4f2893b215df81510e3 Mon Sep 17 00:00:00 2001 From: Andrew Kane Date: Mon, 23 Sep 2024 16:45:19 -0700 Subject: [PATCH] Added minivec type --- Makefile | 4 +- Makefile.win | 6 +- sql/vector--0.7.4--0.8.0.sql | 26 +++ sql/vector.sql | 28 +++ src/minivec.c | 336 +++++++++++++++++++++++++++++++++++ src/minivec.h | 126 +++++++++++++ test/expected/copy.out | 9 + test/expected/minivec.out | 170 ++++++++++++++++++ test/sql/copy.sql | 15 ++ test/sql/minivec.sql | 38 ++++ 10 files changed, 753 insertions(+), 5 deletions(-) create mode 100644 src/minivec.c create mode 100644 src/minivec.h create mode 100644 test/expected/minivec.out create mode 100644 test/sql/minivec.sql diff --git a/Makefile b/Makefile index e7ae85e..406e59b 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,8 @@ EXTVERSION = 0.7.4 MODULE_big = vector DATA = $(wildcard sql/*--*--*.sql) DATA_built = sql/$(EXTENSION)--$(EXTVERSION).sql -OBJS = src/bitutils.o src/bitvec.o src/halfutils.o src/halfvec.o src/hnsw.o src/hnswbuild.o src/hnswinsert.o src/hnswscan.o src/hnswutils.o src/hnswvacuum.o src/ivfbuild.o src/ivfflat.o src/ivfinsert.o src/ivfkmeans.o src/ivfscan.o src/ivfutils.o src/ivfvacuum.o src/sparsevec.o src/vector.o -HEADERS = src/halfvec.h src/sparsevec.h src/vector.h +OBJS = src/bitutils.o src/bitvec.o src/halfutils.o src/halfvec.o src/hnsw.o src/hnswbuild.o src/hnswinsert.o src/hnswscan.o src/hnswutils.o src/hnswvacuum.o src/ivfbuild.o src/ivfflat.o src/ivfinsert.o src/ivfkmeans.o src/ivfscan.o src/ivfutils.o src/minivec.o src/ivfvacuum.o src/sparsevec.o src/vector.o +HEADERS = src/halfvec.h src/minivec.h src/sparsevec.h src/vector.h TESTS = $(wildcard test/sql/*.sql) REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS)) diff --git a/Makefile.win b/Makefile.win index c44cb1f..e75287a 100644 --- a/Makefile.win +++ b/Makefile.win @@ -2,10 +2,10 @@ EXTENSION = vector EXTVERSION = 0.7.4 DATA_built = sql\$(EXTENSION)--$(EXTVERSION).sql -OBJS = src\bitutils.obj src\bitvec.obj src\halfutils.obj src\halfvec.obj 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\sparsevec.obj src\vector.obj -HEADERS = src\halfvec.h src\sparsevec.h src\vector.h +OBJS = src\bitutils.obj src\bitvec.obj src\halfutils.obj src\halfvec.obj 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\minivec.obj src\sparsevec.obj src\vector.obj +HEADERS = src\halfvec.h src\minivec.h src\sparsevec.h src\vector.h -REGRESS = bit btree cast copy halfvec hnsw_bit hnsw_halfvec hnsw_sparsevec hnsw_vector ivfflat_bit ivfflat_halfvec ivfflat_vector sparsevec vector_type +REGRESS = bit btree cast copy halfvec hnsw_bit hnsw_halfvec hnsw_sparsevec hnsw_vector ivfflat_bit ivfflat_halfvec ivfflat_vector minivec sparsevec vector_type REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION) # For /arch flags diff --git a/sql/vector--0.7.4--0.8.0.sql b/sql/vector--0.7.4--0.8.0.sql index e00348d..931cd19 100644 --- a/sql/vector--0.7.4--0.8.0.sql +++ b/sql/vector--0.7.4--0.8.0.sql @@ -1,6 +1,32 @@ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "ALTER EXTENSION vector UPDATE TO '0.8.0'" to load this file. \quit +CREATE TYPE minivec; + +CREATE FUNCTION minivec_in(cstring, oid, integer) RETURNS minivec + AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION minivec_out(minivec) RETURNS cstring + AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION minivec_typmod_in(cstring[]) RETURNS integer + AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION minivec_recv(internal, oid, integer) RETURNS minivec + AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION minivec_send(minivec) RETURNS bytea + AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE minivec ( + INPUT = minivec_in, + OUTPUT = minivec_out, + TYPMOD_IN = minivec_typmod_in, + RECEIVE = minivec_recv, + SEND = minivec_send, + STORAGE = external +); + CREATE FUNCTION array_to_sparsevec(integer[], integer, boolean) RETURNS sparsevec AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; diff --git a/sql/vector.sql b/sql/vector.sql index 7fc3671..1781266 100644 --- a/sql/vector.sql +++ b/sql/vector.sql @@ -647,6 +647,34 @@ CREATE OPERATOR CLASS halfvec_l1_ops FUNCTION 1 l1_distance(halfvec, halfvec), FUNCTION 3 hnsw_halfvec_support(internal); +-- minivec type + +CREATE TYPE minivec; + +CREATE FUNCTION minivec_in(cstring, oid, integer) RETURNS minivec + AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION minivec_out(minivec) RETURNS cstring + AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION minivec_typmod_in(cstring[]) RETURNS integer + AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION minivec_recv(internal, oid, integer) RETURNS minivec + AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION minivec_send(minivec) RETURNS bytea + AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; + +CREATE TYPE minivec ( + INPUT = minivec_in, + OUTPUT = minivec_out, + TYPMOD_IN = minivec_typmod_in, + RECEIVE = minivec_recv, + SEND = minivec_send, + STORAGE = external +); + -- bit functions CREATE FUNCTION hamming_distance(bit, bit) RETURNS float8 diff --git a/src/minivec.c b/src/minivec.c new file mode 100644 index 0000000..2ea5a59 --- /dev/null +++ b/src/minivec.c @@ -0,0 +1,336 @@ +#include "postgres.h" + +#include + +#include "catalog/pg_type.h" +#include "common/shortest_dec.h" +#include "fmgr.h" +#include "minivec.h" +#include "lib/stringinfo.h" +#include "libpq/pqformat.h" +#include "port.h" /* for strtof() */ +#include "sparsevec.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/float.h" +#include "utils/lsyscache.h" +#include "utils/numeric.h" +#include "vector.h" + +/* + * 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("minivec must have at least 1 dimension"))); + + if (dim > MINIVEC_MAX_DIM) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("minivec cannot have more than %d dimensions", MINIVEC_MAX_DIM))); +} + +/* + * Ensure finite element + */ +static inline void +CheckElement(fp8 value) +{ + if (Fp8IsNan(value)) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("NaN not allowed in minivec"))); +} + +/* + * Allocate and initialize a new half vector + */ +MiniVector * +InitMiniVector(int dim) +{ + MiniVector *result; + int size; + + size = MINIVEC_SIZE(dim); + result = (MiniVector *) palloc0(size); + SET_VARSIZE(result, size); + result->dim = dim; + + return result; +} + +/* + * Check for whitespace, since array_isspace() is static + */ +static inline bool +minivec_isspace(char ch) +{ + if (ch == ' ' || + ch == '\t' || + ch == '\n' || + ch == '\r' || + ch == '\v' || + ch == '\f') + return true; + return false; +} + +/* + * Convert textual representation to internal representation + */ +FUNCTION_PREFIX PG_FUNCTION_INFO_V1(minivec_in); +Datum +minivec_in(PG_FUNCTION_ARGS) +{ + char *lit = PG_GETARG_CSTRING(0); + int32 typmod = PG_GETARG_INT32(2); + fp8 x[MINIVEC_MAX_DIM]; + int dim = 0; + char *pt = lit; + MiniVector *result; + + while (minivec_isspace(*pt)) + pt++; + + if (*pt != '[') + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type minivec: \"%s\"", lit), + errdetail("Vector contents must start with \"[\"."))); + + pt++; + + while (minivec_isspace(*pt)) + pt++; + + if (*pt == ']') + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("minivec must have at least 1 dimension"))); + + for (;;) + { + float val; + char *stringEnd; + + if (dim == MINIVEC_MAX_DIM) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("minivec cannot have more than %d dimensions", MINIVEC_MAX_DIM))); + + while (minivec_isspace(*pt)) + pt++; + + /* Check for empty string like float4in */ + if (*pt == '\0') + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type minivec: \"%s\"", lit))); + + errno = 0; + + /* Postgres sets LC_NUMERIC to C on startup */ + val = strtof(pt, &stringEnd); + + if (stringEnd == pt) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type minivec: \"%s\"", lit))); + + x[dim] = Float4ToFp8Unchecked(val); + + /* Check for range error like float4in */ + if ((errno == ERANGE && isinf(val)) || (Fp8IsNan(x[dim]) && !isnan(val))) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("\"%s\" is out of range for type minivec", pnstrdup(pt, stringEnd - pt)))); + + CheckElement(x[dim]); + dim++; + + pt = stringEnd; + + while (minivec_isspace(*pt)) + pt++; + + if (*pt == ',') + pt++; + else if (*pt == ']') + { + pt++; + break; + } + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type minivec: \"%s\"", lit))); + } + + /* Only whitespace is allowed after the closing brace */ + while (minivec_isspace(*pt)) + pt++; + + if (*pt != '\0') + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for type minivec: \"%s\"", lit), + errdetail("Junk after closing right brace."))); + + CheckDim(dim); + CheckExpectedDim(typmod, dim); + + result = InitMiniVector(dim); + for (int i = 0; i < dim; i++) + result->x[i] = x[i]; + + PG_RETURN_POINTER(result); +} + +#define AppendChar(ptr, c) (*(ptr)++ = (c)) +#define AppendFloat(ptr, f) ((ptr) += float_to_shortest_decimal_bufn((f), (ptr))) + +/* + * Convert internal representation to textual representation + */ +FUNCTION_PREFIX PG_FUNCTION_INFO_V1(minivec_out); +Datum +minivec_out(PG_FUNCTION_ARGS) +{ + MiniVector *vector = PG_GETARG_MINIVEC_P(0); + int dim = vector->dim; + char *buf; + char *ptr; + + /* + * Need: + * + * dim * (FLOAT_SHORTEST_DECIMAL_LEN - 1) bytes for + * float_to_shortest_decimal_bufn + * + * dim - 1 bytes for separator + * + * 3 bytes for [, ], and \0 + */ + buf = (char *) palloc(FLOAT_SHORTEST_DECIMAL_LEN * dim + 2); + ptr = buf; + + AppendChar(ptr, '['); + + for (int i = 0; i < dim; i++) + { + if (i > 0) + AppendChar(ptr, ','); + + /* + * Use shortest decimal representation of single-precision float for + * simplicity + */ + AppendFloat(ptr, Fp8ToFloat4(vector->x[i])); + } + + AppendChar(ptr, ']'); + *ptr = '\0'; + + PG_FREE_IF_COPY(vector, 0); + PG_RETURN_CSTRING(buf); +} + +/* + * Convert type modifier + */ +FUNCTION_PREFIX PG_FUNCTION_INFO_V1(minivec_typmod_in); +Datum +minivec_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 minivec must be at least 1"))); + + if (*tl > MINIVEC_MAX_DIM) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("dimensions for type minivec cannot exceed %d", MINIVEC_MAX_DIM))); + + PG_RETURN_INT32(*tl); +} + +/* + * Convert external binary representation to internal representation + */ +FUNCTION_PREFIX PG_FUNCTION_INFO_V1(minivec_recv); +Datum +minivec_recv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + int32 typmod = PG_GETARG_INT32(2); + MiniVector *result; + int16 dim; + int16 unused; + + dim = pq_getmsgint(buf, sizeof(int16)); + unused = pq_getmsgint(buf, sizeof(int16)); + + CheckDim(dim); + CheckExpectedDim(typmod, dim); + + if (unused != 0) + ereport(ERROR, + (errcode(ERRCODE_DATA_EXCEPTION), + errmsg("expected unused to be 0, not %d", unused))); + + result = InitMiniVector(dim); + for (int i = 0; i < dim; i++) + { + result->x[i] = pq_getmsgint(buf, sizeof(uint8)); + CheckElement(result->x[i]); + } + + PG_RETURN_POINTER(result); +} + +/* + * Convert internal representation to the external binary representation + */ +FUNCTION_PREFIX PG_FUNCTION_INFO_V1(minivec_send); +Datum +minivec_send(PG_FUNCTION_ARGS) +{ + MiniVector *vec = PG_GETARG_MINIVEC_P(0); + StringInfoData buf; + + pq_begintypsend(&buf); + pq_sendint(&buf, vec->dim, sizeof(int16)); + pq_sendint(&buf, vec->unused, sizeof(int16)); + for (int i = 0; i < vec->dim; i++) + pq_sendint8(&buf, vec->x[i]); + + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); +} diff --git a/src/minivec.h b/src/minivec.h new file mode 100644 index 0000000..7acd813 --- /dev/null +++ b/src/minivec.h @@ -0,0 +1,126 @@ +#ifndef MINIVEC_H +#define MINIVEC_H + +#include + +#define MINIVEC_MAX_DIM 16000 + +#define fp8 uint8 + +#define MINIVEC_SIZE(_dim) (offsetof(MiniVector, x) + sizeof(fp8)*(_dim)) +#define DatumGetMiniVector(x) ((MiniVector *) PG_DETOAST_DATUM(x)) +#define PG_GETARG_MINIVEC_P(x) DatumGetMiniVector(PG_GETARG_DATUM(x)) +#define PG_RETURN_MINIVEC_P(x) PG_RETURN_POINTER(x) + +typedef struct MiniVector +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + int16 dim; /* number of dimensions */ + int16 unused; /* reserved for future use, always zero */ + fp8 x[FLEXIBLE_ARRAY_MEMBER]; +} MiniVector; + +MiniVector *InitMiniVector(int dim); + +/* + * Check if fp8 is NaN + */ +static inline bool +Fp8IsNan(fp8 num) +{ + return (num & 0x7F) == 0x7F; +} + +float lookup[128] = {0, 0.00195312, 0.00390625, 0.00585938, 0.0078125, 0.00976562, 0.0117188, 0.0136719, 0.015625, 0.0175781, 0.0195312, 0.0214844, 0.0234375, 0.0253906, 0.0273438, 0.0292969, 0.03125, 0.0351562, 0.0390625, 0.0429688, 0.046875, 0.0507812, 0.0546875, 0.0585938, 0.0625, 0.0703125, 0.078125, 0.0859375, 0.09375, 0.101562, 0.109375, 0.117188, 0.125, 0.140625, 0.15625, 0.171875, 0.1875, 0.203125, 0.21875, 0.234375, 0.25, 0.28125, 0.3125, 0.34375, 0.375, 0.40625, 0.4375, 0.46875, 0.5, 0.5625, 0.625, 0.6875, 0.75, 0.8125, 0.875, 0.9375, 1, 1.125, 1.25, 1.375, 1.5, 1.625, 1.75, 1.875, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5, 3.75, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24, 26, 28, 30, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 88, 96, 104, 112, 120, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, NAN}; + +/* + * Convert a fp8 to a float4 + */ +static inline float +Fp8ToFloat4(fp8 num) +{ + float v = lookup[num & 0x7F]; + + return (num & 0x80) == 0x80 ? -v : v; +} + +/* + * Convert a float4 to a fp8 + */ +static inline fp8 +Float4ToFp8Unchecked(float num) +{ + union + { + float f; + uint32 i; + } swapfloat; + + uint32 bin; + int exponent; + int mantissa; + uint8 result; + + swapfloat.f = num; + bin = swapfloat.i; + exponent = (bin & 0x7F800000) >> 23; + mantissa = bin & 0x007FFFFF; + + /* Sign */ + result = (bin & 0x80000000) >> 24; + + if (isinf(num) || isnan(num)) + { + /* NaN */ + result |= 0x7F; + } + else if (exponent > 116) + { + int m; + int gr; + int s; + + exponent -= 127; + s = mantissa & 0x000FFFFF; + + /* Subnormal */ + if (exponent < -6) + { + int diff = -exponent - 6; + + mantissa >>= diff; + mantissa += 1 << (23 - diff); + s |= mantissa & 0x000FFFFF; + } + + m = mantissa >> 20; + + /* Round */ + gr = (mantissa >> 19) % 4; + if (gr == 3 || (gr == 1 && s != 0)) + m += 1; + + if (m == 8) + { + m = 0; + exponent += 1; + } + + if (exponent > 8) + { + /* Infinite, which is NaN */ + result |= 0x7F; + } + else + { + if (exponent >= -7) + result |= (exponent + 7) << 3; + + result |= m; + } + } + + return result; +} + +#endif diff --git a/test/expected/copy.out b/test/expected/copy.out index 9b4ebc0..44b0a51 100644 --- a/test/expected/copy.out +++ b/test/expected/copy.out @@ -30,6 +30,15 @@ SELECT * FROM t2 ORDER BY val; (4 rows) +DROP TABLE t; +DROP TABLE t2; +-- minivec +CREATE TABLE t (val minivec(3)); +INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL); +CREATE TABLE t2 (val minivec(3)); +\copy t TO 'results/minivec.bin' WITH (FORMAT binary) +\copy t2 FROM 'results/minivec.bin' WITH (FORMAT binary) +--SELECT * FROM t2 ORDER BY val; DROP TABLE t; DROP TABLE t2; -- sparsevec diff --git a/test/expected/minivec.out b/test/expected/minivec.out new file mode 100644 index 0000000..2d28a38 --- /dev/null +++ b/test/expected/minivec.out @@ -0,0 +1,170 @@ +SELECT '[1,2,3]'::minivec; + minivec +--------- + [1,2,3] +(1 row) + +SELECT '[-1,-2,-3]'::minivec; + minivec +------------ + [-1,-2,-3] +(1 row) + +SELECT '[1.,2.,3.]'::minivec; + minivec +--------- + [1,2,3] +(1 row) + +SELECT ' [ 1, 2 , 3 ] '::minivec; + minivec +--------- + [1,2,3] +(1 row) + +SELECT '[1.23456]'::minivec; + minivec +--------- + [1.25] +(1 row) + +SELECT '[hello,1]'::minivec; +ERROR: invalid input syntax for type minivec: "[hello,1]" +LINE 1: SELECT '[hello,1]'::minivec; + ^ +SELECT '[NaN,1]'::minivec; +ERROR: NaN not allowed in minivec +LINE 1: SELECT '[NaN,1]'::minivec; + ^ +SELECT '[Infinity,1]'::minivec; +ERROR: "Infinity" is out of range for type minivec +LINE 1: SELECT '[Infinity,1]'::minivec; + ^ +SELECT '[-Infinity,1]'::minivec; +ERROR: "-Infinity" is out of range for type minivec +LINE 1: SELECT '[-Infinity,1]'::minivec; + ^ +SELECT '[65519,-65519]'::minivec; +ERROR: "65519" is out of range for type minivec +LINE 1: SELECT '[65519,-65519]'::minivec; + ^ +SELECT '[65520,-65520]'::minivec; +ERROR: "65520" is out of range for type minivec +LINE 1: SELECT '[65520,-65520]'::minivec; + ^ +SELECT '[1e-8,-1e-8]'::minivec; + minivec +--------- + [0,-0] +(1 row) + +SELECT '[4e38,1]'::minivec; +ERROR: "4e38" is out of range for type minivec +LINE 1: SELECT '[4e38,1]'::minivec; + ^ +SELECT '[1e-46,1]'::minivec; + minivec +--------- + [0,1] +(1 row) + +SELECT '[1,2,3'::minivec; +ERROR: invalid input syntax for type minivec: "[1,2,3" +LINE 1: SELECT '[1,2,3'::minivec; + ^ +SELECT '[1,2,3]9'::minivec; +ERROR: invalid input syntax for type minivec: "[1,2,3]9" +LINE 1: SELECT '[1,2,3]9'::minivec; + ^ +DETAIL: Junk after closing right brace. +SELECT '1,2,3'::minivec; +ERROR: invalid input syntax for type minivec: "1,2,3" +LINE 1: SELECT '1,2,3'::minivec; + ^ +DETAIL: Vector contents must start with "[". +SELECT ''::minivec; +ERROR: invalid input syntax for type minivec: "" +LINE 1: SELECT ''::minivec; + ^ +DETAIL: Vector contents must start with "[". +SELECT '['::minivec; +ERROR: invalid input syntax for type minivec: "[" +LINE 1: SELECT '['::minivec; + ^ +SELECT '[ '::minivec; +ERROR: invalid input syntax for type minivec: "[ " +LINE 1: SELECT '[ '::minivec; + ^ +SELECT '[,'::minivec; +ERROR: invalid input syntax for type minivec: "[," +LINE 1: SELECT '[,'::minivec; + ^ +SELECT '[]'::minivec; +ERROR: minivec must have at least 1 dimension +LINE 1: SELECT '[]'::minivec; + ^ +SELECT '[ ]'::minivec; +ERROR: minivec must have at least 1 dimension +LINE 1: SELECT '[ ]'::minivec; + ^ +SELECT '[,]'::minivec; +ERROR: invalid input syntax for type minivec: "[,]" +LINE 1: SELECT '[,]'::minivec; + ^ +SELECT '[1,]'::minivec; +ERROR: invalid input syntax for type minivec: "[1,]" +LINE 1: SELECT '[1,]'::minivec; + ^ +SELECT '[1a]'::minivec; +ERROR: invalid input syntax for type minivec: "[1a]" +LINE 1: SELECT '[1a]'::minivec; + ^ +SELECT '[1,,3]'::minivec; +ERROR: invalid input syntax for type minivec: "[1,,3]" +LINE 1: SELECT '[1,,3]'::minivec; + ^ +SELECT '[1, ,3]'::minivec; +ERROR: invalid input syntax for type minivec: "[1, ,3]" +LINE 1: SELECT '[1, ,3]'::minivec; + ^ +SELECT '[1,2,3]'::minivec(3); + minivec +--------- + [1,2,3] +(1 row) + +SELECT '[1,2,3]'::minivec(2); + minivec +--------- + [1,2,3] +(1 row) + +SELECT '[1,2,3]'::minivec(3, 2); +ERROR: invalid type modifier +LINE 1: SELECT '[1,2,3]'::minivec(3, 2); + ^ +SELECT '[1,2,3]'::minivec('a'); +ERROR: invalid input syntax for type integer: "a" +LINE 1: SELECT '[1,2,3]'::minivec('a'); + ^ +SELECT '[1,2,3]'::minivec(0); +ERROR: dimensions for type minivec must be at least 1 +LINE 1: SELECT '[1,2,3]'::minivec(0); + ^ +SELECT '[1,2,3]'::minivec(16001); +ERROR: dimensions for type minivec cannot exceed 16000 +LINE 1: SELECT '[1,2,3]'::minivec(16001); + ^ +SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::minivec[]); + unnest +--------- + [1,2,3] + [4,5,6] +(2 rows) + +SELECT '{"[1,2,3]"}'::minivec(2)[]; + minivec +------------- + {"[1,2,3]"} +(1 row) + diff --git a/test/sql/copy.sql b/test/sql/copy.sql index 2dff3ff..a9431dc 100644 --- a/test/sql/copy.sql +++ b/test/sql/copy.sql @@ -28,6 +28,21 @@ SELECT * FROM t2 ORDER BY val; DROP TABLE t; DROP TABLE t2; +-- minivec + +CREATE TABLE t (val minivec(3)); +INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL); + +CREATE TABLE t2 (val minivec(3)); + +\copy t TO 'results/minivec.bin' WITH (FORMAT binary) +\copy t2 FROM 'results/minivec.bin' WITH (FORMAT binary) + +--SELECT * FROM t2 ORDER BY val; + +DROP TABLE t; +DROP TABLE t2; + -- sparsevec CREATE TABLE t (val sparsevec(3)); diff --git a/test/sql/minivec.sql b/test/sql/minivec.sql new file mode 100644 index 0000000..61e11d1 --- /dev/null +++ b/test/sql/minivec.sql @@ -0,0 +1,38 @@ +SELECT '[1,2,3]'::minivec; +SELECT '[-1,-2,-3]'::minivec; +SELECT '[1.,2.,3.]'::minivec; +SELECT ' [ 1, 2 , 3 ] '::minivec; +SELECT '[1.23456]'::minivec; +SELECT '[hello,1]'::minivec; +SELECT '[NaN,1]'::minivec; +SELECT '[Infinity,1]'::minivec; +SELECT '[-Infinity,1]'::minivec; +SELECT '[65519,-65519]'::minivec; +SELECT '[65520,-65520]'::minivec; +SELECT '[1e-8,-1e-8]'::minivec; +SELECT '[4e38,1]'::minivec; +SELECT '[1e-46,1]'::minivec; +SELECT '[1,2,3'::minivec; +SELECT '[1,2,3]9'::minivec; +SELECT '1,2,3'::minivec; +SELECT ''::minivec; +SELECT '['::minivec; +SELECT '[ '::minivec; +SELECT '[,'::minivec; +SELECT '[]'::minivec; +SELECT '[ ]'::minivec; +SELECT '[,]'::minivec; +SELECT '[1,]'::minivec; +SELECT '[1a]'::minivec; +SELECT '[1,,3]'::minivec; +SELECT '[1, ,3]'::minivec; + +SELECT '[1,2,3]'::minivec(3); +SELECT '[1,2,3]'::minivec(2); +SELECT '[1,2,3]'::minivec(3, 2); +SELECT '[1,2,3]'::minivec('a'); +SELECT '[1,2,3]'::minivec(0); +SELECT '[1,2,3]'::minivec(16001); + +SELECT unnest('{"[1,2,3]", "[4,5,6]"}'::minivec[]); +SELECT '{"[1,2,3]"}'::minivec(2)[];