/* Enlarge b_union to contain b_new. If b_new contains more dimensions than b_union, expand b_union to contain those dimensions. */ static void gidx_merge(GIDX **b_union, GIDX *b_new) { int i, dims_union, dims_new; Assert(b_union); Assert(*b_union); Assert(b_new); dims_union = GIDX_NDIMS(*b_union); dims_new = GIDX_NDIMS(b_new); POSTGIS_DEBUGF(4, "merging gidx (%s) into gidx (%s)", gidx_to_string(b_new), gidx_to_string(*b_union)); if ( dims_new > dims_union ) { POSTGIS_DEBUGF(5, "reallocating b_union from %d dims to %d dims", dims_union, dims_new); *b_union = (GIDX*)repalloc(*b_union, GIDX_SIZE(dims_new)); SET_VARSIZE(*b_union, VARSIZE(b_new)); dims_union = dims_new; } for ( i = 0; i < dims_new; i++ ) { /* Adjust minimums */ GIDX_SET_MIN(*b_union, i, Min(GIDX_GET_MIN(*b_union,i),GIDX_GET_MIN(b_new,i))); /* Adjust maximums */ GIDX_SET_MAX(*b_union, i, Max(GIDX_GET_MAX(*b_union,i),GIDX_GET_MAX(b_new,i))); } POSTGIS_DEBUGF(5, "merge complete (%s)", gidx_to_string(*b_union)); return; }
/* Calculate the volume of the intersection of the boxes. */ static float gidx_inter_volume(GIDX *a, GIDX *b) { int i; float result; POSTGIS_DEBUG(5,"entered function"); if ( a == NULL || b == NULL ) { elog(ERROR, "gidx_inter_volume received a null argument"); return 0.0; } /* Ensure 'a' has the most dimensions. */ gidx_dimensionality_check(&a, &b); /* Initialize with minimal length of first dimension. */ result = Min(GIDX_GET_MAX(a,0),GIDX_GET_MAX(b,0)) - Max(GIDX_GET_MIN(a,0),GIDX_GET_MIN(b,0)); /* If they are disjoint (max < min) then return zero. */ if ( result < 0.0 ) return 0.0; /* Continue for remaining dimensions. */ for ( i = 1; i < GIDX_NDIMS(b); i++ ) { float width = Min(GIDX_GET_MAX(a,i),GIDX_GET_MAX(b,i)) - Max(GIDX_GET_MIN(a,i),GIDX_GET_MIN(b,i)); if ( width < 0.0 ) return 0.0; /* Multiply by minimal length of remaining dimensions. */ result *= width; } POSTGIS_DEBUGF(5, "volume( %s intersection %s ) = %.12g", gidx_to_string(a), gidx_to_string(b), result); return result; }
Datum geography_gist_union(PG_FUNCTION_ARGS) { GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); int *sizep = (int *) PG_GETARG_POINTER(1); /* Size of the return value */ int numranges, i; GIDX *box_cur, *box_union; POSTGIS_DEBUG(4, "[GIST] 'union' function called"); numranges = entryvec->n; box_cur = (GIDX*) DatumGetPointer(entryvec->vector[0].key); box_union = gidx_copy(box_cur); for ( i = 1; i < numranges; i++ ) { box_cur = (GIDX*) DatumGetPointer(entryvec->vector[i].key); gidx_merge(&box_union, box_cur); } *sizep = VARSIZE(box_union); POSTGIS_DEBUGF(4, "[GIST] union called, numranges(%i), pageunion %s", numranges, gidx_to_string(box_union)); PG_RETURN_POINTER(box_union); }
/* ** Peak into a geography to find the bounding box. If the ** box is there, copy it out and return it. If not, calculate the box from the ** full geography and return the box based on that. If no box is available, ** return G_FAILURE, otherwise G_SUCCESS. */ int geography_gidx(GSERIALIZED *g, GIDX *gidx) { int result = G_SUCCESS; POSTGIS_DEBUG(4, "entered function"); POSTGIS_DEBUGF(4, "got flags %d", g->flags); if ( FLAGS_GET_BBOX(g->flags) && FLAGS_GET_GEODETIC(g->flags) ) { const size_t size = 2 * 3 * sizeof(float); POSTGIS_DEBUG(4, "copying box out of serialization"); memcpy(gidx->c, g->data, size); SET_VARSIZE(gidx, VARHDRSZ + size); } else { GBOX gbox; POSTGIS_DEBUG(4, "calculating new box from scratch"); if ( gserialized_calculate_gbox_geocentric_p(g, &gbox) == G_FAILURE ) { POSTGIS_DEBUG(4, "calculated null bbox, returning null"); return G_FAILURE; } result = gidx_from_gbox_p(gbox, gidx); } if ( result == G_SUCCESS ) { POSTGIS_DEBUGF(4, "got gidx %s", gidx_to_string(gidx)); } return result; }
/** * Peak into a #GSERIALIZED datum to find the bounding box. If the * box is there, copy it out and return it. If not, calculate the box from the * full object and return the box based on that. If no box is available, * return #LW_FAILURE, otherwise #LW_SUCCESS. */ int gserialized_datum_get_gidx_p(Datum gsdatum, GIDX *gidx) { GSERIALIZED *gpart; int result = LW_SUCCESS; POSTGIS_DEBUG(4, "entered function"); /* ** The most info we need is the 8 bytes of serialized header plus the 32 bytes ** of floats necessary to hold the 8 floats of the largest XYZM index ** bounding box, so 40 bytes. */ gpart = (GSERIALIZED*)PG_DETOAST_DATUM_SLICE(gsdatum, 0, 40); POSTGIS_DEBUGF(4, "got flags %d", gpart->flags); /* Do we even have a serialized bounding box? */ if ( FLAGS_GET_BBOX(gpart->flags) ) { /* Yes! Copy it out into the GIDX! */ size_t size = gbox_serialized_size(gpart->flags); POSTGIS_DEBUG(4, "copying box out of serialization"); memcpy(gidx->c, gpart->data, size); /* if M is present but Z is not, pad Z and shift M */ if ( FLAGS_GET_M(gpart->flags) && ! FLAGS_GET_Z(gpart->flags) ) { size += 2 * sizeof(float); GIDX_SET_MIN(gidx,3,GIDX_GET_MIN(gidx,2)); GIDX_SET_MAX(gidx,3,GIDX_GET_MAX(gidx,2)); GIDX_SET_MIN(gidx,2,-1*FLT_MAX); GIDX_SET_MAX(gidx,2,FLT_MAX); } SET_VARSIZE(gidx, VARHDRSZ + size); result = LW_SUCCESS; } else { /* No, we need to calculate it from the full object. */ GSERIALIZED *g = (GSERIALIZED*)PG_DETOAST_DATUM(gsdatum); LWGEOM *lwgeom = lwgeom_from_gserialized(g); GBOX gbox; if ( lwgeom_calculate_gbox(lwgeom, &gbox) == LW_FAILURE ) { POSTGIS_DEBUG(4, "could not calculate bbox, returning failure"); lwgeom_free(lwgeom); return LW_FAILURE; } lwgeom_free(lwgeom); result = gidx_from_gbox_p(gbox, gidx); } if ( result == LW_SUCCESS ) { POSTGIS_DEBUGF(4, "got gidx %s", gidx_to_string(gidx)); } return result; }
/* Calculate the volume of the union of the boxes. Avoids creating an intermediate box. */ static float gidx_union_volume(GIDX *a, GIDX *b) { float result; int i; int ndims_a, ndims_b; POSTGIS_DEBUG(5,"entered function"); if ( a == NULL && b == NULL ) { elog(ERROR, "gidx_union_volume received two null arguments"); return 0.0; } if ( a == NULL ) return gidx_volume(b); if ( b == NULL ) return gidx_volume(a); /* Ensure 'a' has the most dimensions. */ gidx_dimensionality_check(&a, &b); ndims_a = GIDX_NDIMS(a); ndims_b = GIDX_NDIMS(b); /* Initialize with maximal length of first dimension. */ result = Max(GIDX_GET_MAX(a,0),GIDX_GET_MAX(b,0)) - Min(GIDX_GET_MIN(a,0),GIDX_GET_MIN(b,0)); /* Multiply by maximal length of remaining dimensions. */ for ( i = 1; i < ndims_b; i++ ) { result *= (Max(GIDX_GET_MAX(a,i),GIDX_GET_MAX(b,i)) - Min(GIDX_GET_MIN(a,i),GIDX_GET_MIN(b,i))); } /* Add in dimensions of higher dimensional box. */ for ( i = ndims_b; i < ndims_a; i++ ) { result *= (GIDX_GET_MAX(a,i) - GIDX_GET_MIN(a,i)); } POSTGIS_DEBUGF(5, "volume( %s union %s ) = %.12g", gidx_to_string(a), gidx_to_string(b), result); return result; }
/* Calculate the volume (in n-d units) of the GIDX */ static float gidx_volume(GIDX *a) { float result; int i; if ( a == NULL ) { /* elog(ERROR, "gidx_volume received a null argument"); */ return 0.0; } result = GIDX_GET_MAX(a,0) - GIDX_GET_MIN(a,0); for ( i = 1; i < GIDX_NDIMS(a); i++ ) result *= (GIDX_GET_MAX(a,i) - GIDX_GET_MIN(a,i)); POSTGIS_DEBUGF(5, "calculated volume of %s as %.12g", gidx_to_string(a), result); return result; }
/* Ensure all minimums are below maximums. */ static inline void gidx_validate(GIDX *b) { int i; Assert(b); POSTGIS_DEBUGF(5,"validating gidx (%s)", gidx_to_string(b)); for ( i = 0; i < GIDX_NDIMS(b); i++ ) { if ( GIDX_GET_MIN(b,i) > GIDX_GET_MAX(b,i) ) { float tmp; tmp = GIDX_GET_MIN(b,i); GIDX_SET_MIN(b,i,GIDX_GET_MAX(b,i)); GIDX_SET_MAX(b,i,tmp); } } return; }
/* Convert a double-based GBOX into a float-based GIDX, ensuring the float box is larger than the double box */ static int gidx_from_gbox_p(GBOX box, GIDX *a) { int ndims; ndims = (FLAGS_GET_GEODETIC(box.flags) ? 3 : FLAGS_NDIMS(box.flags)); SET_VARSIZE(a, VARHDRSZ + ndims * 2 * sizeof(float)); GIDX_SET_MIN(a,0,nextDown_f(box.xmin)); GIDX_SET_MAX(a,0,nextUp_f(box.xmax)); GIDX_SET_MIN(a,1,nextDown_f(box.ymin)); GIDX_SET_MAX(a,1,nextUp_f(box.ymax)); /* Geodetic indexes are always 3d, geocentric x/y/z */ if ( FLAGS_GET_GEODETIC(box.flags) ) { GIDX_SET_MIN(a,2,nextDown_f(box.zmin)); GIDX_SET_MAX(a,2,nextUp_f(box.zmax)); } else { /* Cartesian with Z implies Z is third dimension */ if ( FLAGS_GET_Z(box.flags) ) { GIDX_SET_MIN(a,2,nextDown_f(box.zmin)); GIDX_SET_MAX(a,2,nextUp_f(box.zmax)); if ( FLAGS_GET_M(box.flags) ) { GIDX_SET_MIN(a,3,nextDown_f(box.mmin)); GIDX_SET_MAX(a,3,nextUp_f(box.mmax)); } } /* Unless there's no Z, in which case M is third dimension */ else if ( FLAGS_GET_M(box.flags) ) { GIDX_SET_MIN(a,2,nextDown_f(box.mmin)); GIDX_SET_MAX(a,2,nextUp_f(box.mmax)); } } POSTGIS_DEBUGF(5, "converted %s to %s", gbox_to_string(&box), gidx_to_string(a)); return G_SUCCESS; }
/* Convert a double-based GBOX into a float-based GIDX, ensuring the float box is larger than the double box */ static int gidx_from_gbox_p(GBOX box, GIDX *a) { int ndims; ndims = FLAGS_NDIMS_GIDX(box.flags); SET_VARSIZE(a, VARHDRSZ + ndims * 2 * sizeof(float)); GIDX_SET_MIN(a,0,next_float_down(box.xmin)); GIDX_SET_MAX(a,0,next_float_up(box.xmax)); GIDX_SET_MIN(a,1,next_float_down(box.ymin)); GIDX_SET_MAX(a,1,next_float_up(box.ymax)); /* Geodetic indexes are always 3d, geocentric x/y/z */ if ( FLAGS_GET_GEODETIC(box.flags) ) { GIDX_SET_MIN(a,2,next_float_down(box.zmin)); GIDX_SET_MAX(a,2,next_float_up(box.zmax)); } else { /* Cartesian with Z implies Z is third dimension */ if ( FLAGS_GET_Z(box.flags) ) { GIDX_SET_MIN(a,2,next_float_down(box.zmin)); GIDX_SET_MAX(a,2,next_float_up(box.zmax)); } /* M is always fourth dimension, we pad if needed */ if ( FLAGS_GET_M(box.flags) ) { if ( ! FLAGS_GET_Z(box.flags) ) { GIDX_SET_MIN(a,2,-1*FLT_MAX); GIDX_SET_MAX(a,2,FLT_MAX); } GIDX_SET_MIN(a,3,next_float_down(box.mmin)); GIDX_SET_MAX(a,3,next_float_up(box.mmax)); } } POSTGIS_DEBUGF(5, "converted %s to %s", gbox_to_string(&box), gidx_to_string(a)); return LW_SUCCESS; }
/* ** Peak into a geography (gserialized) datum to find the bounding box. If the ** box is there, copy it out and return it. If not, calculate the box from the ** full geography and return the box based on that. If no box is available, ** return G_FAILURE, otherwise G_SUCCESS. */ int geography_datum_gidx(Datum geography_datum, GIDX *gidx) { GSERIALIZED *gpart; int result = G_SUCCESS; POSTGIS_DEBUG(4, "entered function"); /* ** The most info we need is the 8 bytes of serialized header plus the 24 bytes ** of floats necessary to hold the 6 floats of the geocentric index ** bounding box, so 32 bytes. */ gpart = (GSERIALIZED*)PG_DETOAST_DATUM_SLICE(geography_datum, 0, 32); POSTGIS_DEBUGF(4, "got flags %d", gpart->flags); if ( FLAGS_GET_BBOX(gpart->flags) && FLAGS_GET_GEODETIC(gpart->flags) ) { const size_t size = 2 * 3 * sizeof(float); POSTGIS_DEBUG(4, "copying box out of serialization"); memcpy(gidx->c, gpart->data, size); SET_VARSIZE(gidx, VARHDRSZ + size); } else { GSERIALIZED *g = (GSERIALIZED*)PG_DETOAST_DATUM(geography_datum); GBOX gbox; POSTGIS_DEBUG(4, "calculating new box from scratch"); if ( gserialized_calculate_gbox_geocentric_p(g, &gbox) == G_FAILURE ) { POSTGIS_DEBUG(4, "calculated null bbox, returning null"); return G_FAILURE; } result = gidx_from_gbox_p(gbox, gidx); } if ( result == G_SUCCESS ) { POSTGIS_DEBUGF(4, "got gidx %s", gidx_to_string(gidx)); } return result; }
/* ** Peak into a geography to find the bounding box. If the ** box is there, copy it out and return it. If not, calculate the box from the ** full geography and return the box based on that. If no box is available, ** return LW_FAILURE, otherwise LW_SUCCESS. */ int gserialized_get_gidx_p(GSERIALIZED *g, GIDX *gidx) { int result = LW_SUCCESS; POSTGIS_DEBUG(4, "entered function"); POSTGIS_DEBUGF(4, "got flags %d", g->flags); if ( FLAGS_GET_BBOX(g->flags) ) { int ndims = FLAGS_NDIMS_GIDX(g->flags); const size_t size = 2 * ndims * sizeof(float); POSTGIS_DEBUG(4, "copying box out of serialization"); memcpy(gidx->c, g->data, size); SET_VARSIZE(gidx, VARHDRSZ + size); } else { /* No, we need to calculate it from the full object. */ LWGEOM *lwgeom = lwgeom_from_gserialized(g); GBOX gbox; if ( lwgeom_calculate_gbox(lwgeom, &gbox) == LW_FAILURE ) { POSTGIS_DEBUG(4, "could not calculate bbox, returning failure"); lwgeom_free(lwgeom); return LW_FAILURE; } lwgeom_free(lwgeom); result = gidx_from_gbox_p(gbox, gidx); } if ( result == LW_SUCCESS ) { POSTGIS_DEBUGF(4, "got gidx %s", gidx_to_string(gidx)); } return result; }
Datum geography_gist_compress(PG_FUNCTION_ARGS) { GISTENTRY *entry_in = (GISTENTRY*)PG_GETARG_POINTER(0); GISTENTRY *entry_out = NULL; char gidxmem[GIDX_MAX_SIZE]; GIDX *bbox_out = (GIDX*)gidxmem; int result = G_SUCCESS; int i; POSTGIS_DEBUG(4, "[GIST] 'compress' function called"); /* ** Not a leaf key? There's nothing to do. ** Return the input unchanged. */ if ( ! entry_in->leafkey ) { POSTGIS_DEBUG(4, "[GIST] non-leafkey entry, returning input unaltered"); PG_RETURN_POINTER(entry_in); } POSTGIS_DEBUG(4, "[GIST] processing leafkey input"); entry_out = palloc(sizeof(GISTENTRY)); /* ** Null key? Make a copy of the input entry and ** return. */ if ( DatumGetPointer(entry_in->key) == NULL ) { POSTGIS_DEBUG(4, "[GIST] leafkey is null"); gistentryinit(*entry_out, (Datum) 0, entry_in->rel, entry_in->page, entry_in->offset, FALSE); POSTGIS_DEBUG(4, "[GIST] returning copy of input"); PG_RETURN_POINTER(entry_out); } /* Extract our index key from the GiST entry. */ result = geography_datum_gidx(entry_in->key, bbox_out); /* Is the bounding box valid (non-empty, non-infinite)? If not, return input uncompressed. */ if ( result == G_FAILURE ) { POSTGIS_DEBUG(4, "[GIST] empty geometry!"); PG_RETURN_POINTER(entry_in); } POSTGIS_DEBUGF(4, "[GIST] got entry_in->key: %s", gidx_to_string(bbox_out)); /* Check all the dimensions for finite values */ for ( i = 0; i < GIDX_NDIMS(bbox_out); i++ ) { if ( ! finite(GIDX_GET_MAX(bbox_out, i)) || ! finite(GIDX_GET_MIN(bbox_out, i)) ) { POSTGIS_DEBUG(4, "[GIST] infinite geometry!"); PG_RETURN_POINTER(entry_in); } } /* Enure bounding box has minimums below maximums. */ gidx_validate(bbox_out); /* Prepare GISTENTRY for return. */ gistentryinit(*entry_out, PointerGetDatum(gidx_copy(bbox_out)), entry_in->rel, entry_in->page, entry_in->offset, FALSE); /* Return GISTENTRY. */ POSTGIS_DEBUG(4, "[GIST] 'compress' function complete"); PG_RETURN_POINTER(entry_out); }
Datum geography_gist_picksplit(PG_FUNCTION_ARGS) { GistEntryVector *entryvec = (GistEntryVector*) PG_GETARG_POINTER(0); GIST_SPLITVEC *v = (GIST_SPLITVEC*) PG_GETARG_POINTER(1); OffsetNumber i; /* One union box for each half of the space. */ GIDX **box_union; /* One offset number list for each half of the space. */ OffsetNumber **list; /* One position index for each half of the space. */ int *pos; GIDX *box_pageunion; GIDX *box_current; int direction = -1; bool all_entries_equal = true; OffsetNumber max_offset; int nbytes, ndims_pageunion, d; int posmax = -1; POSTGIS_DEBUG(4, "[GIST] 'picksplit' function called"); /* ** First calculate the bounding box and maximum number of dimensions in this page. */ max_offset = entryvec->n - 1; box_current = (GIDX*) DatumGetPointer(entryvec->vector[FirstOffsetNumber].key); box_pageunion = gidx_copy(box_current); /* Calculate the containing box (box_pageunion) for the whole page we are going to split. */ for ( i = OffsetNumberNext(FirstOffsetNumber); i <= max_offset; i = OffsetNumberNext(i) ) { box_current = (GIDX*) DatumGetPointer(entryvec->vector[i].key); if ( all_entries_equal == true && ! gidx_equals (box_pageunion, box_current) ) all_entries_equal = false; gidx_merge( &box_pageunion, box_current ); } POSTGIS_DEBUGF(3, "[GIST] box_pageunion: %s", gidx_to_string(box_pageunion)); /* Every box in the page is the same! So, we split and just put half the boxes in each child. */ if ( all_entries_equal ) { POSTGIS_DEBUG(4, "[GIST] picksplit finds all entries equal!"); geography_gist_picksplit_fallback(entryvec, v); PG_RETURN_POINTER(v); } /* Initialize memory structures. */ nbytes = (max_offset + 2) * sizeof(OffsetNumber); ndims_pageunion = GIDX_NDIMS(box_pageunion); POSTGIS_DEBUGF(4, "[GIST] ndims_pageunion == %d", ndims_pageunion); pos = palloc(2*ndims_pageunion * sizeof(int)); list = palloc(2*ndims_pageunion * sizeof(OffsetNumber*)); box_union = palloc(2*ndims_pageunion * sizeof(GIDX*)); for ( d = 0; d < ndims_pageunion; d++ ) { list[BELOW(d)] = (OffsetNumber*) palloc(nbytes); list[ABOVE(d)] = (OffsetNumber*) palloc(nbytes); box_union[BELOW(d)] = gidx_new(ndims_pageunion); box_union[ABOVE(d)] = gidx_new(ndims_pageunion); pos[BELOW(d)] = 0; pos[ABOVE(d)] = 0; } /* ** Assign each entry in the node to the volume partitions it belongs to, ** such as "above the x/y plane, left of the y/z plane, below the x/z plane". ** Each entry thereby ends up in three of the six partitions. */ POSTGIS_DEBUG(4, "[GIST] 'picksplit' calculating best split axis"); for ( i = FirstOffsetNumber; i <= max_offset; i = OffsetNumberNext(i) ) { box_current = (GIDX*) DatumGetPointer(entryvec->vector[i].key); for ( d = 0; d < ndims_pageunion; d++ ) { if ( GIDX_GET_MIN(box_current,d)-GIDX_GET_MIN(box_pageunion,d) < GIDX_GET_MAX(box_pageunion,d)-GIDX_GET_MAX(box_current,d) ) { geography_gist_picksplit_addlist(list[BELOW(d)], &(box_union[BELOW(d)]), box_current, &(pos[BELOW(d)]), i); } else { geography_gist_picksplit_addlist(list[ABOVE(d)], &(box_union[ABOVE(d)]), box_current, &(pos[ABOVE(d)]), i); } } } /* ** "Bad disposition", too many entries fell into one octant of the space, so no matter which ** plane we choose to split on, we're going to end up with a mostly full node. Where the ** data is pretty homogeneous (lots of duplicates) entries that are equidistant from the ** sides of the page union box can occasionally all end up in one place, leading ** to this condition. */ if ( geography_gist_picksplit_badratios(pos,ndims_pageunion) == TRUE ) { /* ** Instead we split on center points and see if we do better. ** First calculate the average center point for each axis. */ double *avgCenter = palloc(ndims_pageunion * sizeof(double)); for ( d = 0; d < ndims_pageunion; d++ ) { avgCenter[d] = 0.0; } POSTGIS_DEBUG(4, "[GIST] picksplit can't find good split axis, trying center point method"); for ( i = FirstOffsetNumber; i <= max_offset; i = OffsetNumberNext(i) ) { box_current = (GIDX*) DatumGetPointer(entryvec->vector[i].key); for ( d = 0; d < ndims_pageunion; d++ ) { avgCenter[d] += (GIDX_GET_MAX(box_current,d) + GIDX_GET_MIN(box_current,d)) / 2.0; } } for ( d = 0; d < ndims_pageunion; d++ ) { avgCenter[d] /= max_offset; pos[BELOW(d)] = pos[ABOVE(d)] = 0; /* Re-initialize our counters. */ POSTGIS_DEBUGF(4, "[GIST] picksplit average center point[%d] = %.12g", d, avgCenter[d]); } /* For each of our entries... */ for ( i = FirstOffsetNumber; i <= max_offset; i = OffsetNumberNext(i) ) { double center; box_current = (GIDX*) DatumGetPointer(entryvec->vector[i].key); for ( d = 0; d < ndims_pageunion; d++ ) { center = (GIDX_GET_MIN(box_current,d)+GIDX_GET_MAX(box_current,d))/2.0; if ( center < avgCenter[d] ) geography_gist_picksplit_addlist(list[BELOW(d)], &(box_union[BELOW(d)]), box_current, &(pos[BELOW(d)]), i); else if ( FPeq(center, avgCenter[d]) ) if ( pos[BELOW(d)] > pos[ABOVE(d)] ) geography_gist_picksplit_addlist(list[ABOVE(d)], &(box_union[ABOVE(d)]), box_current, &(pos[ABOVE(d)]), i); else geography_gist_picksplit_addlist(list[BELOW(d)], &(box_union[BELOW(d)]), box_current, &(pos[BELOW(d)]), i); else geography_gist_picksplit_addlist(list[ABOVE(d)], &(box_union[ABOVE(d)]), box_current, &(pos[ABOVE(d)]), i); } } /* Do we have a good disposition now? If not, screw it, just cut the node in half. */ if ( geography_gist_picksplit_badratios(pos,ndims_pageunion) == TRUE ) { POSTGIS_DEBUG(4, "[GIST] picksplit still cannot find a good split! just cutting the node in half"); geography_gist_picksplit_fallback(entryvec, v); PG_RETURN_POINTER(v); } } /* ** Now, what splitting plane gives us the most even ratio of ** entries in our child pages? Since each split region has been apportioned entries ** against the same number of total entries, the axis that has the smallest maximum ** number of entries in its regions is the most evenly distributed. ** TODO: what if the distributions are equal in two or more axes? */ for ( d = 0; d < ndims_pageunion; d++ ) { int posd = Max(pos[ABOVE(d)],pos[BELOW(d)]); if ( posd > posmax ) { direction = d; posmax = posd; } } if ( direction == -1 || posmax == -1 ) { /* ERROR OUT HERE */ elog(ERROR, "Error in building split, unable to determine split direction."); } POSTGIS_DEBUGF(3, "[GIST] 'picksplit' splitting on axis %d", direction); geography_gist_picksplit_constructsplit(v, list[BELOW(direction)], pos[BELOW(direction)], &(box_union[BELOW(direction)]), list[ABOVE(direction)], pos[ABOVE(direction)], &(box_union[ABOVE(direction)]) ); POSTGIS_DEBUGF(4, "[GIST] spl_ldatum: %s", gidx_to_string((GIDX*)v->spl_ldatum)); POSTGIS_DEBUGF(4, "[GIST] spl_rdatum: %s", gidx_to_string((GIDX*)v->spl_rdatum)); POSTGIS_DEBUGF(4, "[GIST] axis %d: parent range (%.12g, %.12g) left range (%.12g, %.12g), right range (%.12g, %.12g)", direction, GIDX_GET_MIN(box_pageunion, direction), GIDX_GET_MAX(box_pageunion, direction), GIDX_GET_MIN((GIDX*)v->spl_ldatum, direction), GIDX_GET_MAX((GIDX*)v->spl_ldatum, direction), GIDX_GET_MIN((GIDX*)v->spl_rdatum, direction), GIDX_GET_MAX((GIDX*)v->spl_rdatum, direction) ); PG_RETURN_POINTER(v); }