/* ** The GiST Union method for boxes ** returns the minimal bounding box that encloses all the entries in entryvec */ Datum g_cube_union(PG_FUNCTION_ARGS) { GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); int *sizep = (int *) PG_GETARG_POINTER(1); NDBOX *out = (NDBOX *) NULL; NDBOX *tmp; int i; /* * fprintf(stderr, "union\n"); */ tmp = DatumGetNDBOX(entryvec->vector[0].key); /* * sizep = sizeof(NDBOX); -- NDBOX has variable size */ *sizep = VARSIZE(tmp); for (i = 1; i < entryvec->n; i++) { out = g_cube_binary_union(tmp, DatumGetNDBOX(entryvec->vector[i].key), sizep); tmp = out; } PG_RETURN_POINTER(out); }
/* ** 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 g_cube_consistent(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); NDBOX *query = PG_GETARG_NDBOX(1); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); /* Oid subtype = PG_GETARG_OID(3); */ bool *recheck = (bool *) PG_GETARG_POINTER(4); bool res; /* All cases served by this function are exact */ *recheck = false; /* * if entry is not leaf, use g_cube_internal_consistent, else use * g_cube_leaf_consistent */ if (GIST_LEAF(entry)) res = g_cube_leaf_consistent(DatumGetNDBOX(entry->key), query, strategy); else res = g_cube_internal_consistent(DatumGetNDBOX(entry->key), query, strategy); PG_FREE_IF_COPY(query, 1); PG_RETURN_BOOL(res); }
Datum g_cube_decompress(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); NDBOX *key = DatumGetNDBOX(PG_DETOAST_DATUM(entry->key)); if (key != DatumGetNDBOX(entry->key)) { GISTENTRY *retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(key), entry->rel, entry->page, entry->offset, FALSE); PG_RETURN_POINTER(retval); } PG_RETURN_POINTER(entry); }
/* ** The GiST Penalty method for boxes ** As in the R-tree paper, we use change in area as our penalty metric */ Datum g_cube_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); NDBOX *ud; double tmp1, tmp2; ud = cube_union_v0(DatumGetNDBOX(origentry->key), DatumGetNDBOX(newentry->key)); rt_cube_size(ud, &tmp1); rt_cube_size(DatumGetNDBOX(origentry->key), &tmp2); *result = (float) (tmp1 - tmp2); /* * fprintf(stderr, "penalty\n"); fprintf(stderr, "\t%g\n", *result); */ PG_RETURN_FLOAT8(*result); }
/* ** 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 g_cube_consistent(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); NDBOX *query = PG_GETARG_NDBOX(1); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); bool res; /* * if entry is not leaf, use g_cube_internal_consistent, else use * g_cube_leaf_consistent */ if (GIST_LEAF(entry)) res = g_cube_leaf_consistent( DatumGetNDBOX(entry->key), query, strategy); else res = g_cube_internal_consistent( DatumGetNDBOX(entry->key), query, strategy); PG_FREE_IF_COPY(query,1); PG_RETURN_BOOL(res); }
/* ** The GiST PickSplit method for boxes ** We use Guttman's poly time split algorithm */ Datum g_cube_picksplit(PG_FUNCTION_ARGS) { GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); OffsetNumber i, j; NDBOX *datum_alpha, *datum_beta; NDBOX *datum_l, *datum_r; NDBOX *union_d, *union_dl, *union_dr; NDBOX *inter_d; bool firsttime; double size_alpha, size_beta, size_union, size_inter; double size_waste, waste; double size_l, size_r; int nbytes; OffsetNumber seed_1 = 1, seed_2 = 2; OffsetNumber *left, *right; OffsetNumber maxoff; /* * fprintf(stderr, "picksplit\n"); */ maxoff = entryvec->n - 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 = DatumGetNDBOX(entryvec->vector[i].key); for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j)) { datum_beta = DatumGetNDBOX(entryvec->vector[j].key); /* compute the wasted space by unioning these guys */ /* size_waste = size_union - size_inter; */ union_d = cube_union_v0(datum_alpha, datum_beta); rt_cube_size(union_d, &size_union); inter_d = DatumGetNDBOX(DirectFunctionCall2(cube_inter, entryvec->vector[i].key, entryvec->vector[j].key)); rt_cube_size(inter_d, &size_inter); size_waste = size_union - size_inter; /* * are these a more promising split than 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; datum_alpha = DatumGetNDBOX(entryvec->vector[seed_1].key); datum_l = cube_union_v0(datum_alpha, datum_alpha); rt_cube_size(datum_l, &size_l); datum_beta = DatumGetNDBOX(entryvec->vector[seed_2].key); datum_r = cube_union_v0(datum_beta, datum_beta); rt_cube_size(datum_r, &size_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 = DatumGetNDBOX(entryvec->vector[i].key); union_dl = cube_union_v0(datum_l, datum_alpha); union_dr = cube_union_v0(datum_r, datum_alpha); rt_cube_size(union_dl, &size_alpha); rt_cube_size(union_dr, &size_beta); /* pick which page to add it to */ if (size_alpha - size_l < size_beta - size_r) { datum_l = union_dl; size_l = size_alpha; *left++ = i; v->spl_nleft++; } else { datum_r = union_dr; size_r = size_beta; *right++ = i; v->spl_nright++; } } *left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */ v->spl_ldatum = PointerGetDatum(datum_l); v->spl_rdatum = PointerGetDatum(datum_r); PG_RETURN_POINTER(v); }