Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Enumerations | Functions

rangetypes_gist.c File Reference

#include "postgres.h"
#include "access/gist.h"
#include "access/skey.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/rangetypes.h"
Include dependency graph for rangetypes_gist.c:

Go to the source code of this file.

Data Structures

struct  SingleBoundSortItem
struct  ConsiderSplitContext
struct  NonEmptyRange
struct  CommonEntry

Defines

#define CLS_NORMAL   0
#define CLS_LOWER_INF   1
#define CLS_UPPER_INF   2
#define CLS_CONTAIN_EMPTY   4
#define CLS_EMPTY   8
#define CLS_COUNT   9
#define LIMIT_RATIO   0.3
#define INFINITE_BOUND_PENALTY   2.0
#define CONTAIN_EMPTY_PENALTY   1.0
#define DEFAULT_SUBTYPE_DIFF_PENALTY   1.0
#define PLACE_LEFT(range, off)
#define PLACE_RIGHT(range, off)
#define rangeCopy(r)

Enumerations

enum  SplitLR { SPLIT_LEFT = 0, SPLIT_RIGHT }

Functions

static RangeTyperange_super_union (TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
static bool range_gist_consistent_int (TypeCacheEntry *typcache, StrategyNumber strategy, RangeType *key, Datum query)
static bool range_gist_consistent_leaf (TypeCacheEntry *typcache, StrategyNumber strategy, RangeType *key, Datum query)
static void range_gist_fallback_split (TypeCacheEntry *typcache, GistEntryVector *entryvec, GIST_SPLITVEC *v)
static void range_gist_class_split (TypeCacheEntry *typcache, GistEntryVector *entryvec, GIST_SPLITVEC *v, SplitLR *classes_groups)
static void range_gist_single_sorting_split (TypeCacheEntry *typcache, GistEntryVector *entryvec, GIST_SPLITVEC *v, bool use_upper_bound)
static void range_gist_double_sorting_split (TypeCacheEntry *typcache, GistEntryVector *entryvec, GIST_SPLITVEC *v)
static void range_gist_consider_split (ConsiderSplitContext *context, RangeBound *right_lower, int min_left_count, RangeBound *left_upper, int max_left_count)
static int get_gist_range_class (RangeType *range)
static int single_bound_cmp (const void *a, const void *b, void *arg)
static int interval_cmp_lower (const void *a, const void *b, void *arg)
static int interval_cmp_upper (const void *a, const void *b, void *arg)
static int common_entry_cmp (const void *i1, const void *i2)
static float8 call_subtype_diff (TypeCacheEntry *typcache, Datum val1, Datum val2)
Datum range_gist_consistent (PG_FUNCTION_ARGS)
Datum range_gist_union (PG_FUNCTION_ARGS)
Datum range_gist_compress (PG_FUNCTION_ARGS)
Datum range_gist_decompress (PG_FUNCTION_ARGS)
Datum range_gist_penalty (PG_FUNCTION_ARGS)
Datum range_gist_picksplit (PG_FUNCTION_ARGS)
Datum range_gist_same (PG_FUNCTION_ARGS)

Define Documentation

#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 
)
Value:
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 
)
Value:
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  ) 
Value:

Definition at line 132 of file rangetypes_gist.c.

Referenced by range_super_union().


Enumeration Type Documentation

enum SplitLR
Enumerator:
SPLIT_LEFT 
SPLIT_RIGHT 

Definition at line 61 of file rangetypes_gist.c.

{
    SPLIT_LEFT = 0,             /* makes initialization to SPLIT_LEFT easier */
    SPLIT_RIGHT
} SplitLR;


Function Documentation

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.

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.

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]