/* *AdditionalSQLlevelsupportfunctions * *ProcedurenumbersmustnotusevaluesreservedforBRINitself;see *brin_internal.h.
*/ #define MINMAX_MAX_PROCNUMS 1/* maximum support procs we need */ #define PROCNUM_DISTANCE 11/* required, distance between values */
/* *StoragetypeforBRIN'sminmaxreloptions
*/ typedefstruct MinMaxMultiOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */ int valuesPerRange; /* number of values per range */
} MinMaxMultiOptions;
/* *Thesummaryofminmax-multiindexeshastworepresentations-Rangesfor *convenientprocessing,andSerializedRangesforstorageinbyteavalue. * *TheRangesstructstorestheboundaryvaluesinasinglearray,butwe *treatregularandsingle-pointrangesdifferentlytosavespace.For *regularranges(withdifferentboundaryvalues)wehavetostoreboth *thelowerandupperboundoftherange,whilefor"single-pointranges" *weonlyneedtostoreasinglevalue. * *The'values'arraystoresboundaryvaluesforregularrangesfirst(there *are2*nrangesvaluestostore),andthenthenvaluesboundaryvaluesfor *single-pointranges.Thatis,wehave(2*nranges+nvalues)boundary *valuesinthearray. * *+-------------------------+----------------------------------+ *|ranges(2*nrangesof)|singlepointvalues(nvaluesof)| *+-------------------------+----------------------------------+ * *Thisallowsustoquicklyaddnewvalues,andstoreoutlierswithout *havingtowidenanyoftheexistingrangevalues. * *'nsorted'denoteshowmanyof'nvalues'inthevalues[]arrayaresorted. *Whennsorted==nvalues,allsinglepointvaluesaresorted. * *Weneverstoremorethanmaxvaluesvalues(assetbyvalues_per_range *reloption).Ifneededwemergesomeoftheranges. * *Tominimizepallocoverhead,wealwaysallocatethefullarraywith *spaceformaxvalueselements.Thisshouldbefineaslongasthe *maxvaluesisreasonablysmall(64seemsfine),whichisthecase *thankstovalues_per_rangereloptionbeinglimitedto256.
*/ typedefstruct Ranges
{ /* Cache information that we need quite often. */
Oid typid;
Oid colloid;
AttrNumber attno;
FmgrInfo *cmp;
/* (2*nranges + nvalues) <= maxvalues */ int nranges; /* number of ranges in the values[] array */ int nsorted; /* number of nvalues which are sorted */ int nvalues; /* number of point values in values[] array */ int maxvalues; /* number of elements in the values[] array */
/* *Wesimplyaddthevaluesintoalargebuffer,withoutanyexpensive *steps(sorting,deduplication,...).Thebufferisamultipleofthe *targetnumberofvalues,sothecompactionhappenslessoften, *amortizingthecosts.Wekeeptheactualtargetandcompacttothe *requestednumberofvaluesattheveryend,beforeserializingto *on-diskrepresentation.
*/ /* requested number of values */ int target_maxvalues;
/* values stored for this range - either raw values, or ranges */
Datum values[FLEXIBLE_ARRAY_MEMBER];
} Ranges;
/* *On-diskthesummaryisstoredasabyteavalue,withasimpleheader *withbasicmetadata,followedbytheboundaryvalues.Ithasavarlena *header,socanbetreatedasvarlenadirectly. * *Seebrin_range_serialize/brin_range_deserializeforserializationdetails.
*/ typedefstruct SerializedRanges
{ /* varlena header (do not touch directly!) */
int32 vl_len_;
/* type of values stored in the data array */
Oid typid;
/* (2*nranges + nvalues) <= maxvalues */ int nranges; /* number of ranges in the array (stored) */ int nvalues; /* number of values in the data array (all) */ int maxvalues; /* maximum number of values (reloption) */
/* contains the actual data */ char data[FLEXIBLE_ARRAY_MEMBER];
} SerializedRanges;
#ifdef USE_ASSERT_CHECKING /* *Checkthattheorderofthearrayvaluesiscorrect,usingthecmp *function(whichshouldbeBTLessStrategyNumber).
*/ staticvoid
AssertArrayOrder(FmgrInfo *cmp, Oid colloid, Datum *values, int nvalues)
{ int i;
Datum lt;
for (i = 0; i < (nvalues - 1); i++)
{
lt = FunctionCall2Coll(cmp, colloid, values[i], values[i + 1]);
Assert(DatumGetBool(lt));
}
} #endif
/* *ComprehensivecheckoftheRangesstructure.
*/ staticvoid
AssertCheckRanges(Ranges *ranges, FmgrInfo *cmpFn, Oid colloid)
{ #ifdef USE_ASSERT_CHECKING int i;
/* then the single-point ranges (with nvalues boundary values ) */
AssertArrayOrder(cmpFn, colloid, &ranges->values[2 * ranges->nranges],
ranges->nsorted);
/* *Checkthatnoneofthevaluesarenotcoveredbyranges(bothsorted *andunsorted)
*/ if (ranges->nranges > 0)
{ for (i = 0; i < ranges->nvalues; i++)
{
Datum compar; int start,
end;
Datum minvalue = ranges->values[0];
Datum maxvalue = ranges->values[2 * ranges->nranges - 1];
Datum value = ranges->values[2 * ranges->nranges + i];
/* *Likewise,ifthevalueislargerthantheupperboundofthe *finalrange,thenitcannotpossiblybeinsideanyofthe *ranges.
*/ if (DatumGetBool(compar)) continue;
/* bsearch the ranges to see if 'value' fits within any of them */
start = 0; /* first range */
end = ranges->nranges - 1; /* last range */ while (true)
{ int midpoint = (start + end) / 2;
/* this means we ran out of ranges in the last step */ if (start > end) break;
/* copy the min/max values from the ranges */
minvalue = ranges->values[2 * midpoint];
maxvalue = ranges->values[2 * midpoint + 1];
/* *Eachrangeindependentlyshouldbevalid,i.e.thatfortheboundary *values(lower<=upper).
*/ for (i = 0; i < nranges; i++)
{
Datum r;
Datum minval = ranges[i].minval;
Datum maxval = ranges[i].maxval;
if (ranges[i].collapsed) /* collapsed: minval == maxval */
r = FunctionCall2Coll(eq, colloid, minval, maxval); else/* non-collapsed: minval < maxval */
r = FunctionCall2Coll(lt, colloid, minval, maxval);
Assert(DatumGetBool(r));
}
/* *Andtherangesshouldbeorderedandmustnotoverlap,i.e.upper< *lowerforboundariesofconsecutiveranges.
*/ for (i = 0; i < nranges - 1; i++)
{
Datum r;
Datum maxval = ranges[i].maxval;
Datum minval = ranges[i + 1].minval;
r = FunctionCall2Coll(lt, colloid, maxval, minval);
n = 1; for (i = 1; i < range->nvalues; i++)
{ /* same as preceding value, so store it */ if (compare_values(&range->values[start + i - 1],
&range->values[start + i],
&cxt) == 0) continue;
/* *Andnowcopyalsotheboundaryvalues(likethelengthcalculationthis *dependsontheparticulardatatype).
*/
ptr = serialized->data; /* start of the serialized data */
for (int i = 0; i < nvalues; i++)
{ if (typbyval) /* simple by-value data types */
{
Datum tmp;
/* first compare minvals */
r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, ra->minval, rb->minval);
if (DatumGetBool(r)) return -1;
r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, rb->minval, ra->minval);
if (DatumGetBool(r)) return1;
/* then compare maxvals */
r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, ra->maxval, rb->maxval);
if (DatumGetBool(r)) return -1;
r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, rb->maxval, ra->maxval);
if (DatumGetBool(r)) return1;
return0;
}
/* *compare_values *Comparethevalues.
*/ staticint
compare_values(constvoid *a, constvoid *b, void *arg)
{
Datum *da = (Datum *) a;
Datum *db = (Datum *) b;
Datum r;
compare_context *cxt = (compare_context *) arg;
r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, *da, *db);
if (DatumGetBool(r)) return -1;
r = FunctionCall2Coll(cxt->cmpFn, cxt->colloid, *db, *da);
if (DatumGetBool(r)) return1;
return0;
}
/* *Checkifthenewvaluematchesoneoftheexistingranges.
*/ staticbool
has_matching_range(BrinDesc *bdesc, Oid colloid, Ranges *ranges,
Datum newval, AttrNumber attno, Oid typid)
{
Datum compar;
/* *Soweknowit'sinthegeneralmin/max,thequestioniswhetherit *fallsinoneoftherangesorgaps.We'lldoabinarysearchon *individualranges-foreachrangewecheckequality(valuefallsinto *therange),andthencheckrangeseitheraboveorbelowthecurrent *range.
*/
start = 0; /* first range */
end = (ranges->nranges - 1); /* last range */ while (true)
{ int midpoint = (start + end) / 2;
/* this means we ran out of ranges in the last step */ if (start > end) returnfalse;
/* copy the min/max values from the ranges */
minvalue = ranges->values[2 * midpoint];
maxvalue = ranges->values[2 * midpoint + 1];
/* *Deduplicatetheranges-simplycompareeachrangetothepreceding *one,andskiptheduplicateones.
*/
n = 1; for (i = 1; i < neranges; i++)
{ /* if the current range is equal to the preceding one, do nothing */ if (!compare_expanded_ranges(&eranges[i - 1], &eranges[i], &cxt)) continue;
/* otherwise, copy it to n-th place (if not already there) */ if (i != n)
memcpy(&eranges[n], &eranges[i], sizeof(ExpandedRange));
n++;
}
Assert((n > 0) && (n <= neranges));
return n;
}
/* *WhencombiningmultipleRangevalues(inunionfunction),someofthe *rangesmayoverlap.Wesimplymergetheoverlappingrangestofixthat. * *XXXThisassumestheexpandedrangeswerepreviouslysorted(byminval *andthenmaxval).Weleveragethiswhendetectingoverlap.
*/ staticint
merge_overlapping_ranges(FmgrInfo *cmp, Oid colloid,
ExpandedRange *eranges, int neranges)
{ int idx;
/* Merge ranges (idx) and (idx+1) if they overlap. */
idx = 0; while (idx < (neranges - 1))
{
Datum r;
/* *Nope,maxval<minval,sonooverlap.Andweknowtherangesare *ordered,sotherearenomoreoverlaps,becausealltheremaining *rangeshavegreaterorequalminval.
*/ if (DatumGetBool(r))
{ /* proceed to the next range */
idx += 1; continue;
}
/* allocate space for the boundary values */
nvalues = 0;
values = (Datum *) palloc(sizeof(Datum) * max_values);
/* add the global min/max values, from the first/last range */
values[nvalues++] = eranges[0].minval;
values[nvalues++] = eranges[neranges - 1].maxval;
/* add boundary values for enough gaps */ for (i = 0; i < keep; i++)
{ /* index of the gap between (index) and (index+1) ranges */ int index = distances[i].index;
Assert((index >= 0) && ((index + 1) < neranges));
/* add max from the preceding range, minval from the next one */
values[nvalues++] = eranges[index].maxval;
values[nvalues++] = eranges[index + 1].minval;
Assert(nvalues <= max_values);
}
/* We should have an even number of range values. */
Assert(nvalues % 2 == 0);
/* We have nvalues boundary values, which means nvalues/2 ranges. */ for (i = 0; i < (nvalues / 2); i++)
{
eranges[i].minval = values[2 * i];
eranges[i].maxval = values[2 * i + 1];
/* if the boundary values are the same, it's a collapsed range */
eranges[i].collapsed = (compare_values(&values[2 * i],
&values[2 * i + 1],
&cxt) == 0);
}
return (nvalues / 2);
}
/* *StoretheboundaryvaluesfromExpandedRangesbackinto'ranges'(using *onlytheminimalnumberofvaluesneeded).
*/ staticvoid
store_expanded_ranges(Ranges *ranges, ExpandedRange *eranges, int neranges)
{ int i; int idx = 0;
/* first copy in the regular ranges */
ranges->nranges = 0; for (i = 0; i < neranges; i++)
{ if (!eranges[i].collapsed)
{
ranges->values[idx++] = eranges[i].minval;
ranges->values[idx++] = eranges[i].maxval;
ranges->nranges++;
}
}
/* now copy in the collapsed ones */
ranges->nvalues = 0; for (i = 0; i < neranges; i++)
{ if (eranges[i].collapsed)
{
ranges->values[idx++] = eranges[i].minval;
ranges->nvalues++;
}
}
/* all the values are sorted */
ranges->nsorted = ranges->nvalues;
/* we'll certainly need the comparator, so just look it up now */
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
/* deduplicate values, if there's an unsorted part */
range_deduplicate_values(range);
/* Is the result of reducing expanded ranges correct? */
AssertCheckExpandedRanges(bdesc, colloid, attno, attr, eranges, neranges);
/* Make sure we've sufficiently reduced the number of ranges. */
Assert(count_values(eranges, neranges) <= range->maxvalues * MINMAX_BUFFER_LOAD_FACTOR);
/* decompose the expanded ranges into regular ranges and single values */
store_expanded_ranges(range, eranges, neranges);
/* we'll certainly need the comparator, so just look it up now */
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
/* comprehensive checks of the input ranges */
AssertCheckRanges(ranges, cmpFn, colloid);
/* we'll certainly need the comparator, so just look it up now */
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, ranges->attno, ranges->typid,
BTLessStrategyNumber);
/* and we'll also need the 'distance' procedure */
distanceFn = minmax_multi_get_procinfo(bdesc, ranges->attno, PROCNUM_DISTANCE);
/* *Computethedistancebetweentwonumericvalues(plainsubtraction).
*/
Datum
brin_minmax_multi_distance_numeric(PG_FUNCTION_ARGS)
{
Datum d;
Datum a1 = PG_GETARG_DATUM(0);
Datum a2 = PG_GETARG_DATUM(1);
/* Calculate the difference between the addresses. */
delta = 0;
for (i = len - 1; i >= 0; i--)
{
unsigned char a = addra[i];
unsigned char b = addrb[i];
delta += (float8) b - (float8) a;
delta /= 256;
}
Assert((delta >= 0) && (delta <= 1));
pfree(addra);
pfree(addrb);
PG_RETURN_FLOAT8(delta);
}
static void
brin_minmax_multi_serialize(BrinDesc *bdesc, Datum src, Datum *dst)
{
Ranges *ranges = (Ranges *) DatumGetPointer(src);
SerializedRanges *s;
/*
* In batch mode, we need to compress the accumulated values to the
* actually requested number of values/ranges.
*/
compactify_ranges(bdesc, ranges, ranges->target_maxvalues);
/* At this point everything has to be fully sorted. */
Assert(ranges->nsorted == ranges->nvalues);
s = brin_range_serialize(ranges);
dst[0] = PointerGetDatum(s);
}
static int
brin_minmax_multi_get_values(BrinDesc *bdesc, MinMaxMultiOptions *opts)
{
return MinMaxMultiGetValuesPerRange(opts);
}
/*
* Examine the given index tuple (which contains the partial status of a
* certain page range) by comparing it to the given value that comes from
* another heap tuple. If the new value is outside the min/max range
* specified by the existing tuple values, update the index tuple and return
* true. Otherwise, return false and do not modify in this case.
*/
Datum
brin_minmax_multi_add_value(PG_FUNCTION_ARGS)
{
BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
Datum newval = PG_GETARG_DATUM(2);
bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(3);
MinMaxMultiOptions *opts = (MinMaxMultiOptions *) PG_GET_OPCLASS_OPTIONS();
Oid colloid = PG_GET_COLLATION();
bool modified = false;
Form_pg_attribute attr;
AttrNumber attno;
Ranges *ranges;
SerializedRanges *serialized = NULL;
/* use the already deserialized value, if possible */
ranges = (Ranges *) DatumGetPointer(column->bv_mem_value);
/*
* If this is the first non-null value, we need to initialize the range
* list. Otherwise, just extract the existing range list from BrinValues.
*
* When starting with an empty range, we assume this is a batch mode and
* we use a larger buffer. The buffer size is derived from the BRIN range
* size, number of rows per page, with some sensible min/max values. A
* small buffer would be bad for performance, but a large buffer might
* require a lot of memory (because of keeping all the values).
*/
if (column->bv_allnulls)
{
MemoryContext oldctx;
int target_maxvalues;
int maxvalues;
BlockNumber pagesPerRange = BrinGetPagesPerRange(bdesc->bd_index);
/* what was specified as a reloption? */
target_maxvalues = brin_minmax_multi_get_values(bdesc, opts);
/*
* Determine the insert buffer size - we use 10x the target, capped to
* the maximum number of values in the heap range. This is more than
* enough, considering the actual number of rows per page is likely
* much lower, but meh.
*/
maxvalues = Min(target_maxvalues * MINMAX_BUFFER_FACTOR,
MaxHeapTuplesPerPage * pagesPerRange);
/* but always at least the original value */
maxvalues = Max(maxvalues, target_maxvalues);
/* always cap by MIN/MAX */
maxvalues = Max(maxvalues, MINMAX_BUFFER_MIN);
maxvalues = Min(maxvalues, MINMAX_BUFFER_MAX);
/* we'll certainly need the comparator, so just look it up now */
ranges->cmp = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
/*
* Determine the insert buffer size - we use 10x the target, capped to
* the maximum number of values in the heap range. This is more than
* enough, considering the actual number of rows per page is likely
* much lower, but meh.
*/
maxvalues = Min(serialized->maxvalues * MINMAX_BUFFER_FACTOR,
MaxHeapTuplesPerPage * pagesPerRange);
/* but always at least the original value */
maxvalues = Max(maxvalues, serialized->maxvalues);
/* always cap by MIN/MAX */
maxvalues = Max(maxvalues, MINMAX_BUFFER_MIN);
maxvalues = Min(maxvalues, MINMAX_BUFFER_MAX);
/* we'll certainly need the comparator, so just look it up now */
ranges->cmp = minmax_multi_get_strategy_procinfo(bdesc, attno, attr->atttypid,
BTLessStrategyNumber);
/*
* Try to add the new value to the range. We need to update the modified
* flag, so that we serialize the updated summary later.
*/
modified |= range_add_value(bdesc, colloid, attno, attr, ranges, newval);
PG_RETURN_BOOL(modified);
}
/*
* Given an index tuple corresponding to a certain page range and a scan key,
* return whether the scan key is consistent with the index tuple's min/max
* values. Return true if so, false otherwise.
*/
Datum
brin_minmax_multi_consistent(PG_FUNCTION_ARGS)
{
BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
ScanKey *keys = (ScanKey *) PG_GETARG_POINTER(2);
int nkeys = PG_GETARG_INT32(3);
Oid colloid = PG_GET_COLLATION(),
subtype;
AttrNumber attno;
Datum value;
FmgrInfo *finfo;
SerializedRanges *serialized;
Ranges *ranges;
int keyno;
int rangeno;
int i;
/* inspect the ranges, and for each one evaluate the scan keys */
for (rangeno = 0; rangeno < ranges->nranges; rangeno++)
{
Datum minval = ranges->values[2 * rangeno];
Datum maxval = ranges->values[2 * rangeno + 1];
/* assume the range is matching, and we'll try to prove otherwise */
bool matching = true;
/* NULL keys are handled and filtered-out in bringetbitmap */
Assert(!(key->sk_flags & SK_ISNULL));
attno = key->sk_attno;
subtype = key->sk_subtype;
value = key->sk_argument;
switch (key->sk_strategy)
{
case BTLessStrategyNumber:
case BTLessEqualStrategyNumber:
finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
key->sk_strategy);
/* first value from the array */
matches = DatumGetBool(FunctionCall2Coll(finfo, colloid, minval, value));
break;
case BTEqualStrategyNumber:
{
Datum compar;
FmgrInfo *cmpFn;
/* by default this range does not match */
matches = false;
/*
* Otherwise, need to compare the new value with
* boundaries of all the ranges. First check if it's
* less than the absolute minimum, which is the first
* value in the array.
*/
cmpFn = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
BTGreaterStrategyNumber);
compar = FunctionCall2Coll(cmpFn, colloid, minval, value);
/* smaller than the smallest value in this range */
if (DatumGetBool(compar))
break;
/* larger than the largest value in this range */
if (DatumGetBool(compar))
break;
/*
* We haven't managed to eliminate this range, so
* consider it matching.
*/
matches = true;
break;
}
case BTGreaterEqualStrategyNumber:
case BTGreaterStrategyNumber:
finfo = minmax_multi_get_strategy_procinfo(bdesc, attno, subtype,
key->sk_strategy);
/* last value from the array */
matches = DatumGetBool(FunctionCall2Coll(finfo, colloid, maxval, value));
break;
/* the range has to match all the scan keys */
matching &= matches;
/* once we find a non-matching key, we're done */
if (!matching)
break;
}
/*
* have we found a range matching all scan keys? if yes, we're done
*/
if (matching)
PG_RETURN_BOOL(true);
}
/*
* And now inspect the values. We don't bother with doing a binary search
* here, because we're dealing with serialized / fully compacted ranges,
* so there should be only very few values.
*/
for (i = 0; i < ranges->nvalues; i++)
{
Datum val = ranges->values[2 * ranges->nranges + i];
/* assume the range is matching, and we'll try to prove otherwise */
bool matching = true;
/* we've already dealt with NULL keys at the beginning */
if (key->sk_flags & SK_ISNULL)
continue;
attno = key->sk_attno;
subtype = key->sk_subtype;
value = key->sk_argument;
switch (key->sk_strategy)
{
case BTLessStrategyNumber:
case BTLessEqualStrategyNumber:
case BTEqualStrategyNumber:
case BTGreaterEqualStrategyNumber:
case BTGreaterStrategyNumber:
/* the range has to match all the scan keys */
matching &= matches;
/* once we find a non-matching key, we're done */
if (!matching)
break;
}
/* have we found a range matching all scan keys? if yes, we're done */
if (matching)
PG_RETURN_BOOL(true);
}
PG_RETURN_BOOL(false);
}
/*
* Given two BrinValues, update the first of them as a union of the summary
* values contained in both. The second one is untouched.
*/
Datum
brin_minmax_multi_union(PG_FUNCTION_ARGS)
{
BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
/*
* The distanceFn calls (which may internally call e.g. numeric_le) may
* allocate quite a bit of memory, and we must not leak it. Otherwise,
* we'd have problems e.g. when building indexes. So we create a local
* memory context and make sure we free the memory before leaving this
* function (not after every call).
*/
ctx = AllocSetContextCreate(CurrentMemoryContext,
"minmax-multi context",
ALLOCSET_DEFAULT_SIZES);
oldctx = MemoryContextSwitchTo(ctx);
/* allocate and fill */
eranges = (ExpandedRange *) palloc0(neranges * sizeof(ExpandedRange));
/* fill the expanded ranges with entries for the first range */
fill_expanded_ranges(eranges, ranges_a->nranges + ranges_a->nvalues,
ranges_a);
/* and now add combine ranges for the second range */
fill_expanded_ranges(&eranges[ranges_a->nranges + ranges_a->nvalues],
ranges_b->nranges + ranges_b->nvalues,
ranges_b);
/*
* We've loaded two different lists of expanded ranges, so some of them
* may be overlapping. So walk through them and merge them.
*/
neranges = merge_overlapping_ranges(cmpFn, colloid, eranges, neranges);
/* check that the combine ranges are correct (no overlaps, ordering) */
AssertCheckExpandedRanges(bdesc, colloid, attno, attr, eranges, neranges);
/*
* If needed, reduce some of the ranges.
*
* XXX This may be fairly expensive, so maybe we should do it only when
* it's actually needed (when we have too many ranges).
*/
/* build array of gap distances and sort them in ascending order */
distanceFn = minmax_multi_get_procinfo(bdesc, attno, PROCNUM_DISTANCE);
distances = build_distances(distanceFn, colloid, eranges, neranges);
/*
* See how many values would be needed to store the current ranges, and if
* needed combine as many of them to get below the threshold. The
* collapsed ranges will be stored as a single value.
*
* XXX This does not apply the load factor, as we don't expect to add more
* values to the range, so we prefer to keep as many ranges as possible.
*
* XXX Can the maxvalues be different in the two ranges? Perhaps we should
* use maximum of those?
*/
neranges = reduce_expanded_ranges(eranges, neranges, distances,
ranges_a->maxvalues,
cmpFn, colloid);
/* Is the result of reducing expanded ranges correct? */
AssertCheckExpandedRanges(bdesc, colloid, attno, attr, eranges, neranges);
/* update the first range summary */
store_expanded_ranges(ranges_a, eranges, neranges);
/* cleanup and update the serialized value */
pfree(serialized_a);
col_a->bv_values[0] = PointerGetDatum(brin_range_serialize(ranges_a));
PG_RETURN_VOID();
}
/*
* Cache and return minmax multi opclass support procedure
*
* Return the procedure corresponding to the given function support number
* or null if it does not exist.
*/
static FmgrInfo *
minmax_multi_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
{
MinmaxMultiOpaque *opaque;
uint16 basenum = procnum - PROCNUM_BASE;
/*
* We cache these in the opaque struct, to avoid repetitive syscache
* lookups.
*/
opaque = (MinmaxMultiOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
{
if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
procnum)))
fmgr_info_copy(&opaque->extra_procinfos[basenum],
index_getprocinfo(bdesc->bd_index, attno, procnum),
bdesc->bd_context);
else
ereport(ERROR,
errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg_internal("invalid opclass definition"),
errdetail_internal("The operator class is missing support function %d for column %d.",
procnum, attno));
}
return &opaque->extra_procinfos[basenum];
}
/*
* Cache and return the procedure for the given strategy.
*
* Note: this function mirrors minmax_multi_get_strategy_procinfo; see notes
* there. If changes are made here, see that function too.
*/
static FmgrInfo *
minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
uint16 strategynum)
{
MinmaxMultiOpaque *opaque;
/*
* We cache the procedures for the previous subtype in the opaque struct,
* to avoid repetitive syscache lookups. If the subtype changed,
* invalidate all the cached entries.
*/
if (opaque->cached_subtype != subtype)
{
uint16 i;
for (i = 1; i <= BTMaxStrategyNumber; i++)
opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
opaque->cached_subtype = subtype;
}
if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
{
Form_pg_attribute attr;
HeapTuple tuple;
Oid opfamily,
oprid;
/*
* brin_minmax_multi_summary_in
* - input routine for type brin_minmax_multi_summary.
*
* brin_minmax_multi_summary is only used internally to represent summaries
* in BRIN minmax-multi indexes, so it has no operations of its own, and we
* disallow input too.
*/
Datum
brin_minmax_multi_summary_in(PG_FUNCTION_ARGS)
{
/*
* brin_minmax_multi_summary stores the data in binary form and parsing
* text input is not needed, so disallow this.
*/
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot accept a value of type %s", "brin_minmax_multi_summary")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
/*
* brin_minmax_multi_summary_out
* - output routine for type brin_minmax_multi_summary.
*
* BRIN minmax-multi summaries are serialized into a bytea value, but we
* want to output something nicer humans can understand.
*/
Datum
brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
{
int i;
int idx;
SerializedRanges *ranges;
Ranges *ranges_deserialized;
StringInfoData str;
bool isvarlena;
Oid outfunc;
FmgrInfo fmgrinfo;
ArrayBuildState *astate_values = NULL;
/*
* Detoast to get value with full 4B header (can't be stored in a toast
* table, but can use 1B header).
*/
ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
/* lookup output func for the type */
getTypeOutputInfo(ranges->typid, &outfunc, &isvarlena);
fmgr_info(outfunc, &fmgrinfo);
/* deserialize the range info easy-to-process pieces */
ranges_deserialized = brin_range_deserialize(ranges->maxvalues, ranges);
val = makeArrayResult(astate_values, CurrentMemoryContext);
extval = OidOutputFunctionCall(typoutput, val);
appendStringInfo(&str, " values: %s", extval);
}
appendStringInfoChar(&str, '}');
PG_RETURN_CSTRING(str.data);
}
/*
* brin_minmax_multi_summary_recv
* - binary input routine for type brin_minmax_multi_summary.
*/
Datum
brin_minmax_multi_summary_recv(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot accept a value of type %s", "brin_minmax_multi_summary")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
/*
* brin_minmax_multi_summary_send
* - binary output routine for type brin_minmax_multi_summary.
*
* BRIN minmax-multi summaries are serialized in a bytea value (although
* the type is named differently), so let's just send that.
*/
Datum
brin_minmax_multi_summary_send(PG_FUNCTION_ARGS)
{
return byteasend(fcinfo);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.