#include "postgres.h"
#include "access/gist.h"
#include "access/skey.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/rangetypes.h"
Go to the source code of this file.
#define CLS_CONTAIN_EMPTY 4 |
Definition at line 32 of file rangetypes_gist.c.
Referenced by range_gist_picksplit().
#define CLS_COUNT 9 |
Definition at line 35 of file rangetypes_gist.c.
#define CLS_EMPTY 8 |
Definition at line 33 of file rangetypes_gist.c.
#define CLS_LOWER_INF 1 |
Definition at line 30 of file rangetypes_gist.c.
Referenced by range_gist_picksplit().
#define CLS_NORMAL 0 |
Definition at line 29 of file rangetypes_gist.c.
Referenced by range_gist_picksplit().
#define CLS_UPPER_INF 2 |
Definition at line 31 of file rangetypes_gist.c.
Referenced by range_gist_picksplit().
#define CONTAIN_EMPTY_PENALTY 1.0 |
Definition at line 48 of file rangetypes_gist.c.
#define DEFAULT_SUBTYPE_DIFF_PENALTY 1.0 |
Definition at line 49 of file rangetypes_gist.c.
#define INFINITE_BOUND_PENALTY 2.0 |
Definition at line 47 of file rangetypes_gist.c.
#define LIMIT_RATIO 0.3 |
Definition at line 44 of file rangetypes_gist.c.
Referenced by range_gist_consider_split().
#define PLACE_LEFT | ( | range, | ||
off | ||||
) |
do { \ if (v->spl_nleft > 0) \ left_range = range_super_union(typcache, left_range, range); \ else \ left_range = (range); \ v->spl_left[v->spl_nleft++] = (off); \ } while(0)
Definition at line 113 of file rangetypes_gist.c.
Referenced by range_gist_class_split(), range_gist_double_sorting_split(), range_gist_fallback_split(), and range_gist_single_sorting_split().
#define PLACE_RIGHT | ( | range, | ||
off | ||||
) |
do { \ if (v->spl_nright > 0) \ right_range = range_super_union(typcache, right_range, range); \ else \ right_range = (range); \ v->spl_right[v->spl_nright++] = (off); \ } while(0)
Definition at line 122 of file rangetypes_gist.c.
Referenced by range_gist_class_split(), range_gist_double_sorting_split(), range_gist_fallback_split(), and range_gist_single_sorting_split().
#define rangeCopy | ( | r | ) |
((RangeType *) DatumGetPointer(datumCopy(PointerGetDatum(r), \ false, -1)))
Definition at line 132 of file rangetypes_gist.c.
Referenced by range_super_union().
enum SplitLR |
Definition at line 61 of file rangetypes_gist.c.
{ SPLIT_LEFT = 0, /* makes initialization to SPLIT_LEFT easier */ SPLIT_RIGHT } SplitLR;
static float8 call_subtype_diff | ( | TypeCacheEntry * | typcache, | |
Datum | val1, | |||
Datum | val2 | |||
) | [static] |
Definition at line 1530 of file rangetypes_gist.c.
References DatumGetFloat8, FunctionCall2Coll(), TypeCacheEntry::rng_collation, TypeCacheEntry::rng_subdiff_finfo, and value.
Referenced by range_gist_consider_split(), range_gist_double_sorting_split(), and range_gist_penalty().
{ float8 value; value = DatumGetFloat8(FunctionCall2Coll(&typcache->rng_subdiff_finfo, typcache->rng_collation, val1, val2)); /* Cope with buggy subtype_diff function by returning zero */ if (value >= 0.0) return value; return 0.0; }
static int common_entry_cmp | ( | const void * | i1, | |
const void * | i2 | |||
) | [static] |
Definition at line 1512 of file rangetypes_gist.c.
Referenced by range_gist_double_sorting_split().
{ double delta1 = ((CommonEntry *) i1)->delta; double delta2 = ((CommonEntry *) i2)->delta; if (delta1 < delta2) return -1; else if (delta1 > delta2) return 1; else return 0; }
static int get_gist_range_class | ( | RangeType * | range | ) | [static] |
Definition at line 1446 of file rangetypes_gist.c.
References RANGE_CONTAIN_EMPTY, RANGE_EMPTY, range_get_flags(), RANGE_LB_INF, and RANGE_UB_INF.
Referenced by range_gist_class_split(), and range_gist_picksplit().
{ int classNumber; char flags; flags = range_get_flags(range); if (flags & RANGE_EMPTY) { classNumber = CLS_EMPTY; } else { classNumber = 0; if (flags & RANGE_LB_INF) classNumber |= CLS_LOWER_INF; if (flags & RANGE_UB_INF) classNumber |= CLS_UPPER_INF; if (flags & RANGE_CONTAIN_EMPTY) classNumber |= CLS_CONTAIN_EMPTY; } return classNumber; }
static int interval_cmp_lower | ( | const void * | a, | |
const void * | b, | |||
void * | arg | |||
) | [static] |
Definition at line 1486 of file rangetypes_gist.c.
References NonEmptyRange::lower, and range_cmp_bounds().
Referenced by range_gist_double_sorting_split().
{ NonEmptyRange *i1 = (NonEmptyRange *) a; NonEmptyRange *i2 = (NonEmptyRange *) b; TypeCacheEntry *typcache = (TypeCacheEntry *) arg; return range_cmp_bounds(typcache, &i1->lower, &i2->lower); }
static int interval_cmp_upper | ( | const void * | a, | |
const void * | b, | |||
void * | arg | |||
) | [static] |
Definition at line 1499 of file rangetypes_gist.c.
References range_cmp_bounds(), and NonEmptyRange::upper.
Referenced by range_gist_double_sorting_split().
{ NonEmptyRange *i1 = (NonEmptyRange *) a; NonEmptyRange *i2 = (NonEmptyRange *) b; TypeCacheEntry *typcache = (TypeCacheEntry *) arg; return range_cmp_bounds(typcache, &i1->upper, &i2->upper); }
static void range_gist_class_split | ( | TypeCacheEntry * | typcache, | |
GistEntryVector * | entryvec, | |||
GIST_SPLITVEC * | v, | |||
SplitLR * | classes_groups | |||
) | [static] |
Definition at line 929 of file rangetypes_gist.c.
References Assert, DatumGetRangeType, FirstOffsetNumber, get_gist_range_class(), i, GISTENTRY::key, GistEntryVector::n, OffsetNumberNext, PLACE_LEFT, PLACE_RIGHT, range(), RangeTypeGetDatum, GIST_SPLITVEC::spl_ldatum, GIST_SPLITVEC::spl_nleft, GIST_SPLITVEC::spl_nright, GIST_SPLITVEC::spl_rdatum, SPLIT_LEFT, SPLIT_RIGHT, and GistEntryVector::vector.
Referenced by range_gist_picksplit().
{ RangeType *left_range = NULL; RangeType *right_range = NULL; OffsetNumber i, maxoff; maxoff = entryvec->n - 1; v->spl_nleft = 0; v->spl_nright = 0; for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { RangeType *range = DatumGetRangeType(entryvec->vector[i].key); int class; /* Get class of range */ class = get_gist_range_class(range); /* Place range to appropriate page */ if (classes_groups[class] == SPLIT_LEFT) PLACE_LEFT(range, i); else { Assert(classes_groups[class] == SPLIT_RIGHT); PLACE_RIGHT(range, i); } } v->spl_ldatum = RangeTypeGetDatum(left_range); v->spl_rdatum = RangeTypeGetDatum(right_range); }
Datum range_gist_compress | ( | PG_FUNCTION_ARGS | ) |
Definition at line 221 of file rangetypes_gist.c.
References PG_GETARG_POINTER, and PG_RETURN_POINTER.
{ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); PG_RETURN_POINTER(entry); }
static void range_gist_consider_split | ( | ConsiderSplitContext * | context, | |
RangeBound * | right_lower, | |||
int | min_left_count, | |||
RangeBound * | left_upper, | |||
int | max_left_count | |||
) | [static] |
Definition at line 1363 of file rangetypes_gist.c.
References call_subtype_diff(), ConsiderSplitContext::common_left, ConsiderSplitContext::common_right, ConsiderSplitContext::entries_count, ConsiderSplitContext::first, ConsiderSplitContext::has_subtype_diff, ConsiderSplitContext::left_upper, LIMIT_RATIO, Min, ConsiderSplitContext::overlap, ConsiderSplitContext::ratio, ConsiderSplitContext::right_lower, ConsiderSplitContext::typcache, and RangeBound::val.
Referenced by range_gist_double_sorting_split().
{ int left_count, right_count; float4 ratio, overlap; /* * Calculate entries distribution ratio assuming most uniform distribution * of common entries. */ if (min_left_count >= (context->entries_count + 1) / 2) left_count = min_left_count; else if (max_left_count <= context->entries_count / 2) left_count = max_left_count; else left_count = context->entries_count / 2; right_count = context->entries_count - left_count; /* * Ratio of split: quotient between size of smaller group and total * entries count. This is necessarily 0.5 or less; if it's less than * LIMIT_RATIO then we will never accept the new split. */ ratio = ((float4) Min(left_count, right_count)) / ((float4) context->entries_count); if (ratio > LIMIT_RATIO) { bool selectthis = false; /* * The ratio is acceptable, so compare current split with previously * selected one. We search for minimal overlap (allowing negative * values) and minimal ratio secondarily. If subtype_diff is * available, it's used for overlap measure. Without subtype_diff we * use number of "common entries" as an overlap measure. */ if (context->has_subtype_diff) overlap = call_subtype_diff(context->typcache, left_upper->val, right_lower->val); else overlap = max_left_count - min_left_count; /* If there is no previous selection, select this split */ if (context->first) selectthis = true; else { /* * Choose the new split if it has a smaller overlap, or same * overlap but better ratio. */ if (overlap < context->overlap || (overlap == context->overlap && ratio > context->ratio)) selectthis = true; } if (selectthis) { /* save information about selected split */ context->first = false; context->ratio = ratio; context->overlap = overlap; context->right_lower = right_lower; context->left_upper = left_upper; context->common_left = max_left_count - left_count; context->common_right = left_count - min_left_count; } } }
Datum range_gist_consistent | ( | PG_FUNCTION_ARGS | ) |
Definition at line 172 of file rangetypes_gist.c.
References DatumGetRangeType, GIST_LEAF, GISTENTRY::key, PG_GETARG_DATUM, PG_GETARG_POINTER, PG_GETARG_UINT16, PG_RETURN_BOOL, range_get_typcache(), range_gist_consistent_int(), range_gist_consistent_leaf(), and RangeTypeGetOid.
{ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); Datum query = PG_GETARG_DATUM(1); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); /* Oid subtype = PG_GETARG_OID(3); */ bool *recheck = (bool *) PG_GETARG_POINTER(4); RangeType *key = DatumGetRangeType(entry->key); TypeCacheEntry *typcache; /* All operators served by this function are exact */ *recheck = false; typcache = range_get_typcache(fcinfo, RangeTypeGetOid(key)); if (GIST_LEAF(entry)) PG_RETURN_BOOL(range_gist_consistent_leaf(typcache, strategy, key, query)); else PG_RETURN_BOOL(range_gist_consistent_int(typcache, strategy, key, query)); }
static bool range_gist_consistent_int | ( | TypeCacheEntry * | typcache, | |
StrategyNumber | strategy, | |||
RangeType * | key, | |||
Datum | query | |||
) | [static] |
Definition at line 775 of file rangetypes_gist.c.
References DatumGetRangeType, elog, ERROR, range_adjacent_internal(), range_after_internal(), range_before_internal(), range_contains_elem_internal(), range_contains_internal(), range_overlaps_internal(), range_overleft_internal(), range_overright_internal(), RangeIsEmpty, RangeIsOrContainsEmpty, RANGESTRAT_ADJACENT, RANGESTRAT_AFTER, RANGESTRAT_BEFORE, RANGESTRAT_CONTAINED_BY, RANGESTRAT_CONTAINS, RANGESTRAT_CONTAINS_ELEM, RANGESTRAT_EQ, RANGESTRAT_OVERLAPS, RANGESTRAT_OVERLEFT, and RANGESTRAT_OVERRIGHT.
Referenced by range_gist_consistent().
{ switch (strategy) { case RANGESTRAT_BEFORE: if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeType(query))) return false; return (!range_overright_internal(typcache, key, DatumGetRangeType(query))); case RANGESTRAT_OVERLEFT: if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeType(query))) return false; return (!range_after_internal(typcache, key, DatumGetRangeType(query))); case RANGESTRAT_OVERLAPS: return range_overlaps_internal(typcache, key, DatumGetRangeType(query)); case RANGESTRAT_OVERRIGHT: if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeType(query))) return false; return (!range_before_internal(typcache, key, DatumGetRangeType(query))); case RANGESTRAT_AFTER: if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeType(query))) return false; return (!range_overleft_internal(typcache, key, DatumGetRangeType(query))); case RANGESTRAT_ADJACENT: if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeType(query))) return false; if (range_adjacent_internal(typcache, key, DatumGetRangeType(query))) return true; return range_overlaps_internal(typcache, key, DatumGetRangeType(query)); case RANGESTRAT_CONTAINS: return range_contains_internal(typcache, key, DatumGetRangeType(query)); case RANGESTRAT_CONTAINED_BY: /* * Empty ranges are contained by anything, so if key is or * contains any empty ranges, we must descend into it. Otherwise, * descend only if key overlaps the query. */ if (RangeIsOrContainsEmpty(key)) return true; return range_overlaps_internal(typcache, key, DatumGetRangeType(query)); case RANGESTRAT_CONTAINS_ELEM: return range_contains_elem_internal(typcache, key, query); case RANGESTRAT_EQ: /* * If query is empty, descend only if the key is or contains any * empty ranges. Otherwise, descend if key contains query. */ if (RangeIsEmpty(DatumGetRangeType(query))) return RangeIsOrContainsEmpty(key); return range_contains_internal(typcache, key, DatumGetRangeType(query)); default: elog(ERROR, "unrecognized range strategy: %d", strategy); return false; /* keep compiler quiet */ } }
static bool range_gist_consistent_leaf | ( | TypeCacheEntry * | typcache, | |
StrategyNumber | strategy, | |||
RangeType * | key, | |||
Datum | query | |||
) | [static] |
Definition at line 847 of file rangetypes_gist.c.
References DatumGetRangeType, elog, ERROR, range_adjacent_internal(), range_after_internal(), range_before_internal(), range_contained_by_internal(), range_contains_elem_internal(), range_contains_internal(), range_eq_internal(), range_overlaps_internal(), range_overleft_internal(), range_overright_internal(), RANGESTRAT_ADJACENT, RANGESTRAT_AFTER, RANGESTRAT_BEFORE, RANGESTRAT_CONTAINED_BY, RANGESTRAT_CONTAINS, RANGESTRAT_CONTAINS_ELEM, RANGESTRAT_EQ, RANGESTRAT_OVERLAPS, RANGESTRAT_OVERLEFT, and RANGESTRAT_OVERRIGHT.
Referenced by range_gist_consistent().
{ switch (strategy) { case RANGESTRAT_BEFORE: return range_before_internal(typcache, key, DatumGetRangeType(query)); case RANGESTRAT_OVERLEFT: return range_overleft_internal(typcache, key, DatumGetRangeType(query)); case RANGESTRAT_OVERLAPS: return range_overlaps_internal(typcache, key, DatumGetRangeType(query)); case RANGESTRAT_OVERRIGHT: return range_overright_internal(typcache, key, DatumGetRangeType(query)); case RANGESTRAT_AFTER: return range_after_internal(typcache, key, DatumGetRangeType(query)); case RANGESTRAT_ADJACENT: return range_adjacent_internal(typcache, key, DatumGetRangeType(query)); case RANGESTRAT_CONTAINS: return range_contains_internal(typcache, key, DatumGetRangeType(query)); case RANGESTRAT_CONTAINED_BY: return range_contained_by_internal(typcache, key, DatumGetRangeType(query)); case RANGESTRAT_CONTAINS_ELEM: return range_contains_elem_internal(typcache, key, query); case RANGESTRAT_EQ: return range_eq_internal(typcache, key, DatumGetRangeType(query)); default: elog(ERROR, "unrecognized range strategy: %d", strategy); return false; /* keep compiler quiet */ } }
Datum range_gist_decompress | ( | PG_FUNCTION_ARGS | ) |
Definition at line 229 of file rangetypes_gist.c.
References PG_GETARG_POINTER, and PG_RETURN_POINTER.
{ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); PG_RETURN_POINTER(entry); }
static void range_gist_double_sorting_split | ( | TypeCacheEntry * | typcache, | |
GistEntryVector * | entryvec, | |||
GIST_SPLITVEC * | v | |||
) | [static] |
Definition at line 1061 of file rangetypes_gist.c.
References Assert, call_subtype_diff(), common_entry_cmp(), ConsiderSplitContext::common_left, DatumGetRangeType, CommonEntry::delta, ConsiderSplitContext::entries_count, ConsiderSplitContext::first, FirstOffsetNumber, FmgrInfo::fn_oid, ConsiderSplitContext::has_subtype_diff, i, idx(), CommonEntry::index, interval_cmp_lower(), interval_cmp_upper(), GISTENTRY::key, ConsiderSplitContext::left_upper, NonEmptyRange::lower, lower(), GistEntryVector::n, OffsetNumberNext, OidIsValid, palloc(), PLACE_LEFT, PLACE_RIGHT, PointerGetDatum, qsort, qsort_arg(), range_cmp_bounds(), range_deserialize(), range_gist_consider_split(), range_gist_fallback_split(), ConsiderSplitContext::right_lower, TypeCacheEntry::rng_subdiff_finfo, GIST_SPLITVEC::spl_ldatum, GIST_SPLITVEC::spl_left, GIST_SPLITVEC::spl_nleft, GIST_SPLITVEC::spl_nright, GIST_SPLITVEC::spl_rdatum, GIST_SPLITVEC::spl_right, ConsiderSplitContext::typcache, NonEmptyRange::upper, upper(), RangeBound::val, and GistEntryVector::vector.
Referenced by range_gist_picksplit().
{ ConsiderSplitContext context; OffsetNumber i, maxoff; RangeType *range, *left_range = NULL, *right_range = NULL; int common_entries_count; NonEmptyRange *by_lower, *by_upper; CommonEntry *common_entries; int nentries, i1, i2; RangeBound *right_lower, *left_upper; memset(&context, 0, sizeof(ConsiderSplitContext)); context.typcache = typcache; context.has_subtype_diff = OidIsValid(typcache->rng_subdiff_finfo.fn_oid); maxoff = entryvec->n - 1; nentries = context.entries_count = maxoff - FirstOffsetNumber + 1; context.first = true; /* Allocate arrays for sorted range bounds */ by_lower = (NonEmptyRange *) palloc(nentries * sizeof(NonEmptyRange)); by_upper = (NonEmptyRange *) palloc(nentries * sizeof(NonEmptyRange)); /* Fill arrays of bounds */ for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { RangeType *range = DatumGetRangeType(entryvec->vector[i].key); bool empty; range_deserialize(typcache, range, &by_lower[i - FirstOffsetNumber].lower, &by_lower[i - FirstOffsetNumber].upper, &empty); Assert(!empty); } /* * Make two arrays of range bounds: one sorted by lower bound and another * sorted by upper bound. */ memcpy(by_upper, by_lower, nentries * sizeof(NonEmptyRange)); qsort_arg(by_lower, nentries, sizeof(NonEmptyRange), interval_cmp_lower, typcache); qsort_arg(by_upper, nentries, sizeof(NonEmptyRange), interval_cmp_upper, typcache); /*---------- * The goal is to form a left and right range, so that every entry * range is contained by either left or right interval (or both). * * For example, with the ranges (0,1), (1,3), (2,3), (2,4): * * 0 1 2 3 4 * +-+ * +---+ * +-+ * +---+ * * The left and right ranges 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 * upper 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; right_lower = &by_lower[i1].lower; left_upper = &by_upper[i2].lower; while (true) { /* * Find next lower bound of right group. */ while (i1 < nentries && range_cmp_bounds(typcache, right_lower, &by_lower[i1].lower) == 0) { if (range_cmp_bounds(typcache, &by_lower[i1].upper, left_upper) > 0) left_upper = &by_lower[i1].upper; i1++; } if (i1 >= nentries) break; right_lower = &by_lower[i1].lower; /* * Find count of ranges which anyway should be placed to the left * group. */ while (i2 < nentries && range_cmp_bounds(typcache, &by_upper[i2].upper, left_upper) <= 0) i2++; /* * Consider found split to see if it's better than what we had. */ range_gist_consider_split(&context, right_lower, i1, left_upper, i2); } /* * Iterate over upper bound of left group finding greatest possible lower * bound of right group. */ i1 = nentries - 1; i2 = nentries - 1; right_lower = &by_lower[i1].upper; left_upper = &by_upper[i2].upper; while (true) { /* * Find next upper bound of left group. */ while (i2 >= 0 && range_cmp_bounds(typcache, left_upper, &by_upper[i2].upper) == 0) { if (range_cmp_bounds(typcache, &by_upper[i2].lower, right_lower) < 0) right_lower = &by_upper[i2].lower; i2--; } if (i2 < 0) break; left_upper = &by_upper[i2].upper; /* * Find count of intervals which anyway should be placed to the right * group. */ while (i1 >= 0 && range_cmp_bounds(typcache, &by_lower[i1].lower, right_lower) >= 0) i1--; /* * Consider found split to see if it's better than what we had. */ range_gist_consider_split(&context, right_lower, i1 + 1, left_upper, i2 + 1); } /* * If we failed to find any acceptable splits, use trivial split. */ if (context.first) { range_gist_fallback_split(typcache, entryvec, v); return; } /* * Ok, we have now selected bounds of the groups. Now we have to * distribute entries themselves. At first we distribute entries which can * be placed unambiguously and collect "common entries" to array. */ /* 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 an array for "common entries" - entries which can be placed to * either group without affecting overlap along selected axis. */ common_entries_count = 0; common_entries = (CommonEntry *) palloc(nentries * sizeof(CommonEntry)); /* * Distribute entries which can be distributed unambiguously, and collect * common entries. */ for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { RangeBound lower, upper; bool empty; /* * Get upper and lower bounds along selected axis. */ range = DatumGetRangeType(entryvec->vector[i].key); range_deserialize(typcache, range, &lower, &upper, &empty); if (range_cmp_bounds(typcache, &upper, context.left_upper) <= 0) { /* Fits in the left group */ if (range_cmp_bounds(typcache, &lower, context.right_lower) >= 0) { /* Fits also in the right group, so "common entry" */ common_entries[common_entries_count].index = i; if (context.has_subtype_diff) { /* * delta = (lower - context.right_lower) - * (context.left_upper - upper) */ common_entries[common_entries_count].delta = call_subtype_diff(typcache, lower.val, context.right_lower->val) - call_subtype_diff(typcache, context.left_upper->val, upper.val); } else { /* Without subtype_diff, take all deltas as zero */ common_entries[common_entries_count].delta = 0; } common_entries_count++; } else { /* Doesn't fit to the right group, so join to the left group */ PLACE_LEFT(range, i); } } else { /* * Each entry should fit on either left or right group. Since this * entry didn't fit in the left group, it better fit in the right * group. */ Assert(range_cmp_bounds(typcache, &lower, context.right_lower) >= 0); PLACE_RIGHT(range, i); } } /* * Distribute "common entries", if any. */ if (common_entries_count > 0) { /* * Sort "common entries" by calculated deltas in order to distribute * the most ambiguous entries first. */ qsort(common_entries, common_entries_count, sizeof(CommonEntry), common_entry_cmp); /* * Distribute "common entries" between groups according to sorting. */ for (i = 0; i < common_entries_count; i++) { int idx = common_entries[i].index; range = DatumGetRangeType(entryvec->vector[idx].key); /* * Check if we have to place this entry in either group to achieve * LIMIT_RATIO. */ if (i < context.common_left) PLACE_LEFT(range, idx); else PLACE_RIGHT(range, idx); } } v->spl_ldatum = PointerGetDatum(left_range); v->spl_rdatum = PointerGetDatum(right_range); }
static void range_gist_fallback_split | ( | TypeCacheEntry * | typcache, | |
GistEntryVector * | entryvec, | |||
GIST_SPLITVEC * | v | |||
) | [static] |
Definition at line 891 of file rangetypes_gist.c.
References DatumGetRangeType, FirstOffsetNumber, i, GISTENTRY::key, GistEntryVector::n, PLACE_LEFT, PLACE_RIGHT, range(), RangeTypeGetDatum, GIST_SPLITVEC::spl_ldatum, GIST_SPLITVEC::spl_nleft, GIST_SPLITVEC::spl_nright, GIST_SPLITVEC::spl_rdatum, and GistEntryVector::vector.
Referenced by range_gist_double_sorting_split(), and range_gist_picksplit().
{ RangeType *left_range = NULL; RangeType *right_range = NULL; OffsetNumber i, maxoff, split_idx; maxoff = entryvec->n - 1; /* Split entries before this to left page, after to right: */ split_idx = (maxoff - FirstOffsetNumber) / 2 + FirstOffsetNumber; v->spl_nleft = 0; v->spl_nright = 0; for (i = FirstOffsetNumber; i <= maxoff; i++) { RangeType *range = DatumGetRangeType(entryvec->vector[i].key); if (i < split_idx) PLACE_LEFT(range, i); else PLACE_RIGHT(range, i); } v->spl_ldatum = RangeTypeGetDatum(left_range); v->spl_rdatum = RangeTypeGetDatum(right_range); }
Datum range_gist_penalty | ( | PG_FUNCTION_ARGS | ) |
Definition at line 247 of file rangetypes_gist.c.
References call_subtype_diff(), DatumGetRangeType, elog, ERROR, FmgrInfo::fn_oid, get_float4_infinity(), RangeBound::infinite, GISTENTRY::key, OidIsValid, PG_GETARG_POINTER, PG_RETURN_POINTER, range_cmp_bounds(), range_deserialize(), range_get_typcache(), RangeIsOrContainsEmpty, RangeTypeGetOid, TypeCacheEntry::rng_subdiff_finfo, and RangeBound::val.
{ GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1); float *penalty = (float *) PG_GETARG_POINTER(2); RangeType *orig = DatumGetRangeType(origentry->key); RangeType *new = DatumGetRangeType(newentry->key); TypeCacheEntry *typcache; bool has_subtype_diff; RangeBound orig_lower, new_lower, orig_upper, new_upper; bool orig_empty, new_empty; if (RangeTypeGetOid(orig) != RangeTypeGetOid(new)) elog(ERROR, "range types do not match"); typcache = range_get_typcache(fcinfo, RangeTypeGetOid(orig)); has_subtype_diff = OidIsValid(typcache->rng_subdiff_finfo.fn_oid); range_deserialize(typcache, orig, &orig_lower, &orig_upper, &orig_empty); range_deserialize(typcache, new, &new_lower, &new_upper, &new_empty); /* * Distinct branches for handling distinct classes of ranges. Note that * penalty values only need to be commensurate within the same class of * new range. */ if (new_empty) { /* Handle insertion of empty range */ if (orig_empty) { /* * The best case is to insert it to empty original range. * Insertion here means no broadening of original range. Also * original range is the most narrow. */ *penalty = 0.0; } else if (RangeIsOrContainsEmpty(orig)) { /* * The second case is to insert empty range into range which * contains at least one underlying empty range. There is still * no broadening of original range, but original range is not as * narrow as possible. */ *penalty = CONTAIN_EMPTY_PENALTY; } else if (orig_lower.infinite && orig_upper.infinite) { /* * Original range requires broadening. (-inf; +inf) is most far * from normal range in this case. */ *penalty = 2 * CONTAIN_EMPTY_PENALTY; } else if (orig_lower.infinite || orig_upper.infinite) { /* * (-inf, x) or (x, +inf) original ranges are closer to normal * ranges, so it's worse to mix it with empty ranges. */ *penalty = 3 * CONTAIN_EMPTY_PENALTY; } else { /* * The least preferred case is broadening of normal range. */ *penalty = 4 * CONTAIN_EMPTY_PENALTY; } } else if (new_lower.infinite && new_upper.infinite) { /* Handle insertion of (-inf, +inf) range */ if (orig_lower.infinite && orig_upper.infinite) { /* * Best case is inserting to (-inf, +inf) original range. */ *penalty = 0.0; } else if (orig_lower.infinite || orig_upper.infinite) { /* * When original range is (-inf, x) or (x, +inf) it requires * broadening of original range (extension of one bound to * infinity). */ *penalty = INFINITE_BOUND_PENALTY; } else { /* * Insertion to normal original range is least preferred. */ *penalty = 2 * INFINITE_BOUND_PENALTY; } if (RangeIsOrContainsEmpty(orig)) { /* * Original range is narrower when it doesn't contain empty * ranges. Add additional penalty otherwise. */ *penalty += CONTAIN_EMPTY_PENALTY; } } else if (new_lower.infinite) { /* Handle insertion of (-inf, x) range */ if (!orig_empty && orig_lower.infinite) { if (orig_upper.infinite) { /* * (-inf, +inf) range won't be extended by insertion of (-inf, * x) range. It's a less desirable case than insertion to * (-inf, y) original range without extension, because in that * case original range is narrower. But we can't express that * in single float value. */ *penalty = 0.0; } else { if (range_cmp_bounds(typcache, &new_upper, &orig_upper) > 0) { /* * Get extension of original range using subtype_diff. Use * constant if subtype_diff unavailable. */ if (has_subtype_diff) *penalty = call_subtype_diff(typcache, new_upper.val, orig_upper.val); else *penalty = DEFAULT_SUBTYPE_DIFF_PENALTY; } else { /* No extension of original range */ *penalty = 0.0; } } } else { /* * If lower bound of original range is not -inf, then extension of * it is infinity. */ *penalty = get_float4_infinity(); } } else if (new_upper.infinite) { /* Handle insertion of (x, +inf) range */ if (!orig_empty && orig_upper.infinite) { if (orig_lower.infinite) { /* * (-inf, +inf) range won't be extended by insertion of (x, * +inf) range. It's a less desirable case than insertion to * (y, +inf) original range without extension, because in that * case original range is narrower. But we can't express that * in single float value. */ *penalty = 0.0; } else { if (range_cmp_bounds(typcache, &new_lower, &orig_lower) < 0) { /* * Get extension of original range using subtype_diff. Use * constant if subtype_diff unavailable. */ if (has_subtype_diff) *penalty = call_subtype_diff(typcache, orig_lower.val, new_lower.val); else *penalty = DEFAULT_SUBTYPE_DIFF_PENALTY; } else { /* No extension of original range */ *penalty = 0.0; } } } else { /* * If upper bound of original range is not +inf, then extension of * it is infinity. */ *penalty = get_float4_infinity(); } } else { /* Handle insertion of normal (non-empty, non-infinite) range */ if (orig_empty || orig_lower.infinite || orig_upper.infinite) { /* * Avoid mixing normal ranges with infinite and empty ranges. */ *penalty = get_float4_infinity(); } else { /* * Calculate extension of original range by calling subtype_diff. * Use constant if subtype_diff unavailable. */ float8 diff = 0.0; if (range_cmp_bounds(typcache, &new_lower, &orig_lower) < 0) { if (has_subtype_diff) diff += call_subtype_diff(typcache, orig_lower.val, new_lower.val); else diff += DEFAULT_SUBTYPE_DIFF_PENALTY; } if (range_cmp_bounds(typcache, &new_upper, &orig_upper) > 0) { if (has_subtype_diff) diff += call_subtype_diff(typcache, new_upper.val, orig_upper.val); else diff += DEFAULT_SUBTYPE_DIFF_PENALTY; } *penalty = diff; } } PG_RETURN_POINTER(penalty); }
Datum range_gist_picksplit | ( | PG_FUNCTION_ARGS | ) |
Definition at line 504 of file rangetypes_gist.c.
References Abs, Assert, CLS_CONTAIN_EMPTY, CLS_LOWER_INF, CLS_NORMAL, CLS_UPPER_INF, DatumGetRangeType, FirstOffsetNumber, get_gist_range_class(), i, GISTENTRY::key, GistEntryVector::n, OffsetNumberNext, palloc(), PG_GETARG_POINTER, PG_RETURN_POINTER, range(), range_get_typcache(), range_gist_class_split(), range_gist_double_sorting_split(), range_gist_fallback_split(), range_gist_single_sorting_split(), RangeTypeGetOid, GIST_SPLITVEC::spl_left, GIST_SPLITVEC::spl_right, and GistEntryVector::vector.
{ GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); TypeCacheEntry *typcache; OffsetNumber i; RangeType *pred_left; int nbytes; OffsetNumber maxoff; int count_in_classes[CLS_COUNT]; int j; int non_empty_classes_count = 0; int biggest_class = -1; int biggest_class_count = 0; int total_count; /* use first item to look up range type's info */ pred_left = DatumGetRangeType(entryvec->vector[FirstOffsetNumber].key); typcache = range_get_typcache(fcinfo, RangeTypeGetOid(pred_left)); maxoff = entryvec->n - 1; nbytes = (maxoff + 1) * sizeof(OffsetNumber); v->spl_left = (OffsetNumber *) palloc(nbytes); v->spl_right = (OffsetNumber *) palloc(nbytes); /* * Get count distribution of range classes. */ memset(count_in_classes, 0, sizeof(count_in_classes)); for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { RangeType *range = DatumGetRangeType(entryvec->vector[i].key); count_in_classes[get_gist_range_class(range)]++; } /* * Count non-empty classes and find biggest class. */ total_count = maxoff; for (j = 0; j < CLS_COUNT; j++) { if (count_in_classes[j] > 0) { if (count_in_classes[j] > biggest_class_count) { biggest_class_count = count_in_classes[j]; biggest_class = j; } non_empty_classes_count++; } } Assert(non_empty_classes_count > 0); if (non_empty_classes_count == 1) { /* One non-empty class, so split inside class */ if ((biggest_class & ~CLS_CONTAIN_EMPTY) == CLS_NORMAL) { /* double sorting split for normal ranges */ range_gist_double_sorting_split(typcache, entryvec, v); } else if ((biggest_class & ~CLS_CONTAIN_EMPTY) == CLS_LOWER_INF) { /* upper bound sorting split for (-inf, x) ranges */ range_gist_single_sorting_split(typcache, entryvec, v, true); } else if ((biggest_class & ~CLS_CONTAIN_EMPTY) == CLS_UPPER_INF) { /* lower bound sorting split for (x, +inf) ranges */ range_gist_single_sorting_split(typcache, entryvec, v, false); } else { /* trivial split for all (-inf, +inf) or all empty ranges */ range_gist_fallback_split(typcache, entryvec, v); } } else { /* * Class based split. * * To which side of the split should each class go? Initialize them * all to go to the left side. */ SplitLR classes_groups[CLS_COUNT]; memset(classes_groups, 0, sizeof(classes_groups)); if (count_in_classes[CLS_NORMAL] > 0) { /* separate normal ranges if any */ classes_groups[CLS_NORMAL] = SPLIT_RIGHT; } else { /*---------- * Try to split classes in one of two ways: * 1) containing infinities - not containing infinities * 2) containing empty - not containing empty * * Select the way which balances the ranges between left and right * the best. If split in these ways is not possible, there are at * most 3 classes, so just separate biggest class. *---------- */ int infCount, nonInfCount; int emptyCount, nonEmptyCount; nonInfCount = count_in_classes[CLS_NORMAL] + count_in_classes[CLS_CONTAIN_EMPTY] + count_in_classes[CLS_EMPTY]; infCount = total_count - nonInfCount; nonEmptyCount = count_in_classes[CLS_NORMAL] + count_in_classes[CLS_LOWER_INF] + count_in_classes[CLS_UPPER_INF] + count_in_classes[CLS_LOWER_INF | CLS_UPPER_INF]; emptyCount = total_count - nonEmptyCount; if (infCount > 0 && nonInfCount > 0 && (Abs(infCount - nonInfCount) <= Abs(emptyCount - nonEmptyCount))) { classes_groups[CLS_NORMAL] = SPLIT_RIGHT; classes_groups[CLS_CONTAIN_EMPTY] = SPLIT_RIGHT; classes_groups[CLS_EMPTY] = SPLIT_RIGHT; } else if (emptyCount > 0 && nonEmptyCount > 0) { classes_groups[CLS_NORMAL] = SPLIT_RIGHT; classes_groups[CLS_LOWER_INF] = SPLIT_RIGHT; classes_groups[CLS_UPPER_INF] = SPLIT_RIGHT; classes_groups[CLS_LOWER_INF | CLS_UPPER_INF] = SPLIT_RIGHT; } else { /* * Either total_count == emptyCount or total_count == * infCount. */ classes_groups[biggest_class] = SPLIT_RIGHT; } } range_gist_class_split(typcache, entryvec, v, classes_groups); } PG_RETURN_POINTER(v); }
Datum range_gist_same | ( | PG_FUNCTION_ARGS | ) |
Definition at line 663 of file rangetypes_gist.c.
References PG_GETARG_POINTER, PG_GETARG_RANGE, PG_RETURN_POINTER, range_eq_internal(), range_get_flags(), range_get_typcache(), and RangeTypeGetOid.
{ RangeType *r1 = PG_GETARG_RANGE(0); RangeType *r2 = PG_GETARG_RANGE(1); bool *result = (bool *) PG_GETARG_POINTER(2); /* * range_eq will ignore the RANGE_CONTAIN_EMPTY flag, so we have to check * that for ourselves. More generally, if the entries have been properly * normalized, then unequal flags bytes must mean unequal ranges ... so * let's just test all the flag bits at once. */ if (range_get_flags(r1) != range_get_flags(r2)) *result = false; else { TypeCacheEntry *typcache; typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1)); *result = range_eq_internal(typcache, r1, r2); } PG_RETURN_POINTER(result); }
static void range_gist_single_sorting_split | ( | TypeCacheEntry * | typcache, | |
GistEntryVector * | entryvec, | |||
GIST_SPLITVEC * | v, | |||
bool | use_upper_bound | |||
) | [static] |
Definition at line 972 of file rangetypes_gist.c.
References Assert, DatumGetRangeType, FirstOffsetNumber, i, idx(), SingleBoundSortItem::index, GISTENTRY::key, GistEntryVector::n, OffsetNumberNext, palloc(), PLACE_LEFT, PLACE_RIGHT, qsort_arg(), range(), range_deserialize(), RangeTypeGetDatum, single_bound_cmp(), GIST_SPLITVEC::spl_ldatum, GIST_SPLITVEC::spl_nleft, GIST_SPLITVEC::spl_nright, GIST_SPLITVEC::spl_rdatum, and GistEntryVector::vector.
Referenced by range_gist_picksplit().
{ SingleBoundSortItem *sortItems; RangeType *left_range = NULL; RangeType *right_range = NULL; OffsetNumber i, maxoff, split_idx; maxoff = entryvec->n - 1; sortItems = (SingleBoundSortItem *) palloc(maxoff * sizeof(SingleBoundSortItem)); /* * Prepare auxiliary array and sort the values. */ for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { RangeType *range = DatumGetRangeType(entryvec->vector[i].key); RangeBound bound2; bool empty; sortItems[i - 1].index = i; /* Put appropriate bound into array */ if (use_upper_bound) range_deserialize(typcache, range, &bound2, &sortItems[i - 1].bound, &empty); else range_deserialize(typcache, range, &sortItems[i - 1].bound, &bound2, &empty); Assert(!empty); } qsort_arg(sortItems, maxoff, sizeof(SingleBoundSortItem), single_bound_cmp, typcache); split_idx = maxoff / 2; v->spl_nleft = 0; v->spl_nright = 0; for (i = 0; i < maxoff; i++) { int idx = sortItems[i].index; RangeType *range = DatumGetRangeType(entryvec->vector[idx].key); if (i < split_idx) PLACE_LEFT(range, idx); else PLACE_RIGHT(range, idx); } v->spl_ldatum = RangeTypeGetDatum(left_range); v->spl_rdatum = RangeTypeGetDatum(right_range); }
Datum range_gist_union | ( | PG_FUNCTION_ARGS | ) |
Definition at line 198 of file rangetypes_gist.c.
References DatumGetRangeType, i, GistEntryVector::n, PG_GETARG_POINTER, PG_RETURN_RANGE, range_get_typcache(), range_super_union(), RangeTypeGetOid, and GistEntryVector::vector.
{ GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); GISTENTRY *ent = entryvec->vector; RangeType *result_range; TypeCacheEntry *typcache; int i; result_range = DatumGetRangeType(ent[0].key); typcache = range_get_typcache(fcinfo, RangeTypeGetOid(result_range)); for (i = 1; i < entryvec->n; i++) { result_range = range_super_union(typcache, result_range, DatumGetRangeType(ent[i].key)); } PG_RETURN_RANGE(result_range); }
static RangeType * range_super_union | ( | TypeCacheEntry * | typcache, | |
RangeType * | r1, | |||
RangeType * | r2 | |||
) | [static] |
Definition at line 705 of file rangetypes_gist.c.
References make_range(), range_cmp_bounds(), RANGE_CONTAIN_EMPTY, range_deserialize(), RANGE_EMPTY, range_get_flags(), range_set_contain_empty(), and rangeCopy.
Referenced by range_gist_union().
{ RangeType *result; RangeBound lower1, lower2; RangeBound upper1, upper2; bool empty1, empty2; char flags1, flags2; RangeBound *result_lower; RangeBound *result_upper; range_deserialize(typcache, r1, &lower1, &upper1, &empty1); range_deserialize(typcache, r2, &lower2, &upper2, &empty2); flags1 = range_get_flags(r1); flags2 = range_get_flags(r2); if (empty1) { /* We can return r2 as-is if it already is or contains empty */ if (flags2 & (RANGE_EMPTY | RANGE_CONTAIN_EMPTY)) return r2; /* Else we'd better copy it (modify-in-place isn't safe) */ r2 = rangeCopy(r2); range_set_contain_empty(r2); return r2; } if (empty2) { /* We can return r1 as-is if it already is or contains empty */ if (flags1 & (RANGE_EMPTY | RANGE_CONTAIN_EMPTY)) return r1; /* Else we'd better copy it (modify-in-place isn't safe) */ r1 = rangeCopy(r1); range_set_contain_empty(r1); return r1; } if (range_cmp_bounds(typcache, &lower1, &lower2) <= 0) result_lower = &lower1; else result_lower = &lower2; if (range_cmp_bounds(typcache, &upper1, &upper2) >= 0) result_upper = &upper1; else result_upper = &upper2; /* optimization to avoid constructing a new range */ if (result_lower == &lower1 && result_upper == &upper1 && ((flags1 & RANGE_CONTAIN_EMPTY) || !(flags2 & RANGE_CONTAIN_EMPTY))) return r1; if (result_lower == &lower2 && result_upper == &upper2 && ((flags2 & RANGE_CONTAIN_EMPTY) || !(flags1 & RANGE_CONTAIN_EMPTY))) return r2; result = make_range(typcache, result_lower, result_upper, false); if ((flags1 & RANGE_CONTAIN_EMPTY) || (flags2 & RANGE_CONTAIN_EMPTY)) range_set_contain_empty(result); return result; }
static int single_bound_cmp | ( | const void * | a, | |
const void * | b, | |||
void * | arg | |||
) | [static] |
Definition at line 1473 of file rangetypes_gist.c.
References SingleBoundSortItem::bound, and range_cmp_bounds().
Referenced by range_gist_single_sorting_split().
{ SingleBoundSortItem *i1 = (SingleBoundSortItem *) a; SingleBoundSortItem *i2 = (SingleBoundSortItem *) b; TypeCacheEntry *typcache = (TypeCacheEntry *) arg; return range_cmp_bounds(typcache, &i1->bound, &i2->bound); }