/*------------------------------------------------------------------------- * * 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.3 2001/10/01 17:53:11 tgl 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)); }