|
|
|
|
@ -7,7 +7,7 @@ |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* IDENTIFICATION |
|
|
|
|
* $Header: /cvsroot/pgsql/contrib/rtree_gist/Attic/rtree_gist.c,v 1.1 2001/05/31 18:27:18 tgl Exp $ |
|
|
|
|
* $Header: /cvsroot/pgsql/contrib/rtree_gist/Attic/rtree_gist.c,v 1.2 2001/08/22 18:24:26 tgl Exp $ |
|
|
|
|
* |
|
|
|
|
*------------------------------------------------------------------------- |
|
|
|
|
*/ |
|
|
|
|
@ -24,14 +24,6 @@ typedef Datum (*RDF)(PG_FUNCTION_ARGS); |
|
|
|
|
typedef Datum (*BINARY_UNION)(Datum, Datum, int*); |
|
|
|
|
typedef float (*SIZE_BOX)(Datum); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
** Workaround for index_formtuple |
|
|
|
|
*/ |
|
|
|
|
typedef struct polykey { |
|
|
|
|
int32 size; /* size in varlena terms */
|
|
|
|
|
BOX key; |
|
|
|
|
} POLYKEY;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
** box ops |
|
|
|
|
*/ |
|
|
|
|
@ -42,14 +34,13 @@ PG_FUNCTION_INFO_V1(gbox_consistent); |
|
|
|
|
PG_FUNCTION_INFO_V1(gbox_penalty); |
|
|
|
|
PG_FUNCTION_INFO_V1(gbox_same); |
|
|
|
|
|
|
|
|
|
GISTENTRY * gbox_compress(PG_FUNCTION_ARGS); |
|
|
|
|
BOX *gbox_union(PG_FUNCTION_ARGS); |
|
|
|
|
GIST_SPLITVEC * gbox_picksplit(PG_FUNCTION_ARGS); |
|
|
|
|
bool gbox_consistent(PG_FUNCTION_ARGS); |
|
|
|
|
float * gbox_penalty(PG_FUNCTION_ARGS); |
|
|
|
|
bool * gbox_same(PG_FUNCTION_ARGS); |
|
|
|
|
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 Datum gbox_binary_union(Datum r1, Datum r2, int *sizep); |
|
|
|
|
static bool gbox_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy); |
|
|
|
|
static float size_box( Datum box ); |
|
|
|
|
|
|
|
|
|
@ -57,37 +48,19 @@ static float size_box( Datum box ); |
|
|
|
|
** Polygon ops
|
|
|
|
|
*/ |
|
|
|
|
PG_FUNCTION_INFO_V1(gpoly_compress); |
|
|
|
|
PG_FUNCTION_INFO_V1(gpoly_union); |
|
|
|
|
PG_FUNCTION_INFO_V1(gpoly_picksplit); |
|
|
|
|
PG_FUNCTION_INFO_V1(gpoly_consistent); |
|
|
|
|
PG_FUNCTION_INFO_V1(gpoly_penalty); |
|
|
|
|
PG_FUNCTION_INFO_V1(gpoly_same); |
|
|
|
|
|
|
|
|
|
GISTENTRY * gpoly_compress(PG_FUNCTION_ARGS); |
|
|
|
|
POLYKEY *gpoly_union(PG_FUNCTION_ARGS); |
|
|
|
|
GIST_SPLITVEC * gpoly_picksplit(PG_FUNCTION_ARGS); |
|
|
|
|
bool gpoly_consistent(PG_FUNCTION_ARGS); |
|
|
|
|
float * gpoly_penalty(PG_FUNCTION_ARGS); |
|
|
|
|
bool * gpoly_same(PG_FUNCTION_ARGS); |
|
|
|
|
|
|
|
|
|
static Datum gpoly_binary_union(Datum r1, Datum r2, int *sizep); |
|
|
|
|
static float size_polykey( Datum pk ); |
|
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(gpoly_inter); |
|
|
|
|
Datum gpoly_inter(PG_FUNCTION_ARGS); |
|
|
|
|
Datum gpoly_compress(PG_FUNCTION_ARGS); |
|
|
|
|
Datum gpoly_consistent(PG_FUNCTION_ARGS); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
** Common rtree-function (for all ops) |
|
|
|
|
*/ |
|
|
|
|
static Datum rtree_union(bytea *entryvec, int *sizep, BINARY_UNION bu); |
|
|
|
|
static float * rtree_penalty(GISTENTRY *origentry, GISTENTRY *newentry,
|
|
|
|
|
float *result, BINARY_UNION bu, SIZE_BOX sb); |
|
|
|
|
static GIST_SPLITVEC * rtree_picksplit(bytea *entryvec, GIST_SPLITVEC *v,
|
|
|
|
|
int keylen, BINARY_UNION bu, RDF interop, SIZE_BOX sb); |
|
|
|
|
static bool rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy); |
|
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(rtree_decompress); |
|
|
|
|
GISTENTRY * rtree_decompress(PG_FUNCTION_ARGS); |
|
|
|
|
|
|
|
|
|
Datum rtree_decompress(PG_FUNCTION_ARGS); |
|
|
|
|
|
|
|
|
|
/**************************************************
|
|
|
|
|
* Box ops |
|
|
|
|
@ -99,18 +72,19 @@ GISTENTRY * rtree_decompress(PG_FUNCTION_ARGS); |
|
|
|
|
** the predicate x op query == FALSE, where op is the oper |
|
|
|
|
** corresponding to strategy in the pg_amop table. |
|
|
|
|
*/ |
|
|
|
|
bool
|
|
|
|
|
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) ) |
|
|
|
|
return FALSE; |
|
|
|
|
PG_RETURN_BOOL(FALSE); |
|
|
|
|
|
|
|
|
|
if (GIST_LEAF(entry)) |
|
|
|
|
PG_RETURN_BOOL(gbox_leaf_consistent((BOX *) DatumGetPointer(entry->key), query, strategy)); |
|
|
|
|
@ -123,66 +97,221 @@ gbox_consistent(PG_FUNCTION_ARGS) |
|
|
|
|
** The GiST Union method for boxes |
|
|
|
|
** returns the minimal bounding box that encloses all the entries in entryvec |
|
|
|
|
*/ |
|
|
|
|
BOX * |
|
|
|
|
Datum |
|
|
|
|
gbox_union(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
return (BOX*)
|
|
|
|
|
DatumGetPointer(rtree_union( |
|
|
|
|
(bytea*) PG_GETARG_POINTER(0), |
|
|
|
|
(int*) PG_GETARG_POINTER(1), |
|
|
|
|
gbox_binary_union |
|
|
|
|
)); |
|
|
|
|
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. |
|
|
|
|
*/ |
|
|
|
|
GISTENTRY * |
|
|
|
|
Datum |
|
|
|
|
gbox_compress(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
return((GISTENTRY*)PG_GETARG_POINTER(0)); |
|
|
|
|
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 |
|
|
|
|
*/ |
|
|
|
|
float * |
|
|
|
|
Datum |
|
|
|
|
gbox_penalty(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
return rtree_penalty( |
|
|
|
|
(GISTENTRY*) PG_GETARG_POINTER(0), |
|
|
|
|
(GISTENTRY*) PG_GETARG_POINTER(1), |
|
|
|
|
(float*) PG_GETARG_POINTER(2), |
|
|
|
|
gbox_binary_union, |
|
|
|
|
size_box |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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 for boxes |
|
|
|
|
** We use Guttman's poly time split algorithm
|
|
|
|
|
** The GiST PickSplit method |
|
|
|
|
** New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree', |
|
|
|
|
** C.H.Ang and T.C.Tan |
|
|
|
|
*/ |
|
|
|
|
GIST_SPLITVEC * |
|
|
|
|
Datum |
|
|
|
|
gbox_picksplit(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
return rtree_picksplit( |
|
|
|
|
(bytea*)PG_GETARG_POINTER(0), |
|
|
|
|
(GIST_SPLITVEC*)PG_GETARG_POINTER(1), |
|
|
|
|
sizeof(BOX), |
|
|
|
|
gbox_binary_union, |
|
|
|
|
rt_box_inter, |
|
|
|
|
size_box |
|
|
|
|
); |
|
|
|
|
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 if ( sizeLR > sizeBT ) {
|
|
|
|
|
} 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 |
|
|
|
|
*/ |
|
|
|
|
bool * |
|
|
|
|
Datum |
|
|
|
|
gbox_same(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
BOX *b1 = (BOX*) PG_GETARG_POINTER(0); |
|
|
|
|
@ -192,7 +321,7 @@ gbox_same(PG_FUNCTION_ARGS) |
|
|
|
|
*result = DatumGetBool( DirectFunctionCall2( box_same, PointerGetDatum(b1), PointerGetDatum(b2)) ); |
|
|
|
|
else |
|
|
|
|
*result = ( b1==NULL && b2==NULL ) ? TRUE : FALSE;
|
|
|
|
|
return(result); |
|
|
|
|
PG_RETURN_POINTER(result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
@ -236,32 +365,6 @@ gbox_leaf_consistent(BOX *key, |
|
|
|
|
return(retval); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Datum |
|
|
|
|
gbox_binary_union(Datum r1, Datum r2, int *sizep) |
|
|
|
|
{ |
|
|
|
|
BOX *retval; |
|
|
|
|
|
|
|
|
|
if ( ! (DatumGetPointer(r1) != NULL && DatumGetPointer(r2) != NULL) ) { |
|
|
|
|
if ( DatumGetPointer(r1) != NULL ) { |
|
|
|
|
retval = (BOX*) palloc( sizeof(BOX) ); |
|
|
|
|
memcpy( retval, DatumGetPointer(r1), sizeof(BOX) ); |
|
|
|
|
*sizep = sizeof(BOX); |
|
|
|
|
} else if ( DatumGetPointer(r2) != NULL ) { |
|
|
|
|
retval = (BOX*) palloc( sizeof(BOX) ); |
|
|
|
|
memcpy( retval, DatumGetPointer(r2), sizeof(BOX) ); |
|
|
|
|
*sizep = sizeof(BOX); |
|
|
|
|
} else { |
|
|
|
|
*sizep = 0; |
|
|
|
|
retval = NULL; |
|
|
|
|
}
|
|
|
|
|
} else { |
|
|
|
|
retval = (BOX*) DatumGetPointer( |
|
|
|
|
DirectFunctionCall2(rt_box_union, r1, r2)); |
|
|
|
|
*sizep = sizeof(BOX); |
|
|
|
|
} |
|
|
|
|
return PointerGetDatum(retval); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static float
|
|
|
|
|
size_box( Datum box ) { |
|
|
|
|
if ( DatumGetPointer(box) != NULL ) { |
|
|
|
|
@ -278,7 +381,7 @@ size_box( Datum box ) { |
|
|
|
|
* Polygon ops |
|
|
|
|
**************************************************/ |
|
|
|
|
|
|
|
|
|
GISTENTRY * |
|
|
|
|
Datum |
|
|
|
|
gpoly_compress(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
GISTENTRY *entry=(GISTENTRY*)PG_GETARG_POINTER(0); |
|
|
|
|
@ -288,17 +391,16 @@ gpoly_compress(PG_FUNCTION_ARGS) |
|
|
|
|
retval = palloc(sizeof(GISTENTRY)); |
|
|
|
|
if ( DatumGetPointer(entry->key) != NULL ) { |
|
|
|
|
POLYGON *in; |
|
|
|
|
POLYKEY *r; |
|
|
|
|
BOX *r; |
|
|
|
|
in = (POLYGON *) PG_DETOAST_DATUM(entry->key); |
|
|
|
|
r = (POLYKEY *) palloc( sizeof(POLYKEY) ); |
|
|
|
|
r->size = sizeof(POLYKEY); |
|
|
|
|
memcpy( (void*)&(r->key), (void*)&(in->boundbox), sizeof(BOX) ); |
|
|
|
|
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(POLYKEY), FALSE); |
|
|
|
|
entry->offset, sizeof(BOX), FALSE); |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
gistentryinit(*retval, (Datum) 0, |
|
|
|
|
@ -308,355 +410,34 @@ gpoly_compress(PG_FUNCTION_ARGS) |
|
|
|
|
} else { |
|
|
|
|
retval = entry; |
|
|
|
|
} |
|
|
|
|
return( retval ); |
|
|
|
|
PG_RETURN_POINTER( retval ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
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) ) |
|
|
|
|
return FALSE; |
|
|
|
|
PG_RETURN_BOOL(FALSE); |
|
|
|
|
|
|
|
|
|
result = rtree_internal_consistent((BOX*)&( ((POLYKEY *) DatumGetPointer(entry->key))->key ),
|
|
|
|
|
result = rtree_internal_consistent((BOX *) DatumGetPointer(entry->key),
|
|
|
|
|
&(query->boundbox), strategy); |
|
|
|
|
|
|
|
|
|
PG_FREE_IF_COPY(query,1); |
|
|
|
|
PG_RETURN_BOOL( result );
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
POLYKEY * |
|
|
|
|
gpoly_union(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
return (POLYKEY*)
|
|
|
|
|
DatumGetPointer(rtree_union( |
|
|
|
|
(bytea*) PG_GETARG_POINTER(0), |
|
|
|
|
(int*) PG_GETARG_POINTER(1), |
|
|
|
|
gpoly_binary_union |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
float * |
|
|
|
|
gpoly_penalty(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
return rtree_penalty( |
|
|
|
|
(GISTENTRY*) PG_GETARG_POINTER(0), |
|
|
|
|
(GISTENTRY*) PG_GETARG_POINTER(1), |
|
|
|
|
(float*) PG_GETARG_POINTER(2), |
|
|
|
|
gpoly_binary_union, |
|
|
|
|
size_polykey |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
GIST_SPLITVEC * |
|
|
|
|
gpoly_picksplit(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
return rtree_picksplit( |
|
|
|
|
(bytea*)PG_GETARG_POINTER(0), |
|
|
|
|
(GIST_SPLITVEC*)PG_GETARG_POINTER(1), |
|
|
|
|
sizeof(POLYKEY), |
|
|
|
|
gpoly_binary_union, |
|
|
|
|
gpoly_inter, |
|
|
|
|
size_polykey |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool * |
|
|
|
|
gpoly_same(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
POLYKEY *b1 = (POLYKEY*) PG_GETARG_POINTER(0); |
|
|
|
|
POLYKEY *b2 = (POLYKEY*) PG_GETARG_POINTER(1); |
|
|
|
|
|
|
|
|
|
bool *result = (bool*) PG_GETARG_POINTER(2); |
|
|
|
|
if ( b1 && b2 ) |
|
|
|
|
*result = DatumGetBool( DirectFunctionCall2( box_same,
|
|
|
|
|
PointerGetDatum(&(b1->key)),
|
|
|
|
|
PointerGetDatum(&(b2->key))) ); |
|
|
|
|
else |
|
|
|
|
*result = ( b1==NULL && b2==NULL ) ? TRUE : FALSE;
|
|
|
|
|
return(result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
** SUPPORT ROUTINES for polygons |
|
|
|
|
*/ |
|
|
|
|
Datum
|
|
|
|
|
gpoly_inter(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
POLYKEY *b1 = (POLYKEY*) PG_GETARG_POINTER(0); |
|
|
|
|
POLYKEY *b2 = (POLYKEY*) PG_GETARG_POINTER(1); |
|
|
|
|
Datum interd; |
|
|
|
|
|
|
|
|
|
interd = DirectFunctionCall2(rt_box_inter, |
|
|
|
|
PointerGetDatum( &(b1->key) ), |
|
|
|
|
PointerGetDatum( &(b2->key) )); |
|
|
|
|
|
|
|
|
|
if (DatumGetPointer(interd) != NULL) { |
|
|
|
|
POLYKEY *tmp = (POLYKEY*) palloc( sizeof(POLYKEY) ); |
|
|
|
|
tmp->size = sizeof(POLYKEY); |
|
|
|
|
memcpy( &(tmp->key), DatumGetPointer(interd), sizeof(BOX) ); |
|
|
|
|
pfree( DatumGetPointer(interd) ); |
|
|
|
|
PG_RETURN_POINTER( tmp ); |
|
|
|
|
} else
|
|
|
|
|
PG_RETURN_POINTER( NULL ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Datum |
|
|
|
|
gpoly_binary_union(Datum r1, Datum r2, int *sizep) |
|
|
|
|
{ |
|
|
|
|
POLYKEY *retval; |
|
|
|
|
|
|
|
|
|
if ( ! (DatumGetPointer(r1) != NULL && DatumGetPointer(r2) != NULL) ) { |
|
|
|
|
if ( DatumGetPointer(r1) != NULL ) { |
|
|
|
|
retval = (POLYKEY*)palloc( sizeof(POLYKEY) ); |
|
|
|
|
memcpy( (void*)retval, DatumGetPointer(r1), sizeof(POLYKEY) ); |
|
|
|
|
*sizep = sizeof(POLYKEY); |
|
|
|
|
} else if ( DatumGetPointer(r2) != NULL ) { |
|
|
|
|
retval = (POLYKEY*)palloc( sizeof(POLYKEY) ); |
|
|
|
|
memcpy( (void*)retval, DatumGetPointer(r2), sizeof(POLYKEY) ); |
|
|
|
|
*sizep = sizeof(POLYKEY); |
|
|
|
|
} else { |
|
|
|
|
*sizep = 0; |
|
|
|
|
retval = NULL; |
|
|
|
|
}
|
|
|
|
|
} else { |
|
|
|
|
BOX *key = (BOX*)DatumGetPointer( |
|
|
|
|
DirectFunctionCall2( |
|
|
|
|
rt_box_union, |
|
|
|
|
PointerGetDatum( &(((POLYKEY*) DatumGetPointer(r1))->key) ), |
|
|
|
|
PointerGetDatum( &(((POLYKEY*) DatumGetPointer(r2))->key) )) ); |
|
|
|
|
retval = (POLYKEY*)palloc( sizeof(POLYKEY) ); |
|
|
|
|
memcpy( &(retval->key), key, sizeof(BOX) ); |
|
|
|
|
pfree( key ); |
|
|
|
|
*sizep = retval->size = sizeof(POLYKEY); |
|
|
|
|
} |
|
|
|
|
return PointerGetDatum(retval); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static float
|
|
|
|
|
size_polykey( Datum pk ) { |
|
|
|
|
if ( DatumGetPointer(pk) != NULL ) { |
|
|
|
|
float size; |
|
|
|
|
|
|
|
|
|
DirectFunctionCall2( rt_box_size, |
|
|
|
|
PointerGetDatum( &(((POLYKEY*) DatumGetPointer(pk))->key) ), |
|
|
|
|
PointerGetDatum( &size ) ); |
|
|
|
|
return size; |
|
|
|
|
} else |
|
|
|
|
return 0.0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
** Common rtree-function (for all ops) |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static Datum |
|
|
|
|
rtree_union(bytea *entryvec, int *sizep, BINARY_UNION bu) |
|
|
|
|
{ |
|
|
|
|
int numranges, i; |
|
|
|
|
Datum out, |
|
|
|
|
tmp; |
|
|
|
|
|
|
|
|
|
numranges = (VARSIZE(entryvec) - VARHDRSZ)/sizeof(GISTENTRY);
|
|
|
|
|
tmp = ((GISTENTRY *) VARDATA(entryvec))[0].key; |
|
|
|
|
out = (Datum) 0; |
|
|
|
|
|
|
|
|
|
for (i = 1; i < numranges; i++) { |
|
|
|
|
out = (*bu)(tmp, |
|
|
|
|
((GISTENTRY *) VARDATA(entryvec))[i].key, |
|
|
|
|
sizep); |
|
|
|
|
if (i > 1 && DatumGetPointer(tmp) != NULL) |
|
|
|
|
pfree(DatumGetPointer(tmp)); |
|
|
|
|
tmp = out; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return(out); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static float * |
|
|
|
|
rtree_penalty(GISTENTRY *origentry, GISTENTRY *newentry, float *result, BINARY_UNION bu, SIZE_BOX sb) |
|
|
|
|
{ |
|
|
|
|
Datum ud; |
|
|
|
|
float tmp1; |
|
|
|
|
int sizep; |
|
|
|
|
|
|
|
|
|
ud = (*bu)( origentry->key, newentry->key, &sizep ); |
|
|
|
|
tmp1 = (*sb)( ud );
|
|
|
|
|
if (DatumGetPointer(ud) != NULL) pfree(DatumGetPointer(ud)); |
|
|
|
|
|
|
|
|
|
*result = tmp1 - (*sb)( origentry->key ); |
|
|
|
|
return(result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
** The GiST PickSplit method |
|
|
|
|
** We use Guttman's poly time split algorithm
|
|
|
|
|
*/ |
|
|
|
|
static GIST_SPLITVEC * |
|
|
|
|
rtree_picksplit(bytea *entryvec, GIST_SPLITVEC *v, int keylen, BINARY_UNION bu, RDF interop, SIZE_BOX sb) |
|
|
|
|
{ |
|
|
|
|
OffsetNumber i, j; |
|
|
|
|
Datum datum_alpha, datum_beta; |
|
|
|
|
Datum datum_l, datum_r; |
|
|
|
|
Datum union_d, union_dl, union_dr; |
|
|
|
|
Datum inter_d; |
|
|
|
|
bool firsttime; |
|
|
|
|
float size_alpha, size_beta, size_union, size_inter; |
|
|
|
|
float size_waste, waste; |
|
|
|
|
float size_l, size_r; |
|
|
|
|
int nbytes; |
|
|
|
|
int sizep; |
|
|
|
|
OffsetNumber seed_1 = 0, seed_2 = 0; |
|
|
|
|
OffsetNumber *left, *right; |
|
|
|
|
OffsetNumber maxoff; |
|
|
|
|
|
|
|
|
|
maxoff = ((VARSIZE(entryvec) - VARHDRSZ)/sizeof(GISTENTRY)) - 2; |
|
|
|
|
nbytes = (maxoff + 2) * sizeof(OffsetNumber); |
|
|
|
|
v->spl_left = (OffsetNumber *) palloc(nbytes); |
|
|
|
|
v->spl_right = (OffsetNumber *) palloc(nbytes); |
|
|
|
|
|
|
|
|
|
firsttime = true; |
|
|
|
|
waste = 0.0; |
|
|
|
|
|
|
|
|
|
for (i = FirstOffsetNumber; i < maxoff; i = OffsetNumberNext(i)) { |
|
|
|
|
datum_alpha = ((GISTENTRY *) VARDATA(entryvec))[i].key; |
|
|
|
|
for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j)) { |
|
|
|
|
datum_beta = ((GISTENTRY *) VARDATA(entryvec))[j].key; |
|
|
|
|
|
|
|
|
|
/* compute the wasted space by unioning these guys */ |
|
|
|
|
/* size_waste = size_union - size_inter; */
|
|
|
|
|
union_d = (*bu)( datum_alpha, datum_beta, &sizep ); |
|
|
|
|
if ( DatumGetPointer(union_d) != NULL ) { |
|
|
|
|
size_union = (*sb)(union_d); |
|
|
|
|
pfree(DatumGetPointer(union_d)); |
|
|
|
|
} else |
|
|
|
|
size_union = 0.0; |
|
|
|
|
|
|
|
|
|
if ( DatumGetPointer(datum_alpha) != NULL && |
|
|
|
|
DatumGetPointer(datum_beta) != NULL ) { |
|
|
|
|
inter_d = DirectFunctionCall2(interop, |
|
|
|
|
datum_alpha, |
|
|
|
|
datum_beta); |
|
|
|
|
if ( DatumGetPointer(inter_d) != NULL ) { |
|
|
|
|
size_inter = (*sb)(inter_d); |
|
|
|
|
pfree(DatumGetPointer(inter_d)); |
|
|
|
|
} else
|
|
|
|
|
size_inter = 0.0; |
|
|
|
|
} else |
|
|
|
|
size_inter = 0.0; |
|
|
|
|
|
|
|
|
|
size_waste = size_union - size_inter; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* are these a more promising split that what we've |
|
|
|
|
* already seen? |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
if (size_waste > waste || firsttime) { |
|
|
|
|
waste = size_waste; |
|
|
|
|
seed_1 = i; |
|
|
|
|
seed_2 = j; |
|
|
|
|
firsttime = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
left = v->spl_left; |
|
|
|
|
v->spl_nleft = 0; |
|
|
|
|
right = v->spl_right; |
|
|
|
|
v->spl_nright = 0; |
|
|
|
|
|
|
|
|
|
if ( DatumGetPointer(((GISTENTRY *) VARDATA(entryvec))[seed_1].key) != NULL ) |
|
|
|
|
{ |
|
|
|
|
datum_l = PointerGetDatum(palloc( keylen )); |
|
|
|
|
memcpy(DatumGetPointer(datum_l), |
|
|
|
|
DatumGetPointer(((GISTENTRY *) VARDATA(entryvec))[seed_1].key), |
|
|
|
|
keylen); |
|
|
|
|
} else
|
|
|
|
|
datum_l = (Datum) 0; |
|
|
|
|
size_l = (*sb)( datum_l );
|
|
|
|
|
if ( DatumGetPointer(((GISTENTRY *) VARDATA(entryvec))[seed_2].key) != NULL ) |
|
|
|
|
{ |
|
|
|
|
datum_r = PointerGetDatum(palloc( keylen )); |
|
|
|
|
memcpy(DatumGetPointer(datum_r), |
|
|
|
|
DatumGetPointer(((GISTENTRY *) VARDATA(entryvec))[seed_2].key), |
|
|
|
|
keylen); |
|
|
|
|
} else
|
|
|
|
|
datum_r = (Datum) 0; |
|
|
|
|
size_r = (*sb)( datum_r );
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Now split up the regions between the two seeds. An important |
|
|
|
|
* property of this split algorithm is that the split vector v |
|
|
|
|
* has the indices of items to be split in order in its left and |
|
|
|
|
* right vectors. We exploit this property by doing a merge in |
|
|
|
|
* the code that actually splits the page. |
|
|
|
|
* |
|
|
|
|
* For efficiency, we also place the new index tuple in this loop. |
|
|
|
|
* This is handled at the very end, when we have placed all the |
|
|
|
|
* existing tuples and i == maxoff + 1. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
maxoff = OffsetNumberNext(maxoff); |
|
|
|
|
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If we've already decided where to place this item, just |
|
|
|
|
* put it on the right list. Otherwise, we need to figure |
|
|
|
|
* out which page needs the least enlargement in order to |
|
|
|
|
* store the item. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
if (i == seed_1) { |
|
|
|
|
*left++ = i; |
|
|
|
|
v->spl_nleft++; |
|
|
|
|
continue; |
|
|
|
|
} else if (i == seed_2) { |
|
|
|
|
*right++ = i; |
|
|
|
|
v->spl_nright++; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* okay, which page needs least enlargement? */
|
|
|
|
|
datum_alpha = ((GISTENTRY *) VARDATA(entryvec))[i].key; |
|
|
|
|
union_dl = (*bu)( datum_l, datum_alpha, &sizep ); |
|
|
|
|
union_dr = (*bu)( datum_r, datum_alpha, &sizep ); |
|
|
|
|
size_alpha = (*sb)( union_dl );
|
|
|
|
|
size_beta = (*sb)( union_dr );
|
|
|
|
|
|
|
|
|
|
/* pick which page to add it to */ |
|
|
|
|
if (size_alpha - size_l < size_beta - size_r) { |
|
|
|
|
pfree(DatumGetPointer(datum_l)); |
|
|
|
|
pfree(DatumGetPointer(union_dr)); |
|
|
|
|
datum_l = union_dl; |
|
|
|
|
size_l = size_alpha; |
|
|
|
|
*left++ = i; |
|
|
|
|
v->spl_nleft++; |
|
|
|
|
} else { |
|
|
|
|
pfree(DatumGetPointer(datum_r)); |
|
|
|
|
pfree(DatumGetPointer(union_dl)); |
|
|
|
|
datum_r = union_dr; |
|
|
|
|
size_r = size_alpha; |
|
|
|
|
*right++ = i; |
|
|
|
|
v->spl_nright++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
*left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */ |
|
|
|
|
|
|
|
|
|
v->spl_ldatum = datum_l; |
|
|
|
|
v->spl_rdatum = datum_r; |
|
|
|
|
|
|
|
|
|
return( v ); |
|
|
|
|
} |
|
|
|
|
/*****************************************
|
|
|
|
|
* Common rtree-function (for all ops) |
|
|
|
|
*****************************************/ |
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
rtree_internal_consistent(BOX *key, |
|
|
|
|
@ -694,8 +475,8 @@ rtree_internal_consistent(BOX *key, |
|
|
|
|
** GiST DeCompress methods |
|
|
|
|
** do not do anything. |
|
|
|
|
*/ |
|
|
|
|
GISTENTRY * |
|
|
|
|
Datum |
|
|
|
|
rtree_decompress(PG_FUNCTION_ARGS) |
|
|
|
|
{ |
|
|
|
|
return((GISTENTRY*)PG_GETARG_POINTER(0)); |
|
|
|
|
PG_RETURN_POINTER(PG_GETARG_POINTER(0)); |
|
|
|
|
} |
|
|
|
|
|