mirror of
https://github.com/pgvector/pgvector.git
synced 2026-07-24 04:45:45 +08:00
Compare commits
13 Commits
hnsw-less-
...
half
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46eda44530 | ||
|
|
02f55399eb | ||
|
|
a679b584e9 | ||
|
|
006e5c7aca | ||
|
|
ba3730d2a5 | ||
|
|
7aea658b26 | ||
|
|
7620089901 | ||
|
|
544e303d74 | ||
|
|
d09aa9f873 | ||
|
|
95eff595f0 | ||
|
|
4b630d4f27 | ||
|
|
3b2198d52c | ||
|
|
422667f6c6 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -20,8 +20,6 @@ jobs:
|
|||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
- postgres: 12
|
- postgres: 12
|
||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
- postgres: 11
|
|
||||||
os: ubuntu-20.04
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: ankane/setup-postgres@v1
|
- uses: ankane/setup-postgres@v1
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
## 0.5.2 (unreleased)
|
## 0.5.2 (unreleased)
|
||||||
|
|
||||||
- Improved performance of HNSW
|
|
||||||
- Added support for on-disk parallel index builds for HNSW
|
- Added support for on-disk parallel index builds for HNSW
|
||||||
- Reduced WAL generation for HNSW index builds
|
|
||||||
- Fixed `invalid memory alloc request size` error with HNSW index build
|
|
||||||
|
|
||||||
## 0.5.1 (2023-10-10)
|
## 0.5.1 (2023-10-10)
|
||||||
|
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -3,8 +3,8 @@ EXTVERSION = 0.5.1
|
|||||||
|
|
||||||
MODULE_big = vector
|
MODULE_big = vector
|
||||||
DATA = $(wildcard sql/*--*.sql)
|
DATA = $(wildcard sql/*--*.sql)
|
||||||
OBJS = 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/vector.o
|
OBJS = src/half.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/vector.o
|
||||||
HEADERS = src/vector.h
|
HEADERS = src/half.h src/vector.h
|
||||||
|
|
||||||
TESTS = $(wildcard test/sql/*.sql)
|
TESTS = $(wildcard test/sql/*.sql)
|
||||||
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
|
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
EXTENSION = vector
|
EXTENSION = vector
|
||||||
EXTVERSION = 0.5.1
|
EXTVERSION = 0.5.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
|
OBJS = src\half.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\vector.obj
|
||||||
HEADERS = src\vector.h
|
HEADERS = src\half.h src\vector.h
|
||||||
|
|
||||||
REGRESS = btree cast copy functions input ivfflat_cosine ivfflat_ip ivfflat_l2 ivfflat_options ivfflat_unlogged
|
REGRESS = btree cast copy functions input ivfflat_cosine ivfflat_ip ivfflat_l2 ivfflat_options ivfflat_unlogged
|
||||||
REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION)
|
REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION)
|
||||||
|
|||||||
101
sql/vector--0.5.1--0.6.0.sql
Normal file
101
sql/vector--0.5.1--0.6.0.sql
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION vector UPDATE TO '0.6.0'" to load this file. \quit
|
||||||
|
|
||||||
|
CREATE TYPE half;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_in(cstring, oid, integer) RETURNS half
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_out(half) RETURNS cstring
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_recv(internal, oid, integer) RETURNS half
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_send(half) RETURNS bytea
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE TYPE half (
|
||||||
|
INPUT = half_in,
|
||||||
|
OUTPUT = half_out,
|
||||||
|
RECEIVE = half_recv,
|
||||||
|
SEND = half_send,
|
||||||
|
INTERNALLENGTH = 2,
|
||||||
|
PASSEDBYVALUE,
|
||||||
|
ALIGNMENT = int2
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE FUNCTION l2_distance(half[], half[]) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME', 'half_l2_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION inner_product(half[], half[]) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME', 'half_inner_product' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION cosine_distance(half[], half[]) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME', 'half_cosine_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION l1_distance(half[], half[]) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME', 'half_l1_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_l2_squared_distance(half[], half[]) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_negative_inner_product(half[], half[]) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION float4_to_half(real, integer, boolean) RETURNS half
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_to_float4(half, integer, boolean) RETURNS real
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION float8_to_half(float8, integer, boolean) RETURNS half
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_to_float8(half, integer, boolean) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION integer_to_half(integer, integer, boolean) RETURNS half
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION numeric_to_half(numeric, integer, boolean) RETURNS half
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_to_numeric(half, integer, boolean) RETURNS numeric
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE CAST (real AS half)
|
||||||
|
WITH FUNCTION float4_to_half(real, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE CAST (half AS real)
|
||||||
|
WITH FUNCTION half_to_float4(half, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE CAST (float8 AS half)
|
||||||
|
WITH FUNCTION float8_to_half(float8, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE CAST (half AS float8)
|
||||||
|
WITH FUNCTION half_to_float8(half, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE CAST (integer AS half)
|
||||||
|
WITH FUNCTION integer_to_half(integer, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE CAST (numeric AS half)
|
||||||
|
WITH FUNCTION numeric_to_half(numeric, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE CAST (half AS numeric)
|
||||||
|
WITH FUNCTION half_to_numeric(half, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE OPERATOR <-> (
|
||||||
|
LEFTARG = half[], RIGHTARG = half[], PROCEDURE = l2_distance,
|
||||||
|
COMMUTATOR = '<->'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR <#> (
|
||||||
|
LEFTARG = half[], RIGHTARG = half[], PROCEDURE = half_negative_inner_product,
|
||||||
|
COMMUTATOR = '<#>'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR <=> (
|
||||||
|
LEFTARG = half[], RIGHTARG = half[], PROCEDURE = cosine_distance,
|
||||||
|
COMMUTATOR = '<=>'
|
||||||
|
);
|
||||||
111
sql/vector.sql
111
sql/vector.sql
@@ -290,3 +290,114 @@ CREATE OPERATOR CLASS vector_cosine_ops
|
|||||||
OPERATOR 1 <=> (vector, vector) FOR ORDER BY float_ops,
|
OPERATOR 1 <=> (vector, vector) FOR ORDER BY float_ops,
|
||||||
FUNCTION 1 vector_negative_inner_product(vector, vector),
|
FUNCTION 1 vector_negative_inner_product(vector, vector),
|
||||||
FUNCTION 2 vector_norm(vector);
|
FUNCTION 2 vector_norm(vector);
|
||||||
|
|
||||||
|
-- half type
|
||||||
|
|
||||||
|
CREATE TYPE half;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_in(cstring, oid, integer) RETURNS half
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_out(half) RETURNS cstring
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_recv(internal, oid, integer) RETURNS half
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_send(half) RETURNS bytea
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE TYPE half (
|
||||||
|
INPUT = half_in,
|
||||||
|
OUTPUT = half_out,
|
||||||
|
RECEIVE = half_recv,
|
||||||
|
SEND = half_send,
|
||||||
|
INTERNALLENGTH = 2,
|
||||||
|
PASSEDBYVALUE,
|
||||||
|
ALIGNMENT = int2
|
||||||
|
);
|
||||||
|
|
||||||
|
-- half functions
|
||||||
|
|
||||||
|
CREATE FUNCTION l2_distance(half[], half[]) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME', 'half_l2_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION inner_product(half[], half[]) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME', 'half_inner_product' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION cosine_distance(half[], half[]) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME', 'half_cosine_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION l1_distance(half[], half[]) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME', 'half_l1_distance' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
-- half private functions
|
||||||
|
|
||||||
|
CREATE FUNCTION half_l2_squared_distance(half[], half[]) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_negative_inner_product(half[], half[]) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
-- half cast functions
|
||||||
|
|
||||||
|
CREATE FUNCTION float4_to_half(real, integer, boolean) RETURNS half
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_to_float4(half, integer, boolean) RETURNS real
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION float8_to_half(float8, integer, boolean) RETURNS half
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_to_float8(half, integer, boolean) RETURNS float8
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION integer_to_half(integer, integer, boolean) RETURNS half
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION numeric_to_half(numeric, integer, boolean) RETURNS half
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
CREATE FUNCTION half_to_numeric(half, integer, boolean) RETURNS numeric
|
||||||
|
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||||
|
|
||||||
|
-- half casts
|
||||||
|
|
||||||
|
CREATE CAST (real AS half)
|
||||||
|
WITH FUNCTION float4_to_half(real, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE CAST (half AS real)
|
||||||
|
WITH FUNCTION half_to_float4(half, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE CAST (float8 AS half)
|
||||||
|
WITH FUNCTION float8_to_half(float8, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE CAST (half AS float8)
|
||||||
|
WITH FUNCTION half_to_float8(half, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE CAST (integer AS half)
|
||||||
|
WITH FUNCTION integer_to_half(integer, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE CAST (numeric AS half)
|
||||||
|
WITH FUNCTION numeric_to_half(numeric, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
CREATE CAST (half AS numeric)
|
||||||
|
WITH FUNCTION half_to_numeric(half, integer, boolean) AS IMPLICIT;
|
||||||
|
|
||||||
|
-- half operators
|
||||||
|
|
||||||
|
CREATE OPERATOR <-> (
|
||||||
|
LEFTARG = half[], RIGHTARG = half[], PROCEDURE = l2_distance,
|
||||||
|
COMMUTATOR = '<->'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR <#> (
|
||||||
|
LEFTARG = half[], RIGHTARG = half[], PROCEDURE = half_negative_inner_product,
|
||||||
|
COMMUTATOR = '<#>'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE OPERATOR <=> (
|
||||||
|
LEFTARG = half[], RIGHTARG = half[], PROCEDURE = cosine_distance,
|
||||||
|
COMMUTATOR = '<=>'
|
||||||
|
);
|
||||||
|
|||||||
694
src/half.c
Normal file
694
src/half.c
Normal file
@@ -0,0 +1,694 @@
|
|||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "common/shortest_dec.h"
|
||||||
|
#include "fmgr.h"
|
||||||
|
#include "half.h"
|
||||||
|
#include "lib/stringinfo.h"
|
||||||
|
#include "libpq/pqformat.h"
|
||||||
|
#include "utils/array.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/float.h"
|
||||||
|
#include "utils/numeric.h"
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 120003
|
||||||
|
static pg_noinline void
|
||||||
|
float_overflow_error(void)
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("value out of range: overflow")));
|
||||||
|
}
|
||||||
|
|
||||||
|
static pg_noinline void
|
||||||
|
float_underflow_error(void)
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("value out of range: underflow")));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if array is a vector
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
CheckArrayIsVector(ArrayType *array)
|
||||||
|
{
|
||||||
|
if (ARR_NDIM(array) > 1)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||||
|
errmsg("array must be 1-D")));
|
||||||
|
|
||||||
|
if (ARR_HASNULL(array) && array_contains_nulls(array))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
||||||
|
errmsg("array must not contain nulls")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if dimensions are the same
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
CheckDims(ArrayType *a, ArrayType *b)
|
||||||
|
{
|
||||||
|
int dima;
|
||||||
|
int dimb;
|
||||||
|
|
||||||
|
CheckArrayIsVector(a);
|
||||||
|
CheckArrayIsVector(b);
|
||||||
|
|
||||||
|
dima = ARR_DIMS(a)[0];
|
||||||
|
dimb = ARR_DIMS(b)[0];
|
||||||
|
|
||||||
|
if (dima != dimb)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
||||||
|
errmsg("different dimensions %d and %d", dima, dimb)));
|
||||||
|
|
||||||
|
return dima;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the datum representation for a half
|
||||||
|
*/
|
||||||
|
static inline Datum
|
||||||
|
HalfGetDatum(half X)
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
half value;
|
||||||
|
int16 retval;
|
||||||
|
} myunion;
|
||||||
|
|
||||||
|
myunion.value = X;
|
||||||
|
return Int16GetDatum(myunion.retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the half value of a datum
|
||||||
|
*/
|
||||||
|
static inline half
|
||||||
|
DatumGetHalf(Datum X)
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
int16 value;
|
||||||
|
half retval;
|
||||||
|
} myunion;
|
||||||
|
|
||||||
|
myunion.value = DatumGetInt16(X);
|
||||||
|
return myunion.retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a half from a message buffer
|
||||||
|
*/
|
||||||
|
static half
|
||||||
|
pq_getmsghalf(StringInfo msg)
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
half h;
|
||||||
|
uint16 i;
|
||||||
|
} swap;
|
||||||
|
|
||||||
|
swap.i = pq_getmsgint(msg, 2);
|
||||||
|
return swap.h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append a half to a StringInfo buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
pq_sendhalf(StringInfo buf, half h)
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
half h;
|
||||||
|
uint16 i;
|
||||||
|
} swap;
|
||||||
|
|
||||||
|
swap.h = h;
|
||||||
|
pq_sendint16(buf, swap.i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert a half to a float4
|
||||||
|
*/
|
||||||
|
static float
|
||||||
|
HalfToFloat4(half num)
|
||||||
|
{
|
||||||
|
#ifdef FLT16_SUPPORT
|
||||||
|
return (float) num;
|
||||||
|
#else
|
||||||
|
/* TODO Improve performance */
|
||||||
|
/* TODO Check endianness */
|
||||||
|
uint16 bin = *((uint16 *) &num);
|
||||||
|
uint32 exponent = (bin & 0x7C00) >> 10;
|
||||||
|
uint32 mantissa = bin & 0x03FF;
|
||||||
|
|
||||||
|
/* Sign */
|
||||||
|
uint32 result = (bin & 0x8000) << 16;
|
||||||
|
|
||||||
|
if (exponent == 31)
|
||||||
|
{
|
||||||
|
if (mantissa == 0)
|
||||||
|
{
|
||||||
|
/* Infinite */
|
||||||
|
result |= 0x7F800000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* NaN */
|
||||||
|
result |= 0x7FC00000;
|
||||||
|
result |= mantissa << 13;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (exponent == 0)
|
||||||
|
{
|
||||||
|
/* Subnormal */
|
||||||
|
if (mantissa != 0)
|
||||||
|
{
|
||||||
|
exponent = -14;
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
mantissa <<= 1;
|
||||||
|
exponent -= 1;
|
||||||
|
|
||||||
|
if ((mantissa >> 10) % 2 == 1)
|
||||||
|
{
|
||||||
|
mantissa &= 0x03ff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result |= (exponent + 127) << 23;
|
||||||
|
result |= mantissa << 13;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Normal */
|
||||||
|
result |= (exponent - 15 + 127) << 23;
|
||||||
|
result |= mantissa << 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *((float *) &result);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert a float4 to a half
|
||||||
|
*/
|
||||||
|
static half
|
||||||
|
Float4ToHalfUnchecked(float num)
|
||||||
|
{
|
||||||
|
#ifdef FLT16_SUPPORT
|
||||||
|
return (_Float16) num;
|
||||||
|
#else
|
||||||
|
/* TODO Improve performance */
|
||||||
|
/* TODO Check endianness */
|
||||||
|
uint32 bin = *((uint32 *) &num);
|
||||||
|
int exponent = (bin & 0x7F800000) >> 23;
|
||||||
|
int mantissa = bin & 0x007FFFFF;
|
||||||
|
|
||||||
|
/* Sign */
|
||||||
|
uint16 result = (bin & 0x80000000) >> 16;
|
||||||
|
|
||||||
|
if (isinf(num))
|
||||||
|
{
|
||||||
|
/* Infinite */
|
||||||
|
result |= 0x7C00;
|
||||||
|
}
|
||||||
|
else if (isnan(num))
|
||||||
|
{
|
||||||
|
/* NaN */
|
||||||
|
result |= 0x7E00;
|
||||||
|
result |= mantissa >> 13;
|
||||||
|
}
|
||||||
|
else if (exponent > 98)
|
||||||
|
{
|
||||||
|
int m;
|
||||||
|
int gr;
|
||||||
|
int s;
|
||||||
|
|
||||||
|
exponent -= 127;
|
||||||
|
s = mantissa & 0x00000FFF;
|
||||||
|
|
||||||
|
/* Subnormal */
|
||||||
|
if (exponent < -14)
|
||||||
|
{
|
||||||
|
int diff = -exponent - 14;
|
||||||
|
|
||||||
|
mantissa >>= diff;
|
||||||
|
mantissa += 1 << (23 - diff);
|
||||||
|
s |= mantissa & 0x00000FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
m = mantissa >> 13;
|
||||||
|
|
||||||
|
/* Round */
|
||||||
|
gr = (mantissa >> 12) % 4;
|
||||||
|
if (gr == 3 || (gr == 1 && s != 0))
|
||||||
|
m += 1;
|
||||||
|
|
||||||
|
if (m == 1024)
|
||||||
|
{
|
||||||
|
m = 0;
|
||||||
|
exponent += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exponent > 15)
|
||||||
|
{
|
||||||
|
/* Infinite */
|
||||||
|
result |= 0x7C00;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (exponent >= -14)
|
||||||
|
result |= (exponent + 15) << 10;
|
||||||
|
|
||||||
|
result |= m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *((half *) & result);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if half is infinite
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
HalfIsInf(half num)
|
||||||
|
{
|
||||||
|
#ifdef FLT16_SUPPORT
|
||||||
|
return isinf(num);
|
||||||
|
#else
|
||||||
|
return (num << 1) == 0xF800;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if half is zero
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
HalfIsZero(half num)
|
||||||
|
{
|
||||||
|
#ifdef FLT16_SUPPORT
|
||||||
|
return num == 0;
|
||||||
|
#else
|
||||||
|
return (num << 1) == 0x0000;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert a float4 to a half
|
||||||
|
*/
|
||||||
|
static half
|
||||||
|
Float4ToHalf(float num)
|
||||||
|
{
|
||||||
|
half result = Float4ToHalfUnchecked(num);
|
||||||
|
|
||||||
|
if (unlikely(HalfIsInf(result)) && !isinf(num))
|
||||||
|
float_overflow_error();
|
||||||
|
if (unlikely(HalfIsZero(result)) && num != 0.0)
|
||||||
|
float_underflow_error();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert a float8 to a half
|
||||||
|
*/
|
||||||
|
static half
|
||||||
|
Float8ToHalf(double num)
|
||||||
|
{
|
||||||
|
/* TODO Convert directly for greater accuracy */
|
||||||
|
half result = Float4ToHalfUnchecked((float) num);
|
||||||
|
|
||||||
|
if (unlikely(HalfIsInf(result)) && !isinf(num))
|
||||||
|
float_overflow_error();
|
||||||
|
if (unlikely(HalfIsZero(result)) && num != 0.0)
|
||||||
|
float_underflow_error();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert textual representation to internal representation
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_in);
|
||||||
|
Datum
|
||||||
|
half_in(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
char *num = PG_GETARG_CSTRING(0);
|
||||||
|
char *orig_num;
|
||||||
|
float val;
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
orig_num = num;
|
||||||
|
|
||||||
|
/* Skip leading whitespace */
|
||||||
|
while (*num != '\0' && isspace((unsigned char) *num))
|
||||||
|
num++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check for an empty-string input to begin with, to avoid the vagaries of
|
||||||
|
* strtof() on different platforms.
|
||||||
|
*/
|
||||||
|
if (*num == '\0')
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||||
|
errmsg("invalid input syntax for type %s: \"%s\"",
|
||||||
|
"half", orig_num)));
|
||||||
|
|
||||||
|
val = strtof(num, &endptr);
|
||||||
|
|
||||||
|
if (val < -HALF_MAX || val > HALF_MAX)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("\"%s\" is out of range for type %s",
|
||||||
|
orig_num, "half")));
|
||||||
|
|
||||||
|
/* Skip trailing whitespace */
|
||||||
|
while (*endptr != '\0' && isspace((unsigned char) *endptr))
|
||||||
|
endptr++;
|
||||||
|
|
||||||
|
/* If there is any junk left at the end of the string, bail out */
|
||||||
|
if (*endptr != '\0')
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||||
|
errmsg("invalid input syntax for type %s: \"%s\"",
|
||||||
|
"half", orig_num)));
|
||||||
|
|
||||||
|
PG_RETURN_HALF(Float4ToHalf(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert internal representation to textual representation
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_out);
|
||||||
|
Datum
|
||||||
|
half_out(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
float num = HalfToFloat4(PG_GETARG_HALF(0));
|
||||||
|
char *ascii = (char *) palloc(32);
|
||||||
|
int ndig = FLT_DIG + extra_float_digits;
|
||||||
|
|
||||||
|
if (extra_float_digits > 0)
|
||||||
|
{
|
||||||
|
float_to_shortest_decimal_buf(num, ascii);
|
||||||
|
PG_RETURN_CSTRING(ascii);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) pg_strfromd(ascii, 32, ndig, num);
|
||||||
|
PG_RETURN_CSTRING(ascii);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert external binary representation to internal representation
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_recv);
|
||||||
|
Datum
|
||||||
|
half_recv(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
||||||
|
|
||||||
|
PG_RETURN_HALF(pq_getmsghalf(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert internal representation to the external binary representation
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_send);
|
||||||
|
Datum
|
||||||
|
half_send(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
half arg1 = PG_GETARG_HALF(0);
|
||||||
|
StringInfoData buf;
|
||||||
|
|
||||||
|
pq_begintypsend(&buf);
|
||||||
|
pq_sendhalf(&buf, arg1);
|
||||||
|
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert integer to half
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(integer_to_half);
|
||||||
|
Datum
|
||||||
|
integer_to_half(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
int32 i = PG_GETARG_INT32(0);
|
||||||
|
|
||||||
|
/* TODO Figure out correct error */
|
||||||
|
float f = (float) i;
|
||||||
|
half h = Float4ToHalf(f);
|
||||||
|
|
||||||
|
PG_RETURN_HALF(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert numeric to half
|
||||||
|
*
|
||||||
|
* TODO Improve error message if out of range
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(numeric_to_half);
|
||||||
|
Datum
|
||||||
|
numeric_to_half(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Numeric num = PG_GETARG_NUMERIC(0);
|
||||||
|
float f = DatumGetFloat4(DirectFunctionCall1(numeric_float4, NumericGetDatum(num)));
|
||||||
|
half h = Float4ToHalf(f);
|
||||||
|
|
||||||
|
PG_RETURN_HALF(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert half to numeric
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_to_numeric);
|
||||||
|
Datum
|
||||||
|
half_to_numeric(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
half h = PG_GETARG_HALF(0);
|
||||||
|
float f = HalfToFloat4(h);
|
||||||
|
Numeric num = DatumGetNumeric(DirectFunctionCall1(float4_numeric, Float4GetDatum(f)));
|
||||||
|
|
||||||
|
PG_RETURN_NUMERIC(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert float4 to half
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(float4_to_half);
|
||||||
|
Datum
|
||||||
|
float4_to_half(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
float f = PG_GETARG_FLOAT4(0);
|
||||||
|
half h = Float4ToHalf(f);
|
||||||
|
|
||||||
|
PG_RETURN_HALF(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert half to float4
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_to_float4);
|
||||||
|
Datum
|
||||||
|
half_to_float4(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
half h = PG_GETARG_HALF(0);
|
||||||
|
float f = HalfToFloat4(h);
|
||||||
|
|
||||||
|
PG_RETURN_FLOAT4(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert float8 to half
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(float8_to_half);
|
||||||
|
Datum
|
||||||
|
float8_to_half(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
float8 d = PG_GETARG_FLOAT8(0);
|
||||||
|
half h = Float8ToHalf(d);
|
||||||
|
|
||||||
|
PG_RETURN_HALF(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert half to float8
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_to_float8);
|
||||||
|
Datum
|
||||||
|
half_to_float8(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
half h = PG_GETARG_HALF(0);
|
||||||
|
float f = HalfToFloat4(h);
|
||||||
|
|
||||||
|
PG_RETURN_FLOAT8((double) f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the L2 distance between half arrays
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_l2_distance);
|
||||||
|
Datum
|
||||||
|
half_l2_distance(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
|
ArrayType *b = PG_GETARG_ARRAYTYPE_P(1);
|
||||||
|
half *ax = (half *) ARR_DATA_PTR(a);
|
||||||
|
half *bx = (half *) ARR_DATA_PTR(b);
|
||||||
|
float distance = 0.0;
|
||||||
|
int dim = CheckDims(a, b);
|
||||||
|
|
||||||
|
/* Auto-vectorized */
|
||||||
|
for (int i = 0; i < dim; i++)
|
||||||
|
{
|
||||||
|
float diff = HalfToFloat4(ax[i]) - HalfToFloat4(bx[i]);
|
||||||
|
|
||||||
|
distance += diff * diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RETURN_FLOAT8(sqrt((double) distance));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the L2 squared distance between half arrays
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_l2_squared_distance);
|
||||||
|
Datum
|
||||||
|
half_l2_squared_distance(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
|
ArrayType *b = PG_GETARG_ARRAYTYPE_P(1);
|
||||||
|
half *ax = (half *) ARR_DATA_PTR(a);
|
||||||
|
half *bx = (half *) ARR_DATA_PTR(b);
|
||||||
|
float distance = 0.0;
|
||||||
|
int dim = CheckDims(a, b);
|
||||||
|
|
||||||
|
/* Auto-vectorized */
|
||||||
|
for (int i = 0; i < dim; i++)
|
||||||
|
{
|
||||||
|
float diff = HalfToFloat4(ax[i]) - HalfToFloat4(bx[i]);
|
||||||
|
|
||||||
|
distance += diff * diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RETURN_FLOAT8((double) distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the inner product of two half arrays
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_inner_product);
|
||||||
|
Datum
|
||||||
|
half_inner_product(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
|
ArrayType *b = PG_GETARG_ARRAYTYPE_P(1);
|
||||||
|
half *ax = (half *) ARR_DATA_PTR(a);
|
||||||
|
half *bx = (half *) ARR_DATA_PTR(b);
|
||||||
|
float distance = 0.0;
|
||||||
|
int dim = CheckDims(a, b);
|
||||||
|
|
||||||
|
/* Auto-vectorized */
|
||||||
|
for (int i = 0; i < dim; i++)
|
||||||
|
distance += HalfToFloat4(ax[i]) * HalfToFloat4(bx[i]);
|
||||||
|
|
||||||
|
PG_RETURN_FLOAT8((double) distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the negative inner product of two half arrays
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_negative_inner_product);
|
||||||
|
Datum
|
||||||
|
half_negative_inner_product(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
|
ArrayType *b = PG_GETARG_ARRAYTYPE_P(1);
|
||||||
|
half *ax = (half *) ARR_DATA_PTR(a);
|
||||||
|
half *bx = (half *) ARR_DATA_PTR(b);
|
||||||
|
float distance = 0.0;
|
||||||
|
int dim = CheckDims(a, b);
|
||||||
|
|
||||||
|
/* Auto-vectorized */
|
||||||
|
for (int i = 0; i < dim; i++)
|
||||||
|
distance += HalfToFloat4(ax[i]) * HalfToFloat4(bx[i]);
|
||||||
|
|
||||||
|
PG_RETURN_FLOAT8((double) distance * -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the cosine distance between two half arrays
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_cosine_distance);
|
||||||
|
Datum
|
||||||
|
half_cosine_distance(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
|
ArrayType *b = PG_GETARG_ARRAYTYPE_P(1);
|
||||||
|
half *ax = (half *) ARR_DATA_PTR(a);
|
||||||
|
half *bx = (half *) ARR_DATA_PTR(b);
|
||||||
|
float distance = 0.0;
|
||||||
|
float norma = 0.0;
|
||||||
|
float normb = 0.0;
|
||||||
|
double similarity;
|
||||||
|
int dim = CheckDims(a, b);
|
||||||
|
|
||||||
|
/* Auto-vectorized */
|
||||||
|
for (int i = 0; i < dim; i++)
|
||||||
|
{
|
||||||
|
float axi = HalfToFloat4(ax[i]);
|
||||||
|
float bxi = HalfToFloat4(bx[i]);
|
||||||
|
|
||||||
|
distance += axi * bxi;
|
||||||
|
norma += axi * axi;
|
||||||
|
normb += bxi * bxi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use sqrt(a * b) over sqrt(a) * sqrt(b) */
|
||||||
|
similarity = (double) distance / sqrt((double) norma * (double) normb);
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
/* /fp:fast may not propagate NaN */
|
||||||
|
if (isnan(similarity))
|
||||||
|
PG_RETURN_FLOAT8(NAN);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Keep in range */
|
||||||
|
if (similarity > 1)
|
||||||
|
similarity = 1;
|
||||||
|
else if (similarity < -1)
|
||||||
|
similarity = -1;
|
||||||
|
|
||||||
|
PG_RETURN_FLOAT8(1 - similarity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the L1 distance between two half arrays
|
||||||
|
*/
|
||||||
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(half_l1_distance);
|
||||||
|
Datum
|
||||||
|
half_l1_distance(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
|
ArrayType *b = PG_GETARG_ARRAYTYPE_P(1);
|
||||||
|
half *ax = (half *) ARR_DATA_PTR(a);
|
||||||
|
half *bx = (half *) ARR_DATA_PTR(b);
|
||||||
|
float distance = 0.0;
|
||||||
|
int dim = CheckDims(a, b);
|
||||||
|
|
||||||
|
/* Auto-vectorized */
|
||||||
|
for (int i = 0; i < dim; i++)
|
||||||
|
distance += fabsf(HalfToFloat4(ax[i]) - HalfToFloat4(bx[i]));
|
||||||
|
|
||||||
|
PG_RETURN_FLOAT8((double) distance);
|
||||||
|
}
|
||||||
23
src/half.h
Normal file
23
src/half.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#ifndef HALF_H
|
||||||
|
#define HALF_H
|
||||||
|
|
||||||
|
#define __STDC_WANT_IEC_60559_TYPES_EXT__
|
||||||
|
|
||||||
|
#include <float.h>
|
||||||
|
|
||||||
|
#ifdef __FLT16_MAX__
|
||||||
|
#define FLT16_SUPPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FLT16_SUPPORT
|
||||||
|
#define half _Float16
|
||||||
|
#define HALF_MAX FLT16_MAX
|
||||||
|
#else
|
||||||
|
#define half uint16
|
||||||
|
#define HALF_MAX 65504
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PG_GETARG_HALF(n) DatumGetHalf(PG_GETARG_DATUM(n))
|
||||||
|
#define PG_RETURN_HALF(x) return HalfGetDatum(x)
|
||||||
|
|
||||||
|
#endif
|
||||||
38
src/hnsw.h
38
src/hnsw.h
@@ -104,7 +104,6 @@ typedef struct HnswElementData
|
|||||||
List *heaptids;
|
List *heaptids;
|
||||||
uint8 level;
|
uint8 level;
|
||||||
uint8 deleted;
|
uint8 deleted;
|
||||||
uint32 hash;
|
|
||||||
HnswNeighborArray *neighbors;
|
HnswNeighborArray *neighbors;
|
||||||
BlockNumber blkno;
|
BlockNumber blkno;
|
||||||
OffsetNumber offno;
|
OffsetNumber offno;
|
||||||
@@ -304,7 +303,7 @@ typedef struct HnswVacuumState
|
|||||||
Oid collation;
|
Oid collation;
|
||||||
|
|
||||||
/* Variables */
|
/* Variables */
|
||||||
struct tidhash_hash *deleted;
|
HTAB *deleted;
|
||||||
BufferAccessStrategy bas;
|
BufferAccessStrategy bas;
|
||||||
HnswNeighborTuple ntup;
|
HnswNeighborTuple ntup;
|
||||||
HnswElementData highestPoint;
|
HnswElementData highestPoint;
|
||||||
@@ -318,8 +317,10 @@ int HnswGetM(Relation index);
|
|||||||
int HnswGetEfConstruction(Relation index);
|
int HnswGetEfConstruction(Relation index);
|
||||||
FmgrInfo *HnswOptionalProcInfo(Relation index, uint16 procnum);
|
FmgrInfo *HnswOptionalProcInfo(Relation index, uint16 procnum);
|
||||||
bool HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result);
|
bool HnswNormValue(FmgrInfo *procinfo, Oid collation, Datum *value, Vector * result);
|
||||||
|
void HnswCommitBuffer(Buffer buf, GenericXLogState *state);
|
||||||
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 HnswInitRegisterPage(Relation index, Buffer *buf, Page *page, GenericXLogState **state);
|
||||||
void HnswInit(void);
|
void HnswInit(void);
|
||||||
List *HnswSearchLayer(Datum q, List *ep, int ef, int lc, Relation index, FmgrInfo *procinfo, Oid collation, int m, bool inserting, HnswElement skipElement);
|
List *HnswSearchLayer(Datum 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);
|
||||||
@@ -330,12 +331,12 @@ HnswElement HnswInitElementFromBlock(BlockNumber blkno, OffsetNumber offno);
|
|||||||
void HnswInsertElement(HnswElement element, HnswElement entryPoint, Relation index, FmgrInfo *procinfo, Oid collation, int m, int efConstruction, bool existing);
|
void HnswInsertElement(HnswElement element, HnswElement entryPoint, Relation index, FmgrInfo *procinfo, Oid collation, int m, int efConstruction, bool existing);
|
||||||
HnswElement HnswFindDuplicate(HnswElement e);
|
HnswElement HnswFindDuplicate(HnswElement e);
|
||||||
HnswCandidate *HnswEntryCandidate(HnswElement em, Datum q, Relation rel, FmgrInfo *procinfo, Oid collation, bool loadVec);
|
HnswCandidate *HnswEntryCandidate(HnswElement em, Datum 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);
|
||||||
void HnswSetNeighborTuple(HnswNeighborTuple ntup, HnswElement e, int m);
|
void HnswSetNeighborTuple(HnswNeighborTuple ntup, HnswElement e, int m);
|
||||||
void HnswAddHeapTid(HnswElement element, ItemPointer heaptid);
|
void HnswAddHeapTid(HnswElement element, ItemPointer heaptid);
|
||||||
void HnswInitNeighbors(HnswElement element, int m);
|
void HnswInitNeighbors(HnswElement element, int m);
|
||||||
bool HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel, bool building);
|
bool HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel);
|
||||||
void HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting, bool building);
|
void HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting);
|
||||||
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, Datum *q, Relation index, FmgrInfo *procinfo, Oid collation, bool loadVec);
|
||||||
void HnswSetElementTuple(HnswElementTuple etup, HnswElement element);
|
void HnswSetElementTuple(HnswElementTuple etup, HnswElement element);
|
||||||
@@ -359,31 +360,4 @@ void hnswrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys,
|
|||||||
bool hnswgettuple(IndexScanDesc scan, ScanDirection dir);
|
bool hnswgettuple(IndexScanDesc scan, ScanDirection dir);
|
||||||
void hnswendscan(IndexScanDesc scan);
|
void hnswendscan(IndexScanDesc scan);
|
||||||
|
|
||||||
/* Hash tables */
|
|
||||||
typedef struct TidHashEntry
|
|
||||||
{
|
|
||||||
ItemPointerData tid;
|
|
||||||
char status;
|
|
||||||
} TidHashEntry;
|
|
||||||
|
|
||||||
#define SH_PREFIX tidhash
|
|
||||||
#define SH_ELEMENT_TYPE TidHashEntry
|
|
||||||
#define SH_KEY_TYPE ItemPointerData
|
|
||||||
#define SH_SCOPE extern
|
|
||||||
#define SH_DECLARE
|
|
||||||
#include "lib/simplehash.h"
|
|
||||||
|
|
||||||
typedef struct PointerHashEntry
|
|
||||||
{
|
|
||||||
uintptr_t ptr;
|
|
||||||
char status;
|
|
||||||
} PointerHashEntry;
|
|
||||||
|
|
||||||
#define SH_PREFIX pointerhash
|
|
||||||
#define SH_ELEMENT_TYPE PointerHashEntry
|
|
||||||
#define SH_KEY_TYPE uintptr_t
|
|
||||||
#define SH_SCOPE extern
|
|
||||||
#define SH_DECLARE
|
|
||||||
#include "lib/simplehash.h"
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -56,8 +56,6 @@
|
|||||||
#define PARALLEL_KEY_HNSW_SHARED UINT64CONST(0xA000000000000001)
|
#define PARALLEL_KEY_HNSW_SHARED UINT64CONST(0xA000000000000001)
|
||||||
#define PARALLEL_KEY_QUERY_TEXT UINT64CONST(0xA000000000000002)
|
#define PARALLEL_KEY_QUERY_TEXT UINT64CONST(0xA000000000000002)
|
||||||
|
|
||||||
#define LIST_MAX_LENGTH ((1 << 26) - 1)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the metapage
|
* Create the metapage
|
||||||
*/
|
*/
|
||||||
@@ -68,11 +66,11 @@ CreateMetaPage(HnswBuildState * buildstate)
|
|||||||
ForkNumber forkNum = buildstate->forkNum;
|
ForkNumber forkNum = buildstate->forkNum;
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
Page page;
|
Page page;
|
||||||
|
GenericXLogState *state;
|
||||||
HnswMetaPage metap;
|
HnswMetaPage metap;
|
||||||
|
|
||||||
buf = HnswNewBuffer(index, forkNum);
|
buf = HnswNewBuffer(index, forkNum);
|
||||||
page = BufferGetPage(buf);
|
HnswInitRegisterPage(index, &buf, &page, &state);
|
||||||
HnswInitPage(buf, page);
|
|
||||||
|
|
||||||
/* Set metapage data */
|
/* Set metapage data */
|
||||||
metap = HnswPageGetMeta(page);
|
metap = HnswPageGetMeta(page);
|
||||||
@@ -88,14 +86,14 @@ CreateMetaPage(HnswBuildState * buildstate)
|
|||||||
((PageHeader) page)->pd_lower =
|
((PageHeader) page)->pd_lower =
|
||||||
((char *) metap + sizeof(HnswMetaPageData)) - (char *) page;
|
((char *) metap + sizeof(HnswMetaPageData)) - (char *) page;
|
||||||
|
|
||||||
UnlockReleaseBuffer(buf);
|
HnswCommitBuffer(buf, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a new page
|
* Add a new page
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
HnswBuildAppendPage(Relation index, Buffer *buf, Page *page, ForkNumber forkNum)
|
HnswBuildAppendPage(Relation index, Buffer *buf, Page *page, GenericXLogState **state, ForkNumber forkNum)
|
||||||
{
|
{
|
||||||
/* Add a new page */
|
/* Add a new page */
|
||||||
Buffer newbuf = HnswNewBuffer(index, forkNum);
|
Buffer newbuf = HnswNewBuffer(index, forkNum);
|
||||||
@@ -104,6 +102,7 @@ HnswBuildAppendPage(Relation index, Buffer *buf, Page *page, ForkNumber forkNum)
|
|||||||
HnswPageGetOpaque(*page)->nextblkno = BufferGetBlockNumber(newbuf);
|
HnswPageGetOpaque(*page)->nextblkno = BufferGetBlockNumber(newbuf);
|
||||||
|
|
||||||
/* Commit */
|
/* Commit */
|
||||||
|
GenericXLogFinish(*state);
|
||||||
UnlockReleaseBuffer(*buf);
|
UnlockReleaseBuffer(*buf);
|
||||||
|
|
||||||
/* Can take a while, so ensure we can interrupt */
|
/* Can take a while, so ensure we can interrupt */
|
||||||
@@ -114,7 +113,8 @@ HnswBuildAppendPage(Relation index, Buffer *buf, Page *page, ForkNumber forkNum)
|
|||||||
|
|
||||||
/* Prepare new page */
|
/* Prepare new page */
|
||||||
*buf = newbuf;
|
*buf = newbuf;
|
||||||
*page = BufferGetPage(*buf);
|
*state = GenericXLogStart(index);
|
||||||
|
*page = GenericXLogRegisterBuffer(*state, *buf, GENERIC_XLOG_FULL_IMAGE);
|
||||||
HnswInitPage(*buf, *page);
|
HnswInitPage(*buf, *page);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +133,7 @@ CreateElementPages(HnswBuildState * buildstate)
|
|||||||
BlockNumber insertPage;
|
BlockNumber insertPage;
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
Page page;
|
Page page;
|
||||||
|
GenericXLogState *state;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
/* Calculate sizes */
|
/* Calculate sizes */
|
||||||
@@ -145,7 +146,8 @@ CreateElementPages(HnswBuildState * buildstate)
|
|||||||
|
|
||||||
/* Prepare first page */
|
/* Prepare first page */
|
||||||
buf = HnswNewBuffer(index, forkNum);
|
buf = HnswNewBuffer(index, forkNum);
|
||||||
page = BufferGetPage(buf);
|
state = GenericXLogStart(index);
|
||||||
|
page = GenericXLogRegisterBuffer(state, buf, GENERIC_XLOG_FULL_IMAGE);
|
||||||
HnswInitPage(buf, page);
|
HnswInitPage(buf, page);
|
||||||
|
|
||||||
foreach(lc, buildstate->elements)
|
foreach(lc, buildstate->elements)
|
||||||
@@ -171,7 +173,7 @@ CreateElementPages(HnswBuildState * buildstate)
|
|||||||
|
|
||||||
/* Keep element and neighbors on the same page if possible */
|
/* Keep element and neighbors on the same page if possible */
|
||||||
if (PageGetFreeSpace(page) < etupSize || (combinedSize <= maxSize && PageGetFreeSpace(page) < combinedSize))
|
if (PageGetFreeSpace(page) < etupSize || (combinedSize <= maxSize && PageGetFreeSpace(page) < combinedSize))
|
||||||
HnswBuildAppendPage(index, &buf, &page, forkNum);
|
HnswBuildAppendPage(index, &buf, &page, &state, forkNum);
|
||||||
|
|
||||||
/* Calculate offsets */
|
/* Calculate offsets */
|
||||||
element->blkno = BufferGetBlockNumber(buf);
|
element->blkno = BufferGetBlockNumber(buf);
|
||||||
@@ -195,7 +197,7 @@ CreateElementPages(HnswBuildState * buildstate)
|
|||||||
|
|
||||||
/* Add new page if needed */
|
/* Add new page if needed */
|
||||||
if (PageGetFreeSpace(page) < ntupSize)
|
if (PageGetFreeSpace(page) < ntupSize)
|
||||||
HnswBuildAppendPage(index, &buf, &page, forkNum);
|
HnswBuildAppendPage(index, &buf, &page, &state, forkNum);
|
||||||
|
|
||||||
/* Add placeholder for neighbors */
|
/* Add placeholder for neighbors */
|
||||||
if (PageAddItem(page, (Item) ntup, ntupSize, InvalidOffsetNumber, false, false) != element->neighborOffno)
|
if (PageAddItem(page, (Item) ntup, ntupSize, InvalidOffsetNumber, false, false) != element->neighborOffno)
|
||||||
@@ -205,9 +207,10 @@ CreateElementPages(HnswBuildState * buildstate)
|
|||||||
insertPage = BufferGetBlockNumber(buf);
|
insertPage = BufferGetBlockNumber(buf);
|
||||||
|
|
||||||
/* Commit */
|
/* Commit */
|
||||||
|
GenericXLogFinish(state);
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
|
|
||||||
HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_ALWAYS, buildstate->entryPoint, insertPage, forkNum, true);
|
HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_ALWAYS, buildstate->entryPoint, insertPage, forkNum);
|
||||||
|
|
||||||
pfree(etup);
|
pfree(etup);
|
||||||
pfree(ntup);
|
pfree(ntup);
|
||||||
@@ -233,6 +236,7 @@ CreateNeighborPages(HnswBuildState * buildstate)
|
|||||||
HnswElement e = lfirst(lc);
|
HnswElement e = lfirst(lc);
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
Page page;
|
Page page;
|
||||||
|
GenericXLogState *state;
|
||||||
Size ntupSize = HNSW_NEIGHBOR_TUPLE_SIZE(e->level, m);
|
Size ntupSize = HNSW_NEIGHBOR_TUPLE_SIZE(e->level, m);
|
||||||
|
|
||||||
/* Can take a while, so ensure we can interrupt */
|
/* Can take a while, so ensure we can interrupt */
|
||||||
@@ -241,7 +245,8 @@ CreateNeighborPages(HnswBuildState * buildstate)
|
|||||||
|
|
||||||
buf = ReadBufferExtended(index, forkNum, e->neighborPage, RBM_NORMAL, NULL);
|
buf = ReadBufferExtended(index, forkNum, e->neighborPage, RBM_NORMAL, NULL);
|
||||||
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
||||||
page = BufferGetPage(buf);
|
state = GenericXLogStart(index);
|
||||||
|
page = GenericXLogRegisterBuffer(state, buf, 0);
|
||||||
|
|
||||||
HnswSetNeighborTuple(ntup, e, m);
|
HnswSetNeighborTuple(ntup, e, m);
|
||||||
|
|
||||||
@@ -249,6 +254,7 @@ CreateNeighborPages(HnswBuildState * buildstate)
|
|||||||
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
|
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
|
||||||
|
|
||||||
/* Commit */
|
/* Commit */
|
||||||
|
GenericXLogFinish(state);
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,22 +381,21 @@ BuildCallback(Relation index, CALLBACK_ITEM_POINTER, Datum *values,
|
|||||||
if (isnull[0])
|
if (isnull[0])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (buildstate->flushed || buildstate->memoryLeft <= 0 || list_length(buildstate->elements) == LIST_MAX_LENGTH)
|
if (buildstate->memoryLeft <= 0)
|
||||||
{
|
{
|
||||||
if (!buildstate->flushed)
|
if (!buildstate->flushed)
|
||||||
{
|
{
|
||||||
if (buildstate->memoryLeft <= 0)
|
ereport(NOTICE,
|
||||||
ereport(NOTICE,
|
(errmsg("hnsw graph no longer fits into maintenance_work_mem after " INT64_FORMAT " tuples", (int64) buildstate->indtuples),
|
||||||
(errmsg("hnsw graph no longer fits into maintenance_work_mem after " INT64_FORMAT " tuples", (int64) buildstate->indtuples),
|
errdetail("Building will take significantly more time."),
|
||||||
errdetail("Building will take significantly more time."),
|
errhint("Increase maintenance_work_mem to speed up builds.")));
|
||||||
errhint("Increase maintenance_work_mem to speed up builds.")));
|
|
||||||
|
|
||||||
FlushPages(buildstate);
|
FlushPages(buildstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
|
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
|
||||||
|
|
||||||
if (HnswInsertTuple(buildstate->index, values, isnull, tid, buildstate->heap, true))
|
if (HnswInsertTuple(buildstate->index, values, isnull, tid, buildstate->heap))
|
||||||
{
|
{
|
||||||
if (buildstate->hnswshared)
|
if (buildstate->hnswshared)
|
||||||
{
|
{
|
||||||
@@ -872,22 +877,6 @@ BuildGraph(HnswBuildState * buildstate, ForkNumber forkNum)
|
|||||||
HnswEndParallel(buildstate->hnswleader);
|
HnswEndParallel(buildstate->hnswleader);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 110008
|
|
||||||
void
|
|
||||||
log_newpage_range(Relation rel, ForkNumber forkNum, BlockNumber startblk, BlockNumber endblk, bool page_std)
|
|
||||||
{
|
|
||||||
for (BlockNumber blkno = startblk; blkno < endblk; blkno++)
|
|
||||||
{
|
|
||||||
Buffer buf = ReadBufferExtended(rel, forkNum, blkno, RBM_NORMAL, NULL);
|
|
||||||
|
|
||||||
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
|
||||||
MarkBufferDirty(buf);
|
|
||||||
log_newpage_buffer(buf, page_std);
|
|
||||||
UnlockReleaseBuffer(buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Build the index
|
* Build the index
|
||||||
*/
|
*/
|
||||||
@@ -903,9 +892,6 @@ BuildIndex(Relation heap, Relation index, IndexInfo *indexInfo,
|
|||||||
if (!buildstate->flushed)
|
if (!buildstate->flushed)
|
||||||
FlushPages(buildstate);
|
FlushPages(buildstate);
|
||||||
|
|
||||||
if (RelationNeedsWAL(index))
|
|
||||||
log_newpage_range(index, forkNum, 0, RelationGetNumberOfBlocks(index), true);
|
|
||||||
|
|
||||||
FreeBuildState(buildstate);
|
FreeBuildState(buildstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
113
src/hnswinsert.c
113
src/hnswinsert.c
@@ -92,7 +92,7 @@ HnswFreeOffset(Relation index, Buffer buf, Page page, HnswElement element, Size
|
|||||||
* Add a new page
|
* Add a new page
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
HnswInsertAppendPage(Relation index, Buffer *nbuf, Page *npage, GenericXLogState *state, Page page, bool building)
|
HnswInsertAppendPage(Relation index, Buffer *nbuf, Page *npage, GenericXLogState *state, Page page)
|
||||||
{
|
{
|
||||||
/* Add a new page */
|
/* Add a new page */
|
||||||
LockRelationForExtension(index, ExclusiveLock);
|
LockRelationForExtension(index, ExclusiveLock);
|
||||||
@@ -100,11 +100,7 @@ HnswInsertAppendPage(Relation index, Buffer *nbuf, Page *npage, GenericXLogState
|
|||||||
UnlockRelationForExtension(index, ExclusiveLock);
|
UnlockRelationForExtension(index, ExclusiveLock);
|
||||||
|
|
||||||
/* Init new page */
|
/* Init new page */
|
||||||
if (building)
|
*npage = GenericXLogRegisterBuffer(state, *nbuf, GENERIC_XLOG_FULL_IMAGE);
|
||||||
*npage = BufferGetPage(*nbuf);
|
|
||||||
else
|
|
||||||
*npage = GenericXLogRegisterBuffer(state, *nbuf, GENERIC_XLOG_FULL_IMAGE);
|
|
||||||
|
|
||||||
HnswInitPage(*nbuf, *npage);
|
HnswInitPage(*nbuf, *npage);
|
||||||
|
|
||||||
/* Update previous buffer */
|
/* Update previous buffer */
|
||||||
@@ -115,7 +111,7 @@ HnswInsertAppendPage(Relation index, Buffer *nbuf, Page *npage, GenericXLogState
|
|||||||
* Add to element and neighbor pages
|
* Add to element and neighbor pages
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPage, BlockNumber *updatedInsertPage, bool building)
|
WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPage, BlockNumber *updatedInsertPage)
|
||||||
{
|
{
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
Page page;
|
Page page;
|
||||||
@@ -155,16 +151,8 @@ WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPag
|
|||||||
buf = ReadBuffer(index, currentPage);
|
buf = ReadBuffer(index, currentPage);
|
||||||
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
||||||
|
|
||||||
if (building)
|
state = GenericXLogStart(index);
|
||||||
{
|
page = GenericXLogRegisterBuffer(state, buf, 0);
|
||||||
state = NULL;
|
|
||||||
page = BufferGetPage(buf);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state = GenericXLogStart(index);
|
|
||||||
page = GenericXLogRegisterBuffer(state, buf, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Keep track of first page where element at level 0 can fit */
|
/* Keep track of first page where element at level 0 can fit */
|
||||||
if (!BlockNumberIsValid(newInsertPage) && PageGetFreeSpace(page) >= minCombinedSize)
|
if (!BlockNumberIsValid(newInsertPage) && PageGetFreeSpace(page) >= minCombinedSize)
|
||||||
@@ -184,12 +172,7 @@ WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPag
|
|||||||
if (HnswFreeOffset(index, buf, page, e, ntupSize, &nbuf, &npage, &freeOffno, &freeNeighborOffno, &newInsertPage))
|
if (HnswFreeOffset(index, buf, page, e, ntupSize, &nbuf, &npage, &freeOffno, &freeNeighborOffno, &newInsertPage))
|
||||||
{
|
{
|
||||||
if (nbuf != buf)
|
if (nbuf != buf)
|
||||||
{
|
npage = GenericXLogRegisterBuffer(state, nbuf, 0);
|
||||||
if (building)
|
|
||||||
npage = BufferGetPage(nbuf);
|
|
||||||
else
|
|
||||||
npage = GenericXLogRegisterBuffer(state, nbuf, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -198,7 +181,7 @@ WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPag
|
|||||||
/* Skip if both tuples can fit on the same page */
|
/* Skip if both tuples can fit on the same page */
|
||||||
if (combinedSize > maxSize && PageGetFreeSpace(page) >= etupSize && !BlockNumberIsValid(HnswPageGetOpaque(page)->nextblkno))
|
if (combinedSize > maxSize && PageGetFreeSpace(page) >= etupSize && !BlockNumberIsValid(HnswPageGetOpaque(page)->nextblkno))
|
||||||
{
|
{
|
||||||
HnswInsertAppendPage(index, &nbuf, &npage, state, page, building);
|
HnswInsertAppendPage(index, &nbuf, &npage, state, page);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,8 +190,7 @@ WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPag
|
|||||||
if (BlockNumberIsValid(currentPage))
|
if (BlockNumberIsValid(currentPage))
|
||||||
{
|
{
|
||||||
/* Move to next page */
|
/* Move to next page */
|
||||||
if (!building)
|
GenericXLogAbort(state);
|
||||||
GenericXLogAbort(state);
|
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -216,31 +198,22 @@ WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPag
|
|||||||
Buffer newbuf;
|
Buffer newbuf;
|
||||||
Page newpage;
|
Page newpage;
|
||||||
|
|
||||||
HnswInsertAppendPage(index, &newbuf, &newpage, state, page, building);
|
HnswInsertAppendPage(index, &newbuf, &newpage, state, page);
|
||||||
|
|
||||||
/* Commit */
|
/* Commit */
|
||||||
if (!building)
|
GenericXLogFinish(state);
|
||||||
GenericXLogFinish(state);
|
|
||||||
|
|
||||||
/* Unlock previous buffer */
|
/* Unlock previous buffer */
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
|
|
||||||
/* Prepare new buffer */
|
/* Prepare new buffer */
|
||||||
|
state = GenericXLogStart(index);
|
||||||
buf = newbuf;
|
buf = newbuf;
|
||||||
if (building)
|
page = GenericXLogRegisterBuffer(state, buf, 0);
|
||||||
{
|
|
||||||
state = NULL;
|
|
||||||
page = BufferGetPage(buf);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state = GenericXLogStart(index);
|
|
||||||
page = GenericXLogRegisterBuffer(state, buf, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create new page for neighbors if needed */
|
/* Create new page for neighbors if needed */
|
||||||
if (PageGetFreeSpace(page) < combinedSize)
|
if (PageGetFreeSpace(page) < combinedSize)
|
||||||
HnswInsertAppendPage(index, &nbuf, &npage, state, page, building);
|
HnswInsertAppendPage(index, &nbuf, &npage, state, page);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nbuf = buf;
|
nbuf = buf;
|
||||||
@@ -294,8 +267,7 @@ WriteNewElementPages(Relation index, HnswElement e, int m, BlockNumber insertPag
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Commit */
|
/* Commit */
|
||||||
if (!building)
|
GenericXLogFinish(state);
|
||||||
GenericXLogFinish(state);
|
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
if (nbuf != buf)
|
if (nbuf != buf)
|
||||||
UnlockReleaseBuffer(nbuf);
|
UnlockReleaseBuffer(nbuf);
|
||||||
@@ -329,7 +301,7 @@ ConnectionExists(HnswElement e, HnswNeighborTuple ntup, int startIdx, int lm)
|
|||||||
* Update neighbors
|
* Update neighbors
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting, bool building)
|
HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement e, int m, bool checkExisting)
|
||||||
{
|
{
|
||||||
for (int lc = e->level; lc >= 0; lc--)
|
for (int lc = e->level; lc >= 0; lc--)
|
||||||
{
|
{
|
||||||
@@ -370,16 +342,8 @@ HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswE
|
|||||||
/* Register page */
|
/* Register page */
|
||||||
buf = ReadBuffer(index, hc->element->neighborPage);
|
buf = ReadBuffer(index, hc->element->neighborPage);
|
||||||
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
||||||
if (building)
|
state = GenericXLogStart(index);
|
||||||
{
|
page = GenericXLogRegisterBuffer(state, buf, 0);
|
||||||
state = NULL;
|
|
||||||
page = BufferGetPage(buf);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state = GenericXLogStart(index);
|
|
||||||
page = GenericXLogRegisterBuffer(state, buf, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get tuple */
|
/* Get tuple */
|
||||||
itemid = PageGetItemId(page, offno);
|
itemid = PageGetItemId(page, offno);
|
||||||
@@ -421,10 +385,9 @@ HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswE
|
|||||||
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
|
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
|
||||||
|
|
||||||
/* Commit */
|
/* Commit */
|
||||||
if (!building)
|
GenericXLogFinish(state);
|
||||||
GenericXLogFinish(state);
|
|
||||||
}
|
}
|
||||||
else if (!building)
|
else
|
||||||
GenericXLogAbort(state);
|
GenericXLogAbort(state);
|
||||||
|
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
@@ -436,7 +399,7 @@ HnswUpdateNeighborPages(Relation index, FmgrInfo *procinfo, Oid collation, HnswE
|
|||||||
* Add a heap TID to an existing element
|
* Add a heap TID to an existing element
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
HnswAddDuplicate(Relation index, HnswElement element, HnswElement dup, bool building)
|
HnswAddDuplicate(Relation index, HnswElement element, HnswElement dup)
|
||||||
{
|
{
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
Page page;
|
Page page;
|
||||||
@@ -449,16 +412,8 @@ HnswAddDuplicate(Relation index, HnswElement element, HnswElement dup, bool buil
|
|||||||
/* Read page */
|
/* Read page */
|
||||||
buf = ReadBuffer(index, dup->blkno);
|
buf = ReadBuffer(index, dup->blkno);
|
||||||
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
||||||
if (building)
|
state = GenericXLogStart(index);
|
||||||
{
|
page = GenericXLogRegisterBuffer(state, buf, 0);
|
||||||
state = NULL;
|
|
||||||
page = BufferGetPage(buf);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state = GenericXLogStart(index);
|
|
||||||
page = GenericXLogRegisterBuffer(state, buf, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find space */
|
/* Find space */
|
||||||
itemid = PageGetItemId(page, dup->offno);
|
itemid = PageGetItemId(page, dup->offno);
|
||||||
@@ -473,8 +428,7 @@ HnswAddDuplicate(Relation index, HnswElement element, HnswElement dup, bool buil
|
|||||||
/* Either being deleted or we lost our chance to another backend */
|
/* Either being deleted or we lost our chance to another backend */
|
||||||
if (i == 0 || i == HNSW_HEAPTIDS)
|
if (i == 0 || i == HNSW_HEAPTIDS)
|
||||||
{
|
{
|
||||||
if (!building)
|
GenericXLogAbort(state);
|
||||||
GenericXLogAbort(state);
|
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -487,8 +441,7 @@ HnswAddDuplicate(Relation index, HnswElement element, HnswElement dup, bool buil
|
|||||||
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
|
elog(ERROR, "failed to add index item to \"%s\"", RelationGetRelationName(index));
|
||||||
|
|
||||||
/* Commit */
|
/* Commit */
|
||||||
if (!building)
|
GenericXLogFinish(state);
|
||||||
GenericXLogFinish(state);
|
|
||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -498,37 +451,37 @@ HnswAddDuplicate(Relation index, HnswElement element, HnswElement dup, bool buil
|
|||||||
* Write changes to disk
|
* Write changes to disk
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
WriteElement(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement element, int m, int efConstruction, HnswElement dup, HnswElement entryPoint, bool building)
|
WriteElement(Relation index, FmgrInfo *procinfo, Oid collation, HnswElement element, int m, int efConstruction, HnswElement dup, HnswElement entryPoint)
|
||||||
{
|
{
|
||||||
BlockNumber newInsertPage = InvalidBlockNumber;
|
BlockNumber newInsertPage = InvalidBlockNumber;
|
||||||
|
|
||||||
/* Try to add to existing page */
|
/* Try to add to existing page */
|
||||||
if (dup != NULL)
|
if (dup != NULL)
|
||||||
{
|
{
|
||||||
if (HnswAddDuplicate(index, element, dup, building))
|
if (HnswAddDuplicate(index, element, dup))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write element and neighbor tuples */
|
/* Write element and neighbor tuples */
|
||||||
WriteNewElementPages(index, element, m, GetInsertPage(index), &newInsertPage, building);
|
WriteNewElementPages(index, element, m, GetInsertPage(index), &newInsertPage);
|
||||||
|
|
||||||
/* Update insert page if needed */
|
/* Update insert page if needed */
|
||||||
if (BlockNumberIsValid(newInsertPage))
|
if (BlockNumberIsValid(newInsertPage))
|
||||||
HnswUpdateMetaPage(index, 0, NULL, newInsertPage, MAIN_FORKNUM, building);
|
HnswUpdateMetaPage(index, 0, NULL, newInsertPage, MAIN_FORKNUM);
|
||||||
|
|
||||||
/* Update neighbors */
|
/* Update neighbors */
|
||||||
HnswUpdateNeighborPages(index, procinfo, collation, element, m, false, building);
|
HnswUpdateNeighborPages(index, procinfo, collation, element, m, false);
|
||||||
|
|
||||||
/* Update metapage if needed */
|
/* Update metapage if needed */
|
||||||
if (entryPoint == NULL || element->level > entryPoint->level)
|
if (entryPoint == NULL || element->level > entryPoint->level)
|
||||||
HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_GREATER, element, InvalidBlockNumber, MAIN_FORKNUM, building);
|
HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_GREATER, element, InvalidBlockNumber, MAIN_FORKNUM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Insert a tuple into the index
|
* Insert a tuple into the index
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel, bool building)
|
HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid, Relation heapRel)
|
||||||
{
|
{
|
||||||
Datum value;
|
Datum value;
|
||||||
FmgrInfo *normprocinfo;
|
FmgrInfo *normprocinfo;
|
||||||
@@ -587,7 +540,7 @@ HnswInsertTuple(Relation index, Datum *values, bool *isnull, ItemPointer heap_ti
|
|||||||
dup = HnswFindDuplicate(element);
|
dup = HnswFindDuplicate(element);
|
||||||
|
|
||||||
/* Write to disk */
|
/* Write to disk */
|
||||||
WriteElement(index, procinfo, collation, element, m, efConstruction, dup, entryPoint, building);
|
WriteElement(index, procinfo, collation, element, m, efConstruction, dup, entryPoint);
|
||||||
|
|
||||||
/* Release lock */
|
/* Release lock */
|
||||||
UnlockPage(index, HNSW_UPDATE_LOCK, lockmode);
|
UnlockPage(index, HNSW_UPDATE_LOCK, lockmode);
|
||||||
@@ -621,7 +574,7 @@ hnswinsert(Relation index, Datum *values, bool *isnull, ItemPointer heap_tid,
|
|||||||
oldCtx = MemoryContextSwitchTo(insertCtx);
|
oldCtx = MemoryContextSwitchTo(insertCtx);
|
||||||
|
|
||||||
/* Insert tuple */
|
/* Insert tuple */
|
||||||
HnswInsertTuple(index, values, isnull, heap_tid, heap, false);
|
HnswInsertTuple(index, values, isnull, heap_tid, heap);
|
||||||
|
|
||||||
/* Delete memory context */
|
/* Delete memory context */
|
||||||
MemoryContextSwitchTo(oldCtx);
|
MemoryContextSwitchTo(oldCtx);
|
||||||
|
|||||||
159
src/hnswutils.c
159
src/hnswutils.c
@@ -7,89 +7,6 @@
|
|||||||
#include "utils/datum.h"
|
#include "utils/datum.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 130000
|
|
||||||
#include "common/hashfn.h"
|
|
||||||
#else
|
|
||||||
#include "utils/hashutils.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if PG_VERSION_NUM < 170000
|
|
||||||
static inline uint64
|
|
||||||
murmurhash64(uint64 data)
|
|
||||||
{
|
|
||||||
uint64 h = data;
|
|
||||||
|
|
||||||
h ^= h >> 33;
|
|
||||||
h *= 0xff51afd7ed558ccd;
|
|
||||||
h ^= h >> 33;
|
|
||||||
h *= 0xc4ceb9fe1a85ec53;
|
|
||||||
h ^= h >> 33;
|
|
||||||
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* TID hash table */
|
|
||||||
static uint32
|
|
||||||
hash_tid(ItemPointerData tid)
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
uint64 i;
|
|
||||||
ItemPointerData tid;
|
|
||||||
} x;
|
|
||||||
|
|
||||||
/* Initialize unused bytes */
|
|
||||||
x.i = 0;
|
|
||||||
x.tid = tid;
|
|
||||||
|
|
||||||
return murmurhash64(x.i);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SH_PREFIX tidhash
|
|
||||||
#define SH_ELEMENT_TYPE TidHashEntry
|
|
||||||
#define SH_KEY_TYPE ItemPointerData
|
|
||||||
#define SH_KEY tid
|
|
||||||
#define SH_HASH_KEY(tb, key) hash_tid(key)
|
|
||||||
#define SH_EQUAL(tb, a, b) ItemPointerEquals(&a, &b)
|
|
||||||
#define SH_SCOPE extern
|
|
||||||
#define SH_DEFINE
|
|
||||||
#include "lib/simplehash.h"
|
|
||||||
|
|
||||||
/* Needed to include simplehash.h twice */
|
|
||||||
#if PG_VERSION_NUM < 120000
|
|
||||||
#undef SH_EQUAL
|
|
||||||
#define sh_log2 pointerhash_sh_log2
|
|
||||||
#define sh_pow2 pointerhash_sh_pow2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Pointer hash table */
|
|
||||||
static uint32
|
|
||||||
hash_pointer(uintptr_t ptr)
|
|
||||||
{
|
|
||||||
#if SIZEOF_VOID_P == 8
|
|
||||||
return murmurhash64((uint64) ptr);
|
|
||||||
#else
|
|
||||||
return murmurhash32((uint32) ptr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SH_PREFIX pointerhash
|
|
||||||
#define SH_ELEMENT_TYPE PointerHashEntry
|
|
||||||
#define SH_KEY_TYPE uintptr_t
|
|
||||||
#define SH_KEY ptr
|
|
||||||
#define SH_HASH_KEY(tb, key) hash_pointer(key)
|
|
||||||
#define SH_EQUAL(tb, a, b) (a == b)
|
|
||||||
#define SH_SCOPE extern
|
|
||||||
#define SH_DEFINE
|
|
||||||
#include "lib/simplehash.h"
|
|
||||||
|
|
||||||
typedef union
|
|
||||||
{
|
|
||||||
pointerhash_hash *pointers;
|
|
||||||
tidhash_hash *tids;
|
|
||||||
} visited_hash;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the max number of connections in an upper layer for each element in the index
|
* Get the max number of connections in an upper layer for each element in the index
|
||||||
*/
|
*/
|
||||||
@@ -184,6 +101,27 @@ HnswInitPage(Buffer buf, Page page)
|
|||||||
HnswPageGetOpaque(page)->page_id = HNSW_PAGE_ID;
|
HnswPageGetOpaque(page)->page_id = HNSW_PAGE_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init and register page
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
HnswInitRegisterPage(Relation index, Buffer *buf, Page *page, GenericXLogState **state)
|
||||||
|
{
|
||||||
|
*state = GenericXLogStart(index);
|
||||||
|
*page = GenericXLogRegisterBuffer(*state, *buf, GENERIC_XLOG_FULL_IMAGE);
|
||||||
|
HnswInitPage(*buf, *page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Commit buffer
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
HnswCommitBuffer(Buffer buf, GenericXLogState *state)
|
||||||
|
{
|
||||||
|
GenericXLogFinish(state);
|
||||||
|
UnlockReleaseBuffer(buf);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate neighbors
|
* Allocate neighbors
|
||||||
*/
|
*/
|
||||||
@@ -358,7 +296,7 @@ HnswUpdateMetaPageInfo(Page page, int updateEntry, HnswElement entryPoint, Block
|
|||||||
* Update the metapage
|
* Update the metapage
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum, bool building)
|
HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, BlockNumber insertPage, ForkNumber forkNum)
|
||||||
{
|
{
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
Page page;
|
Page page;
|
||||||
@@ -366,22 +304,12 @@ HnswUpdateMetaPage(Relation index, int updateEntry, HnswElement entryPoint, Bloc
|
|||||||
|
|
||||||
buf = ReadBufferExtended(index, forkNum, HNSW_METAPAGE_BLKNO, RBM_NORMAL, NULL);
|
buf = ReadBufferExtended(index, forkNum, HNSW_METAPAGE_BLKNO, RBM_NORMAL, NULL);
|
||||||
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
||||||
if (building)
|
state = GenericXLogStart(index);
|
||||||
{
|
page = GenericXLogRegisterBuffer(state, buf, 0);
|
||||||
state = NULL;
|
|
||||||
page = BufferGetPage(buf);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state = GenericXLogStart(index);
|
|
||||||
page = GenericXLogRegisterBuffer(state, buf, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
HnswUpdateMetaPageInfo(page, updateEntry, entryPoint, insertPage);
|
HnswUpdateMetaPageInfo(page, updateEntry, entryPoint, insertPage);
|
||||||
|
|
||||||
if (!building)
|
HnswCommitBuffer(buf, state);
|
||||||
GenericXLogFinish(state);
|
|
||||||
UnlockReleaseBuffer(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -625,22 +553,16 @@ CreatePairingHeapNode(HnswCandidate * c)
|
|||||||
* Add to visited
|
* Add to visited
|
||||||
*/
|
*/
|
||||||
static inline void
|
static inline void
|
||||||
AddToVisited(visited_hash v, HnswCandidate * hc, Relation index, bool *found)
|
AddToVisited(HTAB *v, HnswCandidate * hc, Relation index, bool *found)
|
||||||
{
|
{
|
||||||
if (index == NULL)
|
if (index == NULL)
|
||||||
{
|
hash_search(v, &hc->element, HASH_ENTER, found);
|
||||||
#if PG_VERSION_NUM >= 130000
|
|
||||||
pointerhash_insert_hash(v.pointers, (uintptr_t) hc->element, hc->element->hash, found);
|
|
||||||
#else
|
|
||||||
pointerhash_insert(v.pointers, (uintptr_t) hc->element, found);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ItemPointerData indextid;
|
ItemPointerData indextid;
|
||||||
|
|
||||||
ItemPointerSet(&indextid, hc->element->blkno, hc->element->offno);
|
ItemPointerSet(&indextid, hc->element->blkno, hc->element->offno);
|
||||||
tidhash_insert(v.tids, indextid, found);
|
hash_search(v, &indextid, HASH_ENTER, found);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,21 +578,30 @@ HnswSearchLayer(Datum q, List *ep, int ef, int lc, Relation index, FmgrInfo *pro
|
|||||||
pairingheap *C = pairingheap_allocate(CompareNearestCandidates, NULL);
|
pairingheap *C = pairingheap_allocate(CompareNearestCandidates, NULL);
|
||||||
pairingheap *W = pairingheap_allocate(CompareFurthestCandidates, NULL);
|
pairingheap *W = pairingheap_allocate(CompareFurthestCandidates, NULL);
|
||||||
int wlen = 0;
|
int wlen = 0;
|
||||||
visited_hash v;
|
HASHCTL hash_ctl;
|
||||||
|
HTAB *v;
|
||||||
|
|
||||||
/* Create hash table */
|
/* Create hash table */
|
||||||
if (index == NULL)
|
if (index == NULL)
|
||||||
v.pointers = pointerhash_create(CurrentMemoryContext, ef * m * 2, NULL);
|
{
|
||||||
|
hash_ctl.keysize = sizeof(HnswElement *);
|
||||||
|
hash_ctl.entrysize = sizeof(HnswElement *);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
v.tids = tidhash_create(CurrentMemoryContext, ef * m * 2, NULL);
|
{
|
||||||
|
hash_ctl.keysize = sizeof(ItemPointerData);
|
||||||
|
hash_ctl.entrysize = sizeof(ItemPointerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_ctl.hcxt = CurrentMemoryContext;
|
||||||
|
v = hash_create("hnsw visited", 256, &hash_ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
|
||||||
|
|
||||||
/* Add entry points to v, C, and W */
|
/* Add entry points to v, C, and W */
|
||||||
foreach(lc2, ep)
|
foreach(lc2, ep)
|
||||||
{
|
{
|
||||||
HnswCandidate *hc = (HnswCandidate *) lfirst(lc2);
|
HnswCandidate *hc = (HnswCandidate *) lfirst(lc2);
|
||||||
bool found;
|
|
||||||
|
|
||||||
AddToVisited(v, hc, index, &found);
|
AddToVisited(v, hc, index, NULL);
|
||||||
|
|
||||||
pairingheap_add(C, &(CreatePairingHeapNode(hc)->ph_node));
|
pairingheap_add(C, &(CreatePairingHeapNode(hc)->ph_node));
|
||||||
pairingheap_add(W, &(CreatePairingHeapNode(hc)->ph_node));
|
pairingheap_add(W, &(CreatePairingHeapNode(hc)->ph_node));
|
||||||
@@ -1090,12 +1021,6 @@ HnswInsertElement(HnswElement element, HnswElement entryPoint, Relation index, F
|
|||||||
Datum q = element->value;
|
Datum q = element->value;
|
||||||
HnswElement skipElement = existing ? element : NULL;
|
HnswElement skipElement = existing ? element : NULL;
|
||||||
|
|
||||||
#if PG_VERSION_NUM >= 130000
|
|
||||||
/* Precompute hash */
|
|
||||||
if (index == NULL)
|
|
||||||
element->hash = hash_pointer((uintptr_t) element);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* No neighbors if no entry point */
|
/* No neighbors if no entry point */
|
||||||
if (entryPoint == NULL)
|
if (entryPoint == NULL)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -12,9 +12,12 @@
|
|||||||
* Check if deleted list contains an index TID
|
* Check if deleted list contains an index TID
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
DeletedContains(tidhash_hash * deleted, ItemPointer indextid)
|
DeletedContains(HTAB *deleted, ItemPointer indextid)
|
||||||
{
|
{
|
||||||
return tidhash_lookup(deleted, *indextid) != NULL;
|
bool found;
|
||||||
|
|
||||||
|
hash_search(deleted, indextid, HASH_FIND, &found);
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -107,13 +110,11 @@ RemoveHeapTids(HnswVacuumState * vacuumstate)
|
|||||||
if (!ItemPointerIsValid(&etup->heaptids[0]))
|
if (!ItemPointerIsValid(&etup->heaptids[0]))
|
||||||
{
|
{
|
||||||
ItemPointerData ip;
|
ItemPointerData ip;
|
||||||
bool found;
|
|
||||||
|
|
||||||
/* Add to deleted list */
|
/* Add to deleted list */
|
||||||
ItemPointerSet(&ip, blkno, offno);
|
ItemPointerSet(&ip, blkno, offno);
|
||||||
|
|
||||||
tidhash_insert(vacuumstate->deleted, ip, &found);
|
(void) hash_search(vacuumstate->deleted, &ip, HASH_ENTER, NULL);
|
||||||
Assert(!found);
|
|
||||||
}
|
}
|
||||||
else if (etup->level > highestLevel && !(entryPoint != NULL && blkno == entryPoint->blkno && offno == entryPoint->offno))
|
else if (etup->level > highestLevel && !(entryPoint != NULL && blkno == entryPoint->blkno && offno == entryPoint->offno))
|
||||||
{
|
{
|
||||||
@@ -230,7 +231,7 @@ RepairGraphElement(HnswVacuumState * vacuumstate, HnswElement element, HnswEleme
|
|||||||
UnlockReleaseBuffer(buf);
|
UnlockReleaseBuffer(buf);
|
||||||
|
|
||||||
/* Update neighbors */
|
/* Update neighbors */
|
||||||
HnswUpdateNeighborPages(index, procinfo, collation, element, m, true, false);
|
HnswUpdateNeighborPages(index, procinfo, collation, element, m, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -286,7 +287,7 @@ RepairGraphEntryPoint(HnswVacuumState * vacuumstate)
|
|||||||
* point is outdated and empty, the entry point will be empty
|
* point is outdated and empty, the entry point will be empty
|
||||||
* until an element is repaired.
|
* until an element is repaired.
|
||||||
*/
|
*/
|
||||||
HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_ALWAYS, highestPoint, InvalidBlockNumber, MAIN_FORKNUM, false);
|
HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_ALWAYS, highestPoint, InvalidBlockNumber, MAIN_FORKNUM);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -419,7 +420,7 @@ RepairGraph(HnswVacuumState * vacuumstate)
|
|||||||
* was replaced and highest point was outdated.
|
* was replaced and highest point was outdated.
|
||||||
*/
|
*/
|
||||||
if (entryPoint == NULL || element->level > entryPoint->level)
|
if (entryPoint == NULL || element->level > entryPoint->level)
|
||||||
HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_GREATER, element, InvalidBlockNumber, MAIN_FORKNUM, false);
|
HnswUpdateMetaPage(index, HNSW_UPDATE_ENTRY_GREATER, element, InvalidBlockNumber, MAIN_FORKNUM);
|
||||||
|
|
||||||
/* Release lock */
|
/* Release lock */
|
||||||
UnlockPage(index, HNSW_UPDATE_LOCK, lockmode);
|
UnlockPage(index, HNSW_UPDATE_LOCK, lockmode);
|
||||||
@@ -564,7 +565,7 @@ MarkDeleted(HnswVacuumState * vacuumstate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Update insert page last, after everything has been marked as deleted */
|
/* Update insert page last, after everything has been marked as deleted */
|
||||||
HnswUpdateMetaPage(index, 0, NULL, insertPage, MAIN_FORKNUM, false);
|
HnswUpdateMetaPage(index, 0, NULL, insertPage, MAIN_FORKNUM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -574,6 +575,7 @@ static void
|
|||||||
InitVacuumState(HnswVacuumState * vacuumstate, IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
|
InitVacuumState(HnswVacuumState * vacuumstate, IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
|
||||||
{
|
{
|
||||||
Relation index = info->index;
|
Relation index = info->index;
|
||||||
|
HASHCTL hash_ctl;
|
||||||
|
|
||||||
if (stats == NULL)
|
if (stats == NULL)
|
||||||
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
|
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
|
||||||
@@ -595,7 +597,10 @@ InitVacuumState(HnswVacuumState * vacuumstate, IndexVacuumInfo *info, IndexBulkD
|
|||||||
HnswGetMetaPageInfo(index, &vacuumstate->m, NULL);
|
HnswGetMetaPageInfo(index, &vacuumstate->m, NULL);
|
||||||
|
|
||||||
/* Create hash table */
|
/* Create hash table */
|
||||||
vacuumstate->deleted = tidhash_create(CurrentMemoryContext, 256, NULL);
|
hash_ctl.keysize = sizeof(ItemPointerData);
|
||||||
|
hash_ctl.entrysize = sizeof(ItemPointerData);
|
||||||
|
hash_ctl.hcxt = CurrentMemoryContext;
|
||||||
|
vacuumstate->deleted = hash_create("hnswbulkdelete indextids", 256, &hash_ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -604,7 +609,7 @@ InitVacuumState(HnswVacuumState * vacuumstate, IndexVacuumInfo *info, IndexBulkD
|
|||||||
static void
|
static void
|
||||||
FreeVacuumState(HnswVacuumState * vacuumstate)
|
FreeVacuumState(HnswVacuumState * vacuumstate)
|
||||||
{
|
{
|
||||||
tidhash_destroy(vacuumstate->deleted);
|
hash_destroy(vacuumstate->deleted);
|
||||||
FreeAccessStrategy(vacuumstate->bas);
|
FreeAccessStrategy(vacuumstate->bas);
|
||||||
pfree(vacuumstate->ntup);
|
pfree(vacuumstate->ntup);
|
||||||
MemoryContextDelete(vacuumstate->tmpCtx);
|
MemoryContextDelete(vacuumstate->tmpCtx);
|
||||||
|
|||||||
30
src/vector.c
30
src/vector.c
@@ -724,7 +724,7 @@ vector_spherical_distance(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the L1 distance between two vectors
|
* Get the L1 distance between vectors
|
||||||
*/
|
*/
|
||||||
PGDLLEXPORT PG_FUNCTION_INFO_V1(l1_distance);
|
PGDLLEXPORT PG_FUNCTION_INFO_V1(l1_distance);
|
||||||
Datum
|
Datum
|
||||||
@@ -903,8 +903,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_lt);
|
|||||||
Datum
|
Datum
|
||||||
vector_lt(PG_FUNCTION_ARGS)
|
vector_lt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
PG_RETURN_BOOL(vector_cmp_internal(a, b) < 0);
|
PG_RETURN_BOOL(vector_cmp_internal(a, b) < 0);
|
||||||
}
|
}
|
||||||
@@ -916,8 +916,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_le);
|
|||||||
Datum
|
Datum
|
||||||
vector_le(PG_FUNCTION_ARGS)
|
vector_le(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
PG_RETURN_BOOL(vector_cmp_internal(a, b) <= 0);
|
PG_RETURN_BOOL(vector_cmp_internal(a, b) <= 0);
|
||||||
}
|
}
|
||||||
@@ -929,8 +929,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_eq);
|
|||||||
Datum
|
Datum
|
||||||
vector_eq(PG_FUNCTION_ARGS)
|
vector_eq(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
PG_RETURN_BOOL(vector_cmp_internal(a, b) == 0);
|
PG_RETURN_BOOL(vector_cmp_internal(a, b) == 0);
|
||||||
}
|
}
|
||||||
@@ -942,8 +942,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ne);
|
|||||||
Datum
|
Datum
|
||||||
vector_ne(PG_FUNCTION_ARGS)
|
vector_ne(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
PG_RETURN_BOOL(vector_cmp_internal(a, b) != 0);
|
PG_RETURN_BOOL(vector_cmp_internal(a, b) != 0);
|
||||||
}
|
}
|
||||||
@@ -955,8 +955,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_ge);
|
|||||||
Datum
|
Datum
|
||||||
vector_ge(PG_FUNCTION_ARGS)
|
vector_ge(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
PG_RETURN_BOOL(vector_cmp_internal(a, b) >= 0);
|
PG_RETURN_BOOL(vector_cmp_internal(a, b) >= 0);
|
||||||
}
|
}
|
||||||
@@ -968,8 +968,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_gt);
|
|||||||
Datum
|
Datum
|
||||||
vector_gt(PG_FUNCTION_ARGS)
|
vector_gt(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
PG_RETURN_BOOL(vector_cmp_internal(a, b) > 0);
|
PG_RETURN_BOOL(vector_cmp_internal(a, b) > 0);
|
||||||
}
|
}
|
||||||
@@ -981,8 +981,8 @@ PGDLLEXPORT PG_FUNCTION_INFO_V1(vector_cmp);
|
|||||||
Datum
|
Datum
|
||||||
vector_cmp(PG_FUNCTION_ARGS)
|
vector_cmp(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Vector *a = PG_GETARG_VECTOR_P(0);
|
Vector *a = (Vector *) PG_GETARG_VECTOR_P(0);
|
||||||
Vector *b = PG_GETARG_VECTOR_P(1);
|
Vector *b = (Vector *) PG_GETARG_VECTOR_P(1);
|
||||||
|
|
||||||
PG_RETURN_INT32(vector_cmp_internal(a, b));
|
PG_RETURN_INT32(vector_cmp_internal(a, b));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3), val2 half[]);
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
INSERT INTO t (val, val2) VALUES ('[0,0,0]', '{0,0,0}'), ('[1,2,3]', '{1,2,3}'), ('[1,1,1]', '{1,1,1}'), (NULL, NULL);
|
||||||
CREATE TABLE t2 (val vector(3));
|
CREATE TABLE t2 (val vector(3), val2 half[]);
|
||||||
\copy t TO 'results/data.bin' WITH (FORMAT binary)
|
\copy t TO 'results/data.bin' WITH (FORMAT binary)
|
||||||
\copy t2 FROM 'results/data.bin' WITH (FORMAT binary)
|
\copy t2 FROM 'results/data.bin' WITH (FORMAT binary)
|
||||||
SELECT * FROM t2 ORDER BY val;
|
SELECT * FROM t2 ORDER BY val;
|
||||||
val
|
val | val2
|
||||||
---------
|
---------+---------
|
||||||
[0,0,0]
|
[0,0,0] | {0,0,0}
|
||||||
[1,1,1]
|
[1,1,1] | {1,1,1}
|
||||||
[1,2,3]
|
[1,2,3] | {1,2,3}
|
||||||
|
|
|
||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
DROP TABLE t;
|
DROP TABLE t;
|
||||||
|
|||||||
@@ -54,105 +54,105 @@ SELECT vector_norm('[3e37,4e37]')::real;
|
|||||||
5e+37
|
5e+37
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT l2_distance('[0,0]', '[3,4]');
|
SELECT l2_distance('[0,0]'::vector, '[3,4]');
|
||||||
l2_distance
|
l2_distance
|
||||||
-------------
|
-------------
|
||||||
5
|
5
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT l2_distance('[0,0]', '[0,1]');
|
SELECT l2_distance('[0,0]'::vector, '[0,1]');
|
||||||
l2_distance
|
l2_distance
|
||||||
-------------
|
-------------
|
||||||
1
|
1
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT l2_distance('[1,2]', '[3]');
|
SELECT l2_distance('[1,2]'::vector, '[3]');
|
||||||
ERROR: different vector dimensions 2 and 1
|
ERROR: different vector dimensions 2 and 1
|
||||||
SELECT l2_distance('[3e38]', '[-3e38]');
|
SELECT l2_distance('[3e38]'::vector, '[-3e38]');
|
||||||
l2_distance
|
l2_distance
|
||||||
-------------
|
-------------
|
||||||
Infinity
|
Infinity
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT inner_product('[1,2]', '[3,4]');
|
SELECT inner_product('[1,2]'::vector, '[3,4]');
|
||||||
inner_product
|
inner_product
|
||||||
---------------
|
---------------
|
||||||
11
|
11
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT inner_product('[1,2]', '[3]');
|
SELECT inner_product('[1,2]'::vector, '[3]');
|
||||||
ERROR: different vector dimensions 2 and 1
|
ERROR: different vector dimensions 2 and 1
|
||||||
SELECT inner_product('[3e38]', '[3e38]');
|
SELECT inner_product('[3e38]'::vector, '[3e38]');
|
||||||
inner_product
|
inner_product
|
||||||
---------------
|
---------------
|
||||||
Infinity
|
Infinity
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT cosine_distance('[1,2]', '[2,4]');
|
SELECT cosine_distance('[1,2]'::vector, '[2,4]');
|
||||||
cosine_distance
|
cosine_distance
|
||||||
-----------------
|
-----------------
|
||||||
0
|
0
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT cosine_distance('[1,2]', '[0,0]');
|
SELECT cosine_distance('[1,2]'::vector, '[0,0]');
|
||||||
cosine_distance
|
cosine_distance
|
||||||
-----------------
|
-----------------
|
||||||
NaN
|
NaN
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT cosine_distance('[1,1]', '[1,1]');
|
SELECT cosine_distance('[1,1]'::vector, '[1,1]');
|
||||||
cosine_distance
|
cosine_distance
|
||||||
-----------------
|
-----------------
|
||||||
0
|
0
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT cosine_distance('[1,0]', '[0,2]');
|
SELECT cosine_distance('[1,0]'::vector, '[0,2]');
|
||||||
cosine_distance
|
cosine_distance
|
||||||
-----------------
|
-----------------
|
||||||
1
|
1
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT cosine_distance('[1,1]', '[-1,-1]');
|
SELECT cosine_distance('[1,1]'::vector, '[-1,-1]');
|
||||||
cosine_distance
|
cosine_distance
|
||||||
-----------------
|
-----------------
|
||||||
2
|
2
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT cosine_distance('[1,2]', '[3]');
|
SELECT cosine_distance('[1,2]'::vector, '[3]');
|
||||||
ERROR: different vector dimensions 2 and 1
|
ERROR: different vector dimensions 2 and 1
|
||||||
SELECT cosine_distance('[1,1]', '[1.1,1.1]');
|
SELECT cosine_distance('[1,1]'::vector, '[1.1,1.1]');
|
||||||
cosine_distance
|
cosine_distance
|
||||||
-----------------
|
-----------------
|
||||||
0
|
0
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT cosine_distance('[1,1]', '[-1.1,-1.1]');
|
SELECT cosine_distance('[1,1]'::vector, '[-1.1,-1.1]');
|
||||||
cosine_distance
|
cosine_distance
|
||||||
-----------------
|
-----------------
|
||||||
2
|
2
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT cosine_distance('[3e38]', '[3e38]');
|
SELECT cosine_distance('[3e38]'::vector, '[3e38]');
|
||||||
cosine_distance
|
cosine_distance
|
||||||
-----------------
|
-----------------
|
||||||
NaN
|
NaN
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT l1_distance('[0,0]', '[3,4]');
|
SELECT l1_distance('[0,0]'::vector, '[3,4]');
|
||||||
l1_distance
|
l1_distance
|
||||||
-------------
|
-------------
|
||||||
7
|
7
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT l1_distance('[0,0]', '[0,1]');
|
SELECT l1_distance('[0,0]'::vector, '[0,1]');
|
||||||
l1_distance
|
l1_distance
|
||||||
-------------
|
-------------
|
||||||
1
|
1
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT l1_distance('[1,2]', '[3]');
|
SELECT l1_distance('[1,2]'::vector, '[3]');
|
||||||
ERROR: different vector dimensions 2 and 1
|
ERROR: different vector dimensions 2 and 1
|
||||||
SELECT l1_distance('[3e38]', '[-3e38]');
|
SELECT l1_distance('[3e38]'::vector, '[-3e38]');
|
||||||
l1_distance
|
l1_distance
|
||||||
-------------
|
-------------
|
||||||
Infinity
|
Infinity
|
||||||
|
|||||||
226
test/expected/half.out
Normal file
226
test/expected/half.out
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
SELECT '1.5'::half;
|
||||||
|
half
|
||||||
|
------
|
||||||
|
1.5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '65504'::half;
|
||||||
|
half
|
||||||
|
-------
|
||||||
|
65504
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '65505'::half;
|
||||||
|
ERROR: "65505" is out of range for type half
|
||||||
|
LINE 1: SELECT '65505'::half;
|
||||||
|
^
|
||||||
|
SELECT '-65504'::half;
|
||||||
|
half
|
||||||
|
--------
|
||||||
|
-65504
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '-65505'::half;
|
||||||
|
ERROR: "-65505" is out of range for type half
|
||||||
|
LINE 1: SELECT '-65505'::half;
|
||||||
|
^
|
||||||
|
SELECT ''::half;
|
||||||
|
ERROR: invalid input syntax for type half: ""
|
||||||
|
LINE 1: SELECT ''::half;
|
||||||
|
^
|
||||||
|
SELECT ' '::half;
|
||||||
|
ERROR: invalid input syntax for type half: " "
|
||||||
|
LINE 1: SELECT ' '::half;
|
||||||
|
^
|
||||||
|
SELECT '-'::half;
|
||||||
|
ERROR: invalid input syntax for type half: "-"
|
||||||
|
LINE 1: SELECT '-'::half;
|
||||||
|
^
|
||||||
|
SELECT ' 1.5'::half;
|
||||||
|
half
|
||||||
|
------
|
||||||
|
1.5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '1.5 '::half;
|
||||||
|
half
|
||||||
|
------
|
||||||
|
1.5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '1.5a'::half;
|
||||||
|
ERROR: invalid input syntax for type half: "1.5a"
|
||||||
|
LINE 1: SELECT '1.5a'::half;
|
||||||
|
^
|
||||||
|
SELECT '{1,2,3}'::half[];
|
||||||
|
half
|
||||||
|
---------
|
||||||
|
{1,2,3}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1,2,3}'::half[]::real[];
|
||||||
|
float4
|
||||||
|
---------
|
||||||
|
{1,2,3}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '65505'::integer::half;
|
||||||
|
half
|
||||||
|
-------
|
||||||
|
65504
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT 'NaN'::real::half;
|
||||||
|
half
|
||||||
|
------
|
||||||
|
NaN
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT 'Infinity'::real::half;
|
||||||
|
half
|
||||||
|
----------
|
||||||
|
Infinity
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '1e-38'::real::half;
|
||||||
|
ERROR: value out of range: underflow
|
||||||
|
SELECT '1.5'::half::real;
|
||||||
|
float4
|
||||||
|
--------
|
||||||
|
1.5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '1.5'::real::half;
|
||||||
|
half
|
||||||
|
------
|
||||||
|
1.5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '1.5'::half::double precision;
|
||||||
|
float8
|
||||||
|
--------
|
||||||
|
1.5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '1.5'::double precision::half;
|
||||||
|
half
|
||||||
|
------
|
||||||
|
1.5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '1.5'::half::numeric;
|
||||||
|
numeric
|
||||||
|
---------
|
||||||
|
1.5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '1.5'::numeric::half;
|
||||||
|
half
|
||||||
|
------
|
||||||
|
1.5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_distance('{0,0}'::half[], '{3,4}'::half[]);
|
||||||
|
l2_distance
|
||||||
|
-------------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_distance('{0,0}'::half[], '{0,1}'::half[]);
|
||||||
|
l2_distance
|
||||||
|
-------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l2_distance('{1,2}'::half[], '{3}'::half[]);
|
||||||
|
ERROR: different dimensions 2 and 1
|
||||||
|
SELECT '{0,0}'::half[] <-> '{3,4}'::half[];
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT inner_product('{1,2}'::half[], '{3,4}'::half[]);
|
||||||
|
inner_product
|
||||||
|
---------------
|
||||||
|
11
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT inner_product('{1,2}'::half[], '{3}'::half[]);
|
||||||
|
ERROR: different dimensions 2 and 1
|
||||||
|
SELECT inner_product('{65504}'::half[], '{65504}'::half[]);
|
||||||
|
inner_product
|
||||||
|
---------------
|
||||||
|
4290774016
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1,2}'::half[] <#> '{3,4}'::half[];
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
-11
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1,2}'::half[], '{2,4}'::half[]);
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1,2}'::half[], '{0,0}'::half[]);
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
NaN
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1,1}'::half[], '{1,1}'::half[]);
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1,0}'::half[], '{0,2}'::half[]);
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1,1}'::half[], '{-1,-1}'::half[]);
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1,2}'::half[], '{3}'::half[]);
|
||||||
|
ERROR: different dimensions 2 and 1
|
||||||
|
SELECT cosine_distance('{1,1}'::half[], '{1.1,1.1}'::half[]);
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1,1}'::half[], '{-1.1,-1.1}'::half[]);
|
||||||
|
cosine_distance
|
||||||
|
-----------------
|
||||||
|
2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT '{1,2}'::half[] <=> '{2,4}'::half[];
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l1_distance('{0,0}'::half[], '{3,4}');
|
||||||
|
l1_distance
|
||||||
|
-------------
|
||||||
|
7
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l1_distance('{0,0}'::half[], '{0,1}');
|
||||||
|
l1_distance
|
||||||
|
-------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT l1_distance('{1,2}'::half[], '{3}');
|
||||||
|
ERROR: different dimensions 2 and 1
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
CREATE TABLE t (val vector(3));
|
CREATE TABLE t (val vector(3), val2 half[]);
|
||||||
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
|
INSERT INTO t (val, val2) VALUES ('[0,0,0]', '{0,0,0}'), ('[1,2,3]', '{1,2,3}'), ('[1,1,1]', '{1,1,1}'), (NULL, NULL);
|
||||||
|
|
||||||
CREATE TABLE t2 (val vector(3));
|
CREATE TABLE t2 (val vector(3), val2 half[]);
|
||||||
|
|
||||||
\copy t TO 'results/data.bin' WITH (FORMAT binary)
|
\copy t TO 'results/data.bin' WITH (FORMAT binary)
|
||||||
\copy t2 FROM 'results/data.bin' WITH (FORMAT binary)
|
\copy t2 FROM 'results/data.bin' WITH (FORMAT binary)
|
||||||
|
|||||||
@@ -13,29 +13,29 @@ SELECT vector_norm('[3,4]');
|
|||||||
SELECT vector_norm('[0,1]');
|
SELECT vector_norm('[0,1]');
|
||||||
SELECT vector_norm('[3e37,4e37]')::real;
|
SELECT vector_norm('[3e37,4e37]')::real;
|
||||||
|
|
||||||
SELECT l2_distance('[0,0]', '[3,4]');
|
SELECT l2_distance('[0,0]'::vector, '[3,4]');
|
||||||
SELECT l2_distance('[0,0]', '[0,1]');
|
SELECT l2_distance('[0,0]'::vector, '[0,1]');
|
||||||
SELECT l2_distance('[1,2]', '[3]');
|
SELECT l2_distance('[1,2]'::vector, '[3]');
|
||||||
SELECT l2_distance('[3e38]', '[-3e38]');
|
SELECT l2_distance('[3e38]'::vector, '[-3e38]');
|
||||||
|
|
||||||
SELECT inner_product('[1,2]', '[3,4]');
|
SELECT inner_product('[1,2]'::vector, '[3,4]');
|
||||||
SELECT inner_product('[1,2]', '[3]');
|
SELECT inner_product('[1,2]'::vector, '[3]');
|
||||||
SELECT inner_product('[3e38]', '[3e38]');
|
SELECT inner_product('[3e38]'::vector, '[3e38]');
|
||||||
|
|
||||||
SELECT cosine_distance('[1,2]', '[2,4]');
|
SELECT cosine_distance('[1,2]'::vector, '[2,4]');
|
||||||
SELECT cosine_distance('[1,2]', '[0,0]');
|
SELECT cosine_distance('[1,2]'::vector, '[0,0]');
|
||||||
SELECT cosine_distance('[1,1]', '[1,1]');
|
SELECT cosine_distance('[1,1]'::vector, '[1,1]');
|
||||||
SELECT cosine_distance('[1,0]', '[0,2]');
|
SELECT cosine_distance('[1,0]'::vector, '[0,2]');
|
||||||
SELECT cosine_distance('[1,1]', '[-1,-1]');
|
SELECT cosine_distance('[1,1]'::vector, '[-1,-1]');
|
||||||
SELECT cosine_distance('[1,2]', '[3]');
|
SELECT cosine_distance('[1,2]'::vector, '[3]');
|
||||||
SELECT cosine_distance('[1,1]', '[1.1,1.1]');
|
SELECT cosine_distance('[1,1]'::vector, '[1.1,1.1]');
|
||||||
SELECT cosine_distance('[1,1]', '[-1.1,-1.1]');
|
SELECT cosine_distance('[1,1]'::vector, '[-1.1,-1.1]');
|
||||||
SELECT cosine_distance('[3e38]', '[3e38]');
|
SELECT cosine_distance('[3e38]'::vector, '[3e38]');
|
||||||
|
|
||||||
SELECT l1_distance('[0,0]', '[3,4]');
|
SELECT l1_distance('[0,0]'::vector, '[3,4]');
|
||||||
SELECT l1_distance('[0,0]', '[0,1]');
|
SELECT l1_distance('[0,0]'::vector, '[0,1]');
|
||||||
SELECT l1_distance('[1,2]', '[3]');
|
SELECT l1_distance('[1,2]'::vector, '[3]');
|
||||||
SELECT l1_distance('[3e38]', '[-3e38]');
|
SELECT l1_distance('[3e38]'::vector, '[-3e38]');
|
||||||
|
|
||||||
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::vector, '[3,5,7]']) v;
|
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::vector, '[3,5,7]']) v;
|
||||||
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::vector, '[3,5,7]', NULL]) v;
|
SELECT avg(v) FROM unnest(ARRAY['[1,2,3]'::vector, '[3,5,7]', NULL]) v;
|
||||||
|
|||||||
53
test/sql/half.sql
Normal file
53
test/sql/half.sql
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
SELECT '1.5'::half;
|
||||||
|
SELECT '65504'::half;
|
||||||
|
SELECT '65505'::half;
|
||||||
|
SELECT '-65504'::half;
|
||||||
|
SELECT '-65505'::half;
|
||||||
|
|
||||||
|
SELECT ''::half;
|
||||||
|
SELECT ' '::half;
|
||||||
|
SELECT '-'::half;
|
||||||
|
SELECT ' 1.5'::half;
|
||||||
|
SELECT '1.5 '::half;
|
||||||
|
SELECT '1.5a'::half;
|
||||||
|
|
||||||
|
SELECT '{1,2,3}'::half[];
|
||||||
|
SELECT '{1,2,3}'::half[]::real[];
|
||||||
|
|
||||||
|
SELECT '65505'::integer::half;
|
||||||
|
SELECT 'NaN'::real::half;
|
||||||
|
SELECT 'Infinity'::real::half;
|
||||||
|
SELECT '1e-38'::real::half;
|
||||||
|
|
||||||
|
SELECT '1.5'::half::real;
|
||||||
|
SELECT '1.5'::real::half;
|
||||||
|
|
||||||
|
SELECT '1.5'::half::double precision;
|
||||||
|
SELECT '1.5'::double precision::half;
|
||||||
|
|
||||||
|
SELECT '1.5'::half::numeric;
|
||||||
|
SELECT '1.5'::numeric::half;
|
||||||
|
|
||||||
|
SELECT l2_distance('{0,0}'::half[], '{3,4}'::half[]);
|
||||||
|
SELECT l2_distance('{0,0}'::half[], '{0,1}'::half[]);
|
||||||
|
SELECT l2_distance('{1,2}'::half[], '{3}'::half[]);
|
||||||
|
SELECT '{0,0}'::half[] <-> '{3,4}'::half[];
|
||||||
|
|
||||||
|
SELECT inner_product('{1,2}'::half[], '{3,4}'::half[]);
|
||||||
|
SELECT inner_product('{1,2}'::half[], '{3}'::half[]);
|
||||||
|
SELECT inner_product('{65504}'::half[], '{65504}'::half[]);
|
||||||
|
SELECT '{1,2}'::half[] <#> '{3,4}'::half[];
|
||||||
|
|
||||||
|
SELECT cosine_distance('{1,2}'::half[], '{2,4}'::half[]);
|
||||||
|
SELECT cosine_distance('{1,2}'::half[], '{0,0}'::half[]);
|
||||||
|
SELECT cosine_distance('{1,1}'::half[], '{1,1}'::half[]);
|
||||||
|
SELECT cosine_distance('{1,0}'::half[], '{0,2}'::half[]);
|
||||||
|
SELECT cosine_distance('{1,1}'::half[], '{-1,-1}'::half[]);
|
||||||
|
SELECT cosine_distance('{1,2}'::half[], '{3}'::half[]);
|
||||||
|
SELECT cosine_distance('{1,1}'::half[], '{1.1,1.1}'::half[]);
|
||||||
|
SELECT cosine_distance('{1,1}'::half[], '{-1.1,-1.1}'::half[]);
|
||||||
|
SELECT '{1,2}'::half[] <=> '{2,4}'::half[];
|
||||||
|
|
||||||
|
SELECT l1_distance('{0,0}'::half[], '{3,4}');
|
||||||
|
SELECT l1_distance('{0,0}'::half[], '{0,1}');
|
||||||
|
SELECT l1_distance('{1,2}'::half[], '{3}');
|
||||||
Reference in New Issue
Block a user