Cube extension kNN support
Hello, hackers.
Here is the patch that introduces kNN search for cubes with euclidean, taxicab and chebyshev distances.
Following distance operators introduced:
<#> taxicab distance
<-> euclidean distance
<=> chebyshev distance
For example:
SELECT * FROM objects ORDER BY objects.coord <-> '(137,42,314)'::cube LIMIT 10;
Also there is operator "->" for selecting ordered rows directly from index.
This request selects rows ordered ascending by 3rd coordinate:
SELECT * FROM objects ORDER BY objects.coord->3 LIMIT 10;
For descendent ordering suggested syntax with minus before coordinate.
This request selects rows ordered descending by 4th coordinate:
SELECT * FROM objects ORDER BY objects.coord->-4 LIMIT 10;
Stas Kelvich.
Attachments:
distances.patchapplication/octet-stream; name=distances.patchDownload
diff --git a/contrib/cube/cube--1.0.sql b/contrib/cube/cube--1.0.sql
index 0307811..9c821de 100644
--- a/contrib/cube/cube--1.0.sql
+++ b/contrib/cube/cube--1.0.sql
@@ -135,7 +135,17 @@ LANGUAGE C IMMUTABLE STRICT;
-- proximity routines
-CREATE FUNCTION cube_distance(cube, cube)
+CREATE FUNCTION distance_taxicab(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION distance_euclid(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION distance_chebyshev(cube, cube)
RETURNS float8
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
@@ -157,6 +167,11 @@ RETURNS float8
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
+CREATE FUNCTION cube_coord(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
CREATE FUNCTION cube(float8) RETURNS cube
AS 'MODULE_PATHNAME', 'cube_f8'
LANGUAGE C IMMUTABLE STRICT;
@@ -246,6 +261,25 @@ CREATE OPERATOR <@ (
RESTRICT = contsel, JOIN = contjoinsel
);
+CREATE OPERATOR -> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+);
+
+CREATE OPERATOR <#> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ COMMUTATOR = '<#>'
+);
+
+CREATE OPERATOR <-> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_euclid,
+ COMMUTATOR = '<->'
+);
+
+CREATE OPERATOR <=> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ COMMUTATOR = '<=>'
+);
+
-- these are obsolete/deprecated:
CREATE OPERATOR @ (
LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
@@ -296,6 +330,10 @@ RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
+CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
-- Create the operator classes for indexing
@@ -316,10 +354,17 @@ CREATE OPERATOR CLASS gist_cube_ops
OPERATOR 8 <@ ,
OPERATOR 13 @ ,
OPERATOR 14 ~ ,
+ OPERATOR 15 -> (cube, int) FOR ORDER BY float_ops,
+ OPERATOR 16 <#> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 17 <-> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 18 <=> (cube, cube) FOR ORDER BY float_ops,
+
FUNCTION 1 g_cube_consistent (internal, cube, int, oid, internal),
FUNCTION 2 g_cube_union (internal, internal),
FUNCTION 3 g_cube_compress (internal),
FUNCTION 4 g_cube_decompress (internal),
FUNCTION 5 g_cube_penalty (internal, internal, internal),
FUNCTION 6 g_cube_picksplit (internal, internal),
- FUNCTION 7 g_cube_same (cube, cube, internal);
+ FUNCTION 7 g_cube_same (cube, cube, internal),
+ FUNCTION 8 g_cube_distance (internal, cube, smallint, oid);
+
diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c
index dab0e6e..0702fac 100644
--- a/contrib/cube/cube.c
+++ b/contrib/cube/cube.c
@@ -45,6 +45,7 @@ PG_FUNCTION_INFO_V1(cube_c_f8_f8);
PG_FUNCTION_INFO_V1(cube_dim);
PG_FUNCTION_INFO_V1(cube_ll_coord);
PG_FUNCTION_INFO_V1(cube_ur_coord);
+PG_FUNCTION_INFO_V1(cube_coord);
PG_FUNCTION_INFO_V1(cube_subset);
Datum cube_in(PG_FUNCTION_ARGS);
@@ -58,6 +59,7 @@ Datum cube_c_f8_f8(PG_FUNCTION_ARGS);
Datum cube_dim(PG_FUNCTION_ARGS);
Datum cube_ll_coord(PG_FUNCTION_ARGS);
Datum cube_ur_coord(PG_FUNCTION_ARGS);
+Datum cube_coord(PG_FUNCTION_ARGS);
Datum cube_subset(PG_FUNCTION_ARGS);
/*
@@ -71,6 +73,7 @@ PG_FUNCTION_INFO_V1(g_cube_penalty);
PG_FUNCTION_INFO_V1(g_cube_picksplit);
PG_FUNCTION_INFO_V1(g_cube_union);
PG_FUNCTION_INFO_V1(g_cube_same);
+PG_FUNCTION_INFO_V1(g_cube_distance);
Datum g_cube_consistent(PG_FUNCTION_ARGS);
Datum g_cube_compress(PG_FUNCTION_ARGS);
@@ -79,6 +82,7 @@ Datum g_cube_penalty(PG_FUNCTION_ARGS);
Datum g_cube_picksplit(PG_FUNCTION_ARGS);
Datum g_cube_union(PG_FUNCTION_ARGS);
Datum g_cube_same(PG_FUNCTION_ARGS);
+Datum g_cube_distance(PG_FUNCTION_ARGS);
/*
** B-tree support functions
@@ -120,11 +124,15 @@ Datum cube_size(PG_FUNCTION_ARGS);
/*
** miscellaneous
*/
-PG_FUNCTION_INFO_V1(cube_distance);
+PG_FUNCTION_INFO_V1(distance_taxicab);
+PG_FUNCTION_INFO_V1(distance_euclid);
+PG_FUNCTION_INFO_V1(distance_chebyshev);
PG_FUNCTION_INFO_V1(cube_is_point);
PG_FUNCTION_INFO_V1(cube_enlarge);
-Datum cube_distance(PG_FUNCTION_ARGS);
+Datum distance_taxicab(PG_FUNCTION_ARGS);
+Datum distance_euclid(PG_FUNCTION_ARGS);
+Datum distance_chebyshev(PG_FUNCTION_ARGS);
Datum cube_is_point(PG_FUNCTION_ARGS);
Datum cube_enlarge(PG_FUNCTION_ARGS);
@@ -1219,6 +1227,61 @@ cube_overlap(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(res);
}
+static double
+distance_1D(double a1, double a2, double b1, double b2)
+{
+ /* interval (a) is entirely on the left of (b) */
+ if ((a1 <= b1) && (a2 <= b1) && (a1 <= b2) && (a2 <= b2))
+ return (Min(b1, b2) - Max(a1, a2));
+
+ /* interval (a) is entirely on the right of (b) */
+ if ((a1 > b1) && (a2 > b1) && (a1 > b2) && (a2 > b2))
+ return (Min(a1, a2) - Max(b1, b2));
+
+ /* the rest are all sorts of intersections */
+ return (0.0);
+}
+
+Datum
+distance_taxicab(PG_FUNCTION_ARGS)
+{
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (a->dim < b->dim)
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < b->dim; i++)
+ distance += abs(distance_1D(a->x[i], a->x[i + a->dim], b->x[i], b->x[i + b->dim]));
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = b->dim; i < a->dim; i++)
+ distance += abs(distance_1D(a->x[i], a->x[i + a->dim], 0.0, 0.0));
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+}
/* Distance */
/* The distance is computed as a per axis sum of the squared distances
@@ -1226,7 +1289,7 @@ cube_overlap(PG_FUNCTION_ARGS)
distance between overlapping projections, this metric coincides with the
"common sense" geometric distance */
Datum
-cube_distance(PG_FUNCTION_ARGS)
+distance_euclid(PG_FUNCTION_ARGS)
{
NDBOX *a = PG_GETARG_NDBOX(0),
*b = PG_GETARG_NDBOX(1);
@@ -1239,7 +1302,6 @@ cube_distance(PG_FUNCTION_ARGS)
if (a->dim < b->dim)
{
NDBOX *tmp = b;
-
b = a;
a = tmp;
swapped = true;
@@ -1250,16 +1312,18 @@ cube_distance(PG_FUNCTION_ARGS)
for (i = 0; i < b->dim; i++)
{
d = distance_1D(a->x[i], a->x[i + a->dim], b->x[i], b->x[i + b->dim]);
- distance += d * d;
+ distance += d*d;
}
/* compute distance to zero for those dimensions in (a) absent in (b) */
for (i = b->dim; i < a->dim; i++)
{
d = distance_1D(a->x[i], a->x[i + a->dim], 0.0, 0.0);
- distance += d * d;
+ distance += d*d;
}
+ distance = sqrt(distance);
+
if (swapped)
{
PG_FREE_IF_COPY(b, 0);
@@ -1271,22 +1335,96 @@ cube_distance(PG_FUNCTION_ARGS)
PG_FREE_IF_COPY(b, 1);
}
- PG_RETURN_FLOAT8(sqrt(distance));
+ PG_RETURN_FLOAT8(distance);
}
-static double
-distance_1D(double a1, double a2, double b1, double b2)
+/* Distance */
+/* The distance is computed as a per axis sum of the squared distances
+ between 1D projections of the boxes onto Cartesian axes. Assuming zero
+ distance between overlapping projections, this metric coincides with the
+ "common sense" geometric distance */
+Datum
+distance_chebyshev(PG_FUNCTION_ARGS)
{
- /* interval (a) is entirely on the left of (b) */
- if ((a1 <= b1) && (a2 <= b1) && (a1 <= b2) && (a2 <= b2))
- return (Min(b1, b2) - Max(a1, a2));
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double d, distance;
+ int i;
- /* interval (a) is entirely on the right of (b) */
- if ((a1 > b1) && (a2 > b1) && (a1 > b2) && (a2 > b2))
- return (Min(a1, a2) - Max(b1, b2));
+ /* swap the box pointers if needed */
+ if (a->dim < b->dim)
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
- /* the rest are all sorts of intersections */
- return (0.0);
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < b->dim; i++)
+ {
+ d = abs(distance_1D(a->x[i], a->x[i + a->dim], b->x[i], b->x[i + b->dim]));
+ if (d > distance)
+ distance = d;
+ }
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = b->dim; i < a->dim; i++)
+ {
+ d = abs(distance_1D(a->x[i], a->x[i + a->dim], 0.0, 0.0));
+ if (d > distance)
+ distance = d;
+ }
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+}
+
+Datum
+g_cube_distance(PG_FUNCTION_ARGS)
+{
+ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+ NDBOX *cube = DatumGetNDBOX(entry->key);
+ double retval;
+
+ if (strategy == 15)
+ {
+ int coord = PG_GETARG_INT32(1);
+ if(coord > 0)
+ retval = cube->x[coord-1];
+ else
+ retval = -cube->x[-coord-1+cube->dim];
+ }
+ else
+ {
+ NDBOX *query = PG_GETARG_NDBOX(1);
+ switch(strategy)
+ {
+ case 16:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_taxicab, PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case 17:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_euclid, PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case 18:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_chebyshev, PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ }
+ }
+ PG_RETURN_FLOAT8(retval);
}
/* Test if a box is also a point */
@@ -1352,6 +1490,22 @@ cube_ur_coord(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+Datum
+cube_coord(PG_FUNCTION_ARGS)
+{
+ NDBOX *cube = PG_GETARG_NDBOX(0);
+ int coord = PG_GETARG_INT16(1);
+
+ if ((-2*cube->dim <= coord) && (coord < 0))
+ PG_RETURN_FLOAT8(-cube->x[-coord - 1]);
+ else if ((0 < coord) && (coord <= 2*cube->dim))
+ PG_RETURN_FLOAT8(cube->x[coord - 1]);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ errmsg("Index out of bounds")));
+}
+
/* Increase or decrease box size by a radius in at least n dimensions. */
Datum
cube_enlarge(PG_FUNCTION_ARGS)
Do you have any benchmarks ?
On Mon, Sep 23, 2013 at 3:38 AM, Stas Kelvich <stas.kelvich@gmail.com>wrote:
Show quoted text
Hello, hackers.
Here is the patch that introduces kNN search for cubes with euclidean,
taxicab and chebyshev distances.Following distance operators introduced:
<#> taxicab distance
<-> euclidean distance
<=> chebyshev distanceFor example:
SELECT * FROM objects ORDER BY objects.coord <-> '(137,42,314)'::cube
LIMIT 10;Also there is operator "->" for selecting ordered rows directly from index.
This request selects rows ordered ascending by 3rd coordinate:SELECT * FROM objects ORDER BY objects.coord->3 LIMIT 10;
For descendent ordering suggested syntax with minus before coordinate.
This request selects rows ordered descending by 4th coordinate:SELECT * FROM objects ORDER BY objects.coord->-4 LIMIT 10;
Stas Kelvich.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 9/22/13 7:38 PM, Stas Kelvich wrote:
Here is the patch that introduces kNN search for cubes with euclidean, taxicab and chebyshev distances.
cube and earthdistance regression tests fail.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 9/22/13 4:38 PM, Stas Kelvich wrote:
Hello, hackers.
Here is the patch that introduces kNN search for cubes with
euclidean, taxicab and chebyshev distances.
Thanks for this! I decided to give the patch a try at the bleeding edge
with some high-dimensional vectors, specifically the 1.4 million
1000-dimensional Freebase entity vectors from the Google 'word2vec' project:
https://code.google.com/p/word2vec/#Pre-trained_entity_vectors_with_Freebase_naming
Unfortunately, here's what I found:
* with 1000-dimensional vectors, the index size on disk grows to many
times (1000x or more) the size of the indexed data, making other tests
of the index's helpfulness impractical. (Perhaps, other parameter-tuning
can help?)
* with 500-dimensional or 100-dimensional vectors, the index size is
more manageable -- 2x to 6x the data size -- but using the index
significantly hurts performance on top-10 nearest-neighbor searches,
making it much slower than a full table scan. (The planner still tries
to use the index, even though it's hurting.)
Much more detail for the curious is below...
Regarding the dataset & hoped-for results:
The word2vec project's example scripts do their synonym/analogy
demonstrations by loading the entire 5GB+ dataset into main memory
(~3min), do a full scan of all vectors (~40sec) to find those nearest a
target vector.
My motivating question was: could the data be loaded to Postgresql using
the cube type, and kNN-GIST indexed using this patch, then do speedy
index-assisted ranked-nearest-neighbor queries from the database?
(While the patch's distance_euclid is not the same cosine-distance the
word2vec examples use, the freebase entity vectors are all unit vectors,
and even additions of vectors can be scaled to unit length. My intuition
is that euclidean-distances on the unit sphere will be in the same rank
order as cosine-distance, so the cube distance_euclid/<-> should enable
the same sort of synonym/analogy demos.)
Regarding the 1000-dimensional case:
It was necessary to change three compiled-in implementation limits. In
the four steps that the need for change became evident:
(1) in contrib/cube/cubedata.h, increase CUBE_MAX_DIM (to accept vectors
with more than 100 dimensions)
(2) in src/include/pg_config.h, increase BLCKSZ to 16384 (otherwise 1000
64-bit floats in a single column gave an <ERROR: row is too big>,
mentioning the 8160 limit - large cube values not TOASTable?)
(3) in src/include/access/itup.h, increase INDEX_SIZE_MASK to 0x3fff
(otherwise encountering an <ERROR: index row requires 16016 bytes,
maximum size is 8191> when attempting to create the index>
(4) in src/include/pg_config.h, again increase BLCKSZ now to 32768
(otherwise encountering an <ERROR: index row size 16416 exceeds maximum
5440 for index "pg_class_relname_nsp_index"> when attempting to create
the index>
With the cube-kNN patch applied and these other changes, I was able to
import the 1.4M freebase vectors and do a full-scan nearest-neighbors
query. (My starting postgresql codebase was the github mirror of 9.4dev
as of about a week ago.)
The psql transcript:
word2vec=# \timing
Timing is on.
word2vec=# CREATE EXTENSION cube;
CREATE EXTENSION
Time: 42.141 ms
word2vec=# CREATE TABLE word2vec ( word varchar(200), vec cube );
CREATE TABLE
Time: 8.532 ms
word2vec=# COPY word2vec FROM PROGRAM 'zcat
/tmp/pgvectors/freebase-vectors-skipgram1000.pgtxt.gz';
COPY 1422903
Time: 12399065.498 ms
word2vec=# SELECT word, dist FROM (SELECT word,
distance_euclid(vec,(SELECT vec FROM word2vec WHERE word='geoffrey
hinton')) AS dist FROM word2vec) AS subquery ORDER BY dist LIMIT 11;
word | dist
-------------------------+------------------
geoffrey hinton | 0
marvin minsky | 1.03892498287268
paul corkum | 1.05221701690288
william richard peltier | 1.06244397334495
brenda milner | 1.06387762685894
john charles polanyi | 1.07444446452295
leslie valiant | 1.07735786596934
hava siegelmann | 1.08148623006629
hans moravec | 1.0862034591185
david rumelhart | 1.08748431130477
godel prize | 1.08774264379264
(11 rows)
Time: 310753.976 ms
That's 3.5 hours to do the import and 5 minutes to do the query; this is
on a 2009 MacBook Pro with 8GB RAM and SSD.
Confirming the intuition above, these 10-nearest are the same entities
in the same order as on the word2vec project page's example output,
though the euclidean distances are of course different than the cosine
distances.
The DATA directory is 23GB after the import of 1.4 million rows. In the
word2vec uncompressed binary format, this dataset is about 5.4GB, so
this word2vec-table cube-column representation involves about 4X expansion.
So, to the main question: can that query be sped up by building a
kNN-GIST index? Here the problems start.
word2vec=# CREATE INDEX word2vec_index ON word2vec USING gist (vec);
This attempt ran for hours, consuming another 80GB+ before failing due
to disk full.
In fact, the largest table with 1000-dimensional vectors for which I was
able to build a gist index was a mere 100 rows. That index-build took
about 14 minutes and grew the DATA directory like so:
73MB pgdata # empty word2vec table
75MB pgdata # after COPY FROM of 100 vectors
8.4GB pgdata # after CREATE INDEX
Of course with just 100 rows the index isn't practically needed or
helpful for query speed-up.
Even trying just 500 rows, the CREATE INDEX command ran for hours before
failing by consuming all available disk space (about 90GB more). It
seems the knn-GIST index overhead for 1000-dimensional cubes grows
faster than linearly in the number of rows.
I was able to complete index builds with fewer dimensions.
100-dimensions, most-frequent 850K entities:
The dataset was trimmed to the first (most-frequent) 850K entities and
each vector truncated to its first 100-dimensions. (Even though this
would fit in the usual CUBE_MAX_DIM/BLCKSZ/INDEX_SIZE_MASK limits, I
still used the customized build with limits expanded for the 1000d-case.)
Observed data sizes and operation times were:
73MB pgdata # empty word2vec table
2.6GB pgdata # after COPY FROM of 850K vectors, taking 216s
before indexing:
nearest-11 by distance_euclid(): ~2s
nearest-11 by <-> operator: ~2s
5.0GB pgdata # after CREATE INDEX, taking 1344s
after indexing:
nearest-11 by distance_euclid(): ~2s
nearest-11 by <-> operator: ~57s # "Index Scan using word2vec_index�"
So the availability of the index causes a significant slowdown... and
the planner does not learn to choose the faster full sequential-scan.
500-dimensions, most-frequent 100K entities:
The dataset was trimmed to the first (most-frequent) 100K entities and
each vector truncated to its first 500-dimensions. (Again, still using
the custom build with upped CUBE_MAX_DIM/BLCKSZ/INDEX_SIZE_MASK.)
Observed data sizes and operation times were:
73MB pgdata # empty word2vec table
1.6GB pgdata # after COPY FROM of 100K vectors, taking 266s
before indexing:
nearest-11 by distance_euclid(): ~2s
nearest-11 by <-> operator: ~2s
4.8GB pgdata # after CREATE INDEX, taking 977s
after indexing:
nearest-11 by distance_euclid(): ~2s
nearest-11 by <-> operator: ~46s # "Index Scan using word2vec_index�"
Dropping the index makes the <-> query fast again.
Open questions and tentative conclusions:
The massive knn-GIST index overhead for 1000-dimensional vectors makes
it hard to evaluate whether a fully-built index could be useful on large
datasets.
Perhaps, some aspects of the knn-GIST support implicitly assume a
low-dimensionality (2-4) in the data, and large numbers of dimensions
cause pathological index sizes?
Or, something specific about this dataset (all vectors on the unit
sphere) is a challenging case?
In the truncated 100d or 500d cases, the index can be built, but slows
rather than speeds nearest-neighbor queries that use the index.
The evaluated patch's definitions of distance_euclid and other support
function seem straightforward, and give proper results on simple test
cases... so the problem, if any, would appear to be in the general
distance-driven knn-GIST indexing and query-planning.
If there is a type of cube-type represented dimensional data where this
indexing helps, it may only be with far fewer dimensions than 100,
and/or far more rows than 100K/850K.
If a knn-GIST implementor/expert has suggestions for tuning the index
overhead and behavior -- perhaps a different penalty or picksplit
function? -- I'd be happy to try those out and report back.
Otherwise, I hope this write-up will help evaluate the patch or save
time for others tempted to try similar knn-GIST indexing of
higher-dimensional data.
- Gordon Mohr
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Gordon Mohr wrote:
Thanks for this! I decided to give the patch a try at the bleeding
edge with some high-dimensional vectors, specifically the 1.4
million 1000-dimensional Freebase entity vectors from the Google
'word2vec' project:https://code.google.com/p/word2vec/#Pre-trained_entity_vectors_with_Freebase_naming
Unfortunately, here's what I found:
I wonder if these results would improve with this patch:
/messages/by-id/EFEDC2BF-AB35-4E2C-911F-FC88DA6473D7@gmail.com
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 10/23/13 9:05 PM, Alvaro Herrera wrote:
Gordon Mohr wrote:
Thanks for this! I decided to give the patch a try at the bleeding
edge with some high-dimensional vectors, specifically the 1.4
million 1000-dimensional Freebase entity vectors from the Google
'word2vec' project:https://code.google.com/p/word2vec/#Pre-trained_entity_vectors_with_Freebase_naming
Unfortunately, here's what I found:
I wonder if these results would improve with this patch:
/messages/by-id/EFEDC2BF-AB35-4E2C-911F-FC88DA6473D7@gmail.com
Thanks for the pointer; I'd missed that relevant update from Stas
Kelvich. I applied that patch, and reindexed.
On the 100-dimension, 850K vector set:
indexing: 1137s (vs. 1344s)
DATA size: 4.7G (vs 5.0G)
top-11-nearest-neighbor query: 32s (vs ~57s)
On the 500-dimension, 100K vector set:
indexing: 756s (vs. 977s)
DATA size: 4.5G (vs. 4.8G)
top-11-nearest-neighbor query: 18s (vs ~46s)
So, moderate (5-20%) improvements in indexing time and size, and larger
(40-60%) speedups in index-assisted (<->) queries... but those
index-assisted queries are still ~10X+ slower than the sequence-scan
(distance_euclid()) queries, so the existence of the knn-GIST index is
still harming rather than hurting performance.
Will update if my understanding changes; still interested to hear if
I've missed a key factor/switch needed for these indexes to work well.
- Gordon Mohr
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 10/23/13 9:05 PM, Alvaro Herrera wrote:
Gordon Mohr wrote:
Thanks for this! I decided to give the patch a try at the bleeding
edge with some high-dimensional vectors, specifically the 1.4
million 1000-dimensional Freebase entity vectors from the Google
'word2vec' project:https://code.google.com/p/word2vec/#Pre-trained_entity_vectors_with_Freebase_naming
Unfortunately, here's what I found:
I wonder if these results would improve with this patch:
/messages/by-id/EFEDC2BF-AB35-4E2C-911F-FC88DA6473D7@gmail.com
Thanks for the pointer; I'd missed that relevant update from Stas
Kelvich. I applied that patch, and reindexed.
On the 100-dimension, 850K vector set:
indexing: 1137s (vs. 1344s)
DATA size: 4.7G (vs 5.0G)
top-11-nearest-neighbor query: 32s (vs ~57s)
On the 500-dimension, 100K vector set:
indexing: 756s (vs. 977s)
DATA size: 4.5G (vs. 4.8G)
top-11-nearest-neighbor query: 18s (vs ~46s)
So, moderate (5-20%) improvements in indexing time and size, and larger
(40-60%) speedups in index-assisted (<->) queries... but those
index-assisted queries are still ~10X+ slower than the sequence-scan
(distance_euclid()) queries, so the existence of the knn-GIST index is
still harming rather than hurting performance.
Will update if my understanding changes; still interested to hear if
I've missed a key factor/switch needed for these indexes to work well.
- Gordon Mohr
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Oct 24, 2013 at 3:50 AM, Gordon Mohr <gojomo-pgsql@xavvy.com> wrote:
On 9/22/13 4:38 PM, Stas Kelvich wrote:
Hello, hackers.
Here is the patch that introduces kNN search for cubes with
euclidean, taxicab and chebyshev distances.Thanks for this! I decided to give the patch a try at the bleeding edge
with some high-dimensional vectors, specifically the 1.4 million
1000-dimensional Freebase entity vectors from the Google 'word2vec' project:
I believe the curse of dimensionality is affecting you here. I think it is
impossible to get an improvement over sequential scan for 1000 dimensional
vectors. Read here:
http://en.wikipedia.org/wiki/Curse_of_dimensionality#k-nearest_neighbor_classification
Regards
Marcin Mańk
Hi.
cube and earthdistance regression tests fail.
Code updated to work with current HEAD. Also added tests to cover new functionality.
Do you have any benchmarks ?
This patch just introduces functionality of calculating distances between cubes, so this code don't interfere much with kNN search speed. I think it's better to publish such benchmarks in neighbor patch about split algorithm.
Anyway, we can compare kNN with b-tree and full scan:
create table test(a1 float, a2 float, a3 float);
insert into test (select 100*random(), 100*random(), 100*random() from generate_series(1,1000000) as s(a));
create index on test using gist(cube(array[a1,a2,a3]));
select * from test order by a1 limit 15; -- 227.658 ms
select * from test order by cube(array[a1,a2,a3])->1 limit 15; -- 1.275 ms
create index on test(a1);
select * from test order by a1 limit 15; -- 0.103 ms
As we can see, kNN ordering 10 times slower than B-tree (on silly request for R-Tree, just as example), but still 100+ times faster than full scan on this table.
Stas.
On Sep 25, 2013, at 5:25 PM, Peter Eisentraut <peter_e@gmx.net> wrote:
Show quoted text
On 9/22/13 7:38 PM, Stas Kelvich wrote:
Here is the patch that introduces kNN search for cubes with euclidean, taxicab and chebyshev distances.
cube and earthdistance regression tests fail.
Attachments:
distances.patchapplication/octet-stream; name=distances.patchDownload
diff --git a/contrib/cube/cube--1.0.sql b/contrib/cube/cube--1.0.sql
index 0307811..97258dd 100644
--- a/contrib/cube/cube--1.0.sql
+++ b/contrib/cube/cube--1.0.sql
@@ -135,8 +135,24 @@ LANGUAGE C IMMUTABLE STRICT;
-- proximity routines
+CREATE FUNCTION distance_taxicab(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION distance_euclid(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Alias for backword compatibility
CREATE FUNCTION cube_distance(cube, cube)
RETURNS float8
+AS 'MODULE_PATHNAME', 'distance_euclid'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION distance_chebyshev(cube, cube)
+RETURNS float8
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
@@ -157,6 +173,11 @@ RETURNS float8
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
+CREATE FUNCTION cube_coord(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
CREATE FUNCTION cube(float8) RETURNS cube
AS 'MODULE_PATHNAME', 'cube_f8'
LANGUAGE C IMMUTABLE STRICT;
@@ -246,6 +267,25 @@ CREATE OPERATOR <@ (
RESTRICT = contsel, JOIN = contjoinsel
);
+CREATE OPERATOR -> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+);
+
+CREATE OPERATOR <#> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ COMMUTATOR = '<#>'
+);
+
+CREATE OPERATOR <-> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_euclid,
+ COMMUTATOR = '<->'
+);
+
+CREATE OPERATOR <=> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ COMMUTATOR = '<=>'
+);
+
-- these are obsolete/deprecated:
CREATE OPERATOR @ (
LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
@@ -296,6 +336,10 @@ RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE STRICT;
+CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
-- Create the operator classes for indexing
@@ -316,10 +360,17 @@ CREATE OPERATOR CLASS gist_cube_ops
OPERATOR 8 <@ ,
OPERATOR 13 @ ,
OPERATOR 14 ~ ,
+ OPERATOR 15 -> (cube, int) FOR ORDER BY float_ops,
+ OPERATOR 16 <#> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 17 <-> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 18 <=> (cube, cube) FOR ORDER BY float_ops,
+
FUNCTION 1 g_cube_consistent (internal, cube, int, oid, internal),
FUNCTION 2 g_cube_union (internal, internal),
FUNCTION 3 g_cube_compress (internal),
FUNCTION 4 g_cube_decompress (internal),
FUNCTION 5 g_cube_penalty (internal, internal, internal),
FUNCTION 6 g_cube_picksplit (internal, internal),
- FUNCTION 7 g_cube_same (cube, cube, internal);
+ FUNCTION 7 g_cube_same (cube, cube, internal),
+ FUNCTION 8 g_cube_distance (internal, cube, smallint, oid);
+
diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c
index 9524943..fce4955 100644
--- a/contrib/cube/cube.c
+++ b/contrib/cube/cube.c
@@ -45,6 +45,7 @@ PG_FUNCTION_INFO_V1(cube_c_f8_f8);
PG_FUNCTION_INFO_V1(cube_dim);
PG_FUNCTION_INFO_V1(cube_ll_coord);
PG_FUNCTION_INFO_V1(cube_ur_coord);
+PG_FUNCTION_INFO_V1(cube_coord);
PG_FUNCTION_INFO_V1(cube_subset);
Datum cube_in(PG_FUNCTION_ARGS);
@@ -58,6 +59,7 @@ Datum cube_c_f8_f8(PG_FUNCTION_ARGS);
Datum cube_dim(PG_FUNCTION_ARGS);
Datum cube_ll_coord(PG_FUNCTION_ARGS);
Datum cube_ur_coord(PG_FUNCTION_ARGS);
+Datum cube_coord(PG_FUNCTION_ARGS);
Datum cube_subset(PG_FUNCTION_ARGS);
/*
@@ -71,6 +73,7 @@ PG_FUNCTION_INFO_V1(g_cube_penalty);
PG_FUNCTION_INFO_V1(g_cube_picksplit);
PG_FUNCTION_INFO_V1(g_cube_union);
PG_FUNCTION_INFO_V1(g_cube_same);
+PG_FUNCTION_INFO_V1(g_cube_distance);
Datum g_cube_consistent(PG_FUNCTION_ARGS);
Datum g_cube_compress(PG_FUNCTION_ARGS);
@@ -79,6 +82,7 @@ Datum g_cube_penalty(PG_FUNCTION_ARGS);
Datum g_cube_picksplit(PG_FUNCTION_ARGS);
Datum g_cube_union(PG_FUNCTION_ARGS);
Datum g_cube_same(PG_FUNCTION_ARGS);
+Datum g_cube_distance(PG_FUNCTION_ARGS);
/*
** B-tree support functions
@@ -120,11 +124,15 @@ Datum cube_size(PG_FUNCTION_ARGS);
/*
** miscellaneous
*/
-PG_FUNCTION_INFO_V1(cube_distance);
+PG_FUNCTION_INFO_V1(distance_taxicab);
+PG_FUNCTION_INFO_V1(distance_euclid);
+PG_FUNCTION_INFO_V1(distance_chebyshev);
PG_FUNCTION_INFO_V1(cube_is_point);
PG_FUNCTION_INFO_V1(cube_enlarge);
-Datum cube_distance(PG_FUNCTION_ARGS);
+Datum distance_taxicab(PG_FUNCTION_ARGS);
+Datum distance_euclid(PG_FUNCTION_ARGS);
+Datum distance_chebyshev(PG_FUNCTION_ARGS);
Datum cube_is_point(PG_FUNCTION_ARGS);
Datum cube_enlarge(PG_FUNCTION_ARGS);
@@ -1246,14 +1254,13 @@ cube_overlap(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(res);
}
-
/* Distance */
/* The distance is computed as a per axis sum of the squared distances
between 1D projections of the boxes onto Cartesian axes. Assuming zero
distance between overlapping projections, this metric coincides with the
"common sense" geometric distance */
Datum
-cube_distance(PG_FUNCTION_ARGS)
+distance_euclid(PG_FUNCTION_ARGS)
{
NDBOX *a = PG_GETARG_NDBOX(0),
*b = PG_GETARG_NDBOX(1);
@@ -1301,6 +1308,151 @@ cube_distance(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(sqrt(distance));
}
+Datum
+distance_taxicab(PG_FUNCTION_ARGS)
+{
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (DIM(a) < DIM(b))
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < DIM(b); i++)
+ distance += abs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = DIM(b); i < DIM(a); i++)
+ distance += abs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+}
+
+Datum
+distance_chebyshev(PG_FUNCTION_ARGS)
+{
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double d, distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (DIM(a) < DIM(b))
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < DIM(b); i++)
+ {
+ d = abs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+ if (d > distance)
+ distance = d;
+ }
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = DIM(b); i < DIM(a); i++)
+ {
+ d = abs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+ if (d > distance)
+ distance = d;
+ }
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+}
+
+Datum
+g_cube_distance(PG_FUNCTION_ARGS)
+{
+ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+ NDBOX *cube = DatumGetNDBOX(entry->key);
+ double retval;
+
+ if (strategy == 15)
+ {
+ int coord = PG_GETARG_INT32(1);
+
+ if(coord > 0)
+ if IS_POINT(cube)
+ retval = (cube)->x[(coord-1)%DIM(cube)];
+ else
+ /* This is for right traversal of non-leaf elements */
+ retval = Min(
+ (cube)->x[(coord-1)%DIM(cube)],
+ (cube)->x[(coord-1)%DIM(cube) + DIM(cube)]
+ );
+
+ /* negative coordinate user for descending sort */
+ else
+ if IS_POINT(cube)
+ retval = -(cube)->x[(-coord-1)%DIM(cube)];
+ else
+ /* This is for right traversal of non-leaf elements */
+ retval = Min(
+ -(cube)->x[(-coord-1)%DIM(cube)],
+ -(cube)->x[(-coord-1)%DIM(cube) + DIM(cube)]
+ );
+ }
+ else
+ {
+ NDBOX *query = PG_GETARG_NDBOX(1);
+ switch(strategy)
+ {
+ case 16:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_taxicab,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case 17:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_euclid,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case 18:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_chebyshev,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ }
+ }
+ PG_RETURN_FLOAT8(retval);
+}
+
static double
distance_1D(double a1, double a2, double b1, double b2)
{
@@ -1395,6 +1547,38 @@ cube_ur_coord(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+/*
+ * Function returns cube coordinate.
+ * Numbers from 1 to DIM denotes Lower Left corner coordinates.
+ * Numbers from DIM+1 to 2*DIM denotes Upper Right cube corner coordinates.
+ * If negative number passed to function it is treated as it's absolut value,
+ * but resulting coordinate will be returned with changed sign. This
+ * convention useful for descending sort by this coordinate.
+ */
+Datum
+cube_coord(PG_FUNCTION_ARGS)
+{
+ NDBOX *cube = PG_GETARG_NDBOX(0);
+ int coord = PG_GETARG_INT16(1);
+
+ if ((0 < coord) && (coord <= 2*DIM(cube)))
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[(-1+coord)%DIM(cube)] );
+ else
+ PG_RETURN_FLOAT8( (cube)->x[-1+coord] );
+
+ else if ((-2*DIM(cube) <= coord) && (coord < 0))
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( -(cube)->x[(-1-coord)%DIM(cube)] );
+ else
+ PG_RETURN_FLOAT8( -(cube)->x[-1-coord] );
+
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ errmsg("Index out of bounds")));
+}
+
/* Increase or decrease box size by a radius in at least n dimensions. */
Datum
cube_enlarge(PG_FUNCTION_ARGS)
diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out
index ca9555e..a3db4f7 100644
--- a/contrib/cube/expected/cube.out
+++ b/contrib/cube/expected/cube.out
@@ -1381,6 +1381,110 @@ SELECT cube_size('(42,137)'::cube);
0
(1 row)
+-- Test of distances
+--
+SELECT distance_euclid('(1,1)'::cube, '(4,5)'::cube);
+ distance_euclid
+-----------------
+ 5
+(1 row)
+
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+-----
+ 5
+(1 row)
+
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+--------------------
+ 4
+(1 row)
+
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+-----
+ 4
+(1 row)
+
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+------------------
+ 7
+(1 row)
+
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+-----
+ 7
+(1 row)
+
+-- zero for overlapping
+SELECT distance_euclid('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_euclid
+-----------------
+ 0
+(1 row)
+
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+--------------------
+ 0
+(1 row)
+
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+------------------
+ 0
+(1 row)
+
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+----------
+ 60
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+ERROR: Index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+ERROR: Index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ?column?
+----------
+ -10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ?column?
+----------
+ -60
+(1 row)
+
+SELECT cube(array[10,20,30])->3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->6;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->-6;
+ ?column?
+----------
+ -30
+(1 row)
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -1407,3 +1511,199 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+(5 rows)
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c->4 LIMIT 15; -- ascending by 4th coordinate
+ c
+---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+-------------------------------
+ (50027, 49230),(49951, 49214)
+ (49999, 27218),(49908, 27176)
+ (49985, 6436),(49927, 6338)
+ (49981, 34876),(49898, 34786)
+ (49980, 35004),(49937, 34963)
+ (49957, 43390),(49897, 43384)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49907, 30225),(49810, 30158)
+ (49902, 41752),(49818, 41746)
+ (49887, 24274),(49805, 24184)
+ (49853, 18504),(49848, 18503)
+ (49847, 7128),(49798, 7067)
+ (49843, 5175),(49808, 5145)
+ (49836, 35965),(49757, 35871)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c->-4 LIMIT 15; -- descending by 4th coordinate
+ c
+-------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+(15 rows)
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c->5 LIMIT 15; -- should be the same as previous
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c->-5 LIMIT 15; -- should be the same as previous
+ c
+------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+(15 rows)
+
diff --git a/contrib/cube/expected/cube_1.out b/contrib/cube/expected/cube_1.out
index c07d61d..b7acc5e 100644
--- a/contrib/cube/expected/cube_1.out
+++ b/contrib/cube/expected/cube_1.out
@@ -1381,6 +1381,110 @@ SELECT cube_size('(42,137)'::cube);
0
(1 row)
+-- Test of distances
+--
+SELECT distance_euclid('(1,1)'::cube, '(4,5)'::cube);
+ distance_euclid
+-----------------
+ 5
+(1 row)
+
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+-----
+ 5
+(1 row)
+
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+--------------------
+ 4
+(1 row)
+
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+-----
+ 4
+(1 row)
+
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+------------------
+ 7
+(1 row)
+
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+-----
+ 7
+(1 row)
+
+-- zero for overlapping
+SELECT distance_euclid('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_euclid
+-----------------
+ 0
+(1 row)
+
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+--------------------
+ 0
+(1 row)
+
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+------------------
+ 0
+(1 row)
+
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+----------
+ 60
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+ERROR: Index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+ERROR: Index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ?column?
+----------
+ -10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ?column?
+----------
+ -60
+(1 row)
+
+SELECT cube(array[10,20,30])->3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->6;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->-6;
+ ?column?
+----------
+ -30
+(1 row)
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -1407,3 +1511,199 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+(5 rows)
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c->4 LIMIT 15; -- ascending by 4th coordinate
+ c
+---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+-------------------------------
+ (50027, 49230),(49951, 49214)
+ (49999, 27218),(49908, 27176)
+ (49985, 6436),(49927, 6338)
+ (49981, 34876),(49898, 34786)
+ (49980, 35004),(49937, 34963)
+ (49957, 43390),(49897, 43384)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49907, 30225),(49810, 30158)
+ (49902, 41752),(49818, 41746)
+ (49887, 24274),(49805, 24184)
+ (49853, 18504),(49848, 18503)
+ (49847, 7128),(49798, 7067)
+ (49843, 5175),(49808, 5145)
+ (49836, 35965),(49757, 35871)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c->-4 LIMIT 15; -- descending by 4th coordinate
+ c
+-------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+(15 rows)
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c->5 LIMIT 15; -- should be the same as previous
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c->-5 LIMIT 15; -- should be the same as previous
+ c
+------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+(15 rows)
+
diff --git a/contrib/cube/expected/cube_2.out b/contrib/cube/expected/cube_2.out
index 3767d0e..964dc6b 100644
--- a/contrib/cube/expected/cube_2.out
+++ b/contrib/cube/expected/cube_2.out
@@ -1381,6 +1381,110 @@ SELECT cube_size('(42,137)'::cube);
0
(1 row)
+-- Test of distances
+--
+SELECT distance_euclid('(1,1)'::cube, '(4,5)'::cube);
+ distance_euclid
+-----------------
+ 5
+(1 row)
+
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+-----
+ 5
+(1 row)
+
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+--------------------
+ 4
+(1 row)
+
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+-----
+ 4
+(1 row)
+
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+------------------
+ 7
+(1 row)
+
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+-----
+ 7
+(1 row)
+
+-- zero for overlapping
+SELECT distance_euclid('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_euclid
+-----------------
+ 0
+(1 row)
+
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+--------------------
+ 0
+(1 row)
+
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+------------------
+ 0
+(1 row)
+
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+----------
+ 60
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+ERROR: Index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+ERROR: Index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ?column?
+----------
+ -10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ?column?
+----------
+ -60
+(1 row)
+
+SELECT cube(array[10,20,30])->3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->6;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->-6;
+ ?column?
+----------
+ -30
+(1 row)
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -1407,3 +1511,199 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+(5 rows)
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c->4 LIMIT 15; -- ascending by 4th coordinate
+ c
+---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+-------------------------------
+ (50027, 49230),(49951, 49214)
+ (49999, 27218),(49908, 27176)
+ (49985, 6436),(49927, 6338)
+ (49981, 34876),(49898, 34786)
+ (49980, 35004),(49937, 34963)
+ (49957, 43390),(49897, 43384)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49907, 30225),(49810, 30158)
+ (49902, 41752),(49818, 41746)
+ (49887, 24274),(49805, 24184)
+ (49853, 18504),(49848, 18503)
+ (49847, 7128),(49798, 7067)
+ (49843, 5175),(49808, 5145)
+ (49836, 35965),(49757, 35871)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c->-4 LIMIT 15; -- descending by 4th coordinate
+ c
+-------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+(15 rows)
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c->5 LIMIT 15; -- should be the same as previous
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c->-5 LIMIT 15; -- should be the same as previous
+ c
+------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+(15 rows)
+
diff --git a/contrib/cube/expected/cube_3.out b/contrib/cube/expected/cube_3.out
index 2aa42be..7868514 100644
--- a/contrib/cube/expected/cube_3.out
+++ b/contrib/cube/expected/cube_3.out
@@ -1381,6 +1381,110 @@ SELECT cube_size('(42,137)'::cube);
0
(1 row)
+-- Test of distances
+--
+SELECT distance_euclid('(1,1)'::cube, '(4,5)'::cube);
+ distance_euclid
+-----------------
+ 5
+(1 row)
+
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+-----
+ 5
+(1 row)
+
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+--------------------
+ 4
+(1 row)
+
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+-----
+ 4
+(1 row)
+
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+------------------
+ 7
+(1 row)
+
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+-----
+ 7
+(1 row)
+
+-- zero for overlapping
+SELECT distance_euclid('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_euclid
+-----------------
+ 0
+(1 row)
+
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+--------------------
+ 0
+(1 row)
+
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+------------------
+ 0
+(1 row)
+
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+----------
+ 60
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+ERROR: Index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+ERROR: Index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ?column?
+----------
+ -10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ?column?
+----------
+ -60
+(1 row)
+
+SELECT cube(array[10,20,30])->3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->6;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->-6;
+ ?column?
+----------
+ -30
+(1 row)
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -1407,3 +1511,199 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+(5 rows)
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c->4 LIMIT 15; -- ascending by 4th coordinate
+ c
+---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+-------------------------------
+ (50027, 49230),(49951, 49214)
+ (49999, 27218),(49908, 27176)
+ (49985, 6436),(49927, 6338)
+ (49981, 34876),(49898, 34786)
+ (49980, 35004),(49937, 34963)
+ (49957, 43390),(49897, 43384)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49907, 30225),(49810, 30158)
+ (49902, 41752),(49818, 41746)
+ (49887, 24274),(49805, 24184)
+ (49853, 18504),(49848, 18503)
+ (49847, 7128),(49798, 7067)
+ (49843, 5175),(49808, 5145)
+ (49836, 35965),(49757, 35871)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c->-4 LIMIT 15; -- descending by 4th coordinate
+ c
+-------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+(15 rows)
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c->5 LIMIT 15; -- should be the same as previous
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c->-5 LIMIT 15; -- should be the same as previous
+ c
+------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+(15 rows)
+
diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql
index d58974c..d914f72 100644
--- a/contrib/cube/sql/cube.sql
+++ b/contrib/cube/sql/cube.sql
@@ -325,6 +325,29 @@ SELECT cube_inter('(1,2,3)'::cube, '(5,6,3)'::cube); -- point args
SELECT cube_size('(4,8),(15,16)'::cube);
SELECT cube_size('(42,137)'::cube);
+-- Test of distances
+--
+SELECT distance_euclid('(1,1)'::cube, '(4,5)'::cube);
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+-- zero for overlapping
+SELECT distance_euclid('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+SELECT cube(array[10,20,30])->3;
+SELECT cube(array[10,20,30])->6;
+SELECT cube(array[10,20,30])->-6;
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -336,3 +359,24 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' ORDER BY c;
-- Test sorting
SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
+
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+SELECT * FROM test_cube ORDER BY c->4 LIMIT 15; -- ascending by 4th coordinate
+SELECT * FROM test_cube ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+SELECT * FROM test_cube ORDER BY c->-4 LIMIT 15; -- descending by 4th coordinate
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+SELECT * FROM test_point ORDER BY c->5 LIMIT 15; -- should be the same as previous
+SELECT * FROM test_point ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+SELECT * FROM test_point ORDER BY c->-5 LIMIT 15; -- should be the same as previous
+
cube.c: In function �g_cube_distance�:
cube.c:1453:2: warning: �retval� may be used uninitialized in this function [-Wmaybe-uninitialized]
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi everyone,
On Sun, Sep 22, 2013 at 4:38 PM, Stas Kelvich <stas.kelvich@gmail.com> wrote:
Here is the patch that introduces kNN search for cubes with euclidean, taxicab and chebyshev distances.
What is the status of this patch?
--
Kind regards,
Sergey Konoplev
PostgreSQL Consultant and DBA
http://www.linkedin.com/in/grayhemp
+1 (415) 867-9984, +7 (901) 903-0499, +7 (988) 888-1979
gray.ru@gmail.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Mar 27, 2014 at 3:26 PM, Sergey Konoplev <gray.ru@gmail.com> wrote:
On Sun, Sep 22, 2013 at 4:38 PM, Stas Kelvich <stas.kelvich@gmail.com> wrote:
Here is the patch that introduces kNN search for cubes with euclidean, taxicab and chebyshev distances.
What is the status of this patch?
Referring to our private conversation with Alexander Korotkov, the
patch is in WIP state currently, and, hopefully, will be ready by 9.5.
I'm ready to actively participate in its testing on a real world
production set of data.
I'm not sure if it is doable at all, but are there any possibility to
implement here, or, what would be just great, any ready/half ready
solutions of a Hamming distance based kNN search?
--
Kind regards,
Sergey Konoplev
PostgreSQL Consultant and DBA
http://www.linkedin.com/in/grayhemp
+1 (415) 867-9984, +7 (901) 903-0499, +7 (988) 888-1979
gray.ru@gmail.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Mon, Mar 31, 2014 at 10:01 PM, Sergey Konoplev <gray.ru@gmail.com> wrote:
On Thu, Mar 27, 2014 at 3:26 PM, Sergey Konoplev <gray.ru@gmail.com>
wrote:On Sun, Sep 22, 2013 at 4:38 PM, Stas Kelvich <stas.kelvich@gmail.com>
wrote:
Here is the patch that introduces kNN search for cubes with euclidean,
taxicab and chebyshev distances.
What is the status of this patch?
Referring to our private conversation with Alexander Korotkov, the
patch is in WIP state currently, and, hopefully, will be ready by 9.5.
I'm ready to actively participate in its testing on a real world
production set of data.I'm not sure if it is doable at all, but are there any possibility to
implement here, or, what would be just great, any ready/half ready
solutions of a Hamming distance based kNN search?
Cube dealing with float8 numbers. There is another patch making it work
with other number types. But Hamming distance is for bit vectors, isn't it?
----
With best regards,
Alexander Korotkov.
On Mon, Mar 31, 2014 at 12:09 PM, Alexander Korotkov
<aekorotkov@gmail.com> wrote:
I'm not sure if it is doable at all, but are there any possibility to
implement here, or, what would be just great, any ready/half ready
solutions of a Hamming distance based kNN search?Cube dealing with float8 numbers. There is another patch making it work with
other number types. But Hamming distance is for bit vectors, isn't it?
It can be generalized as for character vectors. Though, I agree, that
was an off topic question in some extent. I was wondering if there
were any postgres related indexable Hamming/Manhattan distance
experiments/thoughts/discussions, if kNN can be used here or not,
because from my understanding it can be represented as spatial (I
might be very wrong here).
--
Kind regards,
Sergey Konoplev
PostgreSQL Consultant and DBA
http://www.linkedin.com/in/grayhemp
+1 (415) 867-9984, +7 (901) 903-0499, +7 (988) 888-1979
gray.ru@gmail.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Sergey Konoplev wrote:
On Thu, Mar 27, 2014 at 3:26 PM, Sergey Konoplev <gray.ru@gmail.com> wrote:
On Sun, Sep 22, 2013 at 4:38 PM, Stas Kelvich <stas.kelvich@gmail.com> wrote:
Here is the patch that introduces kNN search for cubes with euclidean, taxicab and chebyshev distances.
What is the status of this patch?
Referring to our private conversation with Alexander Korotkov, the
patch is in WIP state currently, and, hopefully, will be ready by 9.5.
I'm ready to actively participate in its testing on a real world
production set of data.
This patch doesn't seem to have received an updated version. Should we
just punt on it? The assumption would be that Stas or Alexander will be
re-submitting this for 9.6.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi!
Patch is pretty ready, last issue was about changed extension interface, so there should be migration script and version bump.
Attaching a version with all migration stuff.
Attachments:
distances2r4.patchapplication/octet-stream; name=distances2r4.patchDownload
diff --git a/contrib/cube/Makefile b/contrib/cube/Makefile
new file mode 100644
index 67f7867..e2a5d2c
*** a/contrib/cube/Makefile
--- b/contrib/cube/Makefile
*************** MODULE_big = cube
*** 4,10 ****
OBJS= cube.o cubeparse.o $(WIN32RES)
EXTENSION = cube
! DATA = cube--1.0.sql cube--unpackaged--1.0.sql
PGFILEDESC = "cube - multidimensional cube data type"
REGRESS = cube
--- 4,10 ----
OBJS= cube.o cubeparse.o $(WIN32RES)
EXTENSION = cube
! DATA = cube--1.1.sql cube--1.0--1.1.sql cube--unpackaged--1.0.sql
PGFILEDESC = "cube - multidimensional cube data type"
REGRESS = cube
diff --git a/contrib/cube/cube--1.0.sql b/contrib/cube/cube--1.0.sql
new file mode .
index 0307811..e69de29
*** a/contrib/cube/cube--1.0.sql
--- b/contrib/cube/cube--1.0.sql
***************
*** 1,325 ****
- /* contrib/cube/cube--1.0.sql */
-
- -- complain if script is sourced in psql, rather than via CREATE EXTENSION
- \echo Use "CREATE EXTENSION cube" to load this file. \quit
-
- -- Create the user-defined type for N-dimensional boxes
-
- CREATE FUNCTION cube_in(cstring)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(float8[], float8[]) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_a_f8_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(float8[]) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_a_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_out(cube)
- RETURNS cstring
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE TYPE cube (
- INTERNALLENGTH = variable,
- INPUT = cube_in,
- OUTPUT = cube_out,
- ALIGNMENT = double
- );
-
- COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
-
- --
- -- External C-functions for R-tree methods
- --
-
- -- Comparison methods
-
- CREATE FUNCTION cube_eq(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as';
-
- CREATE FUNCTION cube_ne(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different';
-
- CREATE FUNCTION cube_lt(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than';
-
- CREATE FUNCTION cube_gt(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than';
-
- CREATE FUNCTION cube_le(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to';
-
- CREATE FUNCTION cube_ge(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to';
-
- CREATE FUNCTION cube_cmp(cube, cube)
- RETURNS int4
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function';
-
- CREATE FUNCTION cube_contains(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains';
-
- CREATE FUNCTION cube_contained(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in';
-
- CREATE FUNCTION cube_overlap(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps';
-
- -- support routines for indexing
-
- CREATE FUNCTION cube_union(cube, cube)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_inter(cube, cube)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_size(cube)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
-
- -- Misc N-dimensional functions
-
- CREATE FUNCTION cube_subset(cube, int4[])
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- -- proximity routines
-
- CREATE FUNCTION cube_distance(cube, cube)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- -- Extracting elements functions
-
- CREATE FUNCTION cube_dim(cube)
- RETURNS int4
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_ll_coord(cube, int4)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_ur_coord(cube, int4)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(float8, float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_f8_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(cube, float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_c_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(cube, float8, float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_c_f8_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- -- Test if cube is also a point
-
- CREATE FUNCTION cube_is_point(cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- -- Increasing the size of a cube by a radius in at least n dimensions
-
- CREATE FUNCTION cube_enlarge(cube, float8, int4)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- --
- -- OPERATORS
- --
-
- CREATE OPERATOR < (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt,
- COMMUTATOR = '>', NEGATOR = '>=',
- RESTRICT = scalarltsel, JOIN = scalarltjoinsel
- );
-
- CREATE OPERATOR > (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt,
- COMMUTATOR = '<', NEGATOR = '<=',
- RESTRICT = scalargtsel, JOIN = scalargtjoinsel
- );
-
- CREATE OPERATOR <= (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_le,
- COMMUTATOR = '>=', NEGATOR = '>',
- RESTRICT = scalarltsel, JOIN = scalarltjoinsel
- );
-
- CREATE OPERATOR >= (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ge,
- COMMUTATOR = '<=', NEGATOR = '<',
- RESTRICT = scalargtsel, JOIN = scalargtjoinsel
- );
-
- CREATE OPERATOR && (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap,
- COMMUTATOR = '&&',
- RESTRICT = areasel, JOIN = areajoinsel
- );
-
- CREATE OPERATOR = (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_eq,
- COMMUTATOR = '=', NEGATOR = '<>',
- RESTRICT = eqsel, JOIN = eqjoinsel,
- MERGES
- );
-
- CREATE OPERATOR <> (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ne,
- COMMUTATOR = '<>', NEGATOR = '=',
- RESTRICT = neqsel, JOIN = neqjoinsel
- );
-
- CREATE OPERATOR @> (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
- COMMUTATOR = '<@',
- RESTRICT = contsel, JOIN = contjoinsel
- );
-
- CREATE OPERATOR <@ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
- COMMUTATOR = '@>',
- RESTRICT = contsel, JOIN = contjoinsel
- );
-
- -- these are obsolete/deprecated:
- CREATE OPERATOR @ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
- COMMUTATOR = '~',
- RESTRICT = contsel, JOIN = contjoinsel
- );
-
- CREATE OPERATOR ~ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
- COMMUTATOR = '@',
- RESTRICT = contsel, JOIN = contjoinsel
- );
-
-
- -- define the GiST support methods
- CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_compress(internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_decompress(internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_penalty(internal,internal,internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_picksplit(internal, internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_union(internal, internal)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_same(cube, cube, internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
-
- -- Create the operator classes for indexing
-
- CREATE OPERATOR CLASS cube_ops
- DEFAULT FOR TYPE cube USING btree AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 cube_cmp(cube, cube);
-
- CREATE OPERATOR CLASS gist_cube_ops
- DEFAULT FOR TYPE cube USING gist AS
- OPERATOR 3 && ,
- OPERATOR 6 = ,
- OPERATOR 7 @> ,
- OPERATOR 8 <@ ,
- OPERATOR 13 @ ,
- OPERATOR 14 ~ ,
- FUNCTION 1 g_cube_consistent (internal, cube, int, oid, internal),
- FUNCTION 2 g_cube_union (internal, internal),
- FUNCTION 3 g_cube_compress (internal),
- FUNCTION 4 g_cube_decompress (internal),
- FUNCTION 5 g_cube_penalty (internal, internal, internal),
- FUNCTION 6 g_cube_picksplit (internal, internal),
- FUNCTION 7 g_cube_same (cube, cube, internal);
--- 0 ----
diff --git a/contrib/cube/cube--1.1.sql b/contrib/cube/cube--1.1.sql
new file mode 100644
index ...0c68d14
*** a/contrib/cube/cube--1.1.sql
--- b/contrib/cube/cube--1.1.sql
***************
*** 0 ****
--- 1,370 ----
+ /* contrib/cube/cube--1.1.sql */
+
+ -- complain if script is sourced in psql, rather than via CREATE EXTENSION
+ \echo Use "CREATE EXTENSION cube" to load this file. \quit
+
+ -- Create the user-defined type for N-dimensional boxes
+
+ CREATE FUNCTION cube_in(cstring)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(float8[], float8[]) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_a_f8_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(float8[]) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_a_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_out(cube)
+ RETURNS cstring
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE TYPE cube (
+ INTERNALLENGTH = variable,
+ INPUT = cube_in,
+ OUTPUT = cube_out,
+ ALIGNMENT = double
+ );
+
+ COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
+
+ --
+ -- External C-functions for R-tree methods
+ --
+
+ -- Comparison methods
+
+ CREATE FUNCTION cube_eq(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as';
+
+ CREATE FUNCTION cube_ne(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different';
+
+ CREATE FUNCTION cube_lt(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than';
+
+ CREATE FUNCTION cube_gt(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than';
+
+ CREATE FUNCTION cube_le(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to';
+
+ CREATE FUNCTION cube_ge(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to';
+
+ CREATE FUNCTION cube_cmp(cube, cube)
+ RETURNS int4
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function';
+
+ CREATE FUNCTION cube_contains(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains';
+
+ CREATE FUNCTION cube_contained(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in';
+
+ CREATE FUNCTION cube_overlap(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps';
+
+ -- support routines for indexing
+
+ CREATE FUNCTION cube_union(cube, cube)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_inter(cube, cube)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_size(cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+
+ -- Misc N-dimensional functions
+
+ CREATE FUNCTION cube_subset(cube, int4[])
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- proximity routines
+
+ CREATE FUNCTION cube_distance(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION distance_chebyshev(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION distance_taxicab(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- Extracting elements functions
+
+ CREATE FUNCTION cube_dim(cube)
+ RETURNS int4
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_ll_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_ur_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(float8, float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_f8_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(cube, float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_c_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(cube, float8, float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_c_f8_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- Test if cube is also a point
+
+ CREATE FUNCTION cube_is_point(cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- Increasing the size of a cube by a radius in at least n dimensions
+
+ CREATE FUNCTION cube_enlarge(cube, float8, int4)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ --
+ -- OPERATORS
+ --
+
+ CREATE OPERATOR < (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt,
+ COMMUTATOR = '>', NEGATOR = '>=',
+ RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+ );
+
+ CREATE OPERATOR > (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt,
+ COMMUTATOR = '<', NEGATOR = '<=',
+ RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+ );
+
+ CREATE OPERATOR <= (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_le,
+ COMMUTATOR = '>=', NEGATOR = '>',
+ RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+ );
+
+ CREATE OPERATOR >= (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ge,
+ COMMUTATOR = '<=', NEGATOR = '<',
+ RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+ );
+
+ CREATE OPERATOR && (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap,
+ COMMUTATOR = '&&',
+ RESTRICT = areasel, JOIN = areajoinsel
+ );
+
+ CREATE OPERATOR = (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_eq,
+ COMMUTATOR = '=', NEGATOR = '<>',
+ RESTRICT = eqsel, JOIN = eqjoinsel,
+ MERGES
+ );
+
+ CREATE OPERATOR <> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ne,
+ COMMUTATOR = '<>', NEGATOR = '=',
+ RESTRICT = neqsel, JOIN = neqjoinsel
+ );
+
+ CREATE OPERATOR @> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
+ COMMUTATOR = '<@',
+ RESTRICT = contsel, JOIN = contjoinsel
+ );
+
+ CREATE OPERATOR <@ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
+ COMMUTATOR = '@>',
+ RESTRICT = contsel, JOIN = contjoinsel
+ );
+
+ CREATE OPERATOR -> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+ );
+
+ CREATE OPERATOR <#> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ COMMUTATOR = '<#>'
+ );
+
+ CREATE OPERATOR <-> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_distance,
+ COMMUTATOR = '<->'
+ );
+
+ CREATE OPERATOR <=> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ COMMUTATOR = '<=>'
+ );
+
+ -- these are obsolete/deprecated:
+ CREATE OPERATOR @ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
+ COMMUTATOR = '~',
+ RESTRICT = contsel, JOIN = contjoinsel
+ );
+
+ CREATE OPERATOR ~ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
+ COMMUTATOR = '@',
+ RESTRICT = contsel, JOIN = contjoinsel
+ );
+
+
+ -- define the GiST support methods
+ CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_compress(internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_decompress(internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_penalty(internal,internal,internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_picksplit(internal, internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_union(internal, internal)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_same(cube, cube, internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- Create the operator classes for indexing
+
+ CREATE OPERATOR CLASS cube_ops
+ DEFAULT FOR TYPE cube USING btree AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 cube_cmp(cube, cube);
+
+ CREATE OPERATOR CLASS gist_cube_ops
+ DEFAULT FOR TYPE cube USING gist AS
+ OPERATOR 3 && ,
+ OPERATOR 6 = ,
+ OPERATOR 7 @> ,
+ OPERATOR 8 <@ ,
+ OPERATOR 13 @ ,
+ OPERATOR 14 ~ ,
+ OPERATOR 15 -> (cube, int) FOR ORDER BY float_ops,
+ OPERATOR 16 <#> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 17 <-> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 18 <=> (cube, cube) FOR ORDER BY float_ops,
+
+ FUNCTION 1 g_cube_consistent (internal, cube, int, oid, internal),
+ FUNCTION 2 g_cube_union (internal, internal),
+ FUNCTION 3 g_cube_compress (internal),
+ FUNCTION 4 g_cube_decompress (internal),
+ FUNCTION 5 g_cube_penalty (internal, internal, internal),
+ FUNCTION 6 g_cube_picksplit (internal, internal),
+ FUNCTION 7 g_cube_same (cube, cube, internal),
+ FUNCTION 8 g_cube_distance (internal, cube, smallint, oid);
+
diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c
new file mode 100644
index b0305ef..93ea15b
*** a/contrib/cube/cube.c
--- b/contrib/cube/cube.c
*************** PG_FUNCTION_INFO_V1(cube_c_f8_f8);
*** 45,50 ****
--- 45,51 ----
PG_FUNCTION_INFO_V1(cube_dim);
PG_FUNCTION_INFO_V1(cube_ll_coord);
PG_FUNCTION_INFO_V1(cube_ur_coord);
+ PG_FUNCTION_INFO_V1(cube_coord);
PG_FUNCTION_INFO_V1(cube_subset);
/*
*************** PG_FUNCTION_INFO_V1(g_cube_penalty);
*** 58,63 ****
--- 59,66 ----
PG_FUNCTION_INFO_V1(g_cube_picksplit);
PG_FUNCTION_INFO_V1(g_cube_union);
PG_FUNCTION_INFO_V1(g_cube_same);
+ PG_FUNCTION_INFO_V1(g_cube_distance);
+
/*
** B-tree support functions
*************** PG_FUNCTION_INFO_V1(cube_size);
*** 84,90 ****
--- 87,95 ----
/*
** miscellaneous
*/
+ PG_FUNCTION_INFO_V1(distance_taxicab);
PG_FUNCTION_INFO_V1(cube_distance);
+ PG_FUNCTION_INFO_V1(distance_chebyshev);
PG_FUNCTION_INFO_V1(cube_is_point);
PG_FUNCTION_INFO_V1(cube_enlarge);
*************** cube_overlap(PG_FUNCTION_ARGS)
*** 1207,1213 ****
PG_RETURN_BOOL(res);
}
-
/* Distance */
/* The distance is computed as a per axis sum of the squared distances
between 1D projections of the boxes onto Cartesian axes. Assuming zero
--- 1212,1217 ----
*************** cube_distance(PG_FUNCTION_ARGS)
*** 1262,1267 ****
--- 1266,1421 ----
PG_RETURN_FLOAT8(sqrt(distance));
}
+ Datum
+ distance_taxicab(PG_FUNCTION_ARGS)
+ {
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (DIM(a) < DIM(b))
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < DIM(b); i++)
+ distance += fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = DIM(b); i < DIM(a); i++)
+ distance += fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+ }
+
+ Datum
+ distance_chebyshev(PG_FUNCTION_ARGS)
+ {
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double d, distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (DIM(a) < DIM(b))
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < DIM(b); i++)
+ {
+ d = fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+ if (d > distance)
+ distance = d;
+ }
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = DIM(b); i < DIM(a); i++)
+ {
+ d = fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+ if (d > distance)
+ distance = d;
+ }
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+ }
+
+ Datum
+ g_cube_distance(PG_FUNCTION_ARGS)
+ {
+ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+ NDBOX *cube = DatumGetNDBOX(entry->key);
+ double retval;
+
+ if (strategy == 15)
+ {
+ int coord = PG_GETARG_INT32(1);
+
+ if(coord > 0)
+ if IS_POINT(cube)
+ retval = (cube)->x[(coord-1)%DIM(cube)];
+ else
+ {
+ /* This is for right traversal of non-leaf elements */
+ retval = Min(
+ (cube)->x[(coord-1)%DIM(cube)],
+ (cube)->x[(coord-1)%DIM(cube) + DIM(cube)]
+ );
+ }
+
+ /* negative coordinate user for descending sort */
+ else
+ if IS_POINT(cube)
+ retval = -(cube)->x[(-coord-1)%DIM(cube)];
+ else
+ {
+ /* This is for right traversal of non-leaf elements */
+ retval = Min(
+ -(cube)->x[(-coord-1)%DIM(cube)],
+ -(cube)->x[(-coord-1)%DIM(cube) + DIM(cube)]
+ );
+ }
+ }
+ else
+ {
+ NDBOX *query = PG_GETARG_NDBOX(1);
+ switch(strategy)
+ {
+ case 17:
+ retval = DatumGetFloat8(DirectFunctionCall2(cube_distance,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case 18:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_chebyshev,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case 16:
+ default:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_taxicab,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ }
+ }
+ PG_RETURN_FLOAT8(retval);
+ }
+
static double
distance_1D(double a1, double a2, double b1, double b2)
{
*************** cube_ur_coord(PG_FUNCTION_ARGS)
*** 1357,1362 ****
--- 1511,1552 ----
PG_RETURN_FLOAT8(result);
}
+ /*
+ * Function returns cube coordinate.
+ * Numbers from 1 to DIM denotes Lower Left corner coordinates.
+ * Numbers from DIM+1 to 2*DIM denotes Upper Right cube corner coordinates.
+ * If negative number passed to function it is treated as it's absolut value,
+ * but resulting coordinate will be returned with changed sign. This
+ * convention useful for descending sort by this coordinate.
+ */
+ Datum
+ cube_coord(PG_FUNCTION_ARGS)
+ {
+ NDBOX *cube = PG_GETARG_NDBOX(0);
+ int coord = PG_GETARG_INT16(1);
+
+ if ((coord > 0) && (coord <= 2*DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[(coord-1)%DIM(cube)] );
+ else
+ PG_RETURN_FLOAT8( (cube)->x[coord-1] );
+ }
+ else if (coord >= (-2*DIM(cube)) && (coord < 0))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( -(cube)->x[(-coord-1)%DIM(cube)] );
+ else
+ PG_RETURN_FLOAT8( -(cube)->x[-coord-1] );
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ errmsg("Index out of bounds")));
+ }
+ }
+
/* Increase or decrease box size by a radius in at least n dimensions. */
Datum
cube_enlarge(PG_FUNCTION_ARGS)
diff --git a/contrib/cube/cube.control b/contrib/cube/cube.control
new file mode 100644
index ddc8d2e..f84e6c5
*** a/contrib/cube/cube.control
--- b/contrib/cube/cube.control
***************
*** 1,5 ****
# cube extension
comment = 'data type for multidimensional cubes'
! default_version = '1.0'
module_pathname = '$libdir/cube'
relocatable = true
--- 1,5 ----
# cube extension
comment = 'data type for multidimensional cubes'
! default_version = '1.1'
module_pathname = '$libdir/cube'
relocatable = true
diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out
new file mode 100644
index ca9555e..e748bc3
*** a/contrib/cube/expected/cube.out
--- b/contrib/cube/expected/cube.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1490 ----
0
(1 row)
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+ ---------------
+ 5
+ (1 row)
+
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+ -----
+ 5
+ (1 row)
+
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 4
+ (1 row)
+
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+ -----
+ 4
+ (1 row)
+
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+ ------------------
+ 7
+ (1 row)
+
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+ -----
+ 7
+ (1 row)
+
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+ ---------------
+ 0
+ (1 row)
+
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 0
+ (1 row)
+
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+ ------------------
+ 0
+ (1 row)
+
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+ ----------
+ 60
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR: Index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR: Index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ?column?
+ ----------
+ -10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ?column?
+ ----------
+ -60
+ (1 row)
+
+ SELECT cube(array[10,20,30])->3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->6;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->-6;
+ ?column?
+ ----------
+ -30
+ (1 row)
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1511,1709 ----
(2424, 160),(2424, 81)
(5 rows)
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+ (5 rows)
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+ ---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c->4 LIMIT 15; -- ascending by 4th coordinate
+ c
+ ---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+ -------------------------------
+ (50027, 49230),(49951, 49214)
+ (49999, 27218),(49908, 27176)
+ (49985, 6436),(49927, 6338)
+ (49981, 34876),(49898, 34786)
+ (49980, 35004),(49937, 34963)
+ (49957, 43390),(49897, 43384)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49907, 30225),(49810, 30158)
+ (49902, 41752),(49818, 41746)
+ (49887, 24274),(49805, 24184)
+ (49853, 18504),(49848, 18503)
+ (49847, 7128),(49798, 7067)
+ (49843, 5175),(49808, 5145)
+ (49836, 35965),(49757, 35871)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c->-4 LIMIT 15; -- descending by 4th coordinate
+ c
+ -------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+ (15 rows)
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c->5 LIMIT 15; -- should be the same as previous
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+ ------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c->-5 LIMIT 15; -- should be the same as previous
+ c
+ ------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+ (15 rows)
+
diff --git a/contrib/cube/expected/cube_1.out b/contrib/cube/expected/cube_1.out
new file mode 100644
index c07d61d..883290b
*** a/contrib/cube/expected/cube_1.out
--- b/contrib/cube/expected/cube_1.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1490 ----
0
(1 row)
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+ ---------------
+ 5
+ (1 row)
+
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+ -----
+ 5
+ (1 row)
+
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 4
+ (1 row)
+
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+ -----
+ 4
+ (1 row)
+
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+ ------------------
+ 7
+ (1 row)
+
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+ -----
+ 7
+ (1 row)
+
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+ ---------------
+ 0
+ (1 row)
+
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 0
+ (1 row)
+
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+ ------------------
+ 0
+ (1 row)
+
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+ ----------
+ 60
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR: Index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR: Index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ?column?
+ ----------
+ -10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ?column?
+ ----------
+ -60
+ (1 row)
+
+ SELECT cube(array[10,20,30])->3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->6;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->-6;
+ ?column?
+ ----------
+ -30
+ (1 row)
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1511,1709 ----
(2424, 160),(2424, 81)
(5 rows)
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+ (5 rows)
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+ ---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c->4 LIMIT 15; -- ascending by 4th coordinate
+ c
+ ---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+ -------------------------------
+ (50027, 49230),(49951, 49214)
+ (49999, 27218),(49908, 27176)
+ (49985, 6436),(49927, 6338)
+ (49981, 34876),(49898, 34786)
+ (49980, 35004),(49937, 34963)
+ (49957, 43390),(49897, 43384)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49907, 30225),(49810, 30158)
+ (49902, 41752),(49818, 41746)
+ (49887, 24274),(49805, 24184)
+ (49853, 18504),(49848, 18503)
+ (49847, 7128),(49798, 7067)
+ (49843, 5175),(49808, 5145)
+ (49836, 35965),(49757, 35871)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c->-4 LIMIT 15; -- descending by 4th coordinate
+ c
+ -------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+ (15 rows)
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c->5 LIMIT 15; -- should be the same as previous
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+ ------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c->-5 LIMIT 15; -- should be the same as previous
+ c
+ ------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+ (15 rows)
+
diff --git a/contrib/cube/expected/cube_2.out b/contrib/cube/expected/cube_2.out
new file mode 100644
index 3767d0e..7eaad7b
*** a/contrib/cube/expected/cube_2.out
--- b/contrib/cube/expected/cube_2.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1490 ----
0
(1 row)
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+ ---------------
+ 5
+ (1 row)
+
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+ -----
+ 5
+ (1 row)
+
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 4
+ (1 row)
+
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+ -----
+ 4
+ (1 row)
+
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+ ------------------
+ 7
+ (1 row)
+
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+ -----
+ 7
+ (1 row)
+
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+ ---------------
+ 0
+ (1 row)
+
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 0
+ (1 row)
+
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+ ------------------
+ 0
+ (1 row)
+
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+ ----------
+ 60
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR: Index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR: Index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ?column?
+ ----------
+ -10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ?column?
+ ----------
+ -60
+ (1 row)
+
+ SELECT cube(array[10,20,30])->3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->6;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->-6;
+ ?column?
+ ----------
+ -30
+ (1 row)
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1511,1709 ----
(2424, 160),(2424, 81)
(5 rows)
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+ (5 rows)
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+ ---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c->4 LIMIT 15; -- ascending by 4th coordinate
+ c
+ ---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+ -------------------------------
+ (50027, 49230),(49951, 49214)
+ (49999, 27218),(49908, 27176)
+ (49985, 6436),(49927, 6338)
+ (49981, 34876),(49898, 34786)
+ (49980, 35004),(49937, 34963)
+ (49957, 43390),(49897, 43384)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49907, 30225),(49810, 30158)
+ (49902, 41752),(49818, 41746)
+ (49887, 24274),(49805, 24184)
+ (49853, 18504),(49848, 18503)
+ (49847, 7128),(49798, 7067)
+ (49843, 5175),(49808, 5145)
+ (49836, 35965),(49757, 35871)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c->-4 LIMIT 15; -- descending by 4th coordinate
+ c
+ -------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+ (15 rows)
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c->5 LIMIT 15; -- should be the same as previous
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+ ------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c->-5 LIMIT 15; -- should be the same as previous
+ c
+ ------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+ (15 rows)
+
diff --git a/contrib/cube/expected/cube_3.out b/contrib/cube/expected/cube_3.out
new file mode 100644
index 2aa42be..caf0cf7
*** a/contrib/cube/expected/cube_3.out
--- b/contrib/cube/expected/cube_3.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1490 ----
0
(1 row)
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+ ---------------
+ 5
+ (1 row)
+
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+ -----
+ 5
+ (1 row)
+
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 4
+ (1 row)
+
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+ -----
+ 4
+ (1 row)
+
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+ ------------------
+ 7
+ (1 row)
+
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+ -----
+ 7
+ (1 row)
+
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+ ---------------
+ 0
+ (1 row)
+
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 0
+ (1 row)
+
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+ ------------------
+ 0
+ (1 row)
+
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+ ----------
+ 60
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR: Index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR: Index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ?column?
+ ----------
+ -10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ?column?
+ ----------
+ -60
+ (1 row)
+
+ SELECT cube(array[10,20,30])->3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->6;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->-6;
+ ?column?
+ ----------
+ -30
+ (1 row)
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1511,1709 ----
(2424, 160),(2424, 81)
(5 rows)
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+ (5 rows)
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+ ---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c->4 LIMIT 15; -- ascending by 4th coordinate
+ c
+ ---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+ -------------------------------
+ (50027, 49230),(49951, 49214)
+ (49999, 27218),(49908, 27176)
+ (49985, 6436),(49927, 6338)
+ (49981, 34876),(49898, 34786)
+ (49980, 35004),(49937, 34963)
+ (49957, 43390),(49897, 43384)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49907, 30225),(49810, 30158)
+ (49902, 41752),(49818, 41746)
+ (49887, 24274),(49805, 24184)
+ (49853, 18504),(49848, 18503)
+ (49847, 7128),(49798, 7067)
+ (49843, 5175),(49808, 5145)
+ (49836, 35965),(49757, 35871)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c->-4 LIMIT 15; -- descending by 4th coordinate
+ c
+ -------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+ (15 rows)
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c->5 LIMIT 15; -- should be the same as previous
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 32616, 226, 32607)
+ (270, 29508, 264, 29440)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ c
+ ------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c->-5 LIMIT 15; -- should be the same as previous
+ c
+ ------------------------------
+ (50027, 49230, 49951, 49214)
+ (49999, 27218, 49908, 27176)
+ (49985, 6436, 49927, 6338)
+ (49981, 34876, 49898, 34786)
+ (49980, 35004, 49937, 34963)
+ (49957, 43390, 49897, 43384)
+ (49954, 1340, 49905, 1294)
+ (49944, 25163, 49902, 25153)
+ (49907, 30225, 49810, 30158)
+ (49902, 41752, 49818, 41746)
+ (49887, 24274, 49805, 24184)
+ (49853, 18504, 49848, 18503)
+ (49847, 7128, 49798, 7067)
+ (49843, 5175, 49808, 5145)
+ (49836, 35965, 49757, 35871)
+ (15 rows)
+
diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql
new file mode 100644
index d58974c..6da277a
*** a/contrib/cube/sql/cube.sql
--- b/contrib/cube/sql/cube.sql
*************** SELECT cube_inter('(1,2,3)'::cube, '(5,6
*** 325,330 ****
--- 325,353 ----
SELECT cube_size('(4,8),(15,16)'::cube);
SELECT cube_size('(42,137)'::cube);
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ SELECT cube(array[10,20,30])->3;
+ SELECT cube(array[10,20,30])->6;
+ SELECT cube(array[10,20,30])->-6;
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 336,338 ****
--- 359,382 ----
-- Test sorting
SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
+
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ SELECT * FROM test_cube ORDER BY c->4 LIMIT 15; -- ascending by 4th coordinate
+ SELECT * FROM test_cube ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ SELECT * FROM test_cube ORDER BY c->-4 LIMIT 15; -- descending by 4th coordinate
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c->1 LIMIT 15; -- ascending by 1st coordinate
+ SELECT * FROM test_point ORDER BY c->5 LIMIT 15; -- should be the same as previous
+ SELECT * FROM test_point ORDER BY c->-1 LIMIT 15; -- descending by 1st coordinate
+ SELECT * FROM test_point ORDER BY c->-5 LIMIT 15; -- should be the same as previous
+
diff --git a/doc/src/sgml/cube.sgml b/doc/src/sgml/cube.sgml
new file mode 100644
index e2da685..800fd90
*** a/doc/src/sgml/cube.sgml
--- b/doc/src/sgml/cube.sgml
***************
*** 74,80 ****
It does not matter which order the opposite corners of a cube are
entered in. The <type>cube</> functions
automatically swap values if needed to create a uniform
! <quote>lower left — upper right</> internal representation.
</para>
<para>
--- 74,80 ----
It does not matter which order the opposite corners of a cube are
entered in. The <type>cube</> functions
automatically swap values if needed to create a uniform
! <quote>lower left — upper right</> internal representation. When corners coincide cube stores only one corner along with a special flag in order to reduce size wasted.
</para>
<para>
***************
*** 144,149 ****
--- 144,227 ----
</para>
<para>
+ GiST index can be used to retrieve nearest neighbours via several metric operators. As always any of them can be used as ordinary function.
+ </para>
+
+ <table id="cube-gistknn-operators">
+ <title>Cube GiST-kNN Operators</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Operator</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>a <-> b</></entry>
+ <entry>Euclidean distance between a and b</entry>
+ </row>
+
+ <row>
+ <entry><literal>a <#> b</></entry>
+ <entry>Taxicab (L-1 metric) distance between a and b</entry>
+ </row>
+
+ <row>
+ <entry><literal>a <=> b</></entry>
+ <entry>Chebyshev (L-inf metric) distance between a and b</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Selection of nearing neigbours can be done in the following way:
+ </para>
+ <programlisting>
+ SELECT c FROM test
+ ORDER BY cube(array[0.5,0.5,0.5])<->c
+ LIMIT 1;
+ </programlisting>
+
+
+ <para>
+ Also kNN framework allows us to cheat with metrics in order to get results sorted by selected coodinate directly from the index without extra sorting step. That technique significantly faster on small values of LIMIT, however with bigger values of LIMIT planner will switch automatically to standart index scan and sort.
+ That behavior can be achieved using special coordinate operator (cube c)->(int offset), that returns cube coordinate when offset is positive and -1*coordinate when offset is negative.
+ </para>
+ <programlisting>
+ => select cube(array[0.41,0.42,0.43])->2 as coord;
+ coord
+ -------
+ 0.42
+ (1 row)
+
+ => select cube(array[0.41,0.42,0.43])->-2 as coord;
+ coord
+ -------
+ -0.42
+ (1 row)
+ </programlisting>
+
+ <para>
+ So using that operator as kNN metric we can obtain cubes sorted by it's coordinate.
+ </para>
+ <para>
+ To get cubes ordered by first coordinate ascending one can use the following query:
+ </para>
+ <programlisting>
+ SELECT c FROM test ORDER BY c->1 LIMIT 5;
+ </programlisting>
+ <para>
+ And to get cubes descending by third coordinate:
+ </para>
+ <programlisting>
+ SELECT c FROM test ORDER BY c->-3 LIMIT 5;
+ </programlisting>
+
+
+
+ <para>
The standard B-tree operators are also provided, for example
<informaltable>
Hi!
On Sat, May 9, 2015 at 6:53 AM, Stas Kelvich <stas.kelvich@gmail.com> wrote:
Patch is pretty ready, last issue was about changed extension interface,
so there should be migration script and version bump.
Attaching a version with all migration stuff.
I can't see cube--1.0--1.1.sql in the patch. Did forget to include it?
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Hello.
That is updated version of the patch with proper update scripts.
Also i’ve noted that documentation states the wrong thing:
“It does not matter which order the opposite corners of a cube are entered in. The cube functions automatically swap values if needed to create a uniform "lower left — upper right" internal representation."
But in practice cubes stored "as is" and that leads to problems with getting cubes sorted along specific dimension directly from index.
As a simplest workaround i’ve deleted that sentence from docs and implemented two coordinate getters -> and ~>. First one returns
coordinate of cube as it stored, and second returns coordinate of cube normalised to (LL,UR)-form.
Other way to fix thing is to force ’normalization’ while creating cube. But that can produce wrong sorts with already existing data.
On 09 Jul 2015, at 16:40, Alexander Korotkov <aekorotkov@gmail.com> wrote:
Hi!
On Sat, May 9, 2015 at 6:53 AM, Stas Kelvich <stas.kelvich@gmail.com> wrote:
Patch is pretty ready, last issue was about changed extension interface, so there should be migration script and version bump.
Attaching a version with all migration stuff.I can't see cube--1.0--1.1.sql in the patch. Did forget to include it?
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Stas Kelvich
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
distances.patchapplication/octet-stream; name=distances.patchDownload
diff --git a/contrib/cube/Makefile b/contrib/cube/Makefile
new file mode 100644
index 67f7867..e2a5d2c
*** a/contrib/cube/Makefile
--- b/contrib/cube/Makefile
*************** MODULE_big = cube
*** 4,10 ****
OBJS= cube.o cubeparse.o $(WIN32RES)
EXTENSION = cube
! DATA = cube--1.0.sql cube--unpackaged--1.0.sql
PGFILEDESC = "cube - multidimensional cube data type"
REGRESS = cube
--- 4,10 ----
OBJS= cube.o cubeparse.o $(WIN32RES)
EXTENSION = cube
! DATA = cube--1.1.sql cube--1.0--1.1.sql cube--unpackaged--1.0.sql
PGFILEDESC = "cube - multidimensional cube data type"
REGRESS = cube
diff --git a/contrib/cube/cube--1.0--1.1.sql b/contrib/cube/cube--1.0--1.1.sql
new file mode 100644
index ...f032f73
*** a/contrib/cube/cube--1.0--1.1.sql
--- b/contrib/cube/cube--1.0--1.1.sql
***************
*** 0 ****
--- 1,60 ----
+ /* contrib/cube/cube--1.0--1.1.sql */
+
+ -- complain if script is sourced in psql, rather than via ALTER EXTENSION
+ \echo Use "ALTER EXTENSION cube UPDATE TO '1.1'" to load this file. \quit
+
+ CREATE FUNCTION distance_chebyshev(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION distance_taxicab(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_coord_llur(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE OPERATOR -> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+ );
+
+ CREATE OPERATOR ~> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord_llur
+ );
+
+ CREATE OPERATOR <#> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ COMMUTATOR = '<#>'
+ );
+
+ CREATE OPERATOR <-> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_distance,
+ COMMUTATOR = '<->'
+ );
+
+ CREATE OPERATOR <=> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ COMMUTATOR = '<=>'
+ );
+
+ CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ ALTER OPERATOR FAMILY gist_cube_ops USING gist ADD
+ OPERATOR 15 ~> (cube, int) FOR ORDER BY float_ops,
+ OPERATOR 16 <#> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 17 <-> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 18 <=> (cube, cube) FOR ORDER BY float_ops,
+ FUNCTION 8 (cube, cube) g_cube_distance (internal, cube, smallint, oid);
+
diff --git a/contrib/cube/cube--1.0.sql b/contrib/cube/cube--1.0.sql
new file mode .
index 0307811..e69de29
*** a/contrib/cube/cube--1.0.sql
--- b/contrib/cube/cube--1.0.sql
***************
*** 1,325 ****
- /* contrib/cube/cube--1.0.sql */
-
- -- complain if script is sourced in psql, rather than via CREATE EXTENSION
- \echo Use "CREATE EXTENSION cube" to load this file. \quit
-
- -- Create the user-defined type for N-dimensional boxes
-
- CREATE FUNCTION cube_in(cstring)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(float8[], float8[]) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_a_f8_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(float8[]) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_a_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_out(cube)
- RETURNS cstring
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE TYPE cube (
- INTERNALLENGTH = variable,
- INPUT = cube_in,
- OUTPUT = cube_out,
- ALIGNMENT = double
- );
-
- COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
-
- --
- -- External C-functions for R-tree methods
- --
-
- -- Comparison methods
-
- CREATE FUNCTION cube_eq(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as';
-
- CREATE FUNCTION cube_ne(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different';
-
- CREATE FUNCTION cube_lt(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than';
-
- CREATE FUNCTION cube_gt(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than';
-
- CREATE FUNCTION cube_le(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to';
-
- CREATE FUNCTION cube_ge(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to';
-
- CREATE FUNCTION cube_cmp(cube, cube)
- RETURNS int4
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function';
-
- CREATE FUNCTION cube_contains(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains';
-
- CREATE FUNCTION cube_contained(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in';
-
- CREATE FUNCTION cube_overlap(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps';
-
- -- support routines for indexing
-
- CREATE FUNCTION cube_union(cube, cube)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_inter(cube, cube)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_size(cube)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
-
- -- Misc N-dimensional functions
-
- CREATE FUNCTION cube_subset(cube, int4[])
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- -- proximity routines
-
- CREATE FUNCTION cube_distance(cube, cube)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- -- Extracting elements functions
-
- CREATE FUNCTION cube_dim(cube)
- RETURNS int4
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_ll_coord(cube, int4)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_ur_coord(cube, int4)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(float8, float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_f8_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(cube, float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_c_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(cube, float8, float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_c_f8_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- -- Test if cube is also a point
-
- CREATE FUNCTION cube_is_point(cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- -- Increasing the size of a cube by a radius in at least n dimensions
-
- CREATE FUNCTION cube_enlarge(cube, float8, int4)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- --
- -- OPERATORS
- --
-
- CREATE OPERATOR < (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt,
- COMMUTATOR = '>', NEGATOR = '>=',
- RESTRICT = scalarltsel, JOIN = scalarltjoinsel
- );
-
- CREATE OPERATOR > (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt,
- COMMUTATOR = '<', NEGATOR = '<=',
- RESTRICT = scalargtsel, JOIN = scalargtjoinsel
- );
-
- CREATE OPERATOR <= (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_le,
- COMMUTATOR = '>=', NEGATOR = '>',
- RESTRICT = scalarltsel, JOIN = scalarltjoinsel
- );
-
- CREATE OPERATOR >= (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ge,
- COMMUTATOR = '<=', NEGATOR = '<',
- RESTRICT = scalargtsel, JOIN = scalargtjoinsel
- );
-
- CREATE OPERATOR && (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap,
- COMMUTATOR = '&&',
- RESTRICT = areasel, JOIN = areajoinsel
- );
-
- CREATE OPERATOR = (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_eq,
- COMMUTATOR = '=', NEGATOR = '<>',
- RESTRICT = eqsel, JOIN = eqjoinsel,
- MERGES
- );
-
- CREATE OPERATOR <> (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ne,
- COMMUTATOR = '<>', NEGATOR = '=',
- RESTRICT = neqsel, JOIN = neqjoinsel
- );
-
- CREATE OPERATOR @> (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
- COMMUTATOR = '<@',
- RESTRICT = contsel, JOIN = contjoinsel
- );
-
- CREATE OPERATOR <@ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
- COMMUTATOR = '@>',
- RESTRICT = contsel, JOIN = contjoinsel
- );
-
- -- these are obsolete/deprecated:
- CREATE OPERATOR @ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
- COMMUTATOR = '~',
- RESTRICT = contsel, JOIN = contjoinsel
- );
-
- CREATE OPERATOR ~ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
- COMMUTATOR = '@',
- RESTRICT = contsel, JOIN = contjoinsel
- );
-
-
- -- define the GiST support methods
- CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_compress(internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_decompress(internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_penalty(internal,internal,internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_picksplit(internal, internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_union(internal, internal)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_same(cube, cube, internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
-
- -- Create the operator classes for indexing
-
- CREATE OPERATOR CLASS cube_ops
- DEFAULT FOR TYPE cube USING btree AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 cube_cmp(cube, cube);
-
- CREATE OPERATOR CLASS gist_cube_ops
- DEFAULT FOR TYPE cube USING gist AS
- OPERATOR 3 && ,
- OPERATOR 6 = ,
- OPERATOR 7 @> ,
- OPERATOR 8 <@ ,
- OPERATOR 13 @ ,
- OPERATOR 14 ~ ,
- FUNCTION 1 g_cube_consistent (internal, cube, int, oid, internal),
- FUNCTION 2 g_cube_union (internal, internal),
- FUNCTION 3 g_cube_compress (internal),
- FUNCTION 4 g_cube_decompress (internal),
- FUNCTION 5 g_cube_penalty (internal, internal, internal),
- FUNCTION 6 g_cube_picksplit (internal, internal),
- FUNCTION 7 g_cube_same (cube, cube, internal);
--- 0 ----
diff --git a/contrib/cube/cube--1.1.sql b/contrib/cube/cube--1.1.sql
new file mode 100644
index ...c944414
*** a/contrib/cube/cube--1.1.sql
--- b/contrib/cube/cube--1.1.sql
***************
*** 0 ****
--- 1,379 ----
+ /* contrib/cube/cube--1.1.sql */
+
+ -- complain if script is sourced in psql, rather than via CREATE EXTENSION
+ \echo Use "CREATE EXTENSION cube" to load this file. \quit
+
+ -- Create the user-defined type for N-dimensional boxes
+
+ CREATE FUNCTION cube_in(cstring)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(float8[], float8[]) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_a_f8_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(float8[]) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_a_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_out(cube)
+ RETURNS cstring
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE TYPE cube (
+ INTERNALLENGTH = variable,
+ INPUT = cube_in,
+ OUTPUT = cube_out,
+ ALIGNMENT = double
+ );
+
+ COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
+
+ --
+ -- External C-functions for R-tree methods
+ --
+
+ -- Comparison methods
+
+ CREATE FUNCTION cube_eq(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as';
+
+ CREATE FUNCTION cube_ne(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different';
+
+ CREATE FUNCTION cube_lt(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than';
+
+ CREATE FUNCTION cube_gt(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than';
+
+ CREATE FUNCTION cube_le(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to';
+
+ CREATE FUNCTION cube_ge(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to';
+
+ CREATE FUNCTION cube_cmp(cube, cube)
+ RETURNS int4
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function';
+
+ CREATE FUNCTION cube_contains(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains';
+
+ CREATE FUNCTION cube_contained(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in';
+
+ CREATE FUNCTION cube_overlap(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps';
+
+ -- support routines for indexing
+
+ CREATE FUNCTION cube_union(cube, cube)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_inter(cube, cube)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_size(cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+
+ -- Misc N-dimensional functions
+
+ CREATE FUNCTION cube_subset(cube, int4[])
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- proximity routines
+
+ CREATE FUNCTION cube_distance(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION distance_chebyshev(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION distance_taxicab(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- Extracting elements functions
+
+ CREATE FUNCTION cube_dim(cube)
+ RETURNS int4
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_ll_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_ur_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_coord_llur(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(float8, float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_f8_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(cube, float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_c_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(cube, float8, float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_c_f8_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- Test if cube is also a point
+
+ CREATE FUNCTION cube_is_point(cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- Increasing the size of a cube by a radius in at least n dimensions
+
+ CREATE FUNCTION cube_enlarge(cube, float8, int4)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ --
+ -- OPERATORS
+ --
+
+ CREATE OPERATOR < (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt,
+ COMMUTATOR = '>', NEGATOR = '>=',
+ RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+ );
+
+ CREATE OPERATOR > (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt,
+ COMMUTATOR = '<', NEGATOR = '<=',
+ RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+ );
+
+ CREATE OPERATOR <= (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_le,
+ COMMUTATOR = '>=', NEGATOR = '>',
+ RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+ );
+
+ CREATE OPERATOR >= (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ge,
+ COMMUTATOR = '<=', NEGATOR = '<',
+ RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+ );
+
+ CREATE OPERATOR && (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap,
+ COMMUTATOR = '&&',
+ RESTRICT = areasel, JOIN = areajoinsel
+ );
+
+ CREATE OPERATOR = (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_eq,
+ COMMUTATOR = '=', NEGATOR = '<>',
+ RESTRICT = eqsel, JOIN = eqjoinsel,
+ MERGES
+ );
+
+ CREATE OPERATOR <> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ne,
+ COMMUTATOR = '<>', NEGATOR = '=',
+ RESTRICT = neqsel, JOIN = neqjoinsel
+ );
+
+ CREATE OPERATOR @> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
+ COMMUTATOR = '<@',
+ RESTRICT = contsel, JOIN = contjoinsel
+ );
+
+ CREATE OPERATOR <@ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
+ COMMUTATOR = '@>',
+ RESTRICT = contsel, JOIN = contjoinsel
+ );
+
+ CREATE OPERATOR -> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+ );
+
+ CREATE OPERATOR ~> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord_llur
+ );
+
+ CREATE OPERATOR <#> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ COMMUTATOR = '<#>'
+ );
+
+ CREATE OPERATOR <-> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_distance,
+ COMMUTATOR = '<->'
+ );
+
+ CREATE OPERATOR <=> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ COMMUTATOR = '<=>'
+ );
+
+ -- these are obsolete/deprecated:
+ CREATE OPERATOR @ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
+ COMMUTATOR = '~',
+ RESTRICT = contsel, JOIN = contjoinsel
+ );
+
+ CREATE OPERATOR ~ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
+ COMMUTATOR = '@',
+ RESTRICT = contsel, JOIN = contjoinsel
+ );
+
+
+ -- define the GiST support methods
+ CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_compress(internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_decompress(internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_penalty(internal,internal,internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_picksplit(internal, internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_union(internal, internal)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_same(cube, cube, internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- Create the operator classes for indexing
+
+ CREATE OPERATOR CLASS cube_ops
+ DEFAULT FOR TYPE cube USING btree AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 cube_cmp(cube, cube);
+
+ CREATE OPERATOR CLASS gist_cube_ops
+ DEFAULT FOR TYPE cube USING gist AS
+ OPERATOR 3 && ,
+ OPERATOR 6 = ,
+ OPERATOR 7 @> ,
+ OPERATOR 8 <@ ,
+ OPERATOR 13 @ ,
+ OPERATOR 14 ~ ,
+ OPERATOR 15 ~> (cube, int) FOR ORDER BY float_ops,
+ OPERATOR 16 <#> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 17 <-> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 18 <=> (cube, cube) FOR ORDER BY float_ops,
+
+ FUNCTION 1 g_cube_consistent (internal, cube, int, oid, internal),
+ FUNCTION 2 g_cube_union (internal, internal),
+ FUNCTION 3 g_cube_compress (internal),
+ FUNCTION 4 g_cube_decompress (internal),
+ FUNCTION 5 g_cube_penalty (internal, internal, internal),
+ FUNCTION 6 g_cube_picksplit (internal, internal),
+ FUNCTION 7 g_cube_same (cube, cube, internal),
+ FUNCTION 8 g_cube_distance (internal, cube, smallint, oid);
+
diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c
new file mode 100644
index a6be59e..523145a
*** a/contrib/cube/cube.c
--- b/contrib/cube/cube.c
*************** PG_FUNCTION_INFO_V1(cube_c_f8_f8);
*** 40,45 ****
--- 40,47 ----
PG_FUNCTION_INFO_V1(cube_dim);
PG_FUNCTION_INFO_V1(cube_ll_coord);
PG_FUNCTION_INFO_V1(cube_ur_coord);
+ PG_FUNCTION_INFO_V1(cube_coord);
+ PG_FUNCTION_INFO_V1(cube_coord_llur);
PG_FUNCTION_INFO_V1(cube_subset);
/*
*************** PG_FUNCTION_INFO_V1(g_cube_penalty);
*** 53,58 ****
--- 55,61 ----
PG_FUNCTION_INFO_V1(g_cube_picksplit);
PG_FUNCTION_INFO_V1(g_cube_union);
PG_FUNCTION_INFO_V1(g_cube_same);
+ PG_FUNCTION_INFO_V1(g_cube_distance);
/*
** B-tree support functions
*************** PG_FUNCTION_INFO_V1(cube_size);
*** 79,85 ****
--- 82,90 ----
/*
** miscellaneous
*/
+ PG_FUNCTION_INFO_V1(distance_taxicab);
PG_FUNCTION_INFO_V1(cube_distance);
+ PG_FUNCTION_INFO_V1(distance_chebyshev);
PG_FUNCTION_INFO_V1(cube_is_point);
PG_FUNCTION_INFO_V1(cube_enlarge);
*************** cube_distance(PG_FUNCTION_ARGS)
*** 1257,1262 ****
--- 1262,1404 ----
PG_RETURN_FLOAT8(sqrt(distance));
}
+ Datum
+ distance_taxicab(PG_FUNCTION_ARGS)
+ {
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (DIM(a) < DIM(b))
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < DIM(b); i++)
+ distance += fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = DIM(b); i < DIM(a); i++)
+ distance += fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+ }
+
+ Datum
+ distance_chebyshev(PG_FUNCTION_ARGS)
+ {
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double d, distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (DIM(a) < DIM(b))
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < DIM(b); i++)
+ {
+ d = fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+ if (d > distance)
+ distance = d;
+ }
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = DIM(b); i < DIM(a); i++)
+ {
+ d = fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+ if (d > distance)
+ distance = d;
+ }
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+ }
+
+ Datum
+ g_cube_distance(PG_FUNCTION_ARGS)
+ {
+ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+ NDBOX *cube = DatumGetNDBOX(entry->key);
+ double retval;
+
+ if (strategy == 15)
+ {
+ int coord = PG_GETARG_INT32(1);
+
+ if IS_POINT(cube)
+ {
+ retval = (cube)->x[(coord-1)%DIM(cube)];
+ }
+ else
+ {
+ retval = Min(
+ (cube)->x[(coord-1)%DIM(cube)],
+ (cube)->x[(coord-1)%DIM(cube) + DIM(cube)]
+ );
+ }
+ }
+ else
+ {
+ NDBOX *query = PG_GETARG_NDBOX(1);
+ switch(strategy)
+ {
+ case 17:
+ retval = DatumGetFloat8(DirectFunctionCall2(cube_distance,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case 18:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_chebyshev,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case 16:
+ default:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_taxicab,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ }
+ }
+ PG_RETURN_FLOAT8(retval);
+ }
+
static double
distance_1D(double a1, double a2, double b1, double b2)
{
*************** cube_ur_coord(PG_FUNCTION_ARGS)
*** 1352,1357 ****
--- 1494,1564 ----
PG_RETURN_FLOAT8(result);
}
+ /*
+ * Function returns cube coordinate.
+ * Numbers from 1 to DIM denotes first corner coordinates.
+ * Numbers from DIM+1 to 2*DIM denotes second corner coordinates.
+ */
+ Datum
+ cube_coord(PG_FUNCTION_ARGS)
+ {
+ NDBOX *cube = PG_GETARG_NDBOX(0);
+ int coord = PG_GETARG_INT16(1);
+
+ if ((coord > 0) && (coord <= 2*DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[(coord-1)%DIM(cube)] );
+ else
+ PG_RETURN_FLOAT8( (cube)->x[coord-1] );
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ errmsg("Cube index out of bounds")));
+ }
+ }
+
+
+ /*
+ * This function works like cube_coord(),
+ * but rearranges coordinates of corners to get cube representation
+ * in the form of (lower left, upper right).
+ * For historical reasons that extension allows us to create cubes in form
+ * ((2,1),(1,2)) and instead of normalizing such cube to ((1,1),(2,2)) it
+ * stores cube in original way. But to get cubes ordered by one of dimensions
+ * directly from the index without extra sort step we need some
+ * representation-independent coordinate getter. This function implements it.
+ */
+ Datum
+ cube_coord_llur(PG_FUNCTION_ARGS)
+ {
+ NDBOX *cube = PG_GETARG_NDBOX(0);
+ int coord = PG_GETARG_INT16(1);
+
+ if ((coord > 0) && (coord <= DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[coord-1] );
+ else
+ PG_RETURN_FLOAT8( Min((cube)->x[coord-1], (cube)->x[coord-1+DIM(cube)]) );
+ }
+ else if ((coord > DIM(cube)) && (coord <= 2*DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[(coord-1)%DIM(cube)] );
+ else
+ PG_RETURN_FLOAT8( Max((cube)->x[coord-1], (cube)->x[coord-1-DIM(cube)]) );
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ errmsg("Cube index out of bounds")));
+ }
+ }
+
/* Increase or decrease box size by a radius in at least n dimensions. */
Datum
cube_enlarge(PG_FUNCTION_ARGS)
diff --git a/contrib/cube/cube.control b/contrib/cube/cube.control
new file mode 100644
index ddc8d2e..f84e6c5
*** a/contrib/cube/cube.control
--- b/contrib/cube/cube.control
***************
*** 1,5 ****
# cube extension
comment = 'data type for multidimensional cubes'
! default_version = '1.0'
module_pathname = '$libdir/cube'
relocatable = true
--- 1,5 ----
# cube extension
comment = 'data type for multidimensional cubes'
! default_version = '1.1'
module_pathname = '$libdir/cube'
relocatable = true
diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out
new file mode 100644
index ca9555e..4b2d135
*** a/contrib/cube/expected/cube.out
--- b/contrib/cube/expected/cube.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1531 ----
0
(1 row)
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+ ---------------
+ 5
+ (1 row)
+
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+ -----
+ 5
+ (1 row)
+
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 4
+ (1 row)
+
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+ -----
+ 4
+ (1 row)
+
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+ ------------------
+ 7
+ (1 row)
+
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+ -----
+ 7
+ (1 row)
+
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+ ---------------
+ 0
+ (1 row)
+
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 0
+ (1 row)
+
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+ ------------------
+ 0
+ (1 row)
+
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+ ----------
+ 60
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30])->3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->6;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->-6;
+ ERROR: Cube index out of bounds
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1552,1710 ----
(2424, 160),(2424, 81)
(5 rows)
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+ (5 rows)
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+ ---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+ ---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+ -------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+ -------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+ (15 rows)
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+ ------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+ (15 rows)
+
diff --git a/contrib/cube/expected/cube_1.out b/contrib/cube/expected/cube_1.out
new file mode 100644
index c07d61d..15e94c2
*** a/contrib/cube/expected/cube_1.out
--- b/contrib/cube/expected/cube_1.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1531 ----
0
(1 row)
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+ ---------------
+ 5
+ (1 row)
+
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+ -----
+ 5
+ (1 row)
+
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 4
+ (1 row)
+
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+ -----
+ 4
+ (1 row)
+
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+ ------------------
+ 7
+ (1 row)
+
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+ -----
+ 7
+ (1 row)
+
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+ ---------------
+ 0
+ (1 row)
+
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 0
+ (1 row)
+
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+ ------------------
+ 0
+ (1 row)
+
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+ ----------
+ 60
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30])->3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->6;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->-6;
+ ERROR: Cube index out of bounds
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1552,1710 ----
(2424, 160),(2424, 81)
(5 rows)
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+ (5 rows)
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+ ---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+ ---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+ -------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+ -------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+ (15 rows)
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+ ------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+ (15 rows)
+
diff --git a/contrib/cube/expected/cube_2.out b/contrib/cube/expected/cube_2.out
new file mode 100644
index 3767d0e..a6c7d36
*** a/contrib/cube/expected/cube_2.out
--- b/contrib/cube/expected/cube_2.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1531 ----
0
(1 row)
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+ ---------------
+ 5
+ (1 row)
+
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+ -----
+ 5
+ (1 row)
+
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 4
+ (1 row)
+
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+ -----
+ 4
+ (1 row)
+
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+ ------------------
+ 7
+ (1 row)
+
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+ -----
+ 7
+ (1 row)
+
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+ ---------------
+ 0
+ (1 row)
+
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 0
+ (1 row)
+
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+ ------------------
+ 0
+ (1 row)
+
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+ ----------
+ 60
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30])->3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->6;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->-6;
+ ERROR: Cube index out of bounds
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1552,1710 ----
(2424, 160),(2424, 81)
(5 rows)
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+ (5 rows)
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+ ---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+ ---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+ -------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+ -------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+ (15 rows)
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+ ------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+ (15 rows)
+
diff --git a/contrib/cube/expected/cube_3.out b/contrib/cube/expected/cube_3.out
new file mode 100644
index 2aa42be..f671128
*** a/contrib/cube/expected/cube_3.out
--- b/contrib/cube/expected/cube_3.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1531 ----
0
(1 row)
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+ ---------------
+ 5
+ (1 row)
+
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+ -----
+ 5
+ (1 row)
+
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 4
+ (1 row)
+
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+ -----
+ 4
+ (1 row)
+
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+ ------------------
+ 7
+ (1 row)
+
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+ -----
+ 7
+ (1 row)
+
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+ ---------------
+ 0
+ (1 row)
+
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 0
+ (1 row)
+
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+ ------------------
+ 0
+ (1 row)
+
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+ ----------
+ 60
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30])->3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->6;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->-6;
+ ERROR: Cube index out of bounds
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1552,1710 ----
(2424, 160),(2424, 81)
(5 rows)
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+ (5 rows)
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+ ---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+ ---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+ -------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+ -------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+ (15 rows)
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+ ------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+ (15 rows)
+
diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql
new file mode 100644
index d58974c..e225fb7
*** a/contrib/cube/sql/cube.sql
--- b/contrib/cube/sql/cube.sql
*************** SELECT cube_inter('(1,2,3)'::cube, '(5,6
*** 325,330 ****
--- 325,365 ----
SELECT cube_size('(4,8),(15,16)'::cube);
SELECT cube_size('(42,137)'::cube);
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ SELECT cube(array[10,20,30])->3;
+ SELECT cube(array[10,20,30])->6;
+ SELECT cube(array[10,20,30])->-6;
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 336,338 ****
--- 371,391 ----
-- Test sorting
SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
+
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
diff --git a/doc/src/sgml/cube.sgml b/doc/src/sgml/cube.sgml
new file mode 100644
index 0a226ca..ab42419
*** a/doc/src/sgml/cube.sgml
--- b/doc/src/sgml/cube.sgml
***************
*** 71,80 ****
</table>
<para>
! It does not matter which order the opposite corners of a cube are
! entered in. The <type>cube</> functions
! automatically swap values if needed to create a uniform
! <quote>lower left — upper right</> internal representation.
</para>
<para>
--- 71,77 ----
</table>
<para>
! When corners coincide cube stores only one corner along with a special flag in order to reduce size wasted.
</para>
<para>
***************
*** 131,136 ****
--- 128,143 ----
<entry><literal>a <@ b</></entry>
<entry>The cube a is contained in the cube b.</entry>
</row>
+
+ <row>
+ <entry><literal>a -> n</></entry>
+ <entry>Get n-th coordinate of cube.</entry>
+ </row>
+
+ <row>
+ <entry><literal>a ~> n</></entry>
+ <entry>Get n-th coordinate in 'normalized' cube representation. Noramlization means coordinate rearrangement to form (lower left, upper right).</entry>
+ </row>
</tbody>
</tgroup>
</table>
***************
*** 144,149 ****
--- 151,228 ----
</para>
<para>
+ GiST index can be used to retrieve nearest neighbours via several metric operators. As always any of them can be used as ordinary function.
+ </para>
+
+ <table id="cube-gistknn-operators">
+ <title>Cube GiST-kNN Operators</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Operator</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>a <-> b</></entry>
+ <entry>Euclidean distance between a and b</entry>
+ </row>
+
+ <row>
+ <entry><literal>a <#> b</></entry>
+ <entry>Taxicab (L-1 metric) distance between a and b</entry>
+ </row>
+
+ <row>
+ <entry><literal>a <=> b</></entry>
+ <entry>Chebyshev (L-inf metric) distance between a and b</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Selection of nearing neigbours can be done in the following way:
+ </para>
+ <programlisting>
+ SELECT c FROM test
+ ORDER BY cube(array[0.5,0.5,0.5])<->c
+ LIMIT 1;
+ </programlisting>
+
+
+ <para>
+ Also kNN framework allows us to cheat with metrics in order to get results sorted by selected coodinate directly from the index without extra sorting step. That technique significantly faster on small values of LIMIT, however with bigger values of LIMIT planner will switch automatically to standart index scan and sort.
+ That behavior can be achieved using coordinate operator (cube c)~>(int offset).
+ </para>
+ <programlisting>
+ => select cube(array[0.41,0.42,0.43])~>2 as coord;
+ coord
+ -------
+ 0.42
+ (1 row)
+ </programlisting>
+
+ <para>
+ So using that operator as kNN metric we can obtain cubes sorted by it's coordinate.
+ </para>
+ <para>
+ To get cubes ordered by first coordinate of lower left corner ascending one can use the following query:
+ </para>
+ <programlisting>
+ SELECT c FROM test ORDER BY c~>1 LIMIT 5;
+ </programlisting>
+ <para>
+ And to get cubes descending by first coordinate of upper right corner of 2d-cube:
+ </para>
+ <programlisting>
+ SELECT c FROM test ORDER BY c~>3 DESC LIMIT 5;
+ </programlisting>
+
+
+
+ <para>
The standard B-tree operators are also provided, for example
<informaltable>
Patch looks good, but there ara some review notices:
1 gmake installcheck fails:
*** /.../pgsql/contrib/cube/expected/cube_1.out 2015-12-01 17:49:01.768764000
+0300
--- /.../pgsql/contrib/cube/results/cube.out 2015-12-01 17:49:12.190818000
+0300
***************
*** 1382,1388 ****
(1 row)
-- Test of distances
! --
SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
cube_distance
---------------
--- 1382,1388 ----
(1 row)
-- Test of distances
! --
SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
cube_distance
Seems, there a extra space at the end of string
2 Pls, don use in C-code magick constants like 'case 16:'. Use macros to define
some human-readable name (g_cube_distance())
3 Switch in g_cube_distance(): default switch path should generate a error. It
just simplifies a degbug process, may be in future.
4 Docs: pls, don't use a strings with unlimited length.
Stas Kelvich wrote:
Hello.
That is updated version of the patch with proper update scripts.
Also i’ve noted that documentation states the wrong thing:
“It does not matter which order the opposite corners of a cube are entered in. The cube functions automatically swap values if needed to create a uniform "lower left — upper right" internal representation."
But in practice cubes stored "as is" and that leads to problems with getting cubes sorted along specific dimension directly from index.
As a simplest workaround i’ve deleted that sentence from docs and implemented two coordinate getters -> and ~>. First one returns
coordinate of cube as it stored, and second returns coordinate of cube normalised to (LL,UR)-form.Other way to fix thing is to force ’normalization’ while creating cube. But that can produce wrong sorts with already existing data.
On 09 Jul 2015, at 16:40, Alexander Korotkov <aekorotkov@gmail.com> wrote:
Hi!
On Sat, May 9, 2015 at 6:53 AM, Stas Kelvich <stas.kelvich@gmail.com> wrote:
Patch is pretty ready, last issue was about changed extension interface, so there should be migration script and version bump.
Attaching a version with all migration stuff.I can't see cube--1.0--1.1.sql in the patch. Did forget to include it?
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres CompanyStas Kelvich
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello, fixed.
Attachments:
cube_distances.diffapplication/octet-stream; name=cube_distances.diffDownload
diff --git a/contrib/cube/Makefile b/contrib/cube/Makefile
new file mode 100644
index 67f7867..e2a5d2c
*** a/contrib/cube/Makefile
--- b/contrib/cube/Makefile
*************** MODULE_big = cube
*** 4,10 ****
OBJS= cube.o cubeparse.o $(WIN32RES)
EXTENSION = cube
! DATA = cube--1.0.sql cube--unpackaged--1.0.sql
PGFILEDESC = "cube - multidimensional cube data type"
REGRESS = cube
--- 4,10 ----
OBJS= cube.o cubeparse.o $(WIN32RES)
EXTENSION = cube
! DATA = cube--1.1.sql cube--1.0--1.1.sql cube--unpackaged--1.0.sql
PGFILEDESC = "cube - multidimensional cube data type"
REGRESS = cube
diff --git a/contrib/cube/cube--1.0--1.1.sql b/contrib/cube/cube--1.0--1.1.sql
new file mode 100644
index ...f032f73
*** a/contrib/cube/cube--1.0--1.1.sql
--- b/contrib/cube/cube--1.0--1.1.sql
***************
*** 0 ****
--- 1,60 ----
+ /* contrib/cube/cube--1.0--1.1.sql */
+
+ -- complain if script is sourced in psql, rather than via ALTER EXTENSION
+ \echo Use "ALTER EXTENSION cube UPDATE TO '1.1'" to load this file. \quit
+
+ CREATE FUNCTION distance_chebyshev(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION distance_taxicab(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_coord_llur(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE OPERATOR -> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+ );
+
+ CREATE OPERATOR ~> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord_llur
+ );
+
+ CREATE OPERATOR <#> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ COMMUTATOR = '<#>'
+ );
+
+ CREATE OPERATOR <-> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_distance,
+ COMMUTATOR = '<->'
+ );
+
+ CREATE OPERATOR <=> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ COMMUTATOR = '<=>'
+ );
+
+ CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ ALTER OPERATOR FAMILY gist_cube_ops USING gist ADD
+ OPERATOR 15 ~> (cube, int) FOR ORDER BY float_ops,
+ OPERATOR 16 <#> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 17 <-> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 18 <=> (cube, cube) FOR ORDER BY float_ops,
+ FUNCTION 8 (cube, cube) g_cube_distance (internal, cube, smallint, oid);
+
diff --git a/contrib/cube/cube--1.0.sql b/contrib/cube/cube--1.0.sql
new file mode .
index 0307811..e69de29
*** a/contrib/cube/cube--1.0.sql
--- b/contrib/cube/cube--1.0.sql
***************
*** 1,325 ****
- /* contrib/cube/cube--1.0.sql */
-
- -- complain if script is sourced in psql, rather than via CREATE EXTENSION
- \echo Use "CREATE EXTENSION cube" to load this file. \quit
-
- -- Create the user-defined type for N-dimensional boxes
-
- CREATE FUNCTION cube_in(cstring)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(float8[], float8[]) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_a_f8_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(float8[]) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_a_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_out(cube)
- RETURNS cstring
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE TYPE cube (
- INTERNALLENGTH = variable,
- INPUT = cube_in,
- OUTPUT = cube_out,
- ALIGNMENT = double
- );
-
- COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
-
- --
- -- External C-functions for R-tree methods
- --
-
- -- Comparison methods
-
- CREATE FUNCTION cube_eq(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as';
-
- CREATE FUNCTION cube_ne(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different';
-
- CREATE FUNCTION cube_lt(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than';
-
- CREATE FUNCTION cube_gt(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than';
-
- CREATE FUNCTION cube_le(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to';
-
- CREATE FUNCTION cube_ge(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to';
-
- CREATE FUNCTION cube_cmp(cube, cube)
- RETURNS int4
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function';
-
- CREATE FUNCTION cube_contains(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains';
-
- CREATE FUNCTION cube_contained(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in';
-
- CREATE FUNCTION cube_overlap(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps';
-
- -- support routines for indexing
-
- CREATE FUNCTION cube_union(cube, cube)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_inter(cube, cube)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_size(cube)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
-
- -- Misc N-dimensional functions
-
- CREATE FUNCTION cube_subset(cube, int4[])
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- -- proximity routines
-
- CREATE FUNCTION cube_distance(cube, cube)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- -- Extracting elements functions
-
- CREATE FUNCTION cube_dim(cube)
- RETURNS int4
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_ll_coord(cube, int4)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube_ur_coord(cube, int4)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(float8, float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_f8_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(cube, float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_c_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION cube(cube, float8, float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_c_f8_f8'
- LANGUAGE C IMMUTABLE STRICT;
-
- -- Test if cube is also a point
-
- CREATE FUNCTION cube_is_point(cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- -- Increasing the size of a cube by a radius in at least n dimensions
-
- CREATE FUNCTION cube_enlarge(cube, float8, int4)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- --
- -- OPERATORS
- --
-
- CREATE OPERATOR < (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt,
- COMMUTATOR = '>', NEGATOR = '>=',
- RESTRICT = scalarltsel, JOIN = scalarltjoinsel
- );
-
- CREATE OPERATOR > (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt,
- COMMUTATOR = '<', NEGATOR = '<=',
- RESTRICT = scalargtsel, JOIN = scalargtjoinsel
- );
-
- CREATE OPERATOR <= (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_le,
- COMMUTATOR = '>=', NEGATOR = '>',
- RESTRICT = scalarltsel, JOIN = scalarltjoinsel
- );
-
- CREATE OPERATOR >= (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ge,
- COMMUTATOR = '<=', NEGATOR = '<',
- RESTRICT = scalargtsel, JOIN = scalargtjoinsel
- );
-
- CREATE OPERATOR && (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap,
- COMMUTATOR = '&&',
- RESTRICT = areasel, JOIN = areajoinsel
- );
-
- CREATE OPERATOR = (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_eq,
- COMMUTATOR = '=', NEGATOR = '<>',
- RESTRICT = eqsel, JOIN = eqjoinsel,
- MERGES
- );
-
- CREATE OPERATOR <> (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ne,
- COMMUTATOR = '<>', NEGATOR = '=',
- RESTRICT = neqsel, JOIN = neqjoinsel
- );
-
- CREATE OPERATOR @> (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
- COMMUTATOR = '<@',
- RESTRICT = contsel, JOIN = contjoinsel
- );
-
- CREATE OPERATOR <@ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
- COMMUTATOR = '@>',
- RESTRICT = contsel, JOIN = contjoinsel
- );
-
- -- these are obsolete/deprecated:
- CREATE OPERATOR @ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
- COMMUTATOR = '~',
- RESTRICT = contsel, JOIN = contjoinsel
- );
-
- CREATE OPERATOR ~ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
- COMMUTATOR = '@',
- RESTRICT = contsel, JOIN = contjoinsel
- );
-
-
- -- define the GiST support methods
- CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_compress(internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_decompress(internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_penalty(internal,internal,internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_picksplit(internal, internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_union(internal, internal)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
- CREATE FUNCTION g_cube_same(cube, cube, internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
-
-
- -- Create the operator classes for indexing
-
- CREATE OPERATOR CLASS cube_ops
- DEFAULT FOR TYPE cube USING btree AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 cube_cmp(cube, cube);
-
- CREATE OPERATOR CLASS gist_cube_ops
- DEFAULT FOR TYPE cube USING gist AS
- OPERATOR 3 && ,
- OPERATOR 6 = ,
- OPERATOR 7 @> ,
- OPERATOR 8 <@ ,
- OPERATOR 13 @ ,
- OPERATOR 14 ~ ,
- FUNCTION 1 g_cube_consistent (internal, cube, int, oid, internal),
- FUNCTION 2 g_cube_union (internal, internal),
- FUNCTION 3 g_cube_compress (internal),
- FUNCTION 4 g_cube_decompress (internal),
- FUNCTION 5 g_cube_penalty (internal, internal, internal),
- FUNCTION 6 g_cube_picksplit (internal, internal),
- FUNCTION 7 g_cube_same (cube, cube, internal);
--- 0 ----
diff --git a/contrib/cube/cube--1.1.sql b/contrib/cube/cube--1.1.sql
new file mode 100644
index ...c944414
*** a/contrib/cube/cube--1.1.sql
--- b/contrib/cube/cube--1.1.sql
***************
*** 0 ****
--- 1,379 ----
+ /* contrib/cube/cube--1.1.sql */
+
+ -- complain if script is sourced in psql, rather than via CREATE EXTENSION
+ \echo Use "CREATE EXTENSION cube" to load this file. \quit
+
+ -- Create the user-defined type for N-dimensional boxes
+
+ CREATE FUNCTION cube_in(cstring)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(float8[], float8[]) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_a_f8_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(float8[]) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_a_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_out(cube)
+ RETURNS cstring
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE TYPE cube (
+ INTERNALLENGTH = variable,
+ INPUT = cube_in,
+ OUTPUT = cube_out,
+ ALIGNMENT = double
+ );
+
+ COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
+
+ --
+ -- External C-functions for R-tree methods
+ --
+
+ -- Comparison methods
+
+ CREATE FUNCTION cube_eq(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as';
+
+ CREATE FUNCTION cube_ne(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different';
+
+ CREATE FUNCTION cube_lt(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than';
+
+ CREATE FUNCTION cube_gt(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than';
+
+ CREATE FUNCTION cube_le(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to';
+
+ CREATE FUNCTION cube_ge(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to';
+
+ CREATE FUNCTION cube_cmp(cube, cube)
+ RETURNS int4
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function';
+
+ CREATE FUNCTION cube_contains(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains';
+
+ CREATE FUNCTION cube_contained(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in';
+
+ CREATE FUNCTION cube_overlap(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps';
+
+ -- support routines for indexing
+
+ CREATE FUNCTION cube_union(cube, cube)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_inter(cube, cube)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_size(cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+
+ -- Misc N-dimensional functions
+
+ CREATE FUNCTION cube_subset(cube, int4[])
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- proximity routines
+
+ CREATE FUNCTION cube_distance(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION distance_chebyshev(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION distance_taxicab(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- Extracting elements functions
+
+ CREATE FUNCTION cube_dim(cube)
+ RETURNS int4
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_ll_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_ur_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube_coord_llur(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(float8, float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_f8_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(cube, float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_c_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION cube(cube, float8, float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_c_f8_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- Test if cube is also a point
+
+ CREATE FUNCTION cube_is_point(cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- Increasing the size of a cube by a radius in at least n dimensions
+
+ CREATE FUNCTION cube_enlarge(cube, float8, int4)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ --
+ -- OPERATORS
+ --
+
+ CREATE OPERATOR < (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt,
+ COMMUTATOR = '>', NEGATOR = '>=',
+ RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+ );
+
+ CREATE OPERATOR > (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt,
+ COMMUTATOR = '<', NEGATOR = '<=',
+ RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+ );
+
+ CREATE OPERATOR <= (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_le,
+ COMMUTATOR = '>=', NEGATOR = '>',
+ RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+ );
+
+ CREATE OPERATOR >= (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ge,
+ COMMUTATOR = '<=', NEGATOR = '<',
+ RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+ );
+
+ CREATE OPERATOR && (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap,
+ COMMUTATOR = '&&',
+ RESTRICT = areasel, JOIN = areajoinsel
+ );
+
+ CREATE OPERATOR = (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_eq,
+ COMMUTATOR = '=', NEGATOR = '<>',
+ RESTRICT = eqsel, JOIN = eqjoinsel,
+ MERGES
+ );
+
+ CREATE OPERATOR <> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ne,
+ COMMUTATOR = '<>', NEGATOR = '=',
+ RESTRICT = neqsel, JOIN = neqjoinsel
+ );
+
+ CREATE OPERATOR @> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
+ COMMUTATOR = '<@',
+ RESTRICT = contsel, JOIN = contjoinsel
+ );
+
+ CREATE OPERATOR <@ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
+ COMMUTATOR = '@>',
+ RESTRICT = contsel, JOIN = contjoinsel
+ );
+
+ CREATE OPERATOR -> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+ );
+
+ CREATE OPERATOR ~> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord_llur
+ );
+
+ CREATE OPERATOR <#> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ COMMUTATOR = '<#>'
+ );
+
+ CREATE OPERATOR <-> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_distance,
+ COMMUTATOR = '<->'
+ );
+
+ CREATE OPERATOR <=> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ COMMUTATOR = '<=>'
+ );
+
+ -- these are obsolete/deprecated:
+ CREATE OPERATOR @ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
+ COMMUTATOR = '~',
+ RESTRICT = contsel, JOIN = contjoinsel
+ );
+
+ CREATE OPERATOR ~ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
+ COMMUTATOR = '@',
+ RESTRICT = contsel, JOIN = contjoinsel
+ );
+
+
+ -- define the GiST support methods
+ CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_compress(internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_decompress(internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_penalty(internal,internal,internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_picksplit(internal, internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_union(internal, internal)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_same(cube, cube, internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ -- Create the operator classes for indexing
+
+ CREATE OPERATOR CLASS cube_ops
+ DEFAULT FOR TYPE cube USING btree AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 cube_cmp(cube, cube);
+
+ CREATE OPERATOR CLASS gist_cube_ops
+ DEFAULT FOR TYPE cube USING gist AS
+ OPERATOR 3 && ,
+ OPERATOR 6 = ,
+ OPERATOR 7 @> ,
+ OPERATOR 8 <@ ,
+ OPERATOR 13 @ ,
+ OPERATOR 14 ~ ,
+ OPERATOR 15 ~> (cube, int) FOR ORDER BY float_ops,
+ OPERATOR 16 <#> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 17 <-> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 18 <=> (cube, cube) FOR ORDER BY float_ops,
+
+ FUNCTION 1 g_cube_consistent (internal, cube, int, oid, internal),
+ FUNCTION 2 g_cube_union (internal, internal),
+ FUNCTION 3 g_cube_compress (internal),
+ FUNCTION 4 g_cube_decompress (internal),
+ FUNCTION 5 g_cube_penalty (internal, internal, internal),
+ FUNCTION 6 g_cube_picksplit (internal, internal),
+ FUNCTION 7 g_cube_same (cube, cube, internal),
+ FUNCTION 8 g_cube_distance (internal, cube, smallint, oid);
+
diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c
new file mode 100644
index a6be59e..676d9a9
*** a/contrib/cube/cube.c
--- b/contrib/cube/cube.c
*************** PG_FUNCTION_INFO_V1(cube_c_f8_f8);
*** 40,45 ****
--- 40,47 ----
PG_FUNCTION_INFO_V1(cube_dim);
PG_FUNCTION_INFO_V1(cube_ll_coord);
PG_FUNCTION_INFO_V1(cube_ur_coord);
+ PG_FUNCTION_INFO_V1(cube_coord);
+ PG_FUNCTION_INFO_V1(cube_coord_llur);
PG_FUNCTION_INFO_V1(cube_subset);
/*
*************** PG_FUNCTION_INFO_V1(g_cube_penalty);
*** 53,58 ****
--- 55,62 ----
PG_FUNCTION_INFO_V1(g_cube_picksplit);
PG_FUNCTION_INFO_V1(g_cube_union);
PG_FUNCTION_INFO_V1(g_cube_same);
+ PG_FUNCTION_INFO_V1(g_cube_distance);
+
/*
** B-tree support functions
*************** PG_FUNCTION_INFO_V1(cube_size);
*** 79,85 ****
--- 83,91 ----
/*
** miscellaneous
*/
+ PG_FUNCTION_INFO_V1(distance_taxicab);
PG_FUNCTION_INFO_V1(cube_distance);
+ PG_FUNCTION_INFO_V1(distance_chebyshev);
PG_FUNCTION_INFO_V1(cube_is_point);
PG_FUNCTION_INFO_V1(cube_enlarge);
*************** cube_overlap(PG_FUNCTION_ARGS)
*** 1202,1208 ****
PG_RETURN_BOOL(res);
}
-
/* Distance */
/* The distance is computed as a per axis sum of the squared distances
between 1D projections of the boxes onto Cartesian axes. Assuming zero
--- 1208,1213 ----
*************** cube_distance(PG_FUNCTION_ARGS)
*** 1257,1262 ****
--- 1262,1405 ----
PG_RETURN_FLOAT8(sqrt(distance));
}
+ Datum
+ distance_taxicab(PG_FUNCTION_ARGS)
+ {
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (DIM(a) < DIM(b))
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < DIM(b); i++)
+ distance += fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = DIM(b); i < DIM(a); i++)
+ distance += fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+ }
+
+ Datum
+ distance_chebyshev(PG_FUNCTION_ARGS)
+ {
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double d, distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (DIM(a) < DIM(b))
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < DIM(b); i++)
+ {
+ d = fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+ if (d > distance)
+ distance = d;
+ }
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = DIM(b); i < DIM(a); i++)
+ {
+ d = fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+ if (d > distance)
+ distance = d;
+ }
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+ }
+
+ Datum
+ g_cube_distance(PG_FUNCTION_ARGS)
+ {
+ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+ NDBOX *cube = DatumGetNDBOX(entry->key);
+ double retval;
+
+ if (strategy == CubeKNNDistanceCoord)
+ {
+ int coord = PG_GETARG_INT32(1);
+
+ if IS_POINT(cube)
+ {
+ retval = (cube)->x[(coord-1)%DIM(cube)];
+ }
+ else
+ {
+ retval = Min(
+ (cube)->x[(coord-1)%DIM(cube)],
+ (cube)->x[(coord-1)%DIM(cube) + DIM(cube)]
+ );
+ }
+ }
+ else
+ {
+ NDBOX *query = PG_GETARG_NDBOX(1);
+ switch(strategy)
+ {
+ case CubeKNNDistanceTaxicab:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_taxicab,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case CubeKNNDistanceEuclid:
+ retval = DatumGetFloat8(DirectFunctionCall2(cube_distance,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case CubeKNNDistanceChebyshev:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_chebyshev,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ default:
+ elog(ERROR, "Cube: unknown strategy number.");
+ }
+ }
+ PG_RETURN_FLOAT8(retval);
+ }
+
static double
distance_1D(double a1, double a2, double b1, double b2)
{
*************** cube_ur_coord(PG_FUNCTION_ARGS)
*** 1352,1357 ****
--- 1495,1565 ----
PG_RETURN_FLOAT8(result);
}
+ /*
+ * Function returns cube coordinate.
+ * Numbers from 1 to DIM denotes first corner coordinates.
+ * Numbers from DIM+1 to 2*DIM denotes second corner coordinates.
+ */
+ Datum
+ cube_coord(PG_FUNCTION_ARGS)
+ {
+ NDBOX *cube = PG_GETARG_NDBOX(0);
+ int coord = PG_GETARG_INT16(1);
+
+ if ((coord > 0) && (coord <= 2*DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[(coord-1)%DIM(cube)] );
+ else
+ PG_RETURN_FLOAT8( (cube)->x[coord-1] );
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ errmsg("Cube index out of bounds")));
+ }
+ }
+
+
+ /*
+ * This function works like cube_coord(),
+ * but rearranges coordinates of corners to get cube representation
+ * in the form of (lower left, upper right).
+ * For historical reasons that extension allows us to create cubes in form
+ * ((2,1),(1,2)) and instead of normalizing such cube to ((1,1),(2,2)) it
+ * stores cube in original way. But to get cubes ordered by one of dimensions
+ * directly from the index without extra sort step we need some
+ * representation-independent coordinate getter. This function implements it.
+ */
+ Datum
+ cube_coord_llur(PG_FUNCTION_ARGS)
+ {
+ NDBOX *cube = PG_GETARG_NDBOX(0);
+ int coord = PG_GETARG_INT16(1);
+
+ if ((coord > 0) && (coord <= DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[coord-1] );
+ else
+ PG_RETURN_FLOAT8( Min((cube)->x[coord-1], (cube)->x[coord-1+DIM(cube)]) );
+ }
+ else if ((coord > DIM(cube)) && (coord <= 2*DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[(coord-1)%DIM(cube)] );
+ else
+ PG_RETURN_FLOAT8( Max((cube)->x[coord-1], (cube)->x[coord-1-DIM(cube)]) );
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ errmsg("Cube index out of bounds")));
+ }
+ }
+
/* Increase or decrease box size by a radius in at least n dimensions. */
Datum
cube_enlarge(PG_FUNCTION_ARGS)
diff --git a/contrib/cube/cube.control b/contrib/cube/cube.control
new file mode 100644
index ddc8d2e..f84e6c5
*** a/contrib/cube/cube.control
--- b/contrib/cube/cube.control
***************
*** 1,5 ****
# cube extension
comment = 'data type for multidimensional cubes'
! default_version = '1.0'
module_pathname = '$libdir/cube'
relocatable = true
--- 1,5 ----
# cube extension
comment = 'data type for multidimensional cubes'
! default_version = '1.1'
module_pathname = '$libdir/cube'
relocatable = true
diff --git a/contrib/cube/cubedata.h b/contrib/cube/cubedata.h
new file mode 100644
index 59c23de..7eaac39
*** a/contrib/cube/cubedata.h
--- b/contrib/cube/cubedata.h
*************** typedef struct NDBOX
*** 47,52 ****
--- 47,57 ----
#define PG_GETARG_NDBOX(x) DatumGetNDBOX(PG_GETARG_DATUM(x))
#define PG_RETURN_NDBOX(x) PG_RETURN_POINTER(x)
+ #define CubeKNNDistanceCoord 15 /* ~> */
+ #define CubeKNNDistanceTaxicab 16 /* <#> */
+ #define CubeKNNDistanceEuclid 17 /* <-> */
+ #define CubeKNNDistanceChebyshev 18 /* <=> */
+
/* in cubescan.l */
extern int cube_yylex(void);
extern void cube_yyerror(NDBOX **result, const char *message) pg_attribute_noreturn();
diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out
new file mode 100644
index ca9555e..4b2d135
*** a/contrib/cube/expected/cube.out
--- b/contrib/cube/expected/cube.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1531 ----
0
(1 row)
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+ ---------------
+ 5
+ (1 row)
+
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+ -----
+ 5
+ (1 row)
+
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 4
+ (1 row)
+
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+ -----
+ 4
+ (1 row)
+
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+ ------------------
+ 7
+ (1 row)
+
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+ -----
+ 7
+ (1 row)
+
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+ ---------------
+ 0
+ (1 row)
+
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 0
+ (1 row)
+
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+ ------------------
+ 0
+ (1 row)
+
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+ ----------
+ 60
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30])->3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->6;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->-6;
+ ERROR: Cube index out of bounds
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1552,1710 ----
(2424, 160),(2424, 81)
(5 rows)
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+ (5 rows)
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+ ---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+ ---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+ -------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+ -------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+ (15 rows)
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+ ------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+ (15 rows)
+
diff --git a/contrib/cube/expected/cube_1.out b/contrib/cube/expected/cube_1.out
new file mode 100644
index c07d61d..7178088
*** a/contrib/cube/expected/cube_1.out
--- b/contrib/cube/expected/cube_1.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1531 ----
0
(1 row)
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+ ---------------
+ 5
+ (1 row)
+
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+ -----
+ 5
+ (1 row)
+
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 4
+ (1 row)
+
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+ -----
+ 4
+ (1 row)
+
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+ ------------------
+ 7
+ (1 row)
+
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+ -----
+ 7
+ (1 row)
+
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+ ---------------
+ 0
+ (1 row)
+
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 0
+ (1 row)
+
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+ ------------------
+ 0
+ (1 row)
+
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+ ----------
+ 60
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30])->3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->6;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->-6;
+ ERROR: Cube index out of bounds
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1552,1710 ----
(2424, 160),(2424, 81)
(5 rows)
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+ (5 rows)
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+ ---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+ ---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+ -------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+ -------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+ (15 rows)
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+ ------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+ (15 rows)
+
diff --git a/contrib/cube/expected/cube_2.out b/contrib/cube/expected/cube_2.out
new file mode 100644
index 3767d0e..a6c7d36
*** a/contrib/cube/expected/cube_2.out
--- b/contrib/cube/expected/cube_2.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1531 ----
0
(1 row)
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+ ---------------
+ 5
+ (1 row)
+
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+ -----
+ 5
+ (1 row)
+
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 4
+ (1 row)
+
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+ -----
+ 4
+ (1 row)
+
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+ ------------------
+ 7
+ (1 row)
+
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+ -----
+ 7
+ (1 row)
+
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+ ---------------
+ 0
+ (1 row)
+
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 0
+ (1 row)
+
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+ ------------------
+ 0
+ (1 row)
+
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+ ----------
+ 60
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30])->3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->6;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->-6;
+ ERROR: Cube index out of bounds
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1552,1710 ----
(2424, 160),(2424, 81)
(5 rows)
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+ (5 rows)
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+ ---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+ ---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+ -------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+ -------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+ (15 rows)
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+ ------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+ (15 rows)
+
diff --git a/contrib/cube/expected/cube_3.out b/contrib/cube/expected/cube_3.out
new file mode 100644
index 2aa42be..f671128
*** a/contrib/cube/expected/cube_3.out
--- b/contrib/cube/expected/cube_3.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1531 ----
0
(1 row)
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+ ---------------
+ 5
+ (1 row)
+
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+ -----
+ 5
+ (1 row)
+
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 4
+ (1 row)
+
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+ -----
+ 4
+ (1 row)
+
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+ ------------------
+ 7
+ (1 row)
+
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+ -----
+ 7
+ (1 row)
+
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+ ---------------
+ 0
+ (1 row)
+
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+ --------------------
+ 0
+ (1 row)
+
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+ ------------------
+ 0
+ (1 row)
+
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+ ----------
+ 60
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[10,20,30])->3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->6;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[10,20,30])->-6;
+ ERROR: Cube index out of bounds
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+ ----------
+ 10
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+ ----------
+ 20
+ (1 row)
+
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+ ----------
+ 30
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ ERROR: Cube index out of bounds
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+ ----------
+ 40
+ (1 row)
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1552,1710 ----
(2424, 160),(2424, 81)
(5 rows)
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (5 rows)
+
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+ -------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+ (5 rows)
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+ ---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+ ---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+ -------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+ (15 rows)
+
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+ -------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+ (15 rows)
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+ --------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+ (15 rows)
+
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+ ------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+ (15 rows)
+
diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql
new file mode 100644
index d58974c..c968a3e
*** a/contrib/cube/sql/cube.sql
--- b/contrib/cube/sql/cube.sql
*************** SELECT cube_inter('(1,2,3)'::cube, '(5,6
*** 325,330 ****
--- 325,365 ----
SELECT cube_size('(4,8),(15,16)'::cube);
SELECT cube_size('(42,137)'::cube);
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ SELECT cube(array[10,20,30])->3;
+ SELECT cube(array[10,20,30])->6;
+ SELECT cube(array[10,20,30])->-6;
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 336,338 ****
--- 371,392 ----
-- Test sorting
SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
+
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+
diff --git a/doc/src/sgml/cube.sgml b/doc/src/sgml/cube.sgml
new file mode 100644
index 0a226ca..5a1cc8b
*** a/doc/src/sgml/cube.sgml
--- b/doc/src/sgml/cube.sgml
***************
*** 71,80 ****
</table>
<para>
! It does not matter which order the opposite corners of a cube are
! entered in. The <type>cube</> functions
! automatically swap values if needed to create a uniform
! <quote>lower left — upper right</> internal representation.
</para>
<para>
--- 71,77 ----
</table>
<para>
! When corners coincide cube stores only one corner along with a special flag in order to reduce size wasted.
</para>
<para>
***************
*** 131,136 ****
--- 128,146 ----
<entry><literal>a <@ b</></entry>
<entry>The cube a is contained in the cube b.</entry>
</row>
+
+ <row>
+ <entry><literal>a -> n</></entry>
+ <entry>Get n-th coordinate of cube.</entry>
+ </row>
+
+ <row>
+ <entry><literal>a ~> n</></entry>
+ <entry>
+ Get n-th coordinate in 'normalized' cube representation. Noramlization
+ means coordinate rearrangement to form (lower left, upper right).
+ </entry>
+ </row>
</tbody>
</tgroup>
</table>
***************
*** 144,149 ****
--- 154,240 ----
</para>
<para>
+ GiST index can be used to retrieve nearest neighbours via several metric
+ operators. As always any of them can be used as ordinary function.
+ </para>
+
+ <table id="cube-gistknn-operators">
+ <title>Cube GiST-kNN Operators</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Operator</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>a <-> b</></entry>
+ <entry>Euclidean distance between a and b</entry>
+ </row>
+
+ <row>
+ <entry><literal>a <#> b</></entry>
+ <entry>Taxicab (L-1 metric) distance between a and b</entry>
+ </row>
+
+ <row>
+ <entry><literal>a <=> b</></entry>
+ <entry>Chebyshev (L-inf metric) distance between a and b</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Selection of nearing neigbours can be done in the following way:
+ </para>
+ <programlisting>
+ SELECT c FROM test
+ ORDER BY cube(array[0.5,0.5,0.5])<->c
+ LIMIT 1;
+ </programlisting>
+
+
+ <para>
+ Also kNN framework allows us to cheat with metrics in order to get results
+ sorted by selected coodinate directly from the index without extra sorting
+ step. That technique significantly faster on small values of LIMIT, however
+ with bigger values of LIMIT planner will switch automatically to standart
+ index scan and sort.
+ That behavior can be achieved using coordinate operator
+ (cube c)~>(int offset).
+ </para>
+ <programlisting>
+ => select cube(array[0.41,0.42,0.43])~>2 as coord;
+ coord
+ -------
+ 0.42
+ (1 row)
+ </programlisting>
+
+ <para>
+ So using that operator as kNN metric we can obtain cubes sorted by it's
+ coordinate.
+ </para>
+ <para>
+ To get cubes ordered by first coordinate of lower left corner ascending
+ one can use the following query:
+ </para>
+ <programlisting>
+ SELECT c FROM test ORDER BY c~>1 LIMIT 5;
+ </programlisting>
+ <para>
+ And to get cubes descending by first coordinate of upper right corner
+ of 2d-cube:
+ </para>
+ <programlisting>
+ SELECT c FROM test ORDER BY c~>3 DESC LIMIT 5;
+ </programlisting>
+
+
+
+ <para>
The standard B-tree operators are also provided, for example
<informaltable>
Hi,
On 12/07/2015 03:47 PM, Stas Kelvich wrote:
Hello, fixed.
I've looked at the patch today, seems mostly fine to me.
Three comments though:
1) (nitpicking) There seem to be some minor whitespace issues, i.e.
trailing spaces, empty lines being added/removed, etc.
2) one of the regression tests started to fail
SELECT '-1e-700'::cube AS cube;
This used to return (0) but now I get (-0). As this query existed in
1.0, it's probably due to change in the C code. Now sure where.
3) I wonder why the docs were changed like this:
<para>
- It does not matter which order the opposite corners of a cube are
- entered in. The <type>cube</> functions
- automatically swap values if needed to create a uniform
- <quote>lower left — upper right</> internal representation.
+ When corners coincide cube stores only one corner along with a
special flag in order to reduce size wasted.
</para>
Was the old behavior removed? I don't think so - it seems to behave
as before, so why to remove this information? Maybe it's not useful?
But then why add the bit about optimizing storage of points?
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi, thanks for the review.
1) (nitpicking) There seem to be some minor whitespace issues, i.e.
trailing spaces, empty lines being added/removed, etc.
Fixed, I think
2) one of the regression tests started to fail
SELECT '-1e-700'::cube AS cube;
This used to return (0) but now I get (-0).
Actually that problem emerged because of the first problem. I had extra whitespace in sql file and removed that whitespace from one of the answers file (cube_1.sql), so diff with both cube.sql and cube_1.sql was one line length and you saw diff with cube.sql.
In all systems that available to me (osx/linux/freebsd) I saw that right answers file is cube_1.sql. But in other OS’es you can get +/- 0 or e27/e027. I edited that answers files manually, so there probably can be some other typos.
3) I wonder why the docs were changed like this:
<para> - It does not matter which order the opposite corners of a cube are - entered in. The <type>cube</> functions - automatically swap values if needed to create a uniform - <quote>lower left — upper right</> internal representation. + When corners coincide cube stores only one corner along with a special flag in order to reduce size wasted. </para>Was the old behavior removed? I don't think so - it seems to behave
as before, so why to remove this information? Maybe it's not useful?
But then why add the bit about optimizing storage of points?
I’ve edited it because the statement was mislead (or at least ambiguous) — cube_in function doesn’t swap coordinates.
Simple way to see it:
select '(1,3),(3,1)'::cube;
cube
---------------
(1, 3),(3, 1)
But LowerLeft-UpperRight representation should be (1,1),(3,3)
Updated patch attached.
Attachments:
cube_distances.patchapplication/octet-stream; name=cube_distances.patchDownload
diff --git a/contrib/cube/Makefile b/contrib/cube/Makefile
index 67f7867..e2a5d2c 100644
--- a/contrib/cube/Makefile
+++ b/contrib/cube/Makefile
@@ -4,7 +4,7 @@ MODULE_big = cube
OBJS= cube.o cubeparse.o $(WIN32RES)
EXTENSION = cube
-DATA = cube--1.0.sql cube--unpackaged--1.0.sql
+DATA = cube--1.1.sql cube--1.0--1.1.sql cube--unpackaged--1.0.sql
PGFILEDESC = "cube - multidimensional cube data type"
REGRESS = cube
diff --git a/contrib/cube/cube--1.0--1.1.sql b/contrib/cube/cube--1.0--1.1.sql
new file mode 100644
index 0000000..f032f73
--- /dev/null
+++ b/contrib/cube/cube--1.0--1.1.sql
@@ -0,0 +1,60 @@
+/* contrib/cube/cube--1.0--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION cube UPDATE TO '1.1'" to load this file. \quit
+
+CREATE FUNCTION distance_chebyshev(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION distance_taxicab(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_coord(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_coord_llur(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE OPERATOR -> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+);
+
+CREATE OPERATOR ~> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord_llur
+);
+
+CREATE OPERATOR <#> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ COMMUTATOR = '<#>'
+);
+
+CREATE OPERATOR <-> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_distance,
+ COMMUTATOR = '<->'
+);
+
+CREATE OPERATOR <=> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ COMMUTATOR = '<=>'
+);
+
+CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+ALTER OPERATOR FAMILY gist_cube_ops USING gist ADD
+ OPERATOR 15 ~> (cube, int) FOR ORDER BY float_ops,
+ OPERATOR 16 <#> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 17 <-> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 18 <=> (cube, cube) FOR ORDER BY float_ops,
+ FUNCTION 8 (cube, cube) g_cube_distance (internal, cube, smallint, oid);
+
diff --git a/contrib/cube/cube--1.0.sql b/contrib/cube/cube--1.0.sql
deleted file mode 100644
index 0307811..0000000
--- a/contrib/cube/cube--1.0.sql
+++ /dev/null
@@ -1,325 +0,0 @@
-/* contrib/cube/cube--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION cube" to load this file. \quit
-
--- Create the user-defined type for N-dimensional boxes
-
-CREATE FUNCTION cube_in(cstring)
-RETURNS cube
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube(float8[], float8[]) RETURNS cube
-AS 'MODULE_PATHNAME', 'cube_a_f8_f8'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube(float8[]) RETURNS cube
-AS 'MODULE_PATHNAME', 'cube_a_f8'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube_out(cube)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE cube (
- INTERNALLENGTH = variable,
- INPUT = cube_in,
- OUTPUT = cube_out,
- ALIGNMENT = double
-);
-
-COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
-
---
--- External C-functions for R-tree methods
---
-
--- Comparison methods
-
-CREATE FUNCTION cube_eq(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as';
-
-CREATE FUNCTION cube_ne(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different';
-
-CREATE FUNCTION cube_lt(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than';
-
-CREATE FUNCTION cube_gt(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than';
-
-CREATE FUNCTION cube_le(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to';
-
-CREATE FUNCTION cube_ge(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to';
-
-CREATE FUNCTION cube_cmp(cube, cube)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function';
-
-CREATE FUNCTION cube_contains(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains';
-
-CREATE FUNCTION cube_contained(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in';
-
-CREATE FUNCTION cube_overlap(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps';
-
--- support routines for indexing
-
-CREATE FUNCTION cube_union(cube, cube)
-RETURNS cube
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube_inter(cube, cube)
-RETURNS cube
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube_size(cube)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-
--- Misc N-dimensional functions
-
-CREATE FUNCTION cube_subset(cube, int4[])
-RETURNS cube
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- proximity routines
-
-CREATE FUNCTION cube_distance(cube, cube)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Extracting elements functions
-
-CREATE FUNCTION cube_dim(cube)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube_ll_coord(cube, int4)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube_ur_coord(cube, int4)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube(float8) RETURNS cube
-AS 'MODULE_PATHNAME', 'cube_f8'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube(float8, float8) RETURNS cube
-AS 'MODULE_PATHNAME', 'cube_f8_f8'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube(cube, float8) RETURNS cube
-AS 'MODULE_PATHNAME', 'cube_c_f8'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube(cube, float8, float8) RETURNS cube
-AS 'MODULE_PATHNAME', 'cube_c_f8_f8'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Test if cube is also a point
-
-CREATE FUNCTION cube_is_point(cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Increasing the size of a cube by a radius in at least n dimensions
-
-CREATE FUNCTION cube_enlarge(cube, float8, int4)
-RETURNS cube
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
---
--- OPERATORS
---
-
-CREATE OPERATOR < (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt,
- COMMUTATOR = '>', NEGATOR = '>=',
- RESTRICT = scalarltsel, JOIN = scalarltjoinsel
-);
-
-CREATE OPERATOR > (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt,
- COMMUTATOR = '<', NEGATOR = '<=',
- RESTRICT = scalargtsel, JOIN = scalargtjoinsel
-);
-
-CREATE OPERATOR <= (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_le,
- COMMUTATOR = '>=', NEGATOR = '>',
- RESTRICT = scalarltsel, JOIN = scalarltjoinsel
-);
-
-CREATE OPERATOR >= (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ge,
- COMMUTATOR = '<=', NEGATOR = '<',
- RESTRICT = scalargtsel, JOIN = scalargtjoinsel
-);
-
-CREATE OPERATOR && (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap,
- COMMUTATOR = '&&',
- RESTRICT = areasel, JOIN = areajoinsel
-);
-
-CREATE OPERATOR = (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_eq,
- COMMUTATOR = '=', NEGATOR = '<>',
- RESTRICT = eqsel, JOIN = eqjoinsel,
- MERGES
-);
-
-CREATE OPERATOR <> (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ne,
- COMMUTATOR = '<>', NEGATOR = '=',
- RESTRICT = neqsel, JOIN = neqjoinsel
-);
-
-CREATE OPERATOR @> (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
- COMMUTATOR = '<@',
- RESTRICT = contsel, JOIN = contjoinsel
-);
-
-CREATE OPERATOR <@ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
- COMMUTATOR = '@>',
- RESTRICT = contsel, JOIN = contjoinsel
-);
-
--- these are obsolete/deprecated:
-CREATE OPERATOR @ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
- COMMUTATOR = '~',
- RESTRICT = contsel, JOIN = contjoinsel
-);
-
-CREATE OPERATOR ~ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
- COMMUTATOR = '@',
- RESTRICT = contsel, JOIN = contjoinsel
-);
-
-
--- define the GiST support methods
-CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION g_cube_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION g_cube_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION g_cube_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION g_cube_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION g_cube_union(internal, internal)
-RETURNS cube
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION g_cube_same(cube, cube, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-
--- Create the operator classes for indexing
-
-CREATE OPERATOR CLASS cube_ops
- DEFAULT FOR TYPE cube USING btree AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 cube_cmp(cube, cube);
-
-CREATE OPERATOR CLASS gist_cube_ops
- DEFAULT FOR TYPE cube USING gist AS
- OPERATOR 3 && ,
- OPERATOR 6 = ,
- OPERATOR 7 @> ,
- OPERATOR 8 <@ ,
- OPERATOR 13 @ ,
- OPERATOR 14 ~ ,
- FUNCTION 1 g_cube_consistent (internal, cube, int, oid, internal),
- FUNCTION 2 g_cube_union (internal, internal),
- FUNCTION 3 g_cube_compress (internal),
- FUNCTION 4 g_cube_decompress (internal),
- FUNCTION 5 g_cube_penalty (internal, internal, internal),
- FUNCTION 6 g_cube_picksplit (internal, internal),
- FUNCTION 7 g_cube_same (cube, cube, internal);
diff --git a/contrib/cube/cube--1.1.sql b/contrib/cube/cube--1.1.sql
new file mode 100644
index 0000000..c944414
--- /dev/null
+++ b/contrib/cube/cube--1.1.sql
@@ -0,0 +1,379 @@
+/* contrib/cube/cube--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION cube" to load this file. \quit
+
+-- Create the user-defined type for N-dimensional boxes
+
+CREATE FUNCTION cube_in(cstring)
+RETURNS cube
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube(float8[], float8[]) RETURNS cube
+AS 'MODULE_PATHNAME', 'cube_a_f8_f8'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube(float8[]) RETURNS cube
+AS 'MODULE_PATHNAME', 'cube_a_f8'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_out(cube)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE cube (
+ INTERNALLENGTH = variable,
+ INPUT = cube_in,
+ OUTPUT = cube_out,
+ ALIGNMENT = double
+);
+
+COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
+
+--
+-- External C-functions for R-tree methods
+--
+
+-- Comparison methods
+
+CREATE FUNCTION cube_eq(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as';
+
+CREATE FUNCTION cube_ne(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different';
+
+CREATE FUNCTION cube_lt(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than';
+
+CREATE FUNCTION cube_gt(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than';
+
+CREATE FUNCTION cube_le(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to';
+
+CREATE FUNCTION cube_ge(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to';
+
+CREATE FUNCTION cube_cmp(cube, cube)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function';
+
+CREATE FUNCTION cube_contains(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains';
+
+CREATE FUNCTION cube_contained(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in';
+
+CREATE FUNCTION cube_overlap(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps';
+
+-- support routines for indexing
+
+CREATE FUNCTION cube_union(cube, cube)
+RETURNS cube
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_inter(cube, cube)
+RETURNS cube
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_size(cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+
+-- Misc N-dimensional functions
+
+CREATE FUNCTION cube_subset(cube, int4[])
+RETURNS cube
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- proximity routines
+
+CREATE FUNCTION cube_distance(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION distance_chebyshev(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION distance_taxicab(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Extracting elements functions
+
+CREATE FUNCTION cube_dim(cube)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_ll_coord(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_ur_coord(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_coord(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_coord_llur(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube(float8) RETURNS cube
+AS 'MODULE_PATHNAME', 'cube_f8'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube(float8, float8) RETURNS cube
+AS 'MODULE_PATHNAME', 'cube_f8_f8'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube(cube, float8) RETURNS cube
+AS 'MODULE_PATHNAME', 'cube_c_f8'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube(cube, float8, float8) RETURNS cube
+AS 'MODULE_PATHNAME', 'cube_c_f8_f8'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Test if cube is also a point
+
+CREATE FUNCTION cube_is_point(cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Increasing the size of a cube by a radius in at least n dimensions
+
+CREATE FUNCTION cube_enlarge(cube, float8, int4)
+RETURNS cube
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+--
+-- OPERATORS
+--
+
+CREATE OPERATOR < (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt,
+ COMMUTATOR = '>', NEGATOR = '>=',
+ RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR > (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt,
+ COMMUTATOR = '<', NEGATOR = '<=',
+ RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR <= (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_le,
+ COMMUTATOR = '>=', NEGATOR = '>',
+ RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR >= (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ge,
+ COMMUTATOR = '<=', NEGATOR = '<',
+ RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR && (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap,
+ COMMUTATOR = '&&',
+ RESTRICT = areasel, JOIN = areajoinsel
+);
+
+CREATE OPERATOR = (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_eq,
+ COMMUTATOR = '=', NEGATOR = '<>',
+ RESTRICT = eqsel, JOIN = eqjoinsel,
+ MERGES
+);
+
+CREATE OPERATOR <> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ne,
+ COMMUTATOR = '<>', NEGATOR = '=',
+ RESTRICT = neqsel, JOIN = neqjoinsel
+);
+
+CREATE OPERATOR @> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
+ COMMUTATOR = '<@',
+ RESTRICT = contsel, JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
+ COMMUTATOR = '@>',
+ RESTRICT = contsel, JOIN = contjoinsel
+);
+
+CREATE OPERATOR -> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+);
+
+CREATE OPERATOR ~> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord_llur
+);
+
+CREATE OPERATOR <#> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ COMMUTATOR = '<#>'
+);
+
+CREATE OPERATOR <-> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_distance,
+ COMMUTATOR = '<->'
+);
+
+CREATE OPERATOR <=> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ COMMUTATOR = '<=>'
+);
+
+-- these are obsolete/deprecated:
+CREATE OPERATOR @ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
+ COMMUTATOR = '~',
+ RESTRICT = contsel, JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
+ COMMUTATOR = '@',
+ RESTRICT = contsel, JOIN = contjoinsel
+);
+
+
+-- define the GiST support methods
+CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_union(internal, internal)
+RETURNS cube
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_same(cube, cube, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator classes for indexing
+
+CREATE OPERATOR CLASS cube_ops
+ DEFAULT FOR TYPE cube USING btree AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 cube_cmp(cube, cube);
+
+CREATE OPERATOR CLASS gist_cube_ops
+ DEFAULT FOR TYPE cube USING gist AS
+ OPERATOR 3 && ,
+ OPERATOR 6 = ,
+ OPERATOR 7 @> ,
+ OPERATOR 8 <@ ,
+ OPERATOR 13 @ ,
+ OPERATOR 14 ~ ,
+ OPERATOR 15 ~> (cube, int) FOR ORDER BY float_ops,
+ OPERATOR 16 <#> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 17 <-> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 18 <=> (cube, cube) FOR ORDER BY float_ops,
+
+ FUNCTION 1 g_cube_consistent (internal, cube, int, oid, internal),
+ FUNCTION 2 g_cube_union (internal, internal),
+ FUNCTION 3 g_cube_compress (internal),
+ FUNCTION 4 g_cube_decompress (internal),
+ FUNCTION 5 g_cube_penalty (internal, internal, internal),
+ FUNCTION 6 g_cube_picksplit (internal, internal),
+ FUNCTION 7 g_cube_same (cube, cube, internal),
+ FUNCTION 8 g_cube_distance (internal, cube, smallint, oid);
+
diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c
index a6be59e..35ffb6c 100644
--- a/contrib/cube/cube.c
+++ b/contrib/cube/cube.c
@@ -40,6 +40,8 @@ PG_FUNCTION_INFO_V1(cube_c_f8_f8);
PG_FUNCTION_INFO_V1(cube_dim);
PG_FUNCTION_INFO_V1(cube_ll_coord);
PG_FUNCTION_INFO_V1(cube_ur_coord);
+PG_FUNCTION_INFO_V1(cube_coord);
+PG_FUNCTION_INFO_V1(cube_coord_llur);
PG_FUNCTION_INFO_V1(cube_subset);
/*
@@ -53,6 +55,7 @@ PG_FUNCTION_INFO_V1(g_cube_penalty);
PG_FUNCTION_INFO_V1(g_cube_picksplit);
PG_FUNCTION_INFO_V1(g_cube_union);
PG_FUNCTION_INFO_V1(g_cube_same);
+PG_FUNCTION_INFO_V1(g_cube_distance);
/*
** B-tree support functions
@@ -79,7 +82,9 @@ PG_FUNCTION_INFO_V1(cube_size);
/*
** miscellaneous
*/
+PG_FUNCTION_INFO_V1(distance_taxicab);
PG_FUNCTION_INFO_V1(cube_distance);
+PG_FUNCTION_INFO_V1(distance_chebyshev);
PG_FUNCTION_INFO_V1(cube_is_point);
PG_FUNCTION_INFO_V1(cube_enlarge);
@@ -1257,6 +1262,144 @@ cube_distance(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(sqrt(distance));
}
+Datum
+distance_taxicab(PG_FUNCTION_ARGS)
+{
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (DIM(a) < DIM(b))
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < DIM(b); i++)
+ distance += fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = DIM(b); i < DIM(a); i++)
+ distance += fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+}
+
+Datum
+distance_chebyshev(PG_FUNCTION_ARGS)
+{
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double d, distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (DIM(a) < DIM(b))
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < DIM(b); i++)
+ {
+ d = fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+ if (d > distance)
+ distance = d;
+ }
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = DIM(b); i < DIM(a); i++)
+ {
+ d = fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+ if (d > distance)
+ distance = d;
+ }
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+}
+
+Datum
+g_cube_distance(PG_FUNCTION_ARGS)
+{
+ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+ NDBOX *cube = DatumGetNDBOX(entry->key);
+ double retval;
+
+ if (strategy == CubeKNNDistanceCoord)
+ {
+ int coord = PG_GETARG_INT32(1);
+
+ if IS_POINT(cube)
+ {
+ retval = (cube)->x[(coord-1)%DIM(cube)];
+ }
+ else
+ {
+ retval = Min(
+ (cube)->x[(coord-1)%DIM(cube)],
+ (cube)->x[(coord-1)%DIM(cube) + DIM(cube)]
+ );
+ }
+ }
+ else
+ {
+ NDBOX *query = PG_GETARG_NDBOX(1);
+ switch(strategy)
+ {
+ case CubeKNNDistanceTaxicab:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_taxicab,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case CubeKNNDistanceEuclid:
+ retval = DatumGetFloat8(DirectFunctionCall2(cube_distance,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case CubeKNNDistanceChebyshev:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_chebyshev,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ default:
+ elog(ERROR, "Cube: unknown strategy number.");
+ }
+ }
+ PG_RETURN_FLOAT8(retval);
+}
+
static double
distance_1D(double a1, double a2, double b1, double b2)
{
@@ -1352,6 +1495,71 @@ cube_ur_coord(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+/*
+ * Function returns cube coordinate.
+ * Numbers from 1 to DIM denotes first corner coordinates.
+ * Numbers from DIM+1 to 2*DIM denotes second corner coordinates.
+ */
+Datum
+cube_coord(PG_FUNCTION_ARGS)
+{
+ NDBOX *cube = PG_GETARG_NDBOX(0);
+ int coord = PG_GETARG_INT16(1);
+
+ if ((coord > 0) && (coord <= 2*DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[(coord-1)%DIM(cube)] );
+ else
+ PG_RETURN_FLOAT8( (cube)->x[coord-1] );
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ errmsg("Cube index out of bounds")));
+ }
+}
+
+
+/*
+ * This function works like cube_coord(),
+ * but rearranges coordinates of corners to get cube representation
+ * in the form of (lower left, upper right).
+ * For historical reasons that extension allows us to create cubes in form
+ * ((2,1),(1,2)) and instead of normalizing such cube to ((1,1),(2,2)) it
+ * stores cube in original way. But to get cubes ordered by one of dimensions
+ * directly from the index without extra sort step we need some
+ * representation-independent coordinate getter. This function implements it.
+ */
+Datum
+cube_coord_llur(PG_FUNCTION_ARGS)
+{
+ NDBOX *cube = PG_GETARG_NDBOX(0);
+ int coord = PG_GETARG_INT16(1);
+
+ if ((coord > 0) && (coord <= DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[coord-1] );
+ else
+ PG_RETURN_FLOAT8( Min((cube)->x[coord-1], (cube)->x[coord-1+DIM(cube)]) );
+ }
+ else if ((coord > DIM(cube)) && (coord <= 2*DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[(coord-1)%DIM(cube)] );
+ else
+ PG_RETURN_FLOAT8( Max((cube)->x[coord-1], (cube)->x[coord-1-DIM(cube)]) );
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ errmsg("Cube index out of bounds")));
+ }
+}
+
/* Increase or decrease box size by a radius in at least n dimensions. */
Datum
cube_enlarge(PG_FUNCTION_ARGS)
diff --git a/contrib/cube/cube.control b/contrib/cube/cube.control
index ddc8d2e..f84e6c5 100644
--- a/contrib/cube/cube.control
+++ b/contrib/cube/cube.control
@@ -1,5 +1,5 @@
# cube extension
comment = 'data type for multidimensional cubes'
-default_version = '1.0'
+default_version = '1.1'
module_pathname = '$libdir/cube'
relocatable = true
diff --git a/contrib/cube/cubedata.h b/contrib/cube/cubedata.h
index 59c23de..7eaac39 100644
--- a/contrib/cube/cubedata.h
+++ b/contrib/cube/cubedata.h
@@ -47,6 +47,11 @@ typedef struct NDBOX
#define PG_GETARG_NDBOX(x) DatumGetNDBOX(PG_GETARG_DATUM(x))
#define PG_RETURN_NDBOX(x) PG_RETURN_POINTER(x)
+#define CubeKNNDistanceCoord 15 /* ~> */
+#define CubeKNNDistanceTaxicab 16 /* <#> */
+#define CubeKNNDistanceEuclid 17 /* <-> */
+#define CubeKNNDistanceChebyshev 18 /* <=> */
+
/* in cubescan.l */
extern int cube_yylex(void);
extern void cube_yyerror(NDBOX **result, const char *message) pg_attribute_noreturn();
diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out
index ca9555e..769ad3a 100644
--- a/contrib/cube/expected/cube.out
+++ b/contrib/cube/expected/cube.out
@@ -1381,6 +1381,151 @@ SELECT cube_size('(42,137)'::cube);
0
(1 row)
+-- Test of distances
+--
+SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+---------------
+ 5
+(1 row)
+
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+-----
+ 5
+(1 row)
+
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+--------------------
+ 4
+(1 row)
+
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+-----
+ 4
+(1 row)
+
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+------------------
+ 7
+(1 row)
+
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+-----
+ 7
+(1 row)
+
+-- zero for overlapping
+SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+---------------
+ 0
+(1 row)
+
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+--------------------
+ 0
+(1 row)
+
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+------------------
+ 0
+(1 row)
+
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+----------
+ 60
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30])->3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->6;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->-6;
+ERROR: Cube index out of bounds
+-- "normalized" coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ERROR: Cube index out of bounds
+SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -1407,3 +1552,159 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+(5 rows)
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+-------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+-------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+(15 rows)
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+(15 rows)
+
diff --git a/contrib/cube/expected/cube_1.out b/contrib/cube/expected/cube_1.out
index c07d61d..7178088 100644
--- a/contrib/cube/expected/cube_1.out
+++ b/contrib/cube/expected/cube_1.out
@@ -1381,6 +1381,151 @@ SELECT cube_size('(42,137)'::cube);
0
(1 row)
+-- Test of distances
+--
+SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+---------------
+ 5
+(1 row)
+
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+-----
+ 5
+(1 row)
+
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+--------------------
+ 4
+(1 row)
+
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+-----
+ 4
+(1 row)
+
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+------------------
+ 7
+(1 row)
+
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+-----
+ 7
+(1 row)
+
+-- zero for overlapping
+SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+---------------
+ 0
+(1 row)
+
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+--------------------
+ 0
+(1 row)
+
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+------------------
+ 0
+(1 row)
+
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+----------
+ 60
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30])->3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->6;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->-6;
+ERROR: Cube index out of bounds
+-- "normalized" coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ERROR: Cube index out of bounds
+SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -1407,3 +1552,159 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+(5 rows)
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+-------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+-------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+(15 rows)
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+(15 rows)
+
diff --git a/contrib/cube/expected/cube_2.out b/contrib/cube/expected/cube_2.out
index 3767d0e..c2421c5 100644
--- a/contrib/cube/expected/cube_2.out
+++ b/contrib/cube/expected/cube_2.out
@@ -1381,6 +1381,151 @@ SELECT cube_size('(42,137)'::cube);
0
(1 row)
+-- Test of distances
+--
+SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+---------------
+ 5
+(1 row)
+
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+-----
+ 5
+(1 row)
+
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+--------------------
+ 4
+(1 row)
+
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+-----
+ 4
+(1 row)
+
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+------------------
+ 7
+(1 row)
+
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+-----
+ 7
+(1 row)
+
+-- zero for overlapping
+SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+---------------
+ 0
+(1 row)
+
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+--------------------
+ 0
+(1 row)
+
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+------------------
+ 0
+(1 row)
+
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+----------
+ 60
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30])->3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->6;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->-6;
+ERROR: Cube index out of bounds
+-- "normalized" coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ERROR: Cube index out of bounds
+SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -1407,3 +1552,159 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+(5 rows)
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+-------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+-------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+(15 rows)
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+(15 rows)
+
diff --git a/contrib/cube/expected/cube_3.out b/contrib/cube/expected/cube_3.out
index 2aa42be..b6c961d 100644
--- a/contrib/cube/expected/cube_3.out
+++ b/contrib/cube/expected/cube_3.out
@@ -1381,6 +1381,151 @@ SELECT cube_size('(42,137)'::cube);
0
(1 row)
+-- Test of distances
+--
+SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+---------------
+ 5
+(1 row)
+
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+-----
+ 5
+(1 row)
+
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+--------------------
+ 4
+(1 row)
+
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+-----
+ 4
+(1 row)
+
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+------------------
+ 7
+(1 row)
+
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+-----
+ 7
+(1 row)
+
+-- zero for overlapping
+SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+---------------
+ 0
+(1 row)
+
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+--------------------
+ 0
+(1 row)
+
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+------------------
+ 0
+(1 row)
+
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+----------
+ 60
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30])->3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->6;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->-6;
+ERROR: Cube index out of bounds
+-- "normalized" coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ERROR: Cube index out of bounds
+SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -1407,3 +1552,159 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+(5 rows)
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+-------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+-------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+(15 rows)
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+(15 rows)
+
diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql
index d58974c..e225fb7 100644
--- a/contrib/cube/sql/cube.sql
+++ b/contrib/cube/sql/cube.sql
@@ -325,6 +325,41 @@ SELECT cube_inter('(1,2,3)'::cube, '(5,6,3)'::cube); -- point args
SELECT cube_size('(4,8),(15,16)'::cube);
SELECT cube_size('(42,137)'::cube);
+-- Test of distances
+--
+SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+-- zero for overlapping
+SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+SELECT cube(array[40,50,60], array[10,20,30])->1;
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+SELECT cube(array[10,20,30])->3;
+SELECT cube(array[10,20,30])->6;
+SELECT cube(array[10,20,30])->-6;
+-- "normalized" coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])~>1;
+SELECT cube(array[40,50,60], array[10,20,30])~>1;
+SELECT cube(array[10,20,30], array[40,50,60])~>2;
+SELECT cube(array[40,50,60], array[10,20,30])~>2;
+SELECT cube(array[10,20,30], array[40,50,60])~>3;
+SELECT cube(array[40,50,60], array[10,20,30])~>3;
+
+SELECT cube(array[40,50,60], array[10,20,30])~>0;
+SELECT cube(array[40,50,60], array[10,20,30])~>4;
+SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -336,3 +371,21 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' ORDER BY c;
-- Test sorting
SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
+
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
diff --git a/doc/src/sgml/cube.sgml b/doc/src/sgml/cube.sgml
index 0a226ca..5a1cc8b 100644
--- a/doc/src/sgml/cube.sgml
+++ b/doc/src/sgml/cube.sgml
@@ -71,10 +71,7 @@
</table>
<para>
- It does not matter which order the opposite corners of a cube are
- entered in. The <type>cube</> functions
- automatically swap values if needed to create a uniform
- <quote>lower left — upper right</> internal representation.
+ When corners coincide cube stores only one corner along with a special flag in order to reduce size wasted.
</para>
<para>
@@ -131,6 +128,19 @@
<entry><literal>a <@ b</></entry>
<entry>The cube a is contained in the cube b.</entry>
</row>
+
+ <row>
+ <entry><literal>a -> n</></entry>
+ <entry>Get n-th coordinate of cube.</entry>
+ </row>
+
+ <row>
+ <entry><literal>a ~> n</></entry>
+ <entry>
+ Get n-th coordinate in 'normalized' cube representation. Noramlization
+ means coordinate rearrangement to form (lower left, upper right).
+ </entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -144,6 +154,87 @@
</para>
<para>
+ GiST index can be used to retrieve nearest neighbours via several metric
+ operators. As always any of them can be used as ordinary function.
+ </para>
+
+ <table id="cube-gistknn-operators">
+ <title>Cube GiST-kNN Operators</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Operator</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>a <-> b</></entry>
+ <entry>Euclidean distance between a and b</entry>
+ </row>
+
+ <row>
+ <entry><literal>a <#> b</></entry>
+ <entry>Taxicab (L-1 metric) distance between a and b</entry>
+ </row>
+
+ <row>
+ <entry><literal>a <=> b</></entry>
+ <entry>Chebyshev (L-inf metric) distance between a and b</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Selection of nearing neigbours can be done in the following way:
+ </para>
+<programlisting>
+SELECT c FROM test
+ORDER BY cube(array[0.5,0.5,0.5])<->c
+LIMIT 1;
+</programlisting>
+
+
+ <para>
+ Also kNN framework allows us to cheat with metrics in order to get results
+ sorted by selected coodinate directly from the index without extra sorting
+ step. That technique significantly faster on small values of LIMIT, however
+ with bigger values of LIMIT planner will switch automatically to standart
+ index scan and sort.
+ That behavior can be achieved using coordinate operator
+ (cube c)~>(int offset).
+ </para>
+<programlisting>
+=> select cube(array[0.41,0.42,0.43])~>2 as coord;
+ coord
+-------
+ 0.42
+(1 row)
+</programlisting>
+
+ <para>
+ So using that operator as kNN metric we can obtain cubes sorted by it's
+ coordinate.
+ </para>
+ <para>
+ To get cubes ordered by first coordinate of lower left corner ascending
+ one can use the following query:
+ </para>
+<programlisting>
+SELECT c FROM test ORDER BY c~>1 LIMIT 5;
+</programlisting>
+ <para>
+ And to get cubes descending by first coordinate of upper right corner
+ of 2d-cube:
+ </para>
+<programlisting>
+SELECT c FROM test ORDER BY c~>3 DESC LIMIT 5;
+</programlisting>
+
+
+
+ <para>
The standard B-tree operators are also provided, for example
<informaltable>
Hi,
On 12/16/2015 01:26 PM, Stas Kelvich wrote:
Hi, thanks for the review.
1) (nitpicking) There seem to be some minor whitespace issues, i.e.
trailing spaces, empty lines being added/removed, etc.Fixed, I think
2) one of the regression tests started to fail
SELECT '-1e-700'::cube AS cube;
This used to return (0) but now I get (-0).
Actually that problem emerged because of the first problem. I had
extra whitespace in sql file and removed that whitespace from one of the
answers file (cube_1.sql), so diff with both cube.sql and cube_1.sql was
one line length and you saw diff with cube.sql.
In all systems that available to me (osx/linux/freebsd) I saw that
right answers file is cube_1.sql. But in other OS�es you can get +/- 0
or e27/e027. I edited that answers files manually, so there probably can
be some other typos.
Ah! So that's why I couldn't quickly find the issue in the C code ...
3) I wonder why the docs were changed like this:
<para> - It does not matter which order the opposite corners of a cube are - entered in. The <type>cube</> functions - automatically swap values if needed to create a uniform - <quote>lower left — upper right</> internal representation. + When corners coincide cube stores only one corner along with a special flag in order to reduce size wasted. </para>Was the old behavior removed? I don't think so - it seems to behave
as before, so why to remove this information? Maybe it's not useful?
But then why add the bit about optimizing storage of points?I�ve edited it because the statement was mislead (or at least ambiguous) � cube_in function doesn�t swap coordinates.
Simple way to see it:select '(1,3),(3,1)'::cube;
cube
---------------
(1, 3),(3, 1)But LowerLeft-UpperRight representation should be (1,1),(3,3)
I don't think that's what the comment says, actually. It rather refers
to code like this:
result = Min(LL_COORD(c, n - 1), UR_COORD(c, n - 1));
i.e. if you specifically ask for a particular corner (ll, in this case),
you'll get the proper value.
regards
--
Tomas Vondra http://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
I don't think that's what the comment says, actually. It rather refers to code like this:
result = Min(LL_COORD(c, n - 1), UR_COORD(c, n - 1));
i.e. if you specifically ask for a particular corner (ll, in this case), you'll get the proper value.
Hmm, I was confused by phrase “create a uniform _internal_ representation” and actually internally cube stored “as is”. But probably i just misinterpret that.
So here is the updated version with old documentation restored.
Attachments:
cube_distances.patchapplication/octet-stream; name=cube_distances.patchDownload
diff --git a/contrib/cube/Makefile b/contrib/cube/Makefile
index 67f7867..e2a5d2c 100644
--- a/contrib/cube/Makefile
+++ b/contrib/cube/Makefile
@@ -4,7 +4,7 @@ MODULE_big = cube
OBJS= cube.o cubeparse.o $(WIN32RES)
EXTENSION = cube
-DATA = cube--1.0.sql cube--unpackaged--1.0.sql
+DATA = cube--1.1.sql cube--1.0--1.1.sql cube--unpackaged--1.0.sql
PGFILEDESC = "cube - multidimensional cube data type"
REGRESS = cube
diff --git a/contrib/cube/cube--1.0--1.1.sql b/contrib/cube/cube--1.0--1.1.sql
new file mode 100644
index 0000000..f032f73
--- /dev/null
+++ b/contrib/cube/cube--1.0--1.1.sql
@@ -0,0 +1,60 @@
+/* contrib/cube/cube--1.0--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION cube UPDATE TO '1.1'" to load this file. \quit
+
+CREATE FUNCTION distance_chebyshev(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION distance_taxicab(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_coord(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_coord_llur(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE OPERATOR -> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+);
+
+CREATE OPERATOR ~> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord_llur
+);
+
+CREATE OPERATOR <#> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ COMMUTATOR = '<#>'
+);
+
+CREATE OPERATOR <-> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_distance,
+ COMMUTATOR = '<->'
+);
+
+CREATE OPERATOR <=> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ COMMUTATOR = '<=>'
+);
+
+CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+ALTER OPERATOR FAMILY gist_cube_ops USING gist ADD
+ OPERATOR 15 ~> (cube, int) FOR ORDER BY float_ops,
+ OPERATOR 16 <#> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 17 <-> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 18 <=> (cube, cube) FOR ORDER BY float_ops,
+ FUNCTION 8 (cube, cube) g_cube_distance (internal, cube, smallint, oid);
+
diff --git a/contrib/cube/cube--1.0.sql b/contrib/cube/cube--1.0.sql
deleted file mode 100644
index 0307811..0000000
--- a/contrib/cube/cube--1.0.sql
+++ /dev/null
@@ -1,325 +0,0 @@
-/* contrib/cube/cube--1.0.sql */
-
--- complain if script is sourced in psql, rather than via CREATE EXTENSION
-\echo Use "CREATE EXTENSION cube" to load this file. \quit
-
--- Create the user-defined type for N-dimensional boxes
-
-CREATE FUNCTION cube_in(cstring)
-RETURNS cube
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube(float8[], float8[]) RETURNS cube
-AS 'MODULE_PATHNAME', 'cube_a_f8_f8'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube(float8[]) RETURNS cube
-AS 'MODULE_PATHNAME', 'cube_a_f8'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube_out(cube)
-RETURNS cstring
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE TYPE cube (
- INTERNALLENGTH = variable,
- INPUT = cube_in,
- OUTPUT = cube_out,
- ALIGNMENT = double
-);
-
-COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
-
---
--- External C-functions for R-tree methods
---
-
--- Comparison methods
-
-CREATE FUNCTION cube_eq(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as';
-
-CREATE FUNCTION cube_ne(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different';
-
-CREATE FUNCTION cube_lt(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than';
-
-CREATE FUNCTION cube_gt(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than';
-
-CREATE FUNCTION cube_le(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to';
-
-CREATE FUNCTION cube_ge(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to';
-
-CREATE FUNCTION cube_cmp(cube, cube)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function';
-
-CREATE FUNCTION cube_contains(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains';
-
-CREATE FUNCTION cube_contained(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in';
-
-CREATE FUNCTION cube_overlap(cube, cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps';
-
--- support routines for indexing
-
-CREATE FUNCTION cube_union(cube, cube)
-RETURNS cube
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube_inter(cube, cube)
-RETURNS cube
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube_size(cube)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-
--- Misc N-dimensional functions
-
-CREATE FUNCTION cube_subset(cube, int4[])
-RETURNS cube
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- proximity routines
-
-CREATE FUNCTION cube_distance(cube, cube)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Extracting elements functions
-
-CREATE FUNCTION cube_dim(cube)
-RETURNS int4
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube_ll_coord(cube, int4)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube_ur_coord(cube, int4)
-RETURNS float8
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube(float8) RETURNS cube
-AS 'MODULE_PATHNAME', 'cube_f8'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube(float8, float8) RETURNS cube
-AS 'MODULE_PATHNAME', 'cube_f8_f8'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube(cube, float8) RETURNS cube
-AS 'MODULE_PATHNAME', 'cube_c_f8'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION cube(cube, float8, float8) RETURNS cube
-AS 'MODULE_PATHNAME', 'cube_c_f8_f8'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Test if cube is also a point
-
-CREATE FUNCTION cube_is_point(cube)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
--- Increasing the size of a cube by a radius in at least n dimensions
-
-CREATE FUNCTION cube_enlarge(cube, float8, int4)
-RETURNS cube
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
---
--- OPERATORS
---
-
-CREATE OPERATOR < (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt,
- COMMUTATOR = '>', NEGATOR = '>=',
- RESTRICT = scalarltsel, JOIN = scalarltjoinsel
-);
-
-CREATE OPERATOR > (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt,
- COMMUTATOR = '<', NEGATOR = '<=',
- RESTRICT = scalargtsel, JOIN = scalargtjoinsel
-);
-
-CREATE OPERATOR <= (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_le,
- COMMUTATOR = '>=', NEGATOR = '>',
- RESTRICT = scalarltsel, JOIN = scalarltjoinsel
-);
-
-CREATE OPERATOR >= (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ge,
- COMMUTATOR = '<=', NEGATOR = '<',
- RESTRICT = scalargtsel, JOIN = scalargtjoinsel
-);
-
-CREATE OPERATOR && (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap,
- COMMUTATOR = '&&',
- RESTRICT = areasel, JOIN = areajoinsel
-);
-
-CREATE OPERATOR = (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_eq,
- COMMUTATOR = '=', NEGATOR = '<>',
- RESTRICT = eqsel, JOIN = eqjoinsel,
- MERGES
-);
-
-CREATE OPERATOR <> (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ne,
- COMMUTATOR = '<>', NEGATOR = '=',
- RESTRICT = neqsel, JOIN = neqjoinsel
-);
-
-CREATE OPERATOR @> (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
- COMMUTATOR = '<@',
- RESTRICT = contsel, JOIN = contjoinsel
-);
-
-CREATE OPERATOR <@ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
- COMMUTATOR = '@>',
- RESTRICT = contsel, JOIN = contjoinsel
-);
-
--- these are obsolete/deprecated:
-CREATE OPERATOR @ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
- COMMUTATOR = '~',
- RESTRICT = contsel, JOIN = contjoinsel
-);
-
-CREATE OPERATOR ~ (
- LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
- COMMUTATOR = '@',
- RESTRICT = contsel, JOIN = contjoinsel
-);
-
-
--- define the GiST support methods
-CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal)
-RETURNS bool
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION g_cube_compress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION g_cube_decompress(internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION g_cube_penalty(internal,internal,internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION g_cube_picksplit(internal, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION g_cube_union(internal, internal)
-RETURNS cube
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-CREATE FUNCTION g_cube_same(cube, cube, internal)
-RETURNS internal
-AS 'MODULE_PATHNAME'
-LANGUAGE C IMMUTABLE STRICT;
-
-
--- Create the operator classes for indexing
-
-CREATE OPERATOR CLASS cube_ops
- DEFAULT FOR TYPE cube USING btree AS
- OPERATOR 1 < ,
- OPERATOR 2 <= ,
- OPERATOR 3 = ,
- OPERATOR 4 >= ,
- OPERATOR 5 > ,
- FUNCTION 1 cube_cmp(cube, cube);
-
-CREATE OPERATOR CLASS gist_cube_ops
- DEFAULT FOR TYPE cube USING gist AS
- OPERATOR 3 && ,
- OPERATOR 6 = ,
- OPERATOR 7 @> ,
- OPERATOR 8 <@ ,
- OPERATOR 13 @ ,
- OPERATOR 14 ~ ,
- FUNCTION 1 g_cube_consistent (internal, cube, int, oid, internal),
- FUNCTION 2 g_cube_union (internal, internal),
- FUNCTION 3 g_cube_compress (internal),
- FUNCTION 4 g_cube_decompress (internal),
- FUNCTION 5 g_cube_penalty (internal, internal, internal),
- FUNCTION 6 g_cube_picksplit (internal, internal),
- FUNCTION 7 g_cube_same (cube, cube, internal);
diff --git a/contrib/cube/cube--1.1.sql b/contrib/cube/cube--1.1.sql
new file mode 100644
index 0000000..c944414
--- /dev/null
+++ b/contrib/cube/cube--1.1.sql
@@ -0,0 +1,379 @@
+/* contrib/cube/cube--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION cube" to load this file. \quit
+
+-- Create the user-defined type for N-dimensional boxes
+
+CREATE FUNCTION cube_in(cstring)
+RETURNS cube
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube(float8[], float8[]) RETURNS cube
+AS 'MODULE_PATHNAME', 'cube_a_f8_f8'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube(float8[]) RETURNS cube
+AS 'MODULE_PATHNAME', 'cube_a_f8'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_out(cube)
+RETURNS cstring
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE cube (
+ INTERNALLENGTH = variable,
+ INPUT = cube_in,
+ OUTPUT = cube_out,
+ ALIGNMENT = double
+);
+
+COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
+
+--
+-- External C-functions for R-tree methods
+--
+
+-- Comparison methods
+
+CREATE FUNCTION cube_eq(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as';
+
+CREATE FUNCTION cube_ne(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different';
+
+CREATE FUNCTION cube_lt(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than';
+
+CREATE FUNCTION cube_gt(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than';
+
+CREATE FUNCTION cube_le(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to';
+
+CREATE FUNCTION cube_ge(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to';
+
+CREATE FUNCTION cube_cmp(cube, cube)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function';
+
+CREATE FUNCTION cube_contains(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains';
+
+CREATE FUNCTION cube_contained(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in';
+
+CREATE FUNCTION cube_overlap(cube, cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps';
+
+-- support routines for indexing
+
+CREATE FUNCTION cube_union(cube, cube)
+RETURNS cube
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_inter(cube, cube)
+RETURNS cube
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_size(cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+
+-- Misc N-dimensional functions
+
+CREATE FUNCTION cube_subset(cube, int4[])
+RETURNS cube
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- proximity routines
+
+CREATE FUNCTION cube_distance(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION distance_chebyshev(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION distance_taxicab(cube, cube)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Extracting elements functions
+
+CREATE FUNCTION cube_dim(cube)
+RETURNS int4
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_ll_coord(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_ur_coord(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_coord(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube_coord_llur(cube, int4)
+RETURNS float8
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube(float8) RETURNS cube
+AS 'MODULE_PATHNAME', 'cube_f8'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube(float8, float8) RETURNS cube
+AS 'MODULE_PATHNAME', 'cube_f8_f8'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube(cube, float8) RETURNS cube
+AS 'MODULE_PATHNAME', 'cube_c_f8'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION cube(cube, float8, float8) RETURNS cube
+AS 'MODULE_PATHNAME', 'cube_c_f8_f8'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Test if cube is also a point
+
+CREATE FUNCTION cube_is_point(cube)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Increasing the size of a cube by a radius in at least n dimensions
+
+CREATE FUNCTION cube_enlarge(cube, float8, int4)
+RETURNS cube
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+--
+-- OPERATORS
+--
+
+CREATE OPERATOR < (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt,
+ COMMUTATOR = '>', NEGATOR = '>=',
+ RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR > (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt,
+ COMMUTATOR = '<', NEGATOR = '<=',
+ RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR <= (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_le,
+ COMMUTATOR = '>=', NEGATOR = '>',
+ RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+);
+
+CREATE OPERATOR >= (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ge,
+ COMMUTATOR = '<=', NEGATOR = '<',
+ RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+);
+
+CREATE OPERATOR && (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap,
+ COMMUTATOR = '&&',
+ RESTRICT = areasel, JOIN = areajoinsel
+);
+
+CREATE OPERATOR = (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_eq,
+ COMMUTATOR = '=', NEGATOR = '<>',
+ RESTRICT = eqsel, JOIN = eqjoinsel,
+ MERGES
+);
+
+CREATE OPERATOR <> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ne,
+ COMMUTATOR = '<>', NEGATOR = '=',
+ RESTRICT = neqsel, JOIN = neqjoinsel
+);
+
+CREATE OPERATOR @> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
+ COMMUTATOR = '<@',
+ RESTRICT = contsel, JOIN = contjoinsel
+);
+
+CREATE OPERATOR <@ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
+ COMMUTATOR = '@>',
+ RESTRICT = contsel, JOIN = contjoinsel
+);
+
+CREATE OPERATOR -> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+);
+
+CREATE OPERATOR ~> (
+ LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord_llur
+);
+
+CREATE OPERATOR <#> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ COMMUTATOR = '<#>'
+);
+
+CREATE OPERATOR <-> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_distance,
+ COMMUTATOR = '<->'
+);
+
+CREATE OPERATOR <=> (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ COMMUTATOR = '<=>'
+);
+
+-- these are obsolete/deprecated:
+CREATE OPERATOR @ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
+ COMMUTATOR = '~',
+ RESTRICT = contsel, JOIN = contjoinsel
+);
+
+CREATE OPERATOR ~ (
+ LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
+ COMMUTATOR = '@',
+ RESTRICT = contsel, JOIN = contjoinsel
+);
+
+
+-- define the GiST support methods
+CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal)
+RETURNS bool
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_compress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_decompress(internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_penalty(internal,internal,internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_picksplit(internal, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_union(internal, internal)
+RETURNS cube
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_same(cube, cube, internal)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+RETURNS internal
+AS 'MODULE_PATHNAME'
+LANGUAGE C IMMUTABLE STRICT;
+
+-- Create the operator classes for indexing
+
+CREATE OPERATOR CLASS cube_ops
+ DEFAULT FOR TYPE cube USING btree AS
+ OPERATOR 1 < ,
+ OPERATOR 2 <= ,
+ OPERATOR 3 = ,
+ OPERATOR 4 >= ,
+ OPERATOR 5 > ,
+ FUNCTION 1 cube_cmp(cube, cube);
+
+CREATE OPERATOR CLASS gist_cube_ops
+ DEFAULT FOR TYPE cube USING gist AS
+ OPERATOR 3 && ,
+ OPERATOR 6 = ,
+ OPERATOR 7 @> ,
+ OPERATOR 8 <@ ,
+ OPERATOR 13 @ ,
+ OPERATOR 14 ~ ,
+ OPERATOR 15 ~> (cube, int) FOR ORDER BY float_ops,
+ OPERATOR 16 <#> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 17 <-> (cube, cube) FOR ORDER BY float_ops,
+ OPERATOR 18 <=> (cube, cube) FOR ORDER BY float_ops,
+
+ FUNCTION 1 g_cube_consistent (internal, cube, int, oid, internal),
+ FUNCTION 2 g_cube_union (internal, internal),
+ FUNCTION 3 g_cube_compress (internal),
+ FUNCTION 4 g_cube_decompress (internal),
+ FUNCTION 5 g_cube_penalty (internal, internal, internal),
+ FUNCTION 6 g_cube_picksplit (internal, internal),
+ FUNCTION 7 g_cube_same (cube, cube, internal),
+ FUNCTION 8 g_cube_distance (internal, cube, smallint, oid);
+
diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c
index a6be59e..35ffb6c 100644
--- a/contrib/cube/cube.c
+++ b/contrib/cube/cube.c
@@ -40,6 +40,8 @@ PG_FUNCTION_INFO_V1(cube_c_f8_f8);
PG_FUNCTION_INFO_V1(cube_dim);
PG_FUNCTION_INFO_V1(cube_ll_coord);
PG_FUNCTION_INFO_V1(cube_ur_coord);
+PG_FUNCTION_INFO_V1(cube_coord);
+PG_FUNCTION_INFO_V1(cube_coord_llur);
PG_FUNCTION_INFO_V1(cube_subset);
/*
@@ -53,6 +55,7 @@ PG_FUNCTION_INFO_V1(g_cube_penalty);
PG_FUNCTION_INFO_V1(g_cube_picksplit);
PG_FUNCTION_INFO_V1(g_cube_union);
PG_FUNCTION_INFO_V1(g_cube_same);
+PG_FUNCTION_INFO_V1(g_cube_distance);
/*
** B-tree support functions
@@ -79,7 +82,9 @@ PG_FUNCTION_INFO_V1(cube_size);
/*
** miscellaneous
*/
+PG_FUNCTION_INFO_V1(distance_taxicab);
PG_FUNCTION_INFO_V1(cube_distance);
+PG_FUNCTION_INFO_V1(distance_chebyshev);
PG_FUNCTION_INFO_V1(cube_is_point);
PG_FUNCTION_INFO_V1(cube_enlarge);
@@ -1257,6 +1262,144 @@ cube_distance(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(sqrt(distance));
}
+Datum
+distance_taxicab(PG_FUNCTION_ARGS)
+{
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (DIM(a) < DIM(b))
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < DIM(b); i++)
+ distance += fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = DIM(b); i < DIM(a); i++)
+ distance += fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+}
+
+Datum
+distance_chebyshev(PG_FUNCTION_ARGS)
+{
+ NDBOX *a = PG_GETARG_NDBOX(0),
+ *b = PG_GETARG_NDBOX(1);
+ bool swapped = false;
+ double d, distance;
+ int i;
+
+ /* swap the box pointers if needed */
+ if (DIM(a) < DIM(b))
+ {
+ NDBOX *tmp = b;
+ b = a;
+ a = tmp;
+ swapped = true;
+ }
+
+ distance = 0.0;
+ /* compute within the dimensions of (b) */
+ for (i = 0; i < DIM(b); i++)
+ {
+ d = fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+ if (d > distance)
+ distance = d;
+ }
+
+ /* compute distance to zero for those dimensions in (a) absent in (b) */
+ for (i = DIM(b); i < DIM(a); i++)
+ {
+ d = fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+ if (d > distance)
+ distance = d;
+ }
+
+ if (swapped)
+ {
+ PG_FREE_IF_COPY(b, 0);
+ PG_FREE_IF_COPY(a, 1);
+ }
+ else
+ {
+ PG_FREE_IF_COPY(a, 0);
+ PG_FREE_IF_COPY(b, 1);
+ }
+
+ PG_RETURN_FLOAT8(distance);
+}
+
+Datum
+g_cube_distance(PG_FUNCTION_ARGS)
+{
+ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+ NDBOX *cube = DatumGetNDBOX(entry->key);
+ double retval;
+
+ if (strategy == CubeKNNDistanceCoord)
+ {
+ int coord = PG_GETARG_INT32(1);
+
+ if IS_POINT(cube)
+ {
+ retval = (cube)->x[(coord-1)%DIM(cube)];
+ }
+ else
+ {
+ retval = Min(
+ (cube)->x[(coord-1)%DIM(cube)],
+ (cube)->x[(coord-1)%DIM(cube) + DIM(cube)]
+ );
+ }
+ }
+ else
+ {
+ NDBOX *query = PG_GETARG_NDBOX(1);
+ switch(strategy)
+ {
+ case CubeKNNDistanceTaxicab:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_taxicab,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case CubeKNNDistanceEuclid:
+ retval = DatumGetFloat8(DirectFunctionCall2(cube_distance,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ case CubeKNNDistanceChebyshev:
+ retval = DatumGetFloat8(DirectFunctionCall2(distance_chebyshev,
+ PointerGetDatum(cube), PointerGetDatum(query)));
+ break;
+ default:
+ elog(ERROR, "Cube: unknown strategy number.");
+ }
+ }
+ PG_RETURN_FLOAT8(retval);
+}
+
static double
distance_1D(double a1, double a2, double b1, double b2)
{
@@ -1352,6 +1495,71 @@ cube_ur_coord(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result);
}
+/*
+ * Function returns cube coordinate.
+ * Numbers from 1 to DIM denotes first corner coordinates.
+ * Numbers from DIM+1 to 2*DIM denotes second corner coordinates.
+ */
+Datum
+cube_coord(PG_FUNCTION_ARGS)
+{
+ NDBOX *cube = PG_GETARG_NDBOX(0);
+ int coord = PG_GETARG_INT16(1);
+
+ if ((coord > 0) && (coord <= 2*DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[(coord-1)%DIM(cube)] );
+ else
+ PG_RETURN_FLOAT8( (cube)->x[coord-1] );
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ errmsg("Cube index out of bounds")));
+ }
+}
+
+
+/*
+ * This function works like cube_coord(),
+ * but rearranges coordinates of corners to get cube representation
+ * in the form of (lower left, upper right).
+ * For historical reasons that extension allows us to create cubes in form
+ * ((2,1),(1,2)) and instead of normalizing such cube to ((1,1),(2,2)) it
+ * stores cube in original way. But to get cubes ordered by one of dimensions
+ * directly from the index without extra sort step we need some
+ * representation-independent coordinate getter. This function implements it.
+ */
+Datum
+cube_coord_llur(PG_FUNCTION_ARGS)
+{
+ NDBOX *cube = PG_GETARG_NDBOX(0);
+ int coord = PG_GETARG_INT16(1);
+
+ if ((coord > 0) && (coord <= DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[coord-1] );
+ else
+ PG_RETURN_FLOAT8( Min((cube)->x[coord-1], (cube)->x[coord-1+DIM(cube)]) );
+ }
+ else if ((coord > DIM(cube)) && (coord <= 2*DIM(cube)))
+ {
+ if IS_POINT(cube)
+ PG_RETURN_FLOAT8( (cube)->x[(coord-1)%DIM(cube)] );
+ else
+ PG_RETURN_FLOAT8( Max((cube)->x[coord-1], (cube)->x[coord-1-DIM(cube)]) );
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ errmsg("Cube index out of bounds")));
+ }
+}
+
/* Increase or decrease box size by a radius in at least n dimensions. */
Datum
cube_enlarge(PG_FUNCTION_ARGS)
diff --git a/contrib/cube/cube.control b/contrib/cube/cube.control
index ddc8d2e..f84e6c5 100644
--- a/contrib/cube/cube.control
+++ b/contrib/cube/cube.control
@@ -1,5 +1,5 @@
# cube extension
comment = 'data type for multidimensional cubes'
-default_version = '1.0'
+default_version = '1.1'
module_pathname = '$libdir/cube'
relocatable = true
diff --git a/contrib/cube/cubedata.h b/contrib/cube/cubedata.h
index 59c23de..7eaac39 100644
--- a/contrib/cube/cubedata.h
+++ b/contrib/cube/cubedata.h
@@ -47,6 +47,11 @@ typedef struct NDBOX
#define PG_GETARG_NDBOX(x) DatumGetNDBOX(PG_GETARG_DATUM(x))
#define PG_RETURN_NDBOX(x) PG_RETURN_POINTER(x)
+#define CubeKNNDistanceCoord 15 /* ~> */
+#define CubeKNNDistanceTaxicab 16 /* <#> */
+#define CubeKNNDistanceEuclid 17 /* <-> */
+#define CubeKNNDistanceChebyshev 18 /* <=> */
+
/* in cubescan.l */
extern int cube_yylex(void);
extern void cube_yyerror(NDBOX **result, const char *message) pg_attribute_noreturn();
diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out
index ca9555e..769ad3a 100644
--- a/contrib/cube/expected/cube.out
+++ b/contrib/cube/expected/cube.out
@@ -1381,6 +1381,151 @@ SELECT cube_size('(42,137)'::cube);
0
(1 row)
+-- Test of distances
+--
+SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+---------------
+ 5
+(1 row)
+
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+-----
+ 5
+(1 row)
+
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+--------------------
+ 4
+(1 row)
+
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+-----
+ 4
+(1 row)
+
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+------------------
+ 7
+(1 row)
+
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+-----
+ 7
+(1 row)
+
+-- zero for overlapping
+SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+---------------
+ 0
+(1 row)
+
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+--------------------
+ 0
+(1 row)
+
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+------------------
+ 0
+(1 row)
+
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+----------
+ 60
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30])->3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->6;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->-6;
+ERROR: Cube index out of bounds
+-- "normalized" coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ERROR: Cube index out of bounds
+SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -1407,3 +1552,159 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+(5 rows)
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+-------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+-------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+(15 rows)
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+(15 rows)
+
diff --git a/contrib/cube/expected/cube_1.out b/contrib/cube/expected/cube_1.out
index c07d61d..7178088 100644
--- a/contrib/cube/expected/cube_1.out
+++ b/contrib/cube/expected/cube_1.out
@@ -1381,6 +1381,151 @@ SELECT cube_size('(42,137)'::cube);
0
(1 row)
+-- Test of distances
+--
+SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+---------------
+ 5
+(1 row)
+
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+-----
+ 5
+(1 row)
+
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+--------------------
+ 4
+(1 row)
+
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+-----
+ 4
+(1 row)
+
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+------------------
+ 7
+(1 row)
+
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+-----
+ 7
+(1 row)
+
+-- zero for overlapping
+SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+---------------
+ 0
+(1 row)
+
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+--------------------
+ 0
+(1 row)
+
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+------------------
+ 0
+(1 row)
+
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+----------
+ 60
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30])->3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->6;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->-6;
+ERROR: Cube index out of bounds
+-- "normalized" coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ERROR: Cube index out of bounds
+SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -1407,3 +1552,159 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+(5 rows)
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+-------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+-------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+(15 rows)
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+(15 rows)
+
diff --git a/contrib/cube/expected/cube_2.out b/contrib/cube/expected/cube_2.out
index 3767d0e..c2421c5 100644
--- a/contrib/cube/expected/cube_2.out
+++ b/contrib/cube/expected/cube_2.out
@@ -1381,6 +1381,151 @@ SELECT cube_size('(42,137)'::cube);
0
(1 row)
+-- Test of distances
+--
+SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+---------------
+ 5
+(1 row)
+
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+-----
+ 5
+(1 row)
+
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+--------------------
+ 4
+(1 row)
+
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+-----
+ 4
+(1 row)
+
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+------------------
+ 7
+(1 row)
+
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+-----
+ 7
+(1 row)
+
+-- zero for overlapping
+SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+---------------
+ 0
+(1 row)
+
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+--------------------
+ 0
+(1 row)
+
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+------------------
+ 0
+(1 row)
+
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+----------
+ 60
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30])->3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->6;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->-6;
+ERROR: Cube index out of bounds
+-- "normalized" coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ERROR: Cube index out of bounds
+SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -1407,3 +1552,159 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+(5 rows)
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+-------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+-------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+(15 rows)
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+(15 rows)
+
diff --git a/contrib/cube/expected/cube_3.out b/contrib/cube/expected/cube_3.out
index 2aa42be..b6c961d 100644
--- a/contrib/cube/expected/cube_3.out
+++ b/contrib/cube/expected/cube_3.out
@@ -1381,6 +1381,151 @@ SELECT cube_size('(42,137)'::cube);
0
(1 row)
+-- Test of distances
+--
+SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ cube_distance
+---------------
+ 5
+(1 row)
+
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ d_e
+-----
+ 5
+(1 row)
+
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ distance_chebyshev
+--------------------
+ 4
+(1 row)
+
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ d_c
+-----
+ 4
+(1 row)
+
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ distance_taxicab
+------------------
+ 7
+(1 row)
+
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ d_t
+-----
+ 7
+(1 row)
+
+-- zero for overlapping
+SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ cube_distance
+---------------
+ 0
+(1 row)
+
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_chebyshev
+--------------------
+ 0
+(1 row)
+
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ distance_taxicab
+------------------
+ 0
+(1 row)
+
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])->1;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+ ?column?
+----------
+ 60
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ERROR: Cube index out of bounds
+SELECT cube(array[10,20,30])->3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->6;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[10,20,30])->-6;
+ERROR: Cube index out of bounds
+-- "normalized" coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ ?column?
+----------
+ 10
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ ?column?
+----------
+ 20
+(1 row)
+
+SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ ?column?
+----------
+ 30
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ERROR: Cube index out of bounds
+SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ ?column?
+----------
+ 40
+(1 row)
+
+SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ERROR: Cube index out of bounds
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -1407,3 +1552,159 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
(2424, 160),(2424, 81)
(5 rows)
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------------------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 772.000647668122
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (948, 1201),(907, 1156) | 656
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+(5 rows)
+
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ c | dist
+-------------------------+------
+ (337, 455),(240, 359) | 0
+ (759, 187),(662, 163) | 162
+ (1444, 403),(1346, 344) | 846
+ (369, 1457),(278, 1409) | 909
+ (948, 1201),(907, 1156) | 1063
+(5 rows)
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ c
+---------------------------
+ (54, 38679),(3, 38602)
+ (83, 10271),(15, 10265)
+ (122, 46832),(64, 46762)
+ (167, 17214),(92, 17184)
+ (161, 24465),(107, 24374)
+ (162, 26040),(120, 25963)
+ (154, 4019),(138, 3990)
+ (259, 1850),(175, 1820)
+ (207, 40886),(179, 40879)
+ (288, 49588),(204, 49571)
+ (270, 32616),(226, 32607)
+ (318, 31489),(235, 31404)
+ (337, 455),(240, 359)
+ (270, 29508),(264, 29440)
+ (369, 1457),(278, 1409)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ c
+---------------------------
+ (30333, 50),(30273, 6)
+ (43301, 75),(43227, 43)
+ (19650, 142),(19630, 51)
+ (2424, 160),(2424, 81)
+ (3449, 171),(3354, 108)
+ (18037, 155),(17941, 109)
+ (28511, 208),(28479, 114)
+ (19946, 217),(19941, 118)
+ (16906, 191),(16816, 139)
+ (759, 187),(662, 163)
+ (22684, 266),(22656, 181)
+ (24423, 255),(24360, 213)
+ (45989, 249),(45910, 222)
+ (11399, 377),(11360, 294)
+ (12162, 389),(12103, 309)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ c
+-------------------------------
+ (50027, 49230),(49951, 49214)
+ (49980, 35004),(49937, 34963)
+ (49985, 6436),(49927, 6338)
+ (49999, 27218),(49908, 27176)
+ (49954, 1340),(49905, 1294)
+ (49944, 25163),(49902, 25153)
+ (49981, 34876),(49898, 34786)
+ (49957, 43390),(49897, 43384)
+ (49853, 18504),(49848, 18503)
+ (49902, 41752),(49818, 41746)
+ (49907, 30225),(49810, 30158)
+ (49843, 5175),(49808, 5145)
+ (49887, 24274),(49805, 24184)
+ (49847, 7128),(49798, 7067)
+ (49820, 7990),(49771, 7967)
+(15 rows)
+
+SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ c
+-------------------------------
+ (36311, 50073),(36258, 49987)
+ (30746, 50040),(30727, 49992)
+ (2168, 50012),(2108, 49914)
+ (21551, 49983),(21492, 49885)
+ (17954, 49975),(17865, 49915)
+ (3531, 49962),(3463, 49934)
+ (19128, 49932),(19112, 49849)
+ (31287, 49923),(31236, 49913)
+ (43925, 49912),(43888, 49878)
+ (29261, 49910),(29247, 49818)
+ (14913, 49873),(14849, 49836)
+ (20007, 49858),(19921, 49778)
+ (38266, 49852),(38233, 49844)
+ (37595, 49849),(37581, 49834)
+ (46151, 49848),(46058, 49830)
+(15 rows)
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ c
+--------------------------
+ (54, 38679, 3, 38602)
+ (83, 10271, 15, 10265)
+ (122, 46832, 64, 46762)
+ (154, 4019, 138, 3990)
+ (161, 24465, 107, 24374)
+ (162, 26040, 120, 25963)
+ (167, 17214, 92, 17184)
+ (207, 40886, 179, 40879)
+ (259, 1850, 175, 1820)
+ (270, 29508, 264, 29440)
+ (270, 32616, 226, 32607)
+ (288, 49588, 204, 49571)
+ (318, 31489, 235, 31404)
+ (326, 18837, 285, 18817)
+ (337, 455, 240, 359)
+(15 rows)
+
+SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ c
+------------------------------
+ (30746, 50040, 30727, 49992)
+ (36311, 50073, 36258, 49987)
+ (3531, 49962, 3463, 49934)
+ (17954, 49975, 17865, 49915)
+ (2168, 50012, 2108, 49914)
+ (31287, 49923, 31236, 49913)
+ (21551, 49983, 21492, 49885)
+ (43925, 49912, 43888, 49878)
+ (19128, 49932, 19112, 49849)
+ (38266, 49852, 38233, 49844)
+ (14913, 49873, 14849, 49836)
+ (37595, 49849, 37581, 49834)
+ (46151, 49848, 46058, 49830)
+ (29261, 49910, 29247, 49818)
+ (19233, 49824, 19185, 49794)
+(15 rows)
+
diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql
index d58974c..e225fb7 100644
--- a/contrib/cube/sql/cube.sql
+++ b/contrib/cube/sql/cube.sql
@@ -325,6 +325,41 @@ SELECT cube_inter('(1,2,3)'::cube, '(5,6,3)'::cube); -- point args
SELECT cube_size('(4,8),(15,16)'::cube);
SELECT cube_size('(42,137)'::cube);
+-- Test of distances
+--
+SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+-- zero for overlapping
+SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+-- coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])->1;
+SELECT cube(array[40,50,60], array[10,20,30])->1;
+SELECT cube(array[10,20,30], array[40,50,60])->6;
+SELECT cube(array[10,20,30], array[40,50,60])->0;
+SELECT cube(array[10,20,30], array[40,50,60])->7;
+SELECT cube(array[10,20,30], array[40,50,60])->-1;
+SELECT cube(array[10,20,30], array[40,50,60])->-6;
+SELECT cube(array[10,20,30])->3;
+SELECT cube(array[10,20,30])->6;
+SELECT cube(array[10,20,30])->-6;
+-- "normalized" coordinate access
+SELECT cube(array[10,20,30], array[40,50,60])~>1;
+SELECT cube(array[40,50,60], array[10,20,30])~>1;
+SELECT cube(array[10,20,30], array[40,50,60])~>2;
+SELECT cube(array[40,50,60], array[10,20,30])~>2;
+SELECT cube(array[10,20,30], array[40,50,60])~>3;
+SELECT cube(array[40,50,60], array[10,20,30])~>3;
+
+SELECT cube(array[40,50,60], array[10,20,30])~>0;
+SELECT cube(array[40,50,60], array[10,20,30])~>4;
+SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+
-- Load some example data and build the index
--
CREATE TABLE test_cube (c cube);
@@ -336,3 +371,21 @@ SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' ORDER BY c;
-- Test sorting
SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
+
+-- kNN with index
+SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+
+-- kNN-based sorting
+SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+
+-- same thing for index with points
+CREATE TABLE test_point(c cube);
+INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+CREATE INDEX ON test_point USING gist(c);
+SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
diff --git a/doc/src/sgml/cube.sgml b/doc/src/sgml/cube.sgml
index 0a226ca..fdd30db 100644
--- a/doc/src/sgml/cube.sgml
+++ b/doc/src/sgml/cube.sgml
@@ -75,6 +75,7 @@
entered in. The <type>cube</> functions
automatically swap values if needed to create a uniform
<quote>lower left — upper right</> internal representation.
+ When corners coincide cube stores only one corner along with a special flag in order to reduce size wasted.
</para>
<para>
@@ -131,6 +132,19 @@
<entry><literal>a <@ b</></entry>
<entry>The cube a is contained in the cube b.</entry>
</row>
+
+ <row>
+ <entry><literal>a -> n</></entry>
+ <entry>Get n-th coordinate of cube.</entry>
+ </row>
+
+ <row>
+ <entry><literal>a ~> n</></entry>
+ <entry>
+ Get n-th coordinate in 'normalized' cube representation. Noramlization
+ means coordinate rearrangement to form (lower left, upper right).
+ </entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -144,6 +158,87 @@
</para>
<para>
+ GiST index can be used to retrieve nearest neighbours via several metric
+ operators. As always any of them can be used as ordinary function.
+ </para>
+
+ <table id="cube-gistknn-operators">
+ <title>Cube GiST-kNN Operators</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Operator</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>a <-> b</></entry>
+ <entry>Euclidean distance between a and b</entry>
+ </row>
+
+ <row>
+ <entry><literal>a <#> b</></entry>
+ <entry>Taxicab (L-1 metric) distance between a and b</entry>
+ </row>
+
+ <row>
+ <entry><literal>a <=> b</></entry>
+ <entry>Chebyshev (L-inf metric) distance between a and b</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Selection of nearing neigbours can be done in the following way:
+ </para>
+<programlisting>
+SELECT c FROM test
+ORDER BY cube(array[0.5,0.5,0.5])<->c
+LIMIT 1;
+</programlisting>
+
+
+ <para>
+ Also kNN framework allows us to cheat with metrics in order to get results
+ sorted by selected coodinate directly from the index without extra sorting
+ step. That technique significantly faster on small values of LIMIT, however
+ with bigger values of LIMIT planner will switch automatically to standart
+ index scan and sort.
+ That behavior can be achieved using coordinate operator
+ (cube c)~>(int offset).
+ </para>
+<programlisting>
+=> select cube(array[0.41,0.42,0.43])~>2 as coord;
+ coord
+-------
+ 0.42
+(1 row)
+</programlisting>
+
+ <para>
+ So using that operator as kNN metric we can obtain cubes sorted by it's
+ coordinate.
+ </para>
+ <para>
+ To get cubes ordered by first coordinate of lower left corner ascending
+ one can use the following query:
+ </para>
+<programlisting>
+SELECT c FROM test ORDER BY c~>1 LIMIT 5;
+</programlisting>
+ <para>
+ And to get cubes descending by first coordinate of upper right corner
+ of 2d-cube:
+ </para>
+<programlisting>
+SELECT c FROM test ORDER BY c~>3 DESC LIMIT 5;
+</programlisting>
+
+
+
+ <para>
The standard B-tree operators are also provided, for example
<informaltable>