mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-22 08:03:24 +02:00
534 lines
14 KiB
C
534 lines
14 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* rtree_gist.c
|
|
* pg_amproc entries for GiSTs over 2-D boxes.
|
|
* This gives R-tree behavior, with Guttman's poly-time split algorithm.
|
|
*
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/contrib/rtree_gist/Attic/rtree_gist.c,v 1.4 2001/10/25 05:49:20 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/gist.h"
|
|
#include "access/itup.h"
|
|
#include "access/rtree.h"
|
|
#include "utils/palloc.h"
|
|
#include "utils/geo_decls.h"
|
|
#include "utils/elog.h"
|
|
|
|
typedef Datum (*RDF) (PG_FUNCTION_ARGS);
|
|
typedef Datum (*BINARY_UNION) (Datum, Datum, int *);
|
|
typedef float (*SIZE_BOX) (Datum);
|
|
|
|
/*
|
|
** box ops
|
|
*/
|
|
PG_FUNCTION_INFO_V1(gbox_compress);
|
|
PG_FUNCTION_INFO_V1(gbox_union);
|
|
PG_FUNCTION_INFO_V1(gbox_picksplit);
|
|
PG_FUNCTION_INFO_V1(gbox_consistent);
|
|
PG_FUNCTION_INFO_V1(gbox_penalty);
|
|
PG_FUNCTION_INFO_V1(gbox_same);
|
|
|
|
Datum gbox_compress(PG_FUNCTION_ARGS);
|
|
Datum gbox_union(PG_FUNCTION_ARGS);
|
|
Datum gbox_picksplit(PG_FUNCTION_ARGS);
|
|
Datum gbox_consistent(PG_FUNCTION_ARGS);
|
|
Datum gbox_penalty(PG_FUNCTION_ARGS);
|
|
Datum gbox_same(PG_FUNCTION_ARGS);
|
|
|
|
static bool gbox_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy);
|
|
static float size_box(Datum box);
|
|
|
|
/*
|
|
** Polygon ops
|
|
*/
|
|
PG_FUNCTION_INFO_V1(gpoly_compress);
|
|
PG_FUNCTION_INFO_V1(gpoly_consistent);
|
|
|
|
Datum gpoly_compress(PG_FUNCTION_ARGS);
|
|
Datum gpoly_consistent(PG_FUNCTION_ARGS);
|
|
|
|
/*
|
|
** Common rtree-function (for all ops)
|
|
*/
|
|
static bool rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy);
|
|
|
|
PG_FUNCTION_INFO_V1(rtree_decompress);
|
|
|
|
Datum rtree_decompress(PG_FUNCTION_ARGS);
|
|
|
|
/**************************************************
|
|
* Box ops
|
|
**************************************************/
|
|
|
|
/*
|
|
** The GiST Consistent method for boxes
|
|
** Should return false if for all data items x below entry,
|
|
** the predicate x op query == FALSE, where op is the oper
|
|
** corresponding to strategy in the pg_amop table.
|
|
*/
|
|
Datum
|
|
gbox_consistent(PG_FUNCTION_ARGS)
|
|
{
|
|
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
|
BOX *query = (BOX *) PG_GETARG_POINTER(1);
|
|
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
|
|
|
|
/*
|
|
* * if entry is not leaf, use gbox_internal_consistent, * else use
|
|
* gbox_leaf_consistent
|
|
*/
|
|
if (!(DatumGetPointer(entry->key) != NULL && query))
|
|
PG_RETURN_BOOL(FALSE);
|
|
|
|
if (GIST_LEAF(entry))
|
|
PG_RETURN_BOOL(gbox_leaf_consistent((BOX *) DatumGetPointer(entry->key), query, strategy));
|
|
else
|
|
PG_RETURN_BOOL(rtree_internal_consistent((BOX *) DatumGetPointer(entry->key), query, strategy));
|
|
}
|
|
|
|
|
|
/*
|
|
** The GiST Union method for boxes
|
|
** returns the minimal bounding box that encloses all the entries in entryvec
|
|
*/
|
|
Datum
|
|
gbox_union(PG_FUNCTION_ARGS)
|
|
{
|
|
bytea *entryvec = (bytea *) PG_GETARG_POINTER(0);
|
|
int *sizep = (int *) PG_GETARG_POINTER(1);
|
|
int numranges,
|
|
i;
|
|
BOX *cur,
|
|
*pageunion;
|
|
|
|
numranges = (VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY);
|
|
pageunion = (BOX *) palloc(sizeof(BOX));
|
|
cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[0].key);
|
|
memcpy((void *) pageunion, (void *) cur, sizeof(BOX));
|
|
|
|
for (i = 1; i < numranges; i++)
|
|
{
|
|
cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
|
|
if (pageunion->high.x < cur->high.x)
|
|
pageunion->high.x = cur->high.x;
|
|
if (pageunion->low.x > cur->low.x)
|
|
pageunion->low.x = cur->low.x;
|
|
if (pageunion->high.y < cur->high.y)
|
|
pageunion->high.y = cur->high.y;
|
|
if (pageunion->low.y > cur->low.y)
|
|
pageunion->low.y = cur->low.y;
|
|
}
|
|
*sizep = sizeof(BOX);
|
|
|
|
PG_RETURN_POINTER(pageunion);
|
|
}
|
|
|
|
/*
|
|
** GiST Compress methods for boxes
|
|
** do not do anything.
|
|
*/
|
|
Datum
|
|
gbox_compress(PG_FUNCTION_ARGS)
|
|
{
|
|
PG_RETURN_POINTER(PG_GETARG_POINTER(0));
|
|
}
|
|
|
|
/*
|
|
** The GiST Penalty method for boxes
|
|
** As in the R-tree paper, we use change in area as our penalty metric
|
|
*/
|
|
Datum
|
|
gbox_penalty(PG_FUNCTION_ARGS)
|
|
{
|
|
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
|
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
|
|
float *result = (float *) PG_GETARG_POINTER(2);
|
|
Datum ud;
|
|
float tmp1;
|
|
|
|
ud = DirectFunctionCall2(rt_box_union, origentry->key, newentry->key);
|
|
tmp1 = size_box(ud);
|
|
if (DatumGetPointer(ud) != NULL)
|
|
pfree(DatumGetPointer(ud));
|
|
|
|
*result = tmp1 - size_box(origentry->key);
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
/*
|
|
** The GiST PickSplit method
|
|
** New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree',
|
|
** C.H.Ang and T.C.Tan
|
|
*/
|
|
Datum
|
|
gbox_picksplit(PG_FUNCTION_ARGS)
|
|
{
|
|
bytea *entryvec = (bytea *) PG_GETARG_POINTER(0);
|
|
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
|
|
OffsetNumber i;
|
|
OffsetNumber *listL,
|
|
*listR,
|
|
*listB,
|
|
*listT;
|
|
BOX *unionL,
|
|
*unionR,
|
|
*unionB,
|
|
*unionT;
|
|
int posL,
|
|
posR,
|
|
posB,
|
|
posT;
|
|
BOX pageunion;
|
|
BOX *cur;
|
|
char direction = ' ';
|
|
bool allisequal = true;
|
|
OffsetNumber maxoff;
|
|
int nbytes;
|
|
|
|
posL = posR = posB = posT = 0;
|
|
maxoff = ((VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY)) - 1;
|
|
|
|
cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[FirstOffsetNumber].key);
|
|
memcpy((void *) &pageunion, (void *) cur, sizeof(BOX));
|
|
|
|
/* find MBR */
|
|
for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i))
|
|
{
|
|
cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
|
|
if (pageunion.high.x < cur->high.x)
|
|
{
|
|
allisequal = false;
|
|
pageunion.high.x = cur->high.x;
|
|
}
|
|
if (pageunion.low.x > cur->low.x)
|
|
{
|
|
allisequal = false;
|
|
pageunion.low.x = cur->low.x;
|
|
}
|
|
if (pageunion.high.y < cur->high.y)
|
|
{
|
|
allisequal = false;
|
|
pageunion.high.y = cur->high.y;
|
|
}
|
|
if (pageunion.low.y > cur->low.y)
|
|
{
|
|
allisequal = false;
|
|
pageunion.low.y = cur->low.y;
|
|
}
|
|
}
|
|
|
|
nbytes = (maxoff + 2) * sizeof(OffsetNumber);
|
|
listL = (OffsetNumber *) palloc(nbytes);
|
|
listR = (OffsetNumber *) palloc(nbytes);
|
|
unionL = (BOX *) palloc(sizeof(BOX));
|
|
unionR = (BOX *) palloc(sizeof(BOX));
|
|
if (allisequal)
|
|
{
|
|
cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[OffsetNumberNext(FirstOffsetNumber)].key);
|
|
if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX)) == 0)
|
|
{
|
|
v->spl_left = listL;
|
|
v->spl_right = listR;
|
|
v->spl_nleft = v->spl_nright = 0;
|
|
memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX));
|
|
memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX));
|
|
|
|
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
|
|
{
|
|
if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
|
|
{
|
|
v->spl_left[v->spl_nleft] = i;
|
|
v->spl_nleft++;
|
|
}
|
|
else
|
|
{
|
|
v->spl_right[v->spl_nright] = i;
|
|
v->spl_nright++;
|
|
}
|
|
}
|
|
v->spl_ldatum = BoxPGetDatum(unionL);
|
|
v->spl_rdatum = BoxPGetDatum(unionR);
|
|
|
|
PG_RETURN_POINTER(v);
|
|
}
|
|
}
|
|
|
|
listB = (OffsetNumber *) palloc(nbytes);
|
|
listT = (OffsetNumber *) palloc(nbytes);
|
|
unionB = (BOX *) palloc(sizeof(BOX));
|
|
unionT = (BOX *) palloc(sizeof(BOX));
|
|
|
|
#define ADDLIST( list, unionD, pos ) do { \
|
|
if ( pos ) { \
|
|
if ( unionD->high.x < cur->high.x ) unionD->high.x = cur->high.x; \
|
|
if ( unionD->low.x > cur->low.x ) unionD->low.x = cur->low.x; \
|
|
if ( unionD->high.y < cur->high.y ) unionD->high.y = cur->high.y; \
|
|
if ( unionD->low.y > cur->low.y ) unionD->low.y = cur->low.y; \
|
|
} else { \
|
|
memcpy( (void*)unionD, (void*) cur, sizeof( BOX ) ); \
|
|
} \
|
|
list[pos] = i; \
|
|
(pos)++; \
|
|
} while(0)
|
|
|
|
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
|
|
{
|
|
cur = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key);
|
|
if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x)
|
|
ADDLIST(listL, unionL, posL);
|
|
else
|
|
ADDLIST(listR, unionR, posR);
|
|
if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y)
|
|
ADDLIST(listB, unionB, posB);
|
|
else
|
|
ADDLIST(listT, unionT, posT);
|
|
}
|
|
|
|
/* which split more optimal? */
|
|
|
|
if (Max(posL, posR) < Max(posB, posT))
|
|
direction = 'x';
|
|
else if (Max(posL, posR) > Max(posB, posT))
|
|
direction = 'y';
|
|
else
|
|
{
|
|
Datum interLR = DirectFunctionCall2(rt_box_inter,
|
|
BoxPGetDatum(unionL),
|
|
BoxPGetDatum(unionR));
|
|
Datum interBT = DirectFunctionCall2(rt_box_inter,
|
|
BoxPGetDatum(unionB),
|
|
BoxPGetDatum(unionT));
|
|
float sizeLR,
|
|
sizeBT;
|
|
|
|
sizeLR = size_box(interLR);
|
|
sizeBT = size_box(interBT);
|
|
|
|
if (sizeLR < sizeBT)
|
|
direction = 'x';
|
|
else
|
|
direction = 'y';
|
|
}
|
|
|
|
if (direction == 'x')
|
|
{
|
|
pfree(unionB);
|
|
pfree(listB);
|
|
pfree(unionT);
|
|
pfree(listT);
|
|
|
|
v->spl_left = listL;
|
|
v->spl_right = listR;
|
|
v->spl_nleft = posL;
|
|
v->spl_nright = posR;
|
|
v->spl_ldatum = BoxPGetDatum(unionL);
|
|
v->spl_rdatum = BoxPGetDatum(unionR);
|
|
}
|
|
else
|
|
{
|
|
pfree(unionR);
|
|
pfree(listR);
|
|
pfree(unionL);
|
|
pfree(listL);
|
|
|
|
v->spl_left = listB;
|
|
v->spl_right = listT;
|
|
v->spl_nleft = posB;
|
|
v->spl_nright = posT;
|
|
v->spl_ldatum = BoxPGetDatum(unionB);
|
|
v->spl_rdatum = BoxPGetDatum(unionT);
|
|
}
|
|
|
|
PG_RETURN_POINTER(v);
|
|
}
|
|
|
|
/*
|
|
** Equality method
|
|
*/
|
|
Datum
|
|
gbox_same(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX *b1 = (BOX *) PG_GETARG_POINTER(0);
|
|
BOX *b2 = (BOX *) PG_GETARG_POINTER(1);
|
|
bool *result = (bool *) PG_GETARG_POINTER(2);
|
|
|
|
if (b1 && b2)
|
|
*result = DatumGetBool(DirectFunctionCall2(box_same, PointerGetDatum(b1), PointerGetDatum(b2)));
|
|
else
|
|
*result = (b1 == NULL && b2 == NULL) ? TRUE : FALSE;
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
/*
|
|
** SUPPORT ROUTINES for boxes
|
|
*/
|
|
static bool
|
|
gbox_leaf_consistent(BOX *key,
|
|
BOX *query,
|
|
StrategyNumber strategy)
|
|
{
|
|
bool retval;
|
|
|
|
switch (strategy)
|
|
{
|
|
case RTLeftStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_left, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
case RTOverLeftStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_overleft, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
case RTOverlapStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_overlap, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
case RTOverRightStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_overright, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
case RTRightStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_right, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
case RTSameStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_same, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
case RTContainsStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_contain, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
case RTContainedByStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_contained, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
default:
|
|
retval = FALSE;
|
|
}
|
|
return (retval);
|
|
}
|
|
|
|
static float
|
|
size_box(Datum box)
|
|
{
|
|
if (DatumGetPointer(box) != NULL)
|
|
{
|
|
float size;
|
|
|
|
DirectFunctionCall2(rt_box_size,
|
|
box, PointerGetDatum(&size));
|
|
return size;
|
|
}
|
|
else
|
|
return 0.0;
|
|
}
|
|
|
|
/**************************************************
|
|
* Polygon ops
|
|
**************************************************/
|
|
|
|
Datum
|
|
gpoly_compress(PG_FUNCTION_ARGS)
|
|
{
|
|
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
|
GISTENTRY *retval;
|
|
|
|
if (entry->leafkey)
|
|
{
|
|
retval = palloc(sizeof(GISTENTRY));
|
|
if (DatumGetPointer(entry->key) != NULL)
|
|
{
|
|
POLYGON *in;
|
|
BOX *r;
|
|
|
|
in = (POLYGON *) PG_DETOAST_DATUM(entry->key);
|
|
r = (BOX *) palloc(sizeof(BOX));
|
|
memcpy((void *) r, (void *) &(in->boundbox), sizeof(BOX));
|
|
if (in != (POLYGON *) DatumGetPointer(entry->key))
|
|
pfree(in);
|
|
|
|
gistentryinit(*retval, PointerGetDatum(r),
|
|
entry->rel, entry->page,
|
|
entry->offset, sizeof(BOX), FALSE);
|
|
|
|
}
|
|
else
|
|
{
|
|
gistentryinit(*retval, (Datum) 0,
|
|
entry->rel, entry->page,
|
|
entry->offset, 0, FALSE);
|
|
}
|
|
}
|
|
else
|
|
retval = entry;
|
|
PG_RETURN_POINTER(retval);
|
|
}
|
|
|
|
Datum
|
|
gpoly_consistent(PG_FUNCTION_ARGS)
|
|
{
|
|
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
|
POLYGON *query = (POLYGON *) PG_DETOAST_DATUM(PG_GETARG_POINTER(1));
|
|
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
|
|
bool result;
|
|
|
|
/*
|
|
* * if entry is not leaf, use gbox_internal_consistent, * else use
|
|
* gbox_leaf_consistent
|
|
*/
|
|
if (!(DatumGetPointer(entry->key) != NULL && query))
|
|
PG_RETURN_BOOL(FALSE);
|
|
|
|
result = rtree_internal_consistent((BOX *) DatumGetPointer(entry->key),
|
|
&(query->boundbox), strategy);
|
|
|
|
PG_FREE_IF_COPY(query, 1);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/*****************************************
|
|
* Common rtree-function (for all ops)
|
|
*****************************************/
|
|
|
|
static bool
|
|
rtree_internal_consistent(BOX *key,
|
|
BOX *query,
|
|
StrategyNumber strategy)
|
|
{
|
|
bool retval;
|
|
|
|
switch (strategy)
|
|
{
|
|
case RTLeftStrategyNumber:
|
|
case RTOverLeftStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_overleft, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
case RTOverlapStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_overlap, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
case RTOverRightStrategyNumber:
|
|
case RTRightStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_right, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
case RTSameStrategyNumber:
|
|
case RTContainsStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_contain, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
case RTContainedByStrategyNumber:
|
|
retval = DatumGetBool(DirectFunctionCall2(box_overlap, PointerGetDatum(key), PointerGetDatum(query)));
|
|
break;
|
|
default:
|
|
retval = FALSE;
|
|
}
|
|
return (retval);
|
|
}
|
|
|
|
/*
|
|
** GiST DeCompress methods
|
|
** do not do anything.
|
|
*/
|
|
Datum
|
|
rtree_decompress(PG_FUNCTION_ARGS)
|
|
{
|
|
PG_RETURN_POINTER(PG_GETARG_POINTER(0));
|
|
}
|