/* * SP-GiST inner consistent function */ Datum spg_box_quad_inner_consistent(PG_FUNCTION_ARGS) { spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0); spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1); int i; MemoryContext old_ctx; RectBox *rect_box; uint8 quadrant; RangeBox *centroid, **queries; if (in->allTheSame) { /* Report that all nodes should be visited */ out->nNodes = in->nNodes; out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); for (i = 0; i < in->nNodes; i++) out->nodeNumbers[i] = i; PG_RETURN_VOID(); } /* * We are saving the traversal value or initialize it an unbounded * one, if we have just begun to walk the tree. */ if (in->traversalValue) rect_box = in->traversalValue; else rect_box = initRectBox(); /* * We are casting the prefix and queries to RangeBoxes for ease of * the following operations. */ centroid = getRangeBox(DatumGetBoxP(in->prefixDatum)); queries = (RangeBox **) palloc(in->nkeys * sizeof(RangeBox *)); for (i = 0; i < in->nkeys; i++) queries[i] = getRangeBox(DatumGetBoxP(in->scankeys[i].sk_argument)); /* Allocate enough memory for nodes */ out->nNodes = 0; out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); out->traversalValues = (void **) palloc(sizeof(void *) * in->nNodes); /* * We switch memory context, because we want to allocate memory for * new traversal values (next_rect_box) and pass these pieces of * memory to further call of this function. */ old_ctx = MemoryContextSwitchTo(in->traversalMemoryContext); for (quadrant = 0; quadrant < in->nNodes; quadrant++) { RectBox *next_rect_box = nextRectBox(rect_box, centroid, quadrant); bool flag = true; for (i = 0; i < in->nkeys; i++) { StrategyNumber strategy = in->scankeys[i].sk_strategy; switch (strategy) { case RTOverlapStrategyNumber: flag = overlap4D(next_rect_box, queries[i]); break; case RTContainsStrategyNumber: flag = contain4D(next_rect_box, queries[i]); break; case RTSameStrategyNumber: case RTContainedByStrategyNumber: flag = contained4D(next_rect_box, queries[i]); break; case RTLeftStrategyNumber: flag = left4D(next_rect_box, queries[i]); break; case RTOverLeftStrategyNumber: flag = overLeft4D(next_rect_box, queries[i]); break; case RTRightStrategyNumber: flag = right4D(next_rect_box, queries[i]); break; case RTOverRightStrategyNumber: flag = overRight4D(next_rect_box, queries[i]); break; case RTAboveStrategyNumber: flag = above4D(next_rect_box, queries[i]); break; case RTOverAboveStrategyNumber: flag = overAbove4D(next_rect_box, queries[i]); break; case RTBelowStrategyNumber: flag = below4D(next_rect_box, queries[i]); break; case RTOverBelowStrategyNumber: flag = overBelow4D(next_rect_box, queries[i]); break; default: elog(ERROR, "unrecognized strategy: %d", strategy); } /* If any check is failed, we have found our answer. */ if (!flag) break; } if (flag) { out->traversalValues[out->nNodes] = next_rect_box; out->nodeNumbers[out->nNodes] = quadrant; out->nNodes++; } else { /* * If this node is not selected, we don't need to keep * the next traversal value in the memory context. */ pfree(next_rect_box); } } /* Switch back */ MemoryContextSwitchTo(old_ctx); PG_RETURN_VOID(); }
Datum gist_point_consistent(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); bool *recheck = (bool *) PG_GETARG_POINTER(4); bool result; StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset; switch (strategyGroup) { case PointStrategyNumberGroup: result = gist_point_consistent_internal(strategy % GeoStrategyNumberOffset, GIST_LEAF(entry), DatumGetBoxP(entry->key), PG_GETARG_POINT_P(1)); *recheck = false; break; case BoxStrategyNumberGroup: result = DatumGetBool(DirectFunctionCall5( gist_box_consistent, PointerGetDatum(entry), PG_GETARG_DATUM(1), Int16GetDatum(RTOverlapStrategyNumber), 0, PointerGetDatum(recheck))); break; case PolygonStrategyNumberGroup: { POLYGON *query = PG_GETARG_POLYGON_P(1); result = DatumGetBool(DirectFunctionCall5( gist_poly_consistent, PointerGetDatum(entry), PolygonPGetDatum(query), Int16GetDatum(RTOverlapStrategyNumber), 0, PointerGetDatum(recheck))); if (GIST_LEAF(entry) && result) { /* * We are on leaf page and quick check shows overlapping * of polygon's bounding box and point */ BOX *box = DatumGetBoxP(entry->key); Assert(box->high.x == box->low.x && box->high.y == box->low.y); result = DatumGetBool(DirectFunctionCall2( poly_contain_pt, PolygonPGetDatum(query), PointPGetDatum(&box->high))); *recheck = false; } } break; case CircleStrategyNumberGroup: { CIRCLE *query = PG_GETARG_CIRCLE_P(1); result = DatumGetBool(DirectFunctionCall5( gist_circle_consistent, PointerGetDatum(entry), CirclePGetDatum(query), Int16GetDatum(RTOverlapStrategyNumber), 0, PointerGetDatum(recheck))); if (GIST_LEAF(entry) && result) { /* * We are on leaf page and quick check shows overlapping * of polygon's bounding box and point */ BOX *box = DatumGetBoxP(entry->key); Assert(box->high.x == box->low.x && box->high.y == box->low.y); result = DatumGetBool(DirectFunctionCall2( circle_contain_pt, CirclePGetDatum(query), PointPGetDatum(&box->high))); *recheck = false; } } break; default: elog(ERROR, "unknown strategy number: %d", strategy); result = false; /* keep compiler quiet */ } PG_RETURN_BOOL(result); }
Datum spg_kd_inner_consistent(PG_FUNCTION_ARGS) { spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0); spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1); double coord; int which; int i; Assert(in->hasPrefix); coord = DatumGetFloat8(in->prefixDatum); if (in->allTheSame) elog(ERROR, "allTheSame should not occur for k-d trees"); Assert(in->nNodes == 2); /* "which" is a bitmask of children that satisfy all constraints */ which = (1 << 1) | (1 << 2); for (i = 0; i < in->nkeys; i++) { Point *query = DatumGetPointP(in->scankeys[i].sk_argument); BOX *boxQuery; switch (in->scankeys[i].sk_strategy) { case RTLeftStrategyNumber: if ((in->level % 2) != 0 && FPlt(query->x, coord)) which &= (1 << 1); break; case RTRightStrategyNumber: if ((in->level % 2) != 0 && FPgt(query->x, coord)) which &= (1 << 2); break; case RTSameStrategyNumber: if ((in->level % 2) != 0) { if (FPlt(query->x, coord)) which &= (1 << 1); else if (FPgt(query->x, coord)) which &= (1 << 2); } else { if (FPlt(query->y, coord)) which &= (1 << 1); else if (FPgt(query->y, coord)) which &= (1 << 2); } break; case RTBelowStrategyNumber: if ((in->level % 2) == 0 && FPlt(query->y, coord)) which &= (1 << 1); break; case RTAboveStrategyNumber: if ((in->level % 2) == 0 && FPgt(query->y, coord)) which &= (1 << 2); break; case RTContainedByStrategyNumber: /* * For this operator, the query is a box not a point. We * cheat to the extent of assuming that DatumGetPointP won't * do anything that would be bad for a pointer-to-box. */ boxQuery = DatumGetBoxP(in->scankeys[i].sk_argument); if ((in->level % 2) != 0) { if (FPlt(boxQuery->high.x, coord)) which &= (1 << 1); else if (FPgt(boxQuery->low.x, coord)) which &= (1 << 2); } else { if (FPlt(boxQuery->high.y, coord)) which &= (1 << 1); else if (FPgt(boxQuery->low.y, coord)) which &= (1 << 2); } break; default: elog(ERROR, "unrecognized strategy number: %d", in->scankeys[i].sk_strategy); break; } if (which == 0) break; /* no need to consider remaining conditions */ } /* We must descend into the children identified by which */ out->nodeNumbers = (int *) palloc(sizeof(int) * 2); out->nNodes = 0; for (i = 1; i <= 2; i++) { if (which & (1 << i)) out->nodeNumbers[out->nNodes++] = i - 1; } /* Set up level increments, too */ out->levelAdds = (int *) palloc(sizeof(int) * 2); out->levelAdds[0] = 1; out->levelAdds[1] = 1; PG_RETURN_VOID(); }
/* * SP-GiST pick-split function * * It splits a list of boxes into quadrants by choosing a central 4D * point as the median of the coordinates of the boxes. */ Datum spg_box_quad_picksplit(PG_FUNCTION_ARGS) { spgPickSplitIn *in = (spgPickSplitIn *) PG_GETARG_POINTER(0); spgPickSplitOut *out = (spgPickSplitOut *) PG_GETARG_POINTER(1); BOX *centroid; int median, i; double *lowXs = palloc(sizeof(double) * in->nTuples); double *highXs = palloc(sizeof(double) * in->nTuples); double *lowYs = palloc(sizeof(double) * in->nTuples); double *highYs = palloc(sizeof(double) * in->nTuples); /* Calculate median of all 4D coordinates */ for (i = 0; i < in->nTuples; i++) { BOX *box = DatumGetBoxP(in->datums[i]); lowXs[i] = box->low.x; highXs[i] = box->high.x; lowYs[i] = box->low.y; highYs[i] = box->high.y; } qsort(lowXs, in->nTuples, sizeof(double), compareDoubles); qsort(highXs, in->nTuples, sizeof(double), compareDoubles); qsort(lowYs, in->nTuples, sizeof(double), compareDoubles); qsort(highYs, in->nTuples, sizeof(double), compareDoubles); median = in->nTuples / 2; centroid = palloc(sizeof(BOX)); centroid->low.x = lowXs[median]; centroid->high.x = highXs[median]; centroid->low.y = lowYs[median]; centroid->high.y = highYs[median]; /* Fill the output */ out->hasPrefix = true; out->prefixDatum = BoxPGetDatum(centroid); out->nNodes = 16; out->nodeLabels = NULL; /* We don't need node labels. */ out->mapTuplesToNodes = palloc(sizeof(int) * in->nTuples); out->leafTupleDatums = palloc(sizeof(Datum) * in->nTuples); /* * Assign ranges to corresponding nodes according to quadrants * relative to the "centroid" range */ for (i = 0; i < in->nTuples; i++) { BOX *box = DatumGetBoxP(in->datums[i]); uint8 quadrant = getQuadrant(centroid, box); out->leafTupleDatums[i] = BoxPGetDatum(box); out->mapTuplesToNodes[i] = quadrant; } PG_RETURN_VOID(); }
void yorder_get_order(Datum eorder,Torder *orderp) { bool isnull; HeapTupleHeader tuple = ((HeapTupleHeader) PG_DETOAST_DATUM(eorder)); Oid tupType; int32 tupTypmod; TupleDesc tupDesc; HeapTupleData tmptup; BOX *p; tupType = HeapTupleHeaderGetTypeId(tuple); tupTypmod = HeapTupleHeaderGetTypMod(tuple); tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_data = tuple; orderp->type = DatumGetInt32(heap_getattr(&tmptup,1,tupDesc,&isnull)); if(isnull) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the field type is null in yorder_get_order"))); if(!ORDER_TYPE_IS_VALID(orderp->type)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("order type incorrect in yorder_get_order"))); orderp->id = DatumGetInt32(heap_getattr(&tmptup,2,tupDesc,&isnull)); if(isnull) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the field id is null in yorder_get_order"))); orderp->own = DatumGetInt32(heap_getattr(&tmptup,3,tupDesc,&isnull)); if(isnull) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the field own is null in yorder_get_order"))); orderp->oid = DatumGetInt32(heap_getattr(&tmptup,4,tupDesc,&isnull)); if(isnull) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the field oid is null in yorder_get_order"))); orderp->qtt_requ = DatumGetInt64(heap_getattr(&tmptup,5,tupDesc,&isnull)); if(isnull) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the field qtt_requ is null in yorder_get_order"))); //orderp->qua_requ = (HStore *) PG_DETOAST_DATUM(heap_getattr(&tmptup,6,tupDesc,&isnull)); orderp->qua_requ = (Datum) PG_DETOAST_DATUM(heap_getattr(&tmptup,6,tupDesc,&isnull)); if(isnull) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the field qua_requ is null in yorder_get_order"))); orderp->qtt_prov = DatumGetInt64(heap_getattr(&tmptup,7,tupDesc,&isnull)); if(isnull) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the field qtt_prov is null in yorder_get_order"))); //orderp->qua_prov = (HStore *) PG_DETOAST_DATUM(heap_getattr(&tmptup,8,tupDesc,&isnull)); orderp->qua_prov = (Datum) PG_DETOAST_DATUM(heap_getattr(&tmptup,8,tupDesc,&isnull)); if(isnull) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the field qua_prov is null in yorder_get_order"))); orderp->qtt = DatumGetInt64(heap_getattr(&tmptup,9,tupDesc,&isnull)); if(isnull) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the field qtt is null in yorder_get_order"))); // pos_requ box, p = DatumGetBoxP(heap_getattr(&tmptup,10,tupDesc,&isnull)); if(isnull) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the field pos_requ is null in yorder_get_order"))); GL_CHECK_BOX_S0(p); orderp->pos_requ.x = p->low.x; orderp->pos_requ.y = p->low.y; // pos_prov box, p = DatumGetBoxP(heap_getattr(&tmptup,11,tupDesc,&isnull)); if(isnull) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the field pos_prov is null in yorder_get_order"))); GL_CHECK_BOX_S0(p); orderp->pos_prov.x = p->low.x; orderp->pos_prov.y = p->low.y; // dist flat orderp->dist = DatumGetFloat8(heap_getattr(&tmptup,12,tupDesc,&isnull)); if(isnull) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("the field dist is null in yorder_get_order"))); ReleaseTupleDesc(tupDesc); return; }
Datum spg_quad_inner_consistent(PG_FUNCTION_ARGS) { spgInnerConsistentIn *in = (spgInnerConsistentIn *) PG_GETARG_POINTER(0); spgInnerConsistentOut *out = (spgInnerConsistentOut *) PG_GETARG_POINTER(1); Point *query, *centroid; BOX *boxQuery; query = DatumGetPointP(in->query); Assert(in->hasPrefix); centroid = DatumGetPointP(in->prefixDatum); if (in->allTheSame) { /* Report that all nodes should be visited */ int i; out->nNodes = in->nNodes; out->nodeNumbers = (int *) palloc(sizeof(int) * in->nNodes); for (i = 0; i < in->nNodes; i++) out->nodeNumbers[i] = i; PG_RETURN_VOID(); } Assert(in->nNodes == 4); out->nodeNumbers = (int *) palloc(sizeof(int) * 4); switch (in->strategy) { case RTLeftStrategyNumber: setNodes(out, SPTEST(point_left, centroid, query), 3, 4); break; case RTRightStrategyNumber: setNodes(out, SPTEST(point_right, centroid, query), 1, 2); break; case RTSameStrategyNumber: out->nNodes = 1; out->nodeNumbers[0] = getQuadrant(centroid, query) - 1; break; case RTBelowStrategyNumber: setNodes(out, SPTEST(point_below, centroid, query), 2, 3); break; case RTAboveStrategyNumber: setNodes(out, SPTEST(point_above, centroid, query), 1, 4); break; case RTContainedByStrategyNumber: /* * For this operator, the query is a box not a point. We cheat to * the extent of assuming that DatumGetPointP won't do anything * that would be bad for a pointer-to-box. */ boxQuery = DatumGetBoxP(in->query); if (DatumGetBool(DirectFunctionCall2(box_contain_pt, PointerGetDatum(boxQuery), PointerGetDatum(centroid)))) { /* centroid is in box, so descend to all quadrants */ setNodes(out, true, 0, 0); } else { /* identify quadrant(s) containing all corners of box */ Point p; int i, r = 0; p = boxQuery->low; r |= 1 << (getQuadrant(centroid, &p) - 1); p.y = boxQuery->high.y; r |= 1 << (getQuadrant(centroid, &p) - 1); p = boxQuery->high; r |= 1 << (getQuadrant(centroid, &p) - 1); p.x = boxQuery->low.x; r |= 1 << (getQuadrant(centroid, &p) - 1); /* we must descend into those quadrant(s) */ out->nNodes = 0; for (i = 0; i < 4; i++) { if (r & (1 << i)) { out->nodeNumbers[out->nNodes] = i; out->nNodes++; } } } break; default: elog(ERROR, "unrecognized strategy number: %d", in->strategy); break; } PG_RETURN_VOID(); }
/* * The GiST PickSplit method * * New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree', * C.H.Ang and T.C.Tan */ Datum gist_box_picksplit(PG_FUNCTION_ARGS) { GistEntryVector *entryvec = (GistEntryVector *) 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 = entryvec->n - 1; cur = DatumGetBoxP(entryvec->vector[FirstOffsetNumber].key); memcpy((void *) &pageunion, (void *) cur, sizeof(BOX)); /* find MBR */ for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i)) { cur = DatumGetBoxP(entryvec->vector[i].key); if (allisequal == true && ( pageunion.high.x != cur->high.x || pageunion.high.y != cur->high.y || pageunion.low.x != cur->low.x || pageunion.low.y != cur->low.y )) allisequal = false; adjustBox(&pageunion, cur); } if (allisequal) { /* * All entries are the same */ fallbackSplit(entryvec, v); PG_RETURN_POINTER(v); } nbytes = (maxoff + 2) * sizeof(OffsetNumber); listL = (OffsetNumber *) palloc(nbytes); listR = (OffsetNumber *) palloc(nbytes); listB = (OffsetNumber *) palloc(nbytes); listT = (OffsetNumber *) palloc(nbytes); unionL = (BOX *) palloc(sizeof(BOX)); unionR = (BOX *) palloc(sizeof(BOX)); unionB = (BOX *) palloc(sizeof(BOX)); unionT = (BOX *) palloc(sizeof(BOX)); #define ADDLIST( list, unionD, pos, num ) 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] = num; \ (pos)++; \ } while(0) for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { cur = DatumGetBoxP(entryvec->vector[i].key); if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x) ADDLIST(listL, unionL, posL, i); else ADDLIST(listR, unionR, posR, i); if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y) ADDLIST(listB, unionB, posB, i); else ADDLIST(listT, unionT, posT, i); } #define LIMIT_RATIO 0.1 #define _IS_BADRATIO(x,y) ( (y) == 0 || (float)(x)/(float)(y) < LIMIT_RATIO ) #define IS_BADRATIO(x,y) ( _IS_BADRATIO((x),(y)) || _IS_BADRATIO((y),(x)) ) /* bad disposition, try to split by centers of boxes */ if (IS_BADRATIO(posR, posL) && IS_BADRATIO(posT, posB)) { double avgCenterX = 0.0, avgCenterY = 0.0; double CenterX, CenterY; for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { cur = DatumGetBoxP(entryvec->vector[i].key); avgCenterX += ((double) cur->high.x + (double) cur->low.x) / 2.0; avgCenterY += ((double) cur->high.y + (double) cur->low.y) / 2.0; } avgCenterX /= maxoff; avgCenterY /= maxoff; posL = posR = posB = posT = 0; for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { cur = DatumGetBoxP(entryvec->vector[i].key); CenterX = ((double) cur->high.x + (double) cur->low.x) / 2.0; CenterY = ((double) cur->high.y + (double) cur->low.y) / 2.0; if (CenterX < avgCenterX) ADDLIST(listL, unionL, posL, i); else if (CenterX == avgCenterX) { if (posL > posR) ADDLIST(listR, unionR, posR, i); else ADDLIST(listL, unionL, posL, i); } else ADDLIST(listR, unionR, posR, i); if (CenterY < avgCenterY) ADDLIST(listB, unionB, posB, i); else if (CenterY == avgCenterY) { if (posB > posT) ADDLIST(listT, unionT, posT, i); else ADDLIST(listB, unionB, posB, i); } else ADDLIST(listT, unionT, posT, i); } if (IS_BADRATIO(posR, posL) && IS_BADRATIO(posT, posB)) { fallbackSplit(entryvec, v); PG_RETURN_POINTER(v); } } /* 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)); double sizeLR, sizeBT; sizeLR = size_box(interLR); sizeBT = size_box(interBT); if (sizeLR < sizeBT) direction = 'x'; else direction = 'y'; } if (direction == 'x') chooseLR(v, listL, posL, unionL, listR, posR, unionR); else chooseLR(v, listB, posB, unionB, listT, posT, unionT); PG_RETURN_POINTER(v); }
static void chooseLR(GIST_SPLITVEC *v, OffsetNumber *list1, int nlist1, BOX *union1, OffsetNumber *list2, int nlist2, BOX *union2) { bool firstToLeft = true; if (v->spl_ldatum_exists || v->spl_rdatum_exists) { if (v->spl_ldatum_exists && v->spl_rdatum_exists) { BOX LRl = *union1, LRr = *union2; BOX RLl = *union2, RLr = *union1; double sizeLR, sizeRL; adjustBox(&LRl, DatumGetBoxP(v->spl_ldatum)); adjustBox(&LRr, DatumGetBoxP(v->spl_rdatum)); adjustBox(&RLl, DatumGetBoxP(v->spl_ldatum)); adjustBox(&RLr, DatumGetBoxP(v->spl_rdatum)); sizeLR = size_box(DirectFunctionCall2(rt_box_inter, BoxPGetDatum(&LRl), BoxPGetDatum(&LRr))); sizeRL = size_box(DirectFunctionCall2(rt_box_inter, BoxPGetDatum(&RLl), BoxPGetDatum(&RLr))); if (sizeLR > sizeRL) firstToLeft = false; } else { float p1, p2; GISTENTRY oldUnion, addon; gistentryinit(oldUnion, (v->spl_ldatum_exists) ? v->spl_ldatum : v->spl_rdatum, NULL, NULL, InvalidOffsetNumber, FALSE); gistentryinit(addon, BoxPGetDatum(union1), NULL, NULL, InvalidOffsetNumber, FALSE); DirectFunctionCall3(gist_box_penalty, PointerGetDatum(&oldUnion), PointerGetDatum(&addon), PointerGetDatum(&p1)); gistentryinit(addon, BoxPGetDatum(union2), NULL, NULL, InvalidOffsetNumber, FALSE); DirectFunctionCall3(gist_box_penalty, PointerGetDatum(&oldUnion), PointerGetDatum(&addon), PointerGetDatum(&p2)); if ((v->spl_ldatum_exists && p1 > p2) || (v->spl_rdatum_exists && p1 < p2)) firstToLeft = false; } } if (firstToLeft) { v->spl_left = list1; v->spl_right = list2; v->spl_nleft = nlist1; v->spl_nright = nlist2; if (v->spl_ldatum_exists) adjustBox(union1, DatumGetBoxP(v->spl_ldatum)); v->spl_ldatum = BoxPGetDatum(union1); if (v->spl_rdatum_exists) adjustBox(union2, DatumGetBoxP(v->spl_rdatum)); v->spl_rdatum = BoxPGetDatum(union2); } else { v->spl_left = list2; v->spl_right = list1; v->spl_nleft = nlist2; v->spl_nright = nlist1; if (v->spl_ldatum_exists) adjustBox(union2, DatumGetBoxP(v->spl_ldatum)); v->spl_ldatum = BoxPGetDatum(union2); if (v->spl_rdatum_exists) adjustBox(union1, DatumGetBoxP(v->spl_rdatum)); v->spl_rdatum = BoxPGetDatum(union1); } v->spl_ldatum_exists = v->spl_rdatum_exists = false; }
/* ** 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 (allisequal == true && ( pageunion.high.x != cur->high.x || pageunion.high.y != cur->high.y || pageunion.low.x != cur->low.x || pageunion.low.y != cur->low.y )) allisequal = false; 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; } 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, num ) 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] = num; \ (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, i); else ADDLIST(listR, unionR, posR, i); if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y) ADDLIST(listB, unionB, posB, i); else ADDLIST(listT, unionT, posT, i); } /* bad disposition, sort by ascending and resplit */ if ((posR == 0 || posL == 0) && (posT == 0 || posB == 0)) { KBsort *arr = (KBsort *) palloc(sizeof(KBsort) * maxoff); posL = posR = posB = posT = 0; for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { arr[i - 1].key = DatumGetBoxP(((GISTENTRY *) VARDATA(entryvec))[i].key); arr[i - 1].pos = i; } qsort(arr, maxoff, sizeof(KBsort), compare_KB); for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { cur = arr[i - 1].key; if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x) ADDLIST(listL, unionL, posL, arr[i - 1].pos); else if (cur->low.x - pageunion.low.x == pageunion.high.x - cur->high.x) { if (posL > posR) ADDLIST(listR, unionR, posR, arr[i - 1].pos); else ADDLIST(listL, unionL, posL, arr[i - 1].pos); } else ADDLIST(listR, unionR, posR, arr[i - 1].pos); if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y) ADDLIST(listB, unionB, posB, arr[i - 1].pos); else if (cur->low.y - pageunion.low.y == pageunion.high.y - cur->high.y) { if (posB > posT) ADDLIST(listT, unionT, posT, arr[i - 1].pos); else ADDLIST(listB, unionB, posB, arr[i - 1].pos); } else ADDLIST(listT, unionT, posT, arr[i - 1].pos); } pfree(arr); } /* 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); }
/* * -------------------------------------------------------------------------- * Double sorting split algorithm. This is used for both boxes and points. * * The algorithm finds split of boxes by considering splits along each axis. * Each entry is first projected as an interval on the X-axis, and different * ways to split the intervals into two groups are considered, trying to * minimize the overlap of the groups. Then the same is repeated for the * Y-axis, and the overall best split is chosen. The quality of a split is * determined by overlap along that axis and some other criteria (see * g_box_consider_split). * * After that, all the entries are divided into three groups: * * 1) Entries which should be placed to the left group * 2) Entries which should be placed to the right group * 3) "Common entries" which can be placed to any of groups without affecting * of overlap along selected axis. * * The common entries are distributed by minimizing penalty. * * For details see: * "A new___ double sorting-based node splitting algorithm for R-tree", A. Korotkov * http://syrcose.ispras.ru/2011/files/SYRCoSE2011_Proceedings.pdf#page=36 * -------------------------------------------------------------------------- */ Datum gist_box_picksplit(PG_FUNCTION_ARGS) { GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); OffsetNumber i, maxoff; ConsiderSplitContext context; BOX *box, *leftBox, *rightBox; int dim, commonEntriesCount; SplitInterval *intervalsLower, *intervalsUpper; CommonEntry *commonEntries; int nentries; memset(&context, 0, sizeof(ConsiderSplitContext)); maxoff = entryvec->n - 1; nentries = context.entriesCount = maxoff - FirstOffsetNumber + 1; /* Allocate arrays for intervals along axes */ intervalsLower = (SplitInterval *) palloc(nentries * sizeof(SplitInterval)); intervalsUpper = (SplitInterval *) palloc(nentries * sizeof(SplitInterval)); /* * Calculate the overall minimum bounding box over all the entries. */ for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { box = DatumGetBoxP(entryvec->vector[i].key); if (i == FirstOffsetNumber) context.boundingBox = *box; else adjustBox(&context.boundingBox, box); } /* * Iterate over axes for optimal split searching. */ context.first = true; /* nothing selected yet */ for (dim = 0; dim < 2; dim++) { double leftUpper, rightLower; int i1, i2; /* Project each entry as an interval on the selected axis. */ for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { box = DatumGetBoxP(entryvec->vector[i].key); if (dim == 0) { intervalsLower[i - FirstOffsetNumber].lower = box->low.x; intervalsLower[i - FirstOffsetNumber].upper = box->high.x; } else { intervalsLower[i - FirstOffsetNumber].lower = box->low.y; intervalsLower[i - FirstOffsetNumber].upper = box->high.y; } } /* * Make two arrays of intervals: one sorted by lower bound and another * sorted by upper bound. */ memcpy(intervalsUpper, intervalsLower, sizeof(SplitInterval) * nentries); qsort(intervalsLower, nentries, sizeof(SplitInterval), interval_cmp_lower); qsort(intervalsUpper, nentries, sizeof(SplitInterval), interval_cmp_upper); /*---- * The goal is to form a left and right interval, so that every entry * interval is contained by either left or right interval (or both). * * For example, with the intervals (0,1), (1,3), (2,3), (2,4): * * 0 1 2 3 4 * +-+ * +---+ * +-+ * +---+ * * The left and right intervals are of the form (0,a) and (b,4). * We first consider splits where b is the lower bound of an entry. * We iterate through all entries, and for each b, calculate the * smallest possible a. Then we consider splits where a is the * uppper bound of an entry, and for each a, calculate the greatest * possible b. * * In the above example, the first loop would consider splits: * b=0: (0,1)-(0,4) * b=1: (0,1)-(1,4) * b=2: (0,3)-(2,4) * * And the second loop: * a=1: (0,1)-(1,4) * a=3: (0,3)-(2,4) * a=4: (0,4)-(2,4) */ /* * Iterate over lower bound of right group, finding smallest possible * upper bound of left group. */ i1 = 0; i2 = 0; rightLower = intervalsLower[i1].lower; leftUpper = intervalsUpper[i2].lower; while (true) { /* * Find next lower bound of right group. */ while (i1 < nentries && rightLower == intervalsLower[i1].lower) { leftUpper = Max(leftUpper, intervalsLower[i1].upper); i1++; } if (i1 >= nentries) break; rightLower = intervalsLower[i1].lower; /* * Find count of intervals which anyway should be placed to the * left group. */ while (i2 < nentries && intervalsUpper[i2].upper <= leftUpper) i2++; /* * Consider found split. */ g_box_consider_split(&context, dim, rightLower, i1, leftUpper, i2); } /* * Iterate over upper bound of left group finding greates possible * lower bound of right group. */ i1 = nentries - 1; i2 = nentries - 1; rightLower = intervalsLower[i1].upper; leftUpper = intervalsUpper[i2].upper; while (true) { /* * Find next upper bound of left group. */ while (i2 >= 0 && leftUpper == intervalsUpper[i2].upper) { rightLower = Min(rightLower, intervalsUpper[i2].lower); i2--; } if (i2 < 0) break; leftUpper = intervalsUpper[i2].upper; /* * Find count of intervals which anyway should be placed to the * right group. */ while (i1 >= 0 && intervalsLower[i1].lower >= rightLower) i1--; /* * Consider found split. */ g_box_consider_split(&context, dim, rightLower, i1 + 1, leftUpper, i2 + 1); } } /* * If we failed to find any acceptable splits, use trivial split. */ if (context.first) { fallbackSplit(entryvec, v); PG_RETURN_POINTER(v); } /* * Ok, we have now selected the split across one axis. * * While considering the splits, we already determined that there will be * enough entries in both groups to reach the desired ratio, but we did * not memorize which entries go to which group. So determine that now. */ /* Allocate vectors for results */ v->spl_left = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber)); v->spl_right = (OffsetNumber *) palloc(nentries * sizeof(OffsetNumber)); v->spl_nleft = 0; v->spl_nright = 0; /* Allocate bounding boxes of left and right groups */ leftBox = static_cast<BOX *>(palloc0(sizeof(BOX))); rightBox = static_cast<BOX *>(palloc0(sizeof(BOX))); /* * Allocate an array for "common entries" - entries which can be placed to * either group without affecting overlap along selected axis. */ commonEntriesCount = 0; commonEntries = (CommonEntry *) palloc(nentries * sizeof(CommonEntry)); /* Helper macros to place an entry in the left or right group */ #define PLACE_LEFT(box, off) \ do { \ if (v->spl_nleft > 0) \ adjustBox(leftBox, box); \ else \ *leftBox = *(box); \ v->spl_left[v->spl_nleft++] = off; \ } while(0) #define PLACE_RIGHT(box, off) \ do { \ if (v->spl_nright > 0) \ adjustBox(rightBox, box); \ else \ *rightBox = *(box); \ v->spl_right[v->spl_nright++] = off; \ } while(0) /* * Distribute entries which can be distributed unambiguously, and collect * common entries. */ for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { double lower, upper; /* * Get upper and lower bounds along selected axis. */ box = DatumGetBoxP(entryvec->vector[i].key); if (context.dim == 0) { lower = box->low.x; upper = box->high.x; } else { lower = box->low.y; upper = box->high.y; } if (upper <= context.leftUpper) { /* Fits to the left group */ if (lower >= context.rightLower) { /* Fits also to the right group, so "common entry" */ commonEntries[commonEntriesCount++].index = i; } else { /* Doesn't fit to the right group, so join to the left group */ PLACE_LEFT(box, i); } } else { /* * Each entry should fit on either left or right group. Since this * entry didn't fit on the left group, it better fit in the right * group. */ Assert(lower >= context.rightLower); /* Doesn't fit to the left group, so join to the right group */ PLACE_RIGHT(box, i); } } /* * Distribute "common entries", if any. */ if (commonEntriesCount > 0) { /* * Calculate minimum number of entries that must be placed in both * groups, to reach LIMIT_RATIO. */ int m = ceil(LIMIT_RATIO * (double) nentries); /* * Calculate delta between penalties of join "common entries" to * different groups. */ for (i = 0; i < commonEntriesCount; i++) { box = DatumGetBoxP(entryvec->vector[commonEntries[i].index].key); commonEntries[i].delta = Abs(box_penalty(leftBox, box) - box_penalty(rightBox, box)); } /* * Sort "common entries" by calculated deltas in order to distribute * the most ambiguous entries first. */ qsort(commonEntries, commonEntriesCount, sizeof(CommonEntry), common_entry_cmp); /* * Distribute "common entries" between groups. */ for (i = 0; i < commonEntriesCount; i++) { box = DatumGetBoxP(entryvec->vector[commonEntries[i].index].key); /* * Check if we have to place this entry in either group to achieve * LIMIT_RATIO. */ if (v->spl_nleft + (commonEntriesCount - i) <= m) PLACE_LEFT(box, commonEntries[i].index); else if (v->spl_nright + (commonEntriesCount - i) <= m) PLACE_RIGHT(box, commonEntries[i].index); else { /* Otherwise select the group by minimal penalty */ if (box_penalty(leftBox, box) < box_penalty(rightBox, box)) PLACE_LEFT(box, commonEntries[i].index); else PLACE_RIGHT(box, commonEntries[i].index); } } } v->spl_ldatum = PointerGetDatum(leftBox); v->spl_rdatum = PointerGetDatum(rightBox); PG_RETURN_POINTER(v); }
Datum gist_point_consistent(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); bool *recheck = (bool *) PG_GETARG_POINTER(4); bool result; StrategyNumber strategyGroup = strategy / GeoStrategyNumberOffset; switch (strategyGroup) { case PointStrategyNumberGroup: result = gist_point_consistent_internal(strategy % GeoStrategyNumberOffset, GIST_LEAF(entry), DatumGetBoxP(entry->key), PG_GETARG_POINT_P(1)); *recheck = false; break; case BoxStrategyNumberGroup: { /* * The only operator___ in this group is point <@ box (on_pb), so * we needn't examine strategy again. * * For historical reasons, on_pb uses exact rather than fuzzy * comparisons. We could use box_overlap when at an internal * page, but that would lead to possibly visiting child pages * uselessly, because box_overlap uses fuzzy comparisons. * Instead we write a non-fuzzy overlap test. The same code * will also serve for leaf-page tests, since leaf keys have * high == low. */ BOX *query, *key; query = PG_GETARG_BOX_P(1); key = DatumGetBoxP(entry->key); result = (key->high.x >= query->low.x && key->low.x <= query->high.x && key->high.y >= query->low.y && key->low.y <= query->high.y); *recheck = false; } break; case PolygonStrategyNumberGroup: { POLYGON *query = PG_GETARG_POLYGON_P(1); result = DatumGetBool(DirectFunctionCall5( gist_poly_consistent, PointerGetDatum(entry), PolygonPGetDatum(query), Int16GetDatum(RTOverlapStrategyNumber), 0, PointerGetDatum(recheck))); if (GIST_LEAF(entry) && result) { /* * We are on leaf page and quick check shows overlapping * of polygon's bounding box and point */ BOX *box = DatumGetBoxP(entry->key); Assert(box->high.x == box->low.x && box->high.y == box->low.y); result = DatumGetBool(DirectFunctionCall2( poly_contain_pt, PolygonPGetDatum(query), PointPGetDatum(&box->high))); *recheck = false; } } break; case CircleStrategyNumberGroup: { CIRCLE *query = PG_GETARG_CIRCLE_P(1); result = DatumGetBool(DirectFunctionCall5( gist_circle_consistent, PointerGetDatum(entry), CirclePGetDatum(query), Int16GetDatum(RTOverlapStrategyNumber), 0, PointerGetDatum(recheck))); if (GIST_LEAF(entry) && result) { /* * We are on leaf page and quick check shows overlapping * of polygon's bounding box and point */ BOX *box = DatumGetBoxP(entry->key); Assert(box->high.x == box->low.x && box->high.y == box->low.y); result = DatumGetBool(DirectFunctionCall2( circle_contain_pt, CirclePGetDatum(query), PointPGetDatum(&box->high))); *recheck = false; } } break; default: elog(ERROR, "unrecognized strategy number: %d", strategy); result = false; /* keep compiler quiet */ break; } PG_RETURN_BOOL(result); }