1/******************************************************************************
4 This file contains routines that can be bound to a Postgres backend and
5 called by the backend in the process of processing queries. The calling
6 format for these routines is dictated by Postgres architecture.
7******************************************************************************/
26 * Taken from the intarray contrib header
28 #define ARRPTR(x) ( (double *) ARR_DATA_PTR(x) )
29 #define ARRNELEMS(x) ArrayGetNItems( ARR_NDIM(x), ARR_DIMS(x))
32** Input/Output routines
52** GiST support methods
65** B-tree support functions
76** R-tree support functions
96** For internal use only
108** Auxiliary functions
114/*****************************************************************************
115 * Input/Output functions
116 *****************************************************************************/
118/* NdBox = [(lowerleft),(upperright)] */
119/* [(xLL(1)...xLL(N)),(xUR(1)...xUR(n))] */
130 cube_yyparse(&result, scanbuflen, fcinfo->context, scanner);
132 /* We might as well run this even on failure. */
140** Allows the construction of a cube from 2 float[]'s
157 (
errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
158 errmsg(
"cannot work with arrays containing NULLs")));
163 (
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
164 errmsg(
"can't extend cube"),
165 errdetail(
"A cube cannot have more than %d dimensions.",
170 (
errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
171 errmsg(
"UR and LL arrays must be of same length")));
176 /* Check if it's a point */
178 for (
i = 0;
i < dim;
i++)
180 if (dur[
i] != dll[
i])
192 for (
i = 0;
i < dim;
i++)
193 result->
x[
i] = dur[
i];
197 for (
i = 0;
i < dim;
i++)
198 result->
x[
i + dim] = dll[
i];
207** Allows the construction of a zero-volume cube from a float[]
221 (
errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
222 errmsg(
"cannot work with arrays containing NULLs")));
227 (
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
228 errmsg(
"array is too long"),
229 errdetail(
"A cube cannot have more than %d dimensions.",
240 for (
i = 0;
i < dim;
i++)
241 result->
x[
i] = dur[
i];
259 (
errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
260 errmsg(
"cannot work with arrays containing NULLs")));
267 (
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
268 errmsg(
"array is too long"),
269 errdetail(
"A cube cannot have more than %d dimensions.",
280 for (
i = 0;
i < dim;
i++)
282 if ((dx[
i] <= 0) || (dx[
i] >
DIM(
c)))
284 (
errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
285 errmsg(
"Index out of bounds")));
286 result->
x[
i] =
c->x[dx[
i] - 1];
288 result->
x[
i + dim] =
c->x[dx[
i] +
DIM(
c) - 1];
306 for (
i = 0;
i < dim;
i++)
317 for (
i = 0;
i < dim;
i++)
331 * cube_send - a binary output handler for cube type
345 /* for symmetry with cube_recv, we don't use LL_COORD/UR_COORD here */
353 * cube_recv - a binary input handler for cube type
368 (
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
369 errmsg(
"cube dimension is too large"),
370 errdetail(
"A cube cannot have more than %d dimensions.",
384/*****************************************************************************
386 *****************************************************************************/
389** The GiST Consistent method for boxes
390** Should return false if for all data items x below entry,
391** the predicate x op query == false, where op is the oper
392** corresponding to strategy in the pg_amop table.
401 /* Oid subtype = PG_GETARG_OID(3); */
405 /* All cases served by this function are exact */
409 * if entry is not leaf, use g_cube_internal_consistent, else use
410 * g_cube_leaf_consistent
425** The GiST Union method for boxes
426** returns the minimal bounding box that encloses all the entries in entryvec
440 * sizep = sizeof(NDBOX); -- NDBOX has variable size
444 for (
i = 1;
i < entryvec->
n;
i++)
456** GiST Compress and Decompress methods for boxes
457** do not do anything.
486** The GiST Penalty method for boxes
487** As in the R-tree paper, we use change in area as our penalty metric
503 *result = (float) (tmp1 - tmp2);
511** The GiST PickSplit method for boxes
512** We use Guttman's poly time split algorithm
545 maxoff = entryvec->
n - 2;
560 /* compute the wasted space by unioning these guys */
561 /* size_waste = size_union - size_inter; */
568 size_waste = size_union - size_inter;
571 * are these a more promising split than what we've already seen?
574 if (size_waste > waste || firsttime)
597 * Now split up the regions between the two seeds. An important property
598 * of this split algorithm is that the split vector v has the indices of
599 * items to be split in order in its left and right vectors. We exploit
600 * this property by doing a merge in the code that actually splits the
603 * For efficiency, we also place the new index tuple in this loop. This is
604 * handled at the very end, when we have placed all the existing tuples
605 * and i == maxoff + 1.
612 * If we've already decided where to place this item, just put it on
613 * the right list. Otherwise, we need to figure out which page needs
614 * the least enlargement in order to store the item.
623 else if (
i == seed_2)
630 /* okay, which page needs least enlargement? */
637 /* pick which page to add it to */
638 if (size_alpha - size_l < size_beta - size_r)
763 /* swap the arguments if needed, so that 'a' is always larger than 'b' */
778 /* First compute the union of the dimensions present in both args */
786 /* continue on the higher dimensions only present in 'a' */
789 result->
x[
i] =
Min(0,
792 result->
x[
i + dim] =
Max(0,
798 * Check if the result was in fact a point, and set the flag in the datum
799 * accordingly. (we don't bother to repalloc it smaller)
832 bool swapped =
false;
837 /* swap the arguments if needed, so that 'a' is always larger than 'b' */
853 /* First compute intersection of the dimensions present in both args */
861 /* continue on the higher dimensions only present in 'a' */
864 result->
x[
i] =
Max(0,
873 * Check if the result was in fact a point, and set the flag in the datum
874 * accordingly. (we don't bother to repalloc it smaller)
896 * Is it OK to return a non-null intersection for non-overlapping boxes?
921 /* special case for GiST */
926 /* necessarily has zero size */
938/* make up a metric in which one box will be 'lower' than the other
939 -- this can be useful for sorting and to determine uniqueness */
948 /* compare the common dimensions */
949 for (
i = 0;
i < dim;
i++)
958 for (
i = 0;
i < dim;
i++)
968 /* compare extra dimensions to zero */
987 * if all common dimensions are equal, the cube with more dimensions
1001 for (
i = dim;
i <
DIM(
b);
i++)
1010 * if all common dimensions are equal, the cube with more dimensions
1016 /* They're really equal */
1126/* Box(A) CONTAINS Box(B) IFF pt(A) < pt(B) */
1132 if ((
a == NULL) || (
b == NULL))
1138 * the further comparisons will make sense if the excess dimensions of
1139 * (b) were zeroes Since both UL and UR coordinates must be zero, we
1140 * can check them all without worrying about which is which.
1151 /* Can't care less about the excess dimensions of (a), if any */
1180/* Box(A) Contained by Box(B) IFF Box(B) Contains Box(A) */
1196/* Box(A) Overlap Box(B) IFF (pt(a)LL < pt(B)UR) && (pt(b)LL < pt(a)UR) */
1202 if ((
a == NULL) || (
b == NULL))
1205 /* swap the box pointers if needed */
1214 /* compare within the dimensions of (b) */
1223 /* compare to zero those dimensions in (a) absent in (b) */
1252/* The distance is computed as a per axis sum of the squared distances
1253 between 1D projections of the boxes onto Cartesian axes. Assuming zero
1254 distance between overlapping projections, this metric coincides with the
1255 "common sense" geometric distance */
1261 bool swapped =
false;
1266 /* swap the box pointers if needed */
1277 /* compute within the dimensions of (b) */
1284 /* compute distance to zero for those dimensions in (a) absent in (b) */
1310 bool swapped =
false;
1314 /* swap the box pointers if needed */
1325 /* compute within the dimensions of (b) */
1330 /* compute distance to zero for those dimensions in (a) absent in (b) */
1354 bool swapped =
false;
1359 /* swap the box pointers if needed */
1370 /* compute within the dimensions of (b) */
1379 /* compute distance to zero for those dimensions in (a) absent in (b) */
1412 * Handle ordering by ~> operator. See comments of cube_coord_llur()
1417 bool inverse =
false;
1419 /* 0 is the only unsupported coordinate value */
1422 (
errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
1423 errmsg(
"zero cube index is not defined")));
1425 /* Return inversed value for negative coordinate */
1432 if (coord <= 2 *
DIM(cube))
1434 /* dimension index */
1435 int index = (coord - 1) / 2;
1437 /* whether this is upper bound (lower bound otherwise) */
1438 bool upper = ((coord - 1) % 2 == 1);
1448 /* For leaf just return required upper/lower bound */
1457 * For non-leaf we should always return lower bound,
1458 * because even upper bound of a child in the subtree can
1459 * be as small as our lower bound. For inversed case we
1460 * return upper bound because it becomes lower bound for
1475 /* Inverse return value if needed */
1498 elog(
ERROR,
"unrecognized cube strategy number: %d", strategy);
1499 retval = 0;
/* keep compiler quiet */
1509 /* interval (a) is entirely on the left of (b) */
1510 if ((
a1 <= b1) && (
a2 <= b1) && (
a1 <= b2) && (
a2 <= b2))
1513 /* interval (a) is entirely on the right of (b) */
1514 if ((
a1 > b1) && (
a2 > b1) && (
a1 > b2) && (
a2 > b2))
1517 /* the rest are all sorts of intersections */
1521/* Test if a box is also a point */
1542 * Even if the point-flag is not set, all the lower-left coordinates might
1543 * match the upper-right coordinates, so that the value is in fact a
1544 * point. Such values don't arise with current code - the point flag is
1545 * always set if appropriate - but they might be present on-disk in
1546 * clusters upgraded from pre-9.4 versions.
1548 for (
i = 0;
i <
DIM(cube);
i++)
1556/* Return dimensions in use in the data structure */
1567/* Return a specific normalized LL coordinate */
1575 if (
DIM(
c) >= n && n > 0)
1584/* Return a specific normalized UR coordinate */
1592 if (
DIM(
c) >= n && n > 0)
1602 * Function returns cube coordinate.
1603 * Numbers from 1 to DIM denotes first corner coordinates.
1604 * Numbers from DIM+1 to 2*DIM denotes second corner coordinates.
1612 if (coord <= 0 || coord > 2 *
DIM(cube))
1614 (
errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
1615 errmsg(
"cube index %d is out of bounds", coord)));
1625 * This function works like cube_coord(), but rearranges coordinates in the
1626 * way suitable to support coordinate ordering using KNN-GiST. For historical
1627 * reasons this extension allows us to create cubes in form ((2,1),(1,2)) and
1628 * instead of normalizing such cube to ((1,1),(2,2)) it stores cube in original
1629 * way. But in order to get cubes ordered by one of dimensions from the index
1630 * without explicit sort step we need this representation-independent coordinate
1631 * getter. Moreover, indexed dataset may contain cubes of different dimensions
1632 * number. Accordingly, this coordinate getter should be able to return
1633 * lower/upper bound for particular dimension independently on number of cube
1634 * dimensions. Also, KNN-GiST supports only ascending sorting. In order to
1635 * support descending sorting, this function returns inverse of value when
1636 * negative coordinate is given.
1638 * Long story short, this function uses following meaning of coordinates:
1639 * # (2 * N - 1) -- lower bound of Nth dimension,
1640 * # (2 * N) -- upper bound of Nth dimension,
1641 * # - (2 * N - 1) -- negative of lower bound of Nth dimension,
1642 * # - (2 * N) -- negative of upper bound of Nth dimension.
1644 * When given coordinate exceeds number of cube dimensions, then 0 returned
1645 * (reproducing logic of GiST indexing of variable-length cubes).
1652 bool inverse =
false;
1655 /* 0 is the only unsupported coordinate value */
1658 (
errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
1659 errmsg(
"zero cube index is not defined")));
1661 /* Return inversed value for negative coordinate */
1668 if (coord <= 2 *
DIM(cube))
1670 /* dimension index */
1671 int index = (coord - 1) / 2;
1673 /* whether this is upper bound (lower bound otherwise) */
1674 bool upper = ((coord - 1) % 2 == 1);
1691 * Return zero if coordinate is out of bound. That reproduces logic
1692 * of how cubes with low dimension number are expanded during GiST
1698 /* Inverse value if needed */
1705/* Increase or decrease box size by a radius in at least n dimensions. */
1730 for (
i = 0,
j = dim;
i <
DIM(
a);
i++,
j++)
1742 if (result->
x[
i] > result->
x[
j])
1744 result->
x[
i] = (result->
x[
i] + result->
x[
j]) / 2;
1745 result->
x[
j] = result->
x[
i];
1748 /* dim > a->dim only if r > 0 */
1749 for (;
i < dim;
i++,
j++)
1756 * Check if the result was in fact a point, and set the flag in the datum
1757 * accordingly. (we don't bother to repalloc it smaller)
1770/* Create a one dimensional box with identical upper and lower coordinates */
1788/* Create a one dimensional box */
1819/* Add a dimension to an existing cube with the same values for the new
1832 (
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1833 errmsg(
"can't extend cube"),
1834 errdetail(
"A cube cannot have more than %d dimensions.",
1844 for (
i = 0;
i <
DIM(cube);
i++)
1845 result->
x[
i] = cube->
x[
i];
1846 result->
x[
DIM(result) - 1] =
x;
1854 for (
i = 0;
i <
DIM(cube);
i++)
1856 result->
x[
i] = cube->
x[
i];
1857 result->
x[
DIM(result) +
i] = cube->
x[
DIM(cube) +
i];
1859 result->
x[
DIM(result) - 1] =
x;
1860 result->
x[2 *
DIM(result) - 1] =
x;
1867/* Add a dimension to an existing cube */
1880 (
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1881 errmsg(
"can't extend cube"),
1882 errdetail(
"A cube cannot have more than %d dimensions.",
1892 for (
i = 0;
i <
DIM(cube);
i++)
1893 result->
x[
i] = cube->
x[
i];
1894 result->
x[
DIM(result) - 1] = x1;
1902 for (
i = 0;
i <
DIM(cube);
i++)
1907 result->
x[
DIM(result) - 1] = x1;
1908 result->
x[2 *
DIM(result) - 1] = x2;
Datum idx(PG_FUNCTION_ARGS)
#define PG_GETARG_ARRAYTYPE_P(n)
bool array_contains_nulls(ArrayType *array)
Datum cube_gt(PG_FUNCTION_ARGS)
Datum cube_eq(PG_FUNCTION_ARGS)
Datum cube_le(PG_FUNCTION_ARGS)
Datum cube_coord(PG_FUNCTION_ARGS)
Datum distance_taxicab(PG_FUNCTION_ARGS)
Datum cube_ll_coord(PG_FUNCTION_ARGS)
Datum cube_subset(PG_FUNCTION_ARGS)
Datum cube_distance(PG_FUNCTION_ARGS)
static bool cube_is_point_internal(NDBOX *cube)
bool cube_overlap_v0(NDBOX *a, NDBOX *b)
bool g_cube_leaf_consistent(NDBOX *key, NDBOX *query, StrategyNumber strategy)
bool cube_contains_v0(NDBOX *a, NDBOX *b)
Datum distance_chebyshev(PG_FUNCTION_ARGS)
Datum g_cube_picksplit(PG_FUNCTION_ARGS)
int32 cube_cmp_v0(NDBOX *a, NDBOX *b)
Datum cube_dim(PG_FUNCTION_ARGS)
Datum g_cube_union(PG_FUNCTION_ARGS)
Datum cube_in(PG_FUNCTION_ARGS)
Datum cube_enlarge(PG_FUNCTION_ARGS)
Datum cube_c_f8(PG_FUNCTION_ARGS)
Datum g_cube_same(PG_FUNCTION_ARGS)
Datum cube_ur_coord(PG_FUNCTION_ARGS)
Datum g_cube_distance(PG_FUNCTION_ARGS)
Datum cube_contained(PG_FUNCTION_ARGS)
Datum cube_cmp(PG_FUNCTION_ARGS)
Datum cube_a_f8_f8(PG_FUNCTION_ARGS)
PG_MODULE_MAGIC_EXT(.name="cube",.version=PG_VERSION)
Datum g_cube_consistent(PG_FUNCTION_ARGS)
NDBOX * cube_union_v0(NDBOX *a, NDBOX *b)
static double distance_1D(double a1, double a2, double b1, double b2)
Datum cube_size(PG_FUNCTION_ARGS)
Datum cube_recv(PG_FUNCTION_ARGS)
void rt_cube_size(NDBOX *a, double *size)
Datum cube_lt(PG_FUNCTION_ARGS)
Datum cube_contains(PG_FUNCTION_ARGS)
Datum cube_union(PG_FUNCTION_ARGS)
Datum cube_f8_f8(PG_FUNCTION_ARGS)
Datum g_cube_decompress(PG_FUNCTION_ARGS)
Datum cube_f8(PG_FUNCTION_ARGS)
Datum cube_a_f8(PG_FUNCTION_ARGS)
Datum cube_c_f8_f8(PG_FUNCTION_ARGS)
Datum cube_coord_llur(PG_FUNCTION_ARGS)
Datum cube_inter(PG_FUNCTION_ARGS)
Datum g_cube_compress(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(cube_in)
Datum cube_is_point(PG_FUNCTION_ARGS)
bool g_cube_internal_consistent(NDBOX *key, NDBOX *query, StrategyNumber strategy)
NDBOX * g_cube_binary_union(NDBOX *r1, NDBOX *r2, int *sizep)
Datum cube_send(PG_FUNCTION_ARGS)
Datum cube_overlap(PG_FUNCTION_ARGS)
Datum cube_ge(PG_FUNCTION_ARGS)
Datum cube_out(PG_FUNCTION_ARGS)
Datum g_cube_penalty(PG_FUNCTION_ARGS)
Datum cube_ne(PG_FUNCTION_ARGS)
int cube_yyparse(NDBOX **result, Size scanbuflen, struct Node *escontext, yyscan_t yyscanner)
#define SET_DIM(cube, _dim)
void cube_scanner_init(const char *str, Size *scanbuflen, yyscan_t *yyscannerp)
#define DatumGetNDBOXP(x)
#define LL_COORD(cube, i)
#define CubeKNNDistanceCoord
void cube_scanner_finish(yyscan_t yyscanner)
#define CubeKNNDistanceEuclid
#define PG_RETURN_NDBOX_P(x)
#define CubeKNNDistanceTaxicab
#define SET_POINT_BIT(cube)
#define CubeKNNDistanceChebyshev
#define PG_GETARG_NDBOX_P(x)
#define UR_COORD(cube, i)
int errdetail(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
char * float8out_internal(double num)
#define PG_FREE_IF_COPY(ptr, n)
#define PG_RETURN_BYTEA_P(x)
#define DirectFunctionCall2(func, arg1, arg2)
#define PG_GETARG_FLOAT8(n)
#define PG_RETURN_FLOAT8(x)
#define PG_GETARG_POINTER(n)
#define PG_RETURN_CSTRING(x)
#define PG_GETARG_DATUM(n)
#define PG_GETARG_CSTRING(n)
#define PG_GETARG_UINT16(n)
#define PG_RETURN_INT32(x)
#define PG_GETARG_INT32(n)
#define PG_RETURN_DATUM(x)
#define PG_RETURN_POINTER(x)
#define PG_RETURN_BOOL(x)
#define GistPageIsLeaf(page)
#define gistentryinit(e, k, r, pg, o, l)
static const FormData_pg_attribute a1
static const FormData_pg_attribute a2
if(TABLE==NULL||TABLE_index==NULL)
void * repalloc(void *pointer, Size size)
void * palloc0(Size size)
#define OffsetNumberNext(offsetNumber)
#define FirstOffsetNumber
Datum upper(PG_FUNCTION_ARGS)
static Datum PointerGetDatum(const void *X)
static float8 DatumGetFloat8(Datum X)
#define RTOldContainsStrategyNumber
#define RTOverlapStrategyNumber
#define RTSameStrategyNumber
#define RTContainsStrategyNumber
#define RTOldContainedByStrategyNumber
#define RTContainedByStrategyNumber
struct StringInfoData * StringInfo
void appendStringInfoString(StringInfo str, const char *s)
void appendStringInfoChar(StringInfo str, char ch)
void initStringInfo(StringInfo str)
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
double x[FLEXIBLE_ARRAY_MEMBER]
static Size VARSIZE(const void *PTR)
static void SET_VARSIZE(void *PTR, Size len)