Commit 11da83a0 authored by Tom Lane's avatar Tom Lane

Add uuid to the set of types supported by contrib/btree_gist.

Paul Jungwirth, reviewed and hacked on by Teodor Sigaev, Ildus
Kurbangaliev, Adam Brusselback, Chris Bandy, and myself.

Discussion: https://postgr.es/m/CA+renyUEE29=X01JXdz8_TQvo6n9=2XoEBBRnQ8rkLyr+kjPxQ@mail.gmail.com
Discussion: https://postgr.es/m/55F6EE82.8080209@sigaev.ru
parent 721f7bd3
......@@ -6,16 +6,16 @@ OBJS = btree_gist.o btree_utils_num.o btree_utils_var.o btree_int2.o \
btree_int4.o btree_int8.o btree_float4.o btree_float8.o btree_cash.o \
btree_oid.o btree_ts.o btree_time.o btree_date.o btree_interval.o \
btree_macaddr.o btree_inet.o btree_text.o btree_bytea.o btree_bit.o \
btree_numeric.o $(WIN32RES)
btree_numeric.o btree_uuid.o $(WIN32RES)
EXTENSION = btree_gist
DATA = btree_gist--1.2.sql btree_gist--1.1--1.2.sql btree_gist--1.0--1.1.sql \
btree_gist--unpackaged--1.0.sql
DATA = btree_gist--unpackaged--1.0.sql btree_gist--1.0--1.1.sql \
btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql
PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes"
REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \
time timetz date interval macaddr inet cidr text varchar char bytea \
bit varbit numeric not_equal
bit varbit numeric uuid not_equal
SHLIB_LINK += $(filter -lm, $(LIBS))
......
/* contrib/btree_gist/btree_gist--1.2--1.3.sql */
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.3'" to load this file. \quit
-- Add support for indexing UUID columns
-- define the GiST support methods
CREATE FUNCTION gbt_uuid_consistent(internal,uuid,int2,oid,internal)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION gbt_uuid_fetch(internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION gbt_uuid_compress(internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION gbt_uuid_penalty(internal,internal,internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION gbt_uuid_picksplit(internal, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION gbt_uuid_union(internal, internal)
RETURNS gbtreekey32
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION gbt_uuid_same(gbtreekey32, gbtreekey32, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
-- Create the operator class
CREATE OPERATOR CLASS gist_uuid_ops
DEFAULT FOR TYPE uuid USING gist
AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 gbt_uuid_consistent (internal, uuid, int2, oid, internal),
FUNCTION 2 gbt_uuid_union (internal, internal),
FUNCTION 3 gbt_uuid_compress (internal),
FUNCTION 4 gbt_decompress (internal),
FUNCTION 5 gbt_uuid_penalty (internal, internal, internal),
FUNCTION 6 gbt_uuid_picksplit (internal, internal),
FUNCTION 7 gbt_uuid_same (gbtreekey32, gbtreekey32, internal),
STORAGE gbtreekey32;
-- These are "loose" in the opfamily for consistency with the rest of btree_gist
ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD
OPERATOR 6 <> (uuid, uuid) ,
FUNCTION 9 (uuid, uuid) gbt_uuid_fetch (internal) ;
# btree_gist extension
comment = 'support for indexing common datatypes in GiST'
default_version = '1.2'
default_version = '1.3'
module_pathname = '$libdir/btree_gist'
relocatable = true
......@@ -31,7 +31,8 @@ enum gbtree_type
gbt_t_bpchar,
gbt_t_bytea,
gbt_t_bit,
gbt_t_inet
gbt_t_inet,
gbt_t_uuid
};
#endif
/*
* contrib/btree_gist/btree_uuid.c
*/
#include "postgres.h"
#include "btree_gist.h"
#include "btree_utils_num.h"
#include "port/pg_bswap.h"
#include "utils/uuid.h"
typedef struct
{
pg_uuid_t lower,
upper;
} uuidKEY;
/*
* UUID ops
*/
PG_FUNCTION_INFO_V1(gbt_uuid_compress);
PG_FUNCTION_INFO_V1(gbt_uuid_fetch);
PG_FUNCTION_INFO_V1(gbt_uuid_union);
PG_FUNCTION_INFO_V1(gbt_uuid_picksplit);
PG_FUNCTION_INFO_V1(gbt_uuid_consistent);
PG_FUNCTION_INFO_V1(gbt_uuid_penalty);
PG_FUNCTION_INFO_V1(gbt_uuid_same);
static int
uuid_internal_cmp(const pg_uuid_t *arg1, const pg_uuid_t *arg2)
{
return memcmp(arg1->data, arg2->data, UUID_LEN);
}
static bool
gbt_uuidgt(const void *a, const void *b)
{
return uuid_internal_cmp((const pg_uuid_t *) a, (const pg_uuid_t *) b) > 0;
}
static bool
gbt_uuidge(const void *a, const void *b)
{
return uuid_internal_cmp((const pg_uuid_t *) a, (const pg_uuid_t *) b) >= 0;
}
static bool
gbt_uuideq(const void *a, const void *b)
{
return uuid_internal_cmp((const pg_uuid_t *) a, (const pg_uuid_t *) b) == 0;
}
static bool
gbt_uuidle(const void *a, const void *b)
{
return uuid_internal_cmp((const pg_uuid_t *) a, (const pg_uuid_t *) b) <= 0;
}
static bool
gbt_uuidlt(const void *a, const void *b)
{
return uuid_internal_cmp((const pg_uuid_t *) a, (const pg_uuid_t *) b) < 0;
}
static int
gbt_uuidkey_cmp(const void *a, const void *b)
{
uuidKEY *ia = (uuidKEY *) (((const Nsrt *) a)->t);
uuidKEY *ib = (uuidKEY *) (((const Nsrt *) b)->t);
int res;
res = uuid_internal_cmp(&ia->lower, &ib->lower);
if (res == 0)
res = uuid_internal_cmp(&ia->upper, &ib->upper);
return res;
}
static const gbtree_ninfo tinfo =
{
gbt_t_uuid,
UUID_LEN,
32, /* sizeof(gbtreekey32) */
gbt_uuidgt,
gbt_uuidge,
gbt_uuideq,
gbt_uuidle,
gbt_uuidlt,
gbt_uuidkey_cmp,
NULL
};
/**************************************************
* uuid ops
**************************************************/
Datum
gbt_uuid_compress(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
GISTENTRY *retval;
if (entry->leafkey)
{
char *r = (char *) palloc(2 * UUID_LEN);
pg_uuid_t *key = DatumGetUUIDP(entry->key);
retval = palloc(sizeof(GISTENTRY));
memcpy((void *) r, (void *) key, UUID_LEN);
memcpy((void *) (r + UUID_LEN), (void *) key, UUID_LEN);
gistentryinit(*retval, PointerGetDatum(r),
entry->rel, entry->page,
entry->offset, FALSE);
}
else
retval = entry;
PG_RETURN_POINTER(retval);
}
Datum
gbt_uuid_fetch(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
PG_RETURN_POINTER(gbt_num_fetch(entry, &tinfo));
}
Datum
gbt_uuid_consistent(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
pg_uuid_t *query = PG_GETARG_UUID_P(1);
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
uuidKEY *kkk = (uuidKEY *) DatumGetPointer(entry->key);
GBT_NUMKEY_R key;
/* All cases served by this function are exact */
*recheck = false;
key.lower = (GBT_NUMKEY *) &kkk->lower;
key.upper = (GBT_NUMKEY *) &kkk->upper;
PG_RETURN_BOOL(
gbt_num_consistent(&key, (void *) query, &strategy,
GIST_LEAF(entry), &tinfo)
);
}
Datum
gbt_uuid_union(PG_FUNCTION_ARGS)
{
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
void *out = palloc(sizeof(uuidKEY));
*(int *) PG_GETARG_POINTER(1) = sizeof(uuidKEY);
PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo));
}
/*
* Convert a uuid to a "double" value for estimating sizes of ranges.
*/
static double
uuid_2_double(const pg_uuid_t *u)
{
uint64 uu[2];
const double two64 = 18446744073709551616.0; /* 2^64 */
/* Source data may not be suitably aligned, so copy */
memcpy(uu, u->data, UUID_LEN);
/*
* uuid values should be considered as big-endian numbers, since that
* corresponds to how memcmp will compare them. On a little-endian
* machine, byte-swap each half so we can use native uint64 arithmetic.
*/
#ifndef WORDS_BIGENDIAN
uu[0] = BSWAP64(uu[0]);
uu[1] = BSWAP64(uu[1]);
#endif
/*
* 2^128 is about 3.4e38, which in theory could exceed the range of
* "double" (POSIX only requires 1e37). To avoid any risk of overflow,
* put the decimal point between the two halves rather than treating the
* uuid value as a 128-bit integer.
*/
return (double) uu[0] + (double) uu[1] / two64;
}
Datum
gbt_uuid_penalty(PG_FUNCTION_ARGS)
{
uuidKEY *origentry = (uuidKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
uuidKEY *newentry = (uuidKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
float *result = (float *) PG_GETARG_POINTER(2);
double olower,
oupper,
nlower,
nupper;
olower = uuid_2_double(&origentry->lower);
oupper = uuid_2_double(&origentry->upper);
nlower = uuid_2_double(&newentry->lower);
nupper = uuid_2_double(&newentry->upper);
penalty_num(result, olower, oupper, nlower, nupper);
PG_RETURN_POINTER(result);
}
Datum
gbt_uuid_picksplit(PG_FUNCTION_ARGS)
{
PG_RETURN_POINTER(gbt_num_picksplit(
(GistEntryVector *) PG_GETARG_POINTER(0),
(GIST_SPLITVEC *) PG_GETARG_POINTER(1),
&tinfo
));
}
Datum
gbt_uuid_same(PG_FUNCTION_ARGS)
{
uuidKEY *b1 = (uuidKEY *) PG_GETARG_POINTER(0);
uuidKEY *b2 = (uuidKEY *) PG_GETARG_POINTER(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
*result = gbt_num_same((void *) b1, (void *) b2, &tinfo);
PG_RETURN_POINTER(result);
}
This diff is collapsed.
-- uuid check
CREATE TABLE uuidtmp (a uuid);
\copy uuidtmp from 'data/uuid.data'
SET enable_seqscan=on;
SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203';
count
-------
227
(1 row)
SELECT count(*) FROM uuidtmp WHERE a <= '55e65ca2-4136-4a4b-ba78-cd3fe4678203';
count
-------
228
(1 row)
SELECT count(*) FROM uuidtmp WHERE a = '55e65ca2-4136-4a4b-ba78-cd3fe4678203';
count
-------
1
(1 row)
SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203';
count
-------
376
(1 row)
SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203';
count
-------
375
(1 row)
CREATE INDEX uuididx ON uuidtmp USING gist ( a );
SET enable_seqscan=off;
SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid;
count
-------
227
(1 row)
SELECT count(*) FROM uuidtmp WHERE a <= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid;
count
-------
228
(1 row)
SELECT count(*) FROM uuidtmp WHERE a = '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid;
count
-------
1
(1 row)
SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid;
count
-------
376
(1 row)
SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid;
count
-------
375
(1 row)
-- uuid check
CREATE TABLE uuidtmp (a uuid);
\copy uuidtmp from 'data/uuid.data'
SET enable_seqscan=on;
SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203';
SELECT count(*) FROM uuidtmp WHERE a <= '55e65ca2-4136-4a4b-ba78-cd3fe4678203';
SELECT count(*) FROM uuidtmp WHERE a = '55e65ca2-4136-4a4b-ba78-cd3fe4678203';
SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203';
SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203';
CREATE INDEX uuididx ON uuidtmp USING gist ( a );
SET enable_seqscan=off;
SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid;
SELECT count(*) FROM uuidtmp WHERE a <= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid;
SELECT count(*) FROM uuidtmp WHERE a = '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid;
SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid;
SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid;
......@@ -16,7 +16,8 @@
<type>time without time zone</>, <type>date</>, <type>interval</>,
<type>oid</>, <type>money</>, <type>char</>,
<type>varchar</>, <type>text</>, <type>bytea</>, <type>bit</>,
<type>varbit</>, <type>macaddr</>, <type>inet</>, and <type>cidr</>.
<type>varbit</>, <type>macaddr</>, <type>inet</>, <type>cidr</>,
and <type>uuid</>.
</para>
<para>
......@@ -99,8 +100,9 @@ INSERT 0 1
<para>
Teodor Sigaev (<email>teodor@stack.net</email>),
Oleg Bartunov (<email>oleg@sai.msu.su</email>), and
Janko Richter (<email>jankorichter@yahoo.de</email>). See
Oleg Bartunov (<email>oleg@sai.msu.su</email>),
Janko Richter (<email>jankorichter@yahoo.de</email>), and
Paul Jungwirth (<email>pj@illuminatedcomputing.com</email>). See
<ulink url="http://www.sai.msu.su/~megera/postgres/gist/"></ulink>
for additional information.
</para>
......
......@@ -22,15 +22,6 @@
#include "utils/sortsupport.h"
#include "utils/uuid.h"
/* uuid size in bytes */
#define UUID_LEN 16
/* pg_uuid_t is declared to be struct pg_uuid_t in uuid.h */
struct pg_uuid_t
{
unsigned char data[UUID_LEN];
};
/* sortsupport for uuid */
typedef struct
{
......
......@@ -14,11 +14,13 @@
#ifndef UUID_H
#define UUID_H
/* guid size in bytes */
/* uuid size in bytes */
#define UUID_LEN 16
/* opaque struct; defined in uuid.c */
typedef struct pg_uuid_t pg_uuid_t;
typedef struct pg_uuid_t
{
unsigned char data[UUID_LEN];
} pg_uuid_t;
/* fmgr interface macros */
#define UUIDPGetDatum(X) PointerGetDatum(X)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment